Merge branch '15.0.beta3' of http://gitlab.zcorp.cc/easycorp/zentaopms into 15.0.beta3

This commit is contained in:
holan20180123
2021-06-07 08:16:06 +08:00
75 changed files with 467 additions and 423 deletions
+1 -1
View File
@@ -1 +1 @@
15.1
15.0.1
+1 -1
View File
@@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}}
if(!function_exists('getWebRoot')){function getWebRoot(){}}
/* 基本设置。Basic settings. */
$config->version = '15.1'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->version = '15.0.1'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP.
$config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time.
$config->timezone = 'Asia/Shanghai'; // 时区设置。 The time zone setting, for more see http://www.php.net/manual/en/timezones.php.
+2
View File
@@ -26,3 +26,5 @@ ALTER TABLE `zt_testtask` ADD `testreport` mediumint(8) unsigned NOT NULL AFTER
ALTER TABLE `zt_story` MODIFY COLUMN `reviewedDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00';
REPLACE INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'story', '', 'reviewRules', 'allpass');
UPDATE zt_project SET lifetime = '' WHERE lifetime = 'sprint';
+1 -3
View File
@@ -30,9 +30,6 @@ $config->action->objectNameFields['stakeholder'] = 'user';
$config->action->objectNameFields['budget'] = 'name';
$config->action->objectNameFields['job'] = 'name';
$config->action->objectNameFields['team'] = 'name';
$config->action->objectNameFields['opportunity'] = 'name';
$config->action->objectNameFields['trainplan'] = 'name';
$config->action->objectNameFields['gapanalysis'] = 'account';
$config->action->commonImgSize = 870;
@@ -47,3 +44,4 @@ $config->action->majorList['project'] = array('opened', 'edited');
$config->action->majorList['execution'] = array('opened', 'edited');
$config->action->needGetProjectType = 'build,task,bug,case,testcase,caselib,testtask,testsuite,testreport,doc,issue,release,risk,design,opportunity,trainplan,gapanalysis';
$config->action->needGetRelateField = ',story,productplan,release,task,build,bug,case,testtask,testreport,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,';
-6
View File
@@ -99,9 +99,6 @@ $lang->action->objectTypes['webhook'] = 'Webhook';
$lang->action->objectTypes['job'] = 'Job';
$lang->action->objectTypes['team'] = 'Team';
$lang->action->objectTypes['whitelist'] = 'Whitelist';
$lang->action->objectTypes['opportunity'] = 'Opportunity';
$lang->action->objectTypes['trainplan'] = 'Trainplan';
$lang->action->objectTypes['gapanalysis'] = 'Gap Analysis';
/* 用来描述操作历史记录。*/
$lang->action->desc = new stdclass();
@@ -487,9 +484,6 @@ $lang->action->label->risk = 'Risk|risk|view|riskID=%s';
$lang->action->label->issue = 'Issue|issue|view|issueID=%s';
$lang->action->label->design = 'Design|design|view|designID=%s';
$lang->action->label->stakeholder = 'Stakeholder|stakeholder|view|userID=%s';
$lang->action->label->opportunity = 'Opportunity|opportunity|view|opportunityID=%s';
$lang->action->label->trainplan = 'Trainplan|trainplan|view|trainplanID=%s';
$lang->action->label->gapanalysis = 'Gap Analysis|gapanalysis|view|gapanalysisID=%s';
/* Object type. */
$lang->action->search = new stdclass();
-6
View File
@@ -103,9 +103,6 @@ $lang->action->objectTypes['entry'] = 'Entry';
$lang->action->objectTypes['webhook'] = 'Webhook';
$lang->action->objectTypes['team'] = 'Team';
$lang->action->objectTypes['whitelist'] = 'Whitelist';
$lang->action->objectTypes['opportunity'] = 'Opportunity';
$lang->action->objectTypes['trainplan'] = 'Trainplan';
$lang->action->objectTypes['gapanalysis'] = 'Gap Analysis';
/* Used to describe operation history. */
$lang->action->desc = new stdclass();
@@ -490,9 +487,6 @@ $lang->action->label->risk = 'Risk|risk|view|riskID=%s';
$lang->action->label->issue = 'Issue|issue|view|issueID=%s';
$lang->action->label->design = 'Design|design|view|designID=%s';
$lang->action->label->stakeholder = 'Stakeholder|stakeholder|view|userID=%s';
$lang->action->label->opportunity = 'Opportunity|opportunity|view|opportunityID=%s';
$lang->action->label->trainplan = 'Trainplan|trainplan|view|trainplanID=%s';
$lang->action->label->gapanalysis = 'Gap Analysis|gapanalysis|view|gapanalysisID=%s';
/* Object type. */
$lang->action->search = new stdclass();
-6
View File
@@ -99,9 +99,6 @@ $lang->action->objectTypes['webhook'] = 'Webhook';
$lang->action->objectTypes['job'] = 'Job';
$lang->action->objectTypes['team'] = 'Team';
$lang->action->objectTypes['whitelist'] = 'Whitelist';
$lang->action->objectTypes['opportunity'] = 'Opportunity';
$lang->action->objectTypes['trainplan'] = 'Trainplan';
$lang->action->objectTypes['gapanalysis'] = 'Gap Analysis';
/* 用来描述操作历史记录。*/
$lang->action->desc = new stdclass();
@@ -499,9 +496,6 @@ $lang->action->label->risk = 'Risk|risk|view|riskID=%s';
$lang->action->label->issue = 'Issue|issue|view|issueID=%s';
$lang->action->label->design = 'Design|design|view|designID=%s';
$lang->action->label->stakeholder = 'Stakeholder|stakeholder|view|userID=%s';
$lang->action->label->opportunity = 'Opportunity|opportunity|view|opportunityID=%s';
$lang->action->label->trainplan = 'Trainplan|trainplan|view|trainplanID=%s';
$lang->action->label->gapanalysis = 'Gap Analysis|gapanalysis|view|gapanalysisID=%s';
/* Object type. */
$lang->action->search = new stdclass();
-6
View File
@@ -99,9 +99,6 @@ $lang->action->objectTypes['webhook'] = 'Webhook';
$lang->action->objectTypes['job'] = 'Job';
$lang->action->objectTypes['team'] = 'Team';
$lang->action->objectTypes['whitelist'] = 'Whitelist';
$lang->action->objectTypes['opportunity'] = 'Opportunity';
$lang->action->objectTypes['trainplan'] = 'Trainplan';
$lang->action->objectTypes['gapanalysis'] = 'Gap Analysis';
/* Used to describe operation history. */
$lang->action->desc = new stdclass();
@@ -487,9 +484,6 @@ $lang->action->label->risk = 'Risk|risk|view|riskID%s';
$lang->action->label->issue = 'Issue|issue|view|issueID=%s';
$lang->action->label->design = 'Design|design|view|designID=%s';
$lang->action->label->stakeholder = 'Stakeholder|stakeholder|view|userID=%s';
$lang->action->label->opportunity = 'Opportunity|opportunity|view|opportunityID=%s';
$lang->action->label->trainplan = 'Trainplan|trainplan|view|trainplanID=%s';
$lang->action->label->gapanalysis = 'Gap Analysis|gapanalysis|view|gapanalysisID=%s';
/* Object type. */
$lang->action->search = new stdclass();
-6
View File
@@ -103,9 +103,6 @@ $lang->action->objectTypes['entry'] = '应用';
$lang->action->objectTypes['webhook'] = 'Webhook';
$lang->action->objectTypes['team'] = '团队';
$lang->action->objectTypes['whitelist'] = '白名单';
$lang->action->objectTypes['opportunity'] = '机会';
$lang->action->objectTypes['trainplan'] = '培训计划';
$lang->action->objectTypes['gapanalysis'] = '能力差距分析';
/* 用来描述操作历史记录。*/
$lang->action->desc = new stdclass();
@@ -490,9 +487,6 @@ $lang->action->label->risk = '风险|risk|view|riskID=%s';
$lang->action->label->issue = '问题|issue|view|issueID=%s';
$lang->action->label->design = '设计|design|view|designID=%s';
$lang->action->label->stakeholder = '干系人|stakeholder|view|userID=%s';
$lang->action->label->opportunity = '机会|opportunity|view|opportunityID=%s';
$lang->action->label->trainplan = '培训计划|trainplan|view|trainplanID=%s';
$lang->action->label->gapanalysis = '能力差距分析|gapanalysis|view|gapanalysisID=%s';
/* Object type. */
$lang->action->search = new stdclass();
+37 -31
View File
@@ -49,6 +49,9 @@ class actionModel extends model
$action->date = helper::now();
$action->extra = $extra;
if($objectType == 'story' and $actionType !== 'reviewed' and strpos('reviewclosed,passreviewed,clarifyreviewed', $actionType) !== false) $action->actor = 'System';
/* Use purifier to process comment. Fix bug #2683. */
$action->comment = fixer::stripDataTags($comment);
@@ -60,36 +63,11 @@ class actionModel extends model
}
/* Get product project and execution for this object. */
$relation = $this->getRelatedFields($action->objectType, $objectID);
$action->product = $relation['product'];
$action->project = ($actionType == 'unlinkedfromproject' or $actionType == 'linked2project') ? (int)$extra : (int)$relation['project'];
$action->execution = ($actionType == 'unlinkedfromexecution' or $actionType == 'linked2execution') ? (int)$extra : (int)$relation['execution'];
$relation = $this->getRelatedFields($action->objectType, $objectID, $actionType, $extra);
$action->product = (int)$relation['product'];
$action->project = (int)$relation['project'];
$action->execution = (int)$relation['execution'];
if($objectType == 'story' and ($actionType == 'linked2build' or $actionType == 'unlinkedfrombuild'))
{
$build = $this->dao->select('project,execution')->from(TABLE_BUILD)->where('id')->eq((int)$extra)->fetch();
$action->project = $build->project;
$action->execution = $build->execution;
}
if($objectType == 'story' and $actionType == 'estimated')
{
$action->project = $this->dao->select('project')->from(TABLE_EXECUTION)->where('id')->eq((int)$extra)->fetch('project');
$action->execution = (int)$extra;
}
if($objectType == 'story' and $actionType !== 'reviewed' and strpos('reviewclosed,passreviewed,clarifyreviewed', $actionType) !== false) $action->actor = 'System';
if($objectType == 'case' and (strpos(',linked2testtask,unlinkedfromtesttask,assigned,run,', ',' . $actionType . ',') !== false) and (int)$extra)
{
$testtask = $this->dao->select('project,execution')->from(TABLE_TESTTASK)->where('id')->eq((int)$extra)->fetch();
$action->project = $testtask->project;
$action->execution = $testtask->execution;
}
if($objectType == 'whitelist' and $extra == 'product') $action->product = $objectID;
if($objectType == 'whitelist' and $extra == 'project') $action->project = $objectID;
if($objectType == 'whitelist' and ($extra == 'sprint' or $extra == 'stage')) $action->execution = $objectID;
$this->dao->insert(TABLE_ACTION)->data($action)->autoCheck()->exec();
$actionID = $this->dbh->lastInsertID();
@@ -168,10 +146,12 @@ class actionModel extends model
*
* @param string $objectType
* @param int $objectID
* @param string $actionType
* @param string $extra
* @access public
* @return array
*/
public function getRelatedFields($objectType, $objectID)
public function getRelatedFields($objectType, $objectID, $actionType = '', $extra = '')
{
$emptyRecord = array('product' => ',0,', 'project' => 0, 'execution' => 0);
@@ -203,7 +183,7 @@ class actionModel extends model
}
/* Only process these object types. */
if(strpos(',story,productplan,release,task,build,bug,case,testtask,testreport,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,', ",{$objectType},") !== false)
if(strpos($this->config->action->needGetRelateField, ",{$objectType},") !== false)
{
if(!isset($this->config->objectTables[$objectType])) return $emptyRecord;
@@ -241,6 +221,32 @@ class actionModel extends model
}
}
if($objectType == 'story' and ($actionType == 'linked2build' or $actionType == 'unlinkedfrombuild'))
{
$build = $this->dao->select('project,execution')->from(TABLE_BUILD)->where('id')->eq((int)$extra)->fetch();
$record->project = $build->project;
$record->execution = $build->execution;
}
if($objectType == 'story' and $actionType == 'estimated')
{
$record->project = $this->dao->select('project')->from(TABLE_EXECUTION)->where('id')->eq((int)$extra)->fetch('project');
$record->execution = (int)$extra;
}
if($objectType == 'case' and (strpos(',linked2testtask,unlinkedfromtesttask,assigned,run,', ',' . $actionType . ',') !== false) and (int)$extra)
{
$testtask = $this->dao->select('project,execution')->from(TABLE_TESTTASK)->where('id')->eq((int)$extra)->fetch();
$record->project = $testtask->project;
$record->execution = $testtask->execution;
}
if($objectType == 'whitelist' and $extra == 'product') $record->product = $objectID;
if($objectType == 'whitelist' and $extra == 'project') $record->project = $objectID;
if($objectType == 'whitelist' and ($extra == 'sprint' or $extra == 'stage')) $record->execution = $objectID;
if($actionType == 'unlinkedfromproject' or $actionType == 'linked2project') $record->project = (int)$extra ;
if($actionType == 'unlinkedfromexecution' or $actionType == 'linked2execution') $record->execution = (int)$extra;
if($record)
{
$record = (array)$record;
+18
View File
@@ -1,6 +1,24 @@
$(function()
{
loadModuleRelated();
resolution = $('#resolution').val();
if(resolution == 'fixed')
{
$('#resolvedBuildBox').change(function()
{
if($('#resolvedBuild').val() != oldResolvedBuild)
{
confirmResult = confirm(confirmUnlinkBuild);
if(!confirmResult)
{
$('#resolvedBuild').val(oldResolvedBuild);
$('#resolvedBuild').trigger("chosen:updated");
$('#resolvedBuild').chosen();
}
}
});
}
});
/**
+1
View File
@@ -181,6 +181,7 @@ $lang->bug->remindTask = 'This Bug has been converted to Task. Do you
$lang->bug->skipClose = 'Bug %s is not resolved. You cannot close it.';
$lang->bug->executionAccessDenied = "You access to the {$lang->executionCommon} to which this bug belongs is denied!";
$lang->bug->stepsNotEmpty = "The reproduction step cannot be empty.";
$lang->bug->confirmUnlinkBuild = "Replacing the solution version will disassociate the bug from the old version. Are you sure you want to disassociate the bug from %s?";
/* 模板。*/
$lang->bug->tplStep = "<p>[Steps]</p><br/>";
+1
View File
@@ -181,6 +181,7 @@ $lang->bug->remindTask = 'This bug has been converted to a task. Do y
$lang->bug->skipClose = 'Bug %s is active. You cannot close it.';
$lang->bug->executionAccessDenied = "You access to the {$lang->executionCommon} to which this bug belongs is denied!";
$lang->bug->stepsNotEmpty = "The reproduction step cannot be empty.";
$lang->bug->confirmUnlinkBuild = "Replacing the solution version will disassociate the bug from the old version. Are you sure you want to disassociate the bug from %s?";
/* Template. */
$lang->bug->tplStep = "<p>[Steps]</p><br/>";
+1
View File
@@ -181,6 +181,7 @@ $lang->bug->remindTask = 'Ce bug a été converti en tâche. Voulez-v
$lang->bug->skipClose = 'Bug %s est actif. Vous ne pouvez pas le fermer.';
$lang->bug->executionAccessDenied = "Votre accès à {$lang->executionCommon} auquel ce bug appartient est refusé !";
$lang->bug->stepsNotEmpty = "The reproduction step cannot be empty.";
$lang->bug->confirmUnlinkBuild = "Replacing the solution version will disassociate the bug from the old version. Are you sure you want to disassociate the bug from %s?";
/* 模板。*/
$lang->bug->tplStep = "<p>[Etape]</p><br/>";
+1
View File
@@ -181,6 +181,7 @@ $lang->bug->remindTask = 'Bug này đã chuyển thành một nhiệm
$lang->bug->skipClose = 'Bug %s được kích hoạt. Bạn không thể đóng nó.';
$lang->bug->executionAccessDenied = "Truy cập của bạn tới {$lang->executionCommon} mà Bug này sở hữu bị từ chối!";
$lang->bug->stepsNotEmpty = "The reproduction step cannot be empty.";
$lang->bug->confirmUnlinkBuild = "Replacing the solution version will disassociate the bug from the old version. Are you sure you want to disassociate the bug from %s?";
/* Template. */
$lang->bug->tplStep = "<p>[Các bước]</p><br/>";
+1
View File
@@ -181,6 +181,7 @@ $lang->bug->remindTask = '该Bug已经转化为任务,是否更新
$lang->bug->skipClose = 'Bug %s 不是已解决状态,不能关闭。';
$lang->bug->executionAccessDenied = "您无权访问该Bug所属的{$lang->executionCommon}";
$lang->bug->stepsNotEmpty = "重现步骤不能为空。";
$lang->bug->confirmUnlinkBuild = "更换解决版本将取消与旧版本的关联,您确定取消该bug与%s的关联吗?";
/* 模板。*/
$lang->bug->tplStep = "<p>[步骤]</p><br/>";
+1
View File
@@ -181,6 +181,7 @@ $lang->bug->remindTask = '該Bug已經轉化為任務,是否更新
$lang->bug->skipClose = 'Bug %s 不是已解決狀態,不能關閉。';
$lang->bug->executionAccessDenied = "您無權訪問該Bug所屬的{$lang->executionCommon}";
$lang->bug->stepsNotEmpty = "重現步驟不能為空。";
$lang->bug->confirmUnlinkBuild = "更換解决版本將取消與舊版本的關聯,您確定取消該bug與%s的關聯嗎?";
/* 模板。*/
$lang->bug->tplStep = "<p>[步驟]</p><br/>";
+2 -1
View File
@@ -25,6 +25,7 @@ js::set('oldTaskID' , $bug->task);
js::set('oldOpenedBuild' , $bug->openedBuild);
js::set('oldResolvedBuild' , $bug->resolvedBuild);
js::set('systemMode' , $config->systemMode);
js::set('confirmUnlinkBuild' , sprintf($lang->bug->confirmUnlinkBuild, zget($resolvedBuilds, $bug->resolvedBuild)));
?>
<div class='main-content' id='mainContent'>
@@ -172,7 +173,7 @@ js::set('systemMode' , $config->systemMode);
</table>
</div>
<div class='detail'>
<div class='detail-title'><?php echo $lang->bug->legendExecStoryTask;?></div>
<div class='detail-title'><?php echo $config->systemMode == 'class' ? $lang->bug->legendExecStoryTask : $lang->bug->legendPRJExecStoryTask;?></div>
<table class='table table-form'>
<tbody>
<?php if($config->systemMode == 'new'):?>
+13
View File
@@ -420,6 +420,19 @@ class custom extends control
$this->display();
}
/**
* Set story concept.
*
* @param int $key
* @access public
* @return void
*/
public function setDefaultConcept($key = 0)
{
$this->loadModel('setting')->setItem('system.custom.URSR', $key);
die(js::reload('parent'));
}
/**
* Delete story concept.
*
+1
View File
@@ -38,6 +38,7 @@ $lang->custom->waterfallCommon = "Waterfall";
$lang->custom->buildin = "Buildin";
$lang->custom->editStoryConcept = "Edit Story Concept";
$lang->custom->setStoryConcept = "Set Story Concept";
$lang->custom->setDefaultConcept = "Set Default Concept";
$lang->custom->browseStoryConcept = "List of story concepts";
$lang->custom->deleteStoryConcept = "Delete story Concept";
$lang->custom->URConcept = "UR Concept";
+1
View File
@@ -38,6 +38,7 @@ $lang->custom->waterfallCommon = "Waterfall";
$lang->custom->buildin = "Buildin";
$lang->custom->editStoryConcept = "Edit Story Concept";
$lang->custom->setStoryConcept = "Set Story Concept";
$lang->custom->setDefaultConcept = "Set Default Concept";
$lang->custom->browseStoryConcept = "List of story concepts";
$lang->custom->deleteStoryConcept = "Delete story Concept";
$lang->custom->URConcept = "UR Concept";
+1
View File
@@ -38,6 +38,7 @@ $lang->custom->waterfallCommon = "Waterfall";
$lang->custom->buildin = "Buildin";
$lang->custom->editStoryConcept = "Edit Story Concept";
$lang->custom->setStoryConcept = "Set Story Concept";
$lang->custom->setDefaultConcept = "Set Default Concept";
$lang->custom->browseStoryConcept = "List of story concepts";
$lang->custom->deleteStoryConcept = "Delete story Concept";
$lang->custom->URConcept = "UR Concept";
+1
View File
@@ -38,6 +38,7 @@ $lang->custom->waterfallCommon = "Waterfall";
$lang->custom->buildin = "Buildin";
$lang->custom->editStoryConcept = "Edit Story Concept";
$lang->custom->setStoryConcept = "Set Story Concept";
$lang->custom->setDefaultConcept = "Set Default Concept";
$lang->custom->browseStoryConcept = "List of story concepts";
$lang->custom->deleteStoryConcept = "Delete story Concept";
$lang->custom->URConcept = "UR Concept";
+1
View File
@@ -38,6 +38,7 @@ $lang->custom->waterfallCommon = "瀑布";
$lang->custom->buildin = "系统内置";
$lang->custom->editStoryConcept = "编辑需求概念";
$lang->custom->setStoryConcept = "设置需求概念";
$lang->custom->setDefaultConcept = "设置默认概念";
$lang->custom->browseStoryConcept = "需求概念列表";
$lang->custom->deleteStoryConcept = "删除需求概念";
$lang->custom->URConcept = "用需概念";
+1
View File
@@ -38,6 +38,7 @@ $lang->custom->waterfallCommon = "瀑布";
$lang->custom->buildin = "系統內置";
$lang->custom->editStoryConcept = "編輯需求概念";
$lang->custom->setStoryConcept = "設置需求概念";
$lang->custom->setDefaultConcept = "設置預設概念";
$lang->custom->browseStoryConcept = "需求概念列表";
$lang->custom->deleteStoryConcept = "刪除需求概念";
$lang->custom->URConcept = "用需概念";
@@ -23,6 +23,9 @@
<table class='table table-form'>
<thead>
<tr>
<?php if(common::hasPriv('custom', 'setDefaultConcept')):?>
<th class='w-60px text-center'><?php echo $lang->custom->default;?> </th>
<?php endif;?>
<?php if($this->config->URAndSR):?>
<th class='text-left'><?php echo $lang->custom->URConcept;?> </th>
<?php endif;?>
@@ -33,6 +36,9 @@
<tbody>
<?php foreach($URSRList as $key => $URSR):?>
<tr>
<?php if(common::hasPriv('custom', 'setDefaultConcept')):?>
<td class='text-center'><input type="radio" name='default' value='<?php echo $key;?>' <?php if($key == $config->custom->URSR) echo 'checked';?>></td>
<?php endif;?>
<?php if($this->config->URAndSR):?>
<td class='text-left'><?php echo $URSR['URName'];?></td>
<?php endif;?>
@@ -48,4 +54,11 @@
</table>
</div>
</div>
<script>
$("input[name='default']").change(function()
{
var checked = $(this).val();
hiddenwin.location.href = createLink('custom', 'setDefaultConcept', 'key=' + checked);
})
</script>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -2759,7 +2759,7 @@ class execution extends control
if($this->app->viewType == 'mhtml')
{
$executionID = $this->execution->saveState(0, $this->executions);
$this->execution->setMenu($productID);
$this->execution->setMenu($executionID);
}
/* Load pager and get tasks. */
+27 -42
View File
@@ -293,31 +293,23 @@ class executionModel extends model
return false;
}
if($this->config->systemMode == 'new') $this->checkBeginAndEndDate($_POST['project'], $_POST['begin'], $_POST['end']);
if(dao::isError()) return false;
/* Determine whether to add a sprint or a stage according to the model of the execution. */
$project = $this->loadModel('project')->getByID($_POST['project']);
if($_POST['begin'] < $project->begin and $this->config->systemMode == 'new')
{
dao::$errors['begin'] = sprintf($this->lang->execution->errorBegin, $project->begin);
return false;
}
$type = zget($this->config->execution->modelList, $project->model, 'sprint');
if($_POST['end'] > $project->end and $this->config->systemMode == 'new')
/* If the execution model is a stage, determine whether the product is linked. */
if($type == 'stage' and empty($this->post->products[0]))
{
dao::$errors['end'] = sprintf($this->lang->execution->errorEnd, $project->end);
dao::$errors['message'][] = $this->lang->execution->noLinkProduct;
return false;
}
$type = zget($this->config->execution->modelList, $project->model, 'sprint');
$this->config->execution->create->requiredFields .= ',project';
}
/* If the execution model is a stage, determine whether the product is linked. */
if($type == 'stage' and empty($this->post->products[0]))
{
dao::$errors['message'][] = $this->lang->execution->noLinkProduct;
return false;
}
/* Get the data from the post. */
$sprint = fixer::input('post')
->setDefault('status', 'wait')
@@ -472,18 +464,9 @@ class executionModel extends model
->remove('products, branch, uid, plans')
->get();
$project = $this->dao->select('begin, end')->from(TABLE_PROJECT)->where('id')->eq($oldExecution->project)->fetch();
if($execution->begin < $project->begin and $this->config->systemMode == 'new')
{
dao::$errors['begin'] = sprintf($this->lang->execution->errorBegin, $project->begin);
return false;
}
if($this->config->systemMode == 'new') $this->checkBeginAndEndDate($oldExecution->project, $execution->begin, $execution->end);
if(dao::isError()) return false;
if($execution->end > $project->end and $this->config->systemMode == 'new')
{
dao::$errors['end'] = sprintf($this->lang->execution->errorEnd, $project->end);
return false;
}
/* Child stage inherits parent stage permissions. */
if(!isset($execution->acl)) $execution->acl = $oldExecution->acl;
if($execution->acl == 'open') $execution->whitelist = '';
@@ -500,10 +483,6 @@ class executionModel extends model
if(!empty($execution->realBegan) and !empty($execution->realEnd)) $execution->realDuration = $this->loadModel('programplan')->getDuration($execution->realBegan, $execution->realEnd);
}
/* Replace required language. */
$this->lang->project->name = $this->lang->execution->name;
$this->lang->project->code = $this->lang->execution->code;
/* Update data. */
$this->dao->update(TABLE_EXECUTION)->data($execution)
->autoCheck($skipFields = 'begin,end')
@@ -706,18 +685,8 @@ class executionModel extends model
->remove('comment')
->get();
$project = $this->dao->select('begin, end')->from(TABLE_PROJECT)->where('id')->eq($oldExecution->project)->fetch();
if($execution->begin < $project->begin and $this->config->systemMode == 'new')
{
dao::$errors['begin'] = sprintf($this->lang->execution->errorBegin, $project->begin);
return false;
}
if($execution->end > $project->end and $this->config->systemMode == 'new')
{
dao::$errors['end'] = sprintf($this->lang->execution->errorEnd, $project->end);
return false;
}
if($this->config->systemMode == 'new') $this->checkBeginAndEndDate($oldExecution->project, $execution->begin, $execution->end);
if(dao::isError()) return false;
$this->dao->update(TABLE_EXECUTION)->data($execution)
->autoCheck()
@@ -902,6 +871,22 @@ class executionModel extends model
}
}
/**
* Check begin and end date.
*
* @param string $begin
* @param string $end
* @access public
* @return void
*/
public function checkBeginAndEndDate($projectID, $begin, $end)
{
$project = $this->loadModel('project')->getByID($projectID);
if($begin < $project->begin) dao::$errors['begin'] = sprintf($this->lang->execution->errorBegin, $project->begin);
if($end > $project->end) dao::$errors['end'] = sprintf($this->lang->execution->errorEnd, $project->end);
}
/*
* Get execution switcher.
*
+4 -1
View File
@@ -1081,6 +1081,7 @@ $lang->resource->custom->timezone = 'timezone';
$lang->resource->custom->setStoryConcept = 'setStoryConcept';
$lang->resource->custom->editStoryConcept = 'editStoryConcept';
$lang->resource->custom->browseStoryConcept = 'browseStoryConcept';
$lang->resource->custom->setDefaultConcept = 'setDefaultConcept';
$lang->resource->custom->deleteStoryConcept = 'deleteStoryConcept';
$lang->custom->methodOrder[5] = 'index';
@@ -1095,7 +1096,8 @@ $lang->custom->methodOrder[45] = 'timezone';
$lang->custom->methodOrder[50] = 'setStoryConcept';
$lang->custom->methodOrder[55] = 'editStoryConcept';
$lang->custom->methodOrder[60] = 'browseStoryConcept';
$lang->custom->methodOrder[65] = 'deleteStoryConcept';
$lang->custom->methodOrder[65] = 'setDefaultConcept';
$lang->custom->methodOrder[70] = 'deleteStoryConcept';
$lang->resource->datatable = new stdclass();
$lang->resource->datatable->setGlobal = 'setGlobal';
@@ -1806,6 +1808,7 @@ $lang->changelog['15.0.rc1'][] = 'user-execution';
$lang->changelog['15.0.rc1'][] = 'custom-setStoryConcept';
$lang->changelog['15.0.rc1'][] = 'custom-editStoryConcept';
$lang->changelog['15.0.rc1'][] = 'custom-browseStoryConcept';
$lang->changelog['15.0.rc1'][] = 'custom-setDefaultConcept';
$lang->changelog['15.0.rc1'][] = 'custom-deleteStoryConcept';
$lang->changelog['15.0.rc1'][] = 'search-index';
$lang->changelog['15.0.rc1'][] = 'search-buildIndex';
+2 -2
View File
@@ -26,8 +26,8 @@
}
else
{
echo html::a(inlink($app->rawMethod, "mode=requirement&type=assignedTo&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->assignedToMe}</span>" . ($type == 'assignedTo' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedTo' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=reviewBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->reviewByMe}</span>" . ($type == 'reviewBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=assignedTo&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->assignedToMe}</span>" . ($type == 'assignedTo' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedTo' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=reviewBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->reviewByMe}</span>" . ($type == 'reviewBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewBy' ? ' btn-active-text' : '') . "'");
}
?>
</div>
+2 -2
View File
@@ -26,8 +26,8 @@
}
else
{
echo html::a(inlink($app->rawMethod, "mode=story&type=assignedTo&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->assignedToMe}</span>" . ($type == 'assignedTo' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedTo' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=story&type=reviewBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->reviewByMe}</span>" . ($type == 'reviewBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=story&type=assignedTo&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->assignedToMe}</span>" . ($type == 'assignedTo' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedTo' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=story&type=reviewBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->reviewByMe}</span>" . ($type == 'reviewBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewBy' ? ' btn-active-text' : '') . "'");
}
?>
</div>
+10 -4
View File
@@ -69,9 +69,15 @@ class personnelModel extends model
*/
public function canViewProgram($programID, $account)
{
if(strpos($this->app->company->admins, ",{$account},") !== false) return true;
static $groupAcl = array();
if(empty($groupAcl)) $groupAcl = $this->dao->select('id,acl')->from(TABLE_GROUP)->fetchPairs();
if($this->app->user->admin) return true;
static $groupAcl = array();
static $groupInfo = array();
if(empty($groupAcl))
{
$groupAcl = $this->dao->select('id,acl')->from(TABLE_GROUP)->fetchPairs();
foreach($groupAcl as $groupID => $group) $groupInfo[$groupID] = json_decode($groupAcl[$groupID]);
}
static $userGroups = array();
if(empty($userGroups)) $userGroups = $this->dao->select('*')->from(TABLE_USERGROUP)->fetchGroup('account', 'group');
@@ -81,7 +87,7 @@ class personnelModel extends model
{
foreach($userGroups[$account] as $groupID => $userGroup)
{
$group = json_decode($groupAcl[$groupID]);
$group = $groupInfo[$groupID];
if(!isset($group->programs))
{
$programRight = true;
+2
View File
@@ -230,6 +230,8 @@ class product extends control
/* Process the sql, get the conditon partion, save it to session. */
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', $browseType != 'bysearch' && $this->app->rawModule != 'projectstory');
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
/* Get related tasks, bugs, cases count of each story. */
$storyIdList = array();
foreach($stories as $story)
+21 -2
View File
@@ -18,9 +18,10 @@ class productModel extends model
*
* @param array $products
* @param int $productID
* @param string $currentModule
* @param string $currentMethod
* @param string $extra
* @param string $branch
* @param string $module
* @param string $moduleType
*
* @access public
* @return string
@@ -314,6 +315,24 @@ class productModel extends model
return $pairs;
}
/**
* Get product pairs by project model.
*
* @param string $model all|scrum|waterfall
* @access public
* @return array
*/
public function getPairsByProjectModel($model)
{
return $this->dao->select('t3.id as id, t3.name as name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t1.product=t3.id')
->where('t3.deleted')->eq(0)
->beginIF(!$this->app->user->admin)->andWhere('t3.id')->in($this->app->user->view->products)->fi()
->beginIF($model != 'all')->andWhere('t2.model')->eq($model)
->fetchPairs('id', 'name');
}
/**
* Get products by project.
*
+4
View File
@@ -165,6 +165,10 @@ $lang->project->aclList['open'] = "Open (accessible with project view permiss
$lang->project->acls['private'] = 'Private';
$lang->project->acls['open'] = "Open";
$lang->project->subAclList['private'] = "Private (Only the project leader, team members and stakeholders can access)";
$lang->project->subAclList['open'] = "Open (accessible with project view permissions)";
$lang->project->subAclList['program'] = 'Open in the program (all upper-level program team leaders and stakeholders, the project leader, team members and stakeholders can access)';
$lang->project->authList['extend'] = 'Inherit (system privilege and project privilege)';
$lang->project->authList['reset'] = 'Reset (project privilege only)';
+4
View File
@@ -170,6 +170,10 @@ $lang->project->aclList['open'] = "Open (accessible with project view permiss
$lang->project->acls['private'] = 'Private';
$lang->project->acls['open'] = 'Open';
$lang->project->subAclList['private'] = "Private (Only the project leader, team members and stakeholders can access)";
$lang->project->subAclList['open'] = "Open (accessible with project view permissions)";
$lang->project->subAclList['program'] = 'Open in the program (all upper-level program team leaders and stakeholders, the project leader, team members and stakeholders can access)';
$lang->project->authList['extend'] = 'Inherit (system privilege and project privilege)';
$lang->project->authList['reset'] = 'Reset (project privilege only)';
+4
View File
@@ -165,6 +165,10 @@ $lang->project->aclList['open'] = "Open (accessible with project view permiss
$lang->project->acls['private'] = 'Private';
$lang->project->acls['open'] = "Open";
$lang->project->subAclList['private'] = "Private (Only the project leader, team members and stakeholders can access)";
$lang->project->subAclList['open'] = "Open (accessible with project view permissions)";
$lang->project->subAclList['program'] = 'Open in the program (all upper-level program team leaders and stakeholders, the project leader, team members and stakeholders can access)';
$lang->project->authList['extend'] = 'Inherit (system privilege and project privilege)';
$lang->project->authList['reset'] = 'Reset (project privilege only)';
+4
View File
@@ -165,6 +165,10 @@ $lang->project->aclList['open'] = "Open (accessible with project view permiss
$lang->project->acls['private'] = 'Private';
$lang->project->acls['open'] = "Open";
$lang->project->subAclList['private'] = "Private (Only the project leader, team members and stakeholders can access)";
$lang->project->subAclList['open'] = "Open (accessible with project view permissions)";
$lang->project->subAclList['program'] = 'Open in the program (all upper-level program team leaders and stakeholders, the project leader, team members and stakeholders can access)';
$lang->project->authList['extend'] = 'Inherit (system privilege and project privilege)';
$lang->project->authList['reset'] = 'Reset (project privilege only)';
+4
View File
@@ -170,6 +170,10 @@ $lang->project->aclList['open'] = "公开 (有项目视图权限即可访问)
$lang->project->acls['private'] = '私有';
$lang->project->acls['open'] = '公开';
$lang->project->subAclList['private'] = "私有 (只有本项目负责人、团队成员和干系人可访问)";
$lang->project->subAclList['open'] = "公开 (有项目视图权限即可访问)";
$lang->project->subAclList['program'] = '项目集内公开(所有上级项目集负责人和干系人、本项目负责人、团队成员和干系人可访问)';
$lang->project->authList['extend'] = '继承 (取系统权限与项目权限的合集)';
$lang->project->authList['reset'] = '重新定义 (只取项目权限)';
+1 -1
View File
@@ -200,6 +200,6 @@
<?php echo nl2br(html::radio('acl', $lang->project->aclList, $acl, "onclick='setWhite(this.value);'", 'block'));?>
</div>
<div id='programAcl' class='hidden'>
<?php echo nl2br(html::radio('acl', $lang->program->subAclList, $acl, "onclick='setWhite(this.value);'", 'block'));?>
<?php echo nl2br(html::radio('acl', $lang->project->subAclList, $acl, "onclick='setWhite(this.value);'", 'block'));?>
</div>
<?php include '../../common/view/footer.html.php';?>
+3 -3
View File
@@ -21,7 +21,7 @@
<?php js::set('unmodifiableProducts', $unmodifiableProducts)?>
<?php js::set('tip', $lang->project->notAllowRemoveProducts);?>
<?php js::set('linkedProjectsTip', $lang->project->linkedProjectsTip);?>
<?php $aclList = $project->parent ? $lang->program->subAclList : $lang->project->aclList;?>
<?php $aclList = $project->parent ? $lang->project->subAclList : $lang->project->aclList;?>
<?php $requiredFields = $config->project->edit->requiredFields;?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
@@ -160,7 +160,7 @@
echo html::hidden('model', $project->model);
echo html::submitButton();
$browseLink = $this->session->projectList ? $this->session->projectList : $this->createLink('project', 'browse');
echo isonlybody() ? html::backButton() : html::a($browseLink, $lang->goback, '', 'class="btn btn-back btn-wide"');
if(!isonlybody()) echo html::a($browseLink, $lang->goback, '', 'class="btn btn-back btn-wide"');
?>
</td>
</tr>
@@ -172,7 +172,7 @@
<?php echo nl2br(html::radio('acl', $lang->project->aclList, $project->acl == 'project' ? 'private' : 'open', "onclick='setWhite(this.value);'", 'block'));?>
</div>
<div id='programAcl' class='hidden'>
<?php echo nl2br(html::radio('acl', $lang->program->subAclList, $project->acl, "onclick='setWhite(this.value);'", 'block'));?>
<?php echo nl2br(html::radio('acl', $lang->project->subAclList, $project->acl, "onclick='setWhite(this.value);'", 'block'));?>
</div>
<div class="modal fade" id="promptBox">
<div class="modal-dialog mw-600px">
+18 -20
View File
@@ -610,7 +610,8 @@ class story extends control
$users = $this->user->getPairs('pofirst|nodeleted|noclosed', "$story->assignedTo,$story->openedBy,$story->closedBy");
$isShowReviewer = true;
$reviewerList = $this->dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($story->id)->andWhere('version')->eq($story->version)->fetchPairs('reviewer');
$reviewerList = $this->story->getReviewerPairs($story->id, $story->version);
$reviewerList = array_keys($reviewerList);
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
if(!array_diff($reviewerList, $reviewedBy)) $isShowReviewer = false;
@@ -835,7 +836,7 @@ class story extends control
$this->app->loadLang('execution');
$story = $this->story->getById($storyID);
$reviewer = $this->dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($story->version)->fetchAll('reviewer');
$reviewer = $this->story->getReviewerPairs($storyID, $story->version);
/* Assign. */
$this->view->title = $this->lang->story->change . "STORY" . $this->lang->colon . $this->view->story->title;
@@ -897,6 +898,8 @@ class story extends control
$story = $this->story->getById($storyID, $version, true);
if(!$story) die(js::error($this->lang->notFound) . js::locate('back'));
$story = $this->story->mergeReviewer($story, true);
$this->story->replaceURLang($story->type);
$story->files = $this->loadModel('file')->getByObject('story', $storyID);
@@ -907,8 +910,8 @@ class story extends control
$cases = $this->dao->select('id,title')->from(TABLE_CASE)->where('story')->eq($storyID)->andWhere('deleted')->eq(0)->fetchAll();
$modulePath = $this->tree->getParents($story->module);
$storyModule = empty($story->module) ? '' : $this->tree->getById($story->module);
$users = $this->user->getPairs('noletter');
$reviewers = $this->dao->select('reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($story->version)->fetchAll('reviewer');
$reviewers = $this->story->getReviewerPairs($storyID, $story->version);
/* Set the menu. */
$from = $this->app->openApp;
@@ -945,7 +948,7 @@ class story extends control
$this->view->cases = $cases;
$this->view->story = $story;
$this->view->track = $this->story->getTrackByID($story->id);
$this->view->users = $users;
$this->view->users = $this->user->getPairs('noletter');
$this->view->reviewers = array_keys($reviewers);
$this->view->relations = $this->story->getStoryRelation($story->id, $story->type);
$this->view->executions = $this->execution->getPairs(0, 'all', 'nocode');
@@ -1012,15 +1015,8 @@ class story extends control
if($changes)
{
$result = $this->post->result;
$reasonParam = $result == 'reject' ? ',' . $this->post->closedReason : '';
$story = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch();
$reviewers = $this->dao->select('reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($story->version)->fetchAll('reviewer');
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
$actionID = $this->action->create('story', $storyID, 'Reviewed', $this->post->comment, ucfirst($result) . $reasonParam);
if($story->status == 'closed') $actionID = $this->action->create('story', $storyID, 'ReviewClosed');
if($story->status == 'active') $actionID = $this->action->create('story', $storyID, 'PassReviewed');
if(!array_diff(array_keys($reviewers), $reviewedBy) and ($story->status == 'draft' || $story->status == 'changed')) $actionID = $this->action->create('story', $storyID, 'ClarifyReviewed');
$story = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch();
$actionID = $this->story->recordReviewAction($story, $this->post->result, $this->post->closedReason);
$this->action->logHistory($actionID, $changes);
}
@@ -1028,7 +1024,6 @@ class story extends control
if(isonlybody()) die(js::reload('parent.parent'));
$module = $from == 'project' ? 'projectstory' : 'story';
die(js::locate($this->createLink($module, 'view', "storyID=$storyID"), 'parent'));
}
@@ -1055,10 +1050,13 @@ class story extends control
}
/* Set the review result options. */
$reviewers = $this->dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($story->version)->fetchAll('reviewer');
$reviewers = $this->story->getReviewerPairs($storyID, $story->version);
$this->lang->story->resultList = $this->lang->story->reviewResultList;
if($story->status == 'draft' and $story->version == 1) unset($this->lang->story->resultList['revert']);
if($story->status == 'changed') unset($this->lang->story->resultList['reject']);
if(count($reviewers) > 1) unset($this->lang->story->resultList['revert']);
$this->view->title = $this->lang->story->review . "STORY" . $this->lang->colon . $story->title;
@@ -1947,20 +1945,20 @@ class story extends control
if(!empty($children))
{
$reorderStory = array();
$reorderStories = array();
foreach($stories as $story)
{
$reorderStory[$story->id] = $story;
$reorderStories[$story->id] = $story;
if(isset($children[$story->id]))
{
foreach($children[$story->id] as $childrenID => $childrenStory)
{
$reorderStory[$childrenID] = $childrenStory;
$reorderStories[$childrenID] = $childrenStory;
}
}
unset($stories[$story->id]);
}
$stories = $reorderStory;
$stories = $reorderStories;
}
}
+2 -2
View File
@@ -5,11 +5,11 @@ $(function()
$('#reviewer').attr('disabled', $(this).is(':checked') ? 'disabled' : null).trigger('chosen:updated');
if($(this).is(':checked'))
{
$('#reviewerTd').removeClass('required');
$('#reviewerBox').removeClass('required');
}
else
{
$('#reviewerTd').addClass('required');
$('#reviewerBox').addClass('required');
}
getStatus('create', "product=" + $('#product').val() + ",execution=" + executionID + ",needNotReview=" + ($(this).prop('checked') ? 1 : 0));
+109 -33
View File
@@ -356,7 +356,7 @@ class storyModel extends model
foreach($_POST['needReview'] as $index => $value)
{
if($_POST['title'][$index] and isset($_POST['reviewer'][$index])) $_POST['reviewer'][$index] = array_filter($_POST['reviewer'][$index]);
if($_POST['title'][$index] and $value and !isset($_POST['reviewer'][$index]))
if($_POST['title'][$index] and $value and empty($_POST['reviewer'][$index]))
{
dao::$errors[] = $this->lang->story->errorEmptyReviewedBy;
return false;
@@ -409,8 +409,8 @@ class storyModel extends model
$story->category = $stories->category[$i];
$story->pri = $stories->pri[$i];
$story->estimate = $stories->estimate[$i];
$story->status = ($this->app->openApp == project || $this->app->openApp == execution ||($stories->needReview[$i] == 0 and !$forceReview)) ? 'active' : 'draft';
$story->stage = ($this->app->openApp == project || $this->app->openApp == execution) ? 'projected' : 'wait';
$story->status = ($this->app->openApp == project or $this->app->openApp == execution or ($stories->needReview[$i] == 0 and !$forceReview)) ? 'active' : 'draft';
$story->stage = ($this->app->openApp == project or $this->app->openApp == execution) ? 'projected' : 'wait';
$story->keywords = $stories->keywords[$i];
$story->sourceNote = $stories->sourceNote[$i];
$story->product = $productID;
@@ -633,14 +633,14 @@ class storyModel extends model
unset($story->spec);
unset($oldStory->spec);
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->notin(implode(',', $_POST['reviewer']))->exec();
if(isset($_POST['reviewer'])) $this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->notin(implode(',', $_POST['reviewer']))->exec();
}
/* Update the reviewer. */
$oldReviewerList = $this->dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->fetchPairs('reviewer');
$oldReviewerList = $this->getReviewerPairs($storyID, $oldStory->version);
foreach($_POST['reviewer'] as $reviewer)
{
if(!$specChanged and in_array($reviewer, $oldReviewerList)) continue;
if(!$specChanged and in_array($reviewer, array_keys($oldReviewerList))) continue;
$reviewData = new stdclass();
$reviewData->story = $storyID;
@@ -808,17 +808,16 @@ class storyModel extends model
if(empty($oldStory->plan) or empty($story->plan)) $this->setStage($storyID); // Set new stage for this story.
}
$_POST['reviewer'] = array_filter($_POST['reviewer']);
$oldReviewer = $this->dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->fetchAll('reviewer');
$_POST['reviewer'] = array_filter($_POST['reviewer']);
$oldReviewer = $this->getReviewerPairs($storyID, $oldStory->version);
$oldStory->reviewers = implode(',', array_keys($oldReviewer));
$story->reviewers = implode(',', $_POST['reviewer']);
/* Update story reviewer. */
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->notin(implode(',', $_POST['reviewer']))->exec();
$existedReviewer = $this->dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->fetchPairs('reviewer');
foreach($_POST['reviewer'] as $reviewer)
{
if(in_array($reviewer, $existedReviewer)) continue;
if(in_array($reviewer, array_keys($oldReviewer))) continue;
$reviewData = new stdclass();
$reviewData->story = $storyID;
@@ -1192,6 +1191,7 @@ class storyModel extends model
$story = fixer::input('post')
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
->setDefault('status', $oldStory->status)
->setIF($this->post->result == 'revert', 'version', $this->post->preVersion)
->setIF($this->post->result == 'revert', 'status', 'active')
->removeIF($this->post->result != 'reject', 'closedReason, duplicateStory, childStories')
@@ -1208,11 +1208,12 @@ class storyModel extends model
$this->dao->update(TABLE_STORYREVIEW)->set('result')->eq($this->post->result)->set('reviewDate')->eq($now)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->eq($this->app->user->account)->exec();
/* Update the story status by review rules. */
$reviewerList = $this->dao->select('reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->fetchPairs('reviewer', 'result');
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
$reviewerList = $this->getReviewerPairs($storyID, $oldStory->version);
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
if(!array_diff(array_keys($reviewerList), $reviewedBy))
{
$story->status = $this->setStatusByReviewRules($reviewerList);
$status = $this->setStatusByReviewRules($reviewerList);
$story->status = $status ? $status : $oldStory->status;
if($story->status == 'closed')
{
$story->closedBy = $this->app->user->account;
@@ -1261,29 +1262,35 @@ class storyModel extends model
$actions = array();
$this->loadModel('action');
$oldStories = $this->getByList($storyIdList);
$oldStories = $this->getByList($storyIdList);
$hasResult = $this->dao->select('story,version,result')->from(TABLE_STORYREVIEW)->where('story')->in($storyIdList)->andWhere('reviewer')->eq($this->app->user->account)->andWhere('result')->ne('')->orderBy('version')->fetchAll('story');
$reviewerList = $this->dao->select('story,reviewer,result,version')->from(TABLE_STORYREVIEW)->where('story')->in($storyIdList)->orderBy('version')->fetchGroup('story', 'reviewer');
foreach($storyIdList as $storyID)
{
$oldStory = $oldStories[$storyID];
if($oldStory->status != 'draft' and $oldStory->status != 'changed') continue;
$hasResult = $this->dao->select('result')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->eq($this->app->user->account)->fetch('result');
if($hasResult) continue;
if(isset($hasResult[$storyID])) continue;
$story = new stdClass();
$story->reviewedDate = $now;
$story->lastEditedBy = $this->app->user->account;
$story->lastEditedDate = $now;
$story->reviewedBy = $oldStory->reviewedBy . ',' . $this->app->user->account;
$story->status = $oldStory->status;
$this->dao->update(TABLE_STORYREVIEW)->set('result')->eq($result)->set('reviewDate')->eq($now)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->eq($this->app->user->account)->exec();
/* Update the story status by review rules. */
$reviewerList = $this->dao->select('reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->fetchPairs('reviewer', 'result');
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
if(!array_diff(array_keys($reviewerList), $reviewedBy))
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
if(!array_diff(array_keys($reviewerList[$storyID]), $reviewedBy))
{
$status = $this->setStatusByReviewRules($reviewerList);
$reviewerPairs = array();
foreach($reviewerList[$storyID] as $reviewer => $reviewInfo) $reviewerPairs[$reviewer] = $reviewInfo->result;
$reviewerPairs[$this->app->user->account] = $result;
$status = $this->setStatusByReviewRules($reviewerPairs);
$story->status = $status ? $status : $oldStory->status;
if($story->status == 'closed')
{
$story->closedBy = $this->app->user->account;
@@ -1297,14 +1304,9 @@ class storyModel extends model
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->where('id')->eq($storyID)->exec();
$this->setStage($storyID);
$reasonParam = $result == 'reject' ? ',' . $reason : '';
$reviewers = $this->dao->select('reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->fetchAll('reviewer');
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
$actions[$storyID] = $this->action->create('story', $storyID, 'Reviewed', '', ucfirst($result) . $reasonParam);
if($story->status == 'closed') $this->action->create('story', $storyID, 'ReviewClosed');
if($story->status == 'active') $this->action->create('story', $storyID, 'PassReviewed');
if(!array_diff(array_keys($reviewers), $reviewedBy) and ($story->status == 'draft' || $story->status == 'changed')) $this->action->create('story', $storyID, 'ClarifyReviewed');
$story->id = $storyID;
$story->version = $oldStory->version;
$actions[$storyID] = $this->recordReviewAction($story, $result, $reason);
}
return $actions;
@@ -3273,12 +3275,13 @@ class storyModel extends model
if($story->parent < 0 and $action != 'edit' and $action != 'batchcreate') return false;
global $config, $app, $dao;
$notReview = $dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($story->id)->andWhere('version')->eq($story->version)->andWhere('result')->eq('')->fetchAll('reviewer');
$reviewers = $dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($story->id)->andWhere('version')->eq($story->version)->fetchAll('reviewer');
global $app;
if($action == 'change') return (count($reviewers) == 0 || count($notReview) == 0) and $story->status != 'closed';
if($action == 'review') return in_array($app->user->account, array_keys($notReview)) and ($story->status == 'draft' or $story->status == 'changed');
$story->reviewer = isset($story->reviewer) ? $story->reviewer : array();
$story->notReview = isset($story->notReview) ? $story->notReview : array();
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 == 'close') return $story->status != 'closed';
if($action == 'activate') return $story->status == 'closed';
if($action == 'assignto') return $story->status != 'closed';
@@ -3369,6 +3372,39 @@ class storyModel extends model
return $stories;
}
/**
* Merge story reviewers.
*
* @param array|object $stories
* @param bool $isObject
* @access public
* @return array|object
*/
public function mergeReviewer($stories, $isObject = false)
{
if($isObject)
{
$story = $stories;
$stories = (array)$stories;
$stories[$story->id] = $story;
}
$allReviewers = $this->dao->select('story,reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->in(array_keys($stories))->orderBy('version')->fetchGroup('story', 'reviewer');
foreach($allReviewers as $storyID => $reviewerList)
{
$stories[$storyID]->reviewer = array_keys($reviewerList);
$stories[$storyID]->notReview = array();
foreach($reviewerList as $reviewer => $reviewInfo)
{
if($reviewInfo->result == '') $stories[$storyID]->notReview[] = $reviewer;
}
}
if($isObject) return $stories[$story->id];
return $stories;
}
/**
* Print cell data
*
@@ -4220,6 +4256,19 @@ class storyModel extends model
}
}
/**
* Get story reviewer pairs.
*
* @param int $storyID
* @param int version
* @access public
* @return array
*/
public function getReviewerPairs($storyID, $version)
{
return $this->dao->select('reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($version)->fetchPairs('reviewer', 'result');
}
/**
* Set story status by review rules.
*
@@ -4229,6 +4278,7 @@ class storyModel extends model
*/
public function setStatusByReviewRules($reviewerList)
{
$status = '';
$passCount = 0;
$rejectCount = 0;
$reviewRule = $this->config->story->reviewRules;
@@ -4252,4 +4302,30 @@ class storyModel extends model
return $status;
}
/**
* Record story review actions.
*
* @param object $story
* @param string $result
* @param string $reason
* @access public
* @return int
*/
public function recordReviewAction($story, $result, $reason)
{
$reasonParam = $result == 'reject' ? ',' . $reason : '';
$reviewers = $this->getReviewerPairs($story->id, $story->version);
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
$comment = isset($_POST['comment']) ? $this->post->comment : '';
$actionID = $this->loadModel('action')->create('story', $story->id, 'Reviewed', $comment, ucfirst($result) . $reasonParam);
if($story->status == 'closed') $this->action->create('story', $story->id, 'ReviewClosed');
if($story->status == 'active') $this->action->create('story', $story->id, 'PassReviewed');
if(!array_diff(array_keys($reviewers), $reviewedBy) and ($story->status == 'draft' || $story->status == 'changed')) $this->action->create('story', $story->id, 'ClarifyReviewed');
return $actionID;
}
}
+1 -1
View File
@@ -101,7 +101,7 @@
<tr>
<th><?php echo $lang->story->reviewedBy;?></th>
<?php $colspan = $type == 'story' ? "colspan='4'" : "colspan='2'";?>
<td <?php echo $colspan;?> id='reviewerTd'>
<td <?php echo $colspan;?> id='reviewerBox'>
<div class="table-row">
<div class="table-col">
<?php echo html::select('reviewer[]', $users, empty($needReview) ? $product->PO : '', "class='form-control chosen' multiple");?>
+1 -1
View File
@@ -501,7 +501,7 @@
<?php
foreach($cases as $case)
{
echo "<li title='[C]$case->id $case->title'>" . html::a($this->createLink('testcase', 'view', "caseID=$case->id", '', true), "[C] #$case->id $case->title") . '</li>';
echo "<li title='[C]$case->id $case->title'>" . html::a($this->createLink('testcase', 'view', "caseID=$case->id", '', true), "[C] #$case->id $case->title", '',"data-toggle='modal'") . '</li>';
}
?>
</ul>
+16 -12
View File
@@ -310,8 +310,13 @@ class testreport extends control
$this->view->cases = $cases;
$this->view->caseSummary = $this->testreport->getResultSummary($tasks, $cases, $begin, $end);
$perCaseResult = $this->testreport->getPerCaseResult4Report($tasks, $cases, $begin, $end);
$perCaseRunner = $this->testreport->getPerCaseRunner4Report($tasks, $cases, $begin, $end);
$caseList = array();
foreach($cases as $taskID => $casesList)
{
foreach($casesList as $caseID => $case) $caseList[$caseID] = $case;
}
$perCaseResult = $this->testreport->getPerCaseResult4Report($tasks, array_keys($caseList), $begin, $end);
$perCaseRunner = $this->testreport->getPerCaseRunner4Report($tasks, array_keys($caseList), $begin, $end);
$this->view->datas['testTaskPerRunResult'] = $this->loadModel('report')->computePercent($perCaseResult);
$this->view->datas['testTaskPerRunner'] = $this->report->computePercent($perCaseRunner);
@@ -439,8 +444,8 @@ class testreport extends control
$this->view->cases = $cases;
$this->view->caseSummary = $this->testreport->getResultSummary($tasks, $cases, $begin, $end);
$perCaseResult = $this->testreport->getPerCaseResult4Report($tasks, $cases, $begin, $end);
$perCaseRunner = $this->testreport->getPerCaseRunner4Report($tasks, $cases, $begin, $end);
$perCaseResult = $this->testreport->getPerCaseResult4Report($tasks, $report->cases, $begin, $end);
$perCaseRunner = $this->testreport->getPerCaseRunner4Report($tasks, $report->cases, $begin, $end);
$this->view->datas['testTaskPerRunResult'] = $this->loadModel('report')->computePercent($perCaseResult);
$this->view->datas['testTaskPerRunner'] = $this->report->computePercent($perCaseRunner);
@@ -511,11 +516,10 @@ class testreport extends control
if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
$pager = pager::init($recTotal, $recPerPage, $pageID);
$tasks = $report->tasks ? $this->testtask->getByList($report->tasks) : array();;
$builds = $report->builds ? $this->build->getByList($report->builds) : array();
$cases = $this->testreport->getTaskCases($tasks, $report->begin, $report->end, $report->cases, $pager);
$caseIdList = $this->testreport->getCaseIdList($reportID);
$bugInfo = $this->testreport->getBugInfo($tasks, $report->product, $report->begin, $report->end, $builds);
$tasks = $report->tasks ? $this->testtask->getByList($report->tasks) : array();;
$builds = $report->builds ? $this->build->getByList($report->builds) : array();
$cases = $this->testreport->getTaskCases($tasks, $report->begin, $report->end);
$bugInfo = $this->testreport->getBugInfo($tasks, $report->product, $report->begin, $report->end, $builds);
if($report->objectType == 'testtask')
{
@@ -537,15 +541,15 @@ class testreport extends control
$this->view->stories = $stories;
$this->view->bugs = $report->bugs ? $this->bug->getByList($report->bugs) : array();
$this->view->builds = $builds;
$this->view->cases = $cases;
$this->view->cases = $this->testreport->getTaskCases($tasks, $report->begin, $report->end, $report->cases, $pager);
$this->view->users = $this->user->getPairs('noletter|noclosed|nodeleted');
$this->view->actions = $this->loadModel('action')->getList('testreport', $reportID);
$this->view->storySummary = $this->product->summary($stories);
$this->view->caseSummary = $this->testreport->getResultSummary($tasks, $cases, $report->begin, $report->end);
$perCaseResult = $this->testreport->getPerCaseResult4Report($tasks, $caseIdList, $report->begin, $report->end);
$perCaseRunner = $this->testreport->getPerCaseRunner4Report($tasks, $caseIdList, $report->begin, $report->end);
$perCaseResult = $this->testreport->getPerCaseResult4Report($tasks, $report->cases, $report->begin, $report->end);
$perCaseRunner = $this->testreport->getPerCaseRunner4Report($tasks, $report->cases, $report->begin, $report->end);
$this->view->datas['testTaskPerRunResult'] = $this->loadModel('report')->computePercent($perCaseResult);
$this->view->datas['testTaskPerRunner'] = $this->report->computePercent($perCaseRunner);
+2 -2
View File
@@ -438,7 +438,7 @@ class testreportModel extends model
->on('t1.run= t2.id')
->where('t2.task')->in(array_keys($tasks))
->andwhere('t1.date = t2.lastRunDate')
->andWhere('t1.`case`')->in(array_keys($cases))
->andWhere('t1.`case`')->in($cases)
->andWhere('t1.date')->ge($begin)
->andWhere('t1.date')->le($end . " 23:59:59")
->groupBy('name')
@@ -470,7 +470,7 @@ class testreportModel extends model
->on('t1.run= t2.id')
->where('t2.task')->in(array_keys($tasks))
->andwhere('t1.date = t2.lastRunDate')
->andWhere('t1.`case`')->in(array_keys($cases))
->andWhere('t1.`case`')->in($cases)
->andWhere('t1.date')->ge($begin)
->andWhere('t1.date')->le($end . " 23:59:59")
->groupBy('name')
+3 -3
View File
@@ -244,7 +244,7 @@ class testtask extends control
$executions = empty($productID) ? array() : $this->loadModel('product')->getExecutionPairsByProduct($productID, 0, 'id_desc', $projectID);
$builds = empty($productID) ? array() : $this->loadModel('build')->getProductBuildPairs($productID, 0, 'notrunk', true);
$testreports = empty($build) ? array() : $this->dao->select('id,title')->from(TABLE_TESTREPORT)->where('builds')->like('%' . $build . '%')->fetchPairs('id','title');
$testreports = $this->testtask->getTestReportPairsByBuild($build);
/* Set menu. */
$productID = $this->product->saveState($productID, $this->products);
@@ -750,7 +750,7 @@ class testtask extends control
$projectID = $this->lang->navGroup->testtask == 'qa' ? 0 : $this->session->project;
$executions = empty($productID) ? array() : $this->product->getExecutionPairsByProduct($productID, 0, 'id_desc', $projectID);
$builds = empty($productID) ? array() : $this->loadModel('build')->getProductBuildPairs($productID, 0, 'notrunk', true);
$testreports = $this->dao->select('id,title')->from(TABLE_TESTREPORT)->where('builds')->like('%' . $task->build . '%')->fetchPairs('id','title');
$testreports = $this->testtask->getTestReportPairsByBuild($task->build);
$this->view->task = $task;
$this->view->executions = $executions;
@@ -1451,7 +1451,7 @@ class testtask extends control
public function ajaxGetTestReports($buildID)
{
/* Testreport list. */
$pairs = $this->dao->select('id,title')->from(TABLE_TESTREPORT)->where('builds')->like('%' . $buildID . '%')->fetchPairs('id','title');
$pairs = $this->testtask->getTestReportPairsByBuild($buildID);
die(html::select('testreport', $pairs, '', "class='form-control chosen'"));
}
}
+14 -1
View File
@@ -492,6 +492,20 @@ class testtaskModel extends model
->fetchAll();
}
/**
* Get test report pairs by build.
*
* @param string $build
* @access public
* @return array
*/
public function getTestReportPairsByBuild($build = '')
{
if(empty($build)) return array();
return $this->dao->select('id,title')->from(TABLE_TESTREPORT)->where("CONCAT(',', builds, ',')")->like("%,$build,%")->fetchPairs('id','title');
}
/**
* Get related test tasks.
*
@@ -885,7 +899,6 @@ class testtaskModel extends model
$this->loadModel('action')->create('case', $caseID, 'linked2testtask', '', $taskID);
}
}
}
/**
+2 -2
View File
@@ -105,9 +105,9 @@ $status = $this->session->testTaskVersionStatus;
echo '<div id="action-divider">';
common::printIcon('testtask', 'cases', "taskID=$task->id", $task, 'list', 'sitemap');
common::printIcon('testtask', 'linkCase', "taskID=$task->id&type=all&param=myQueryID", $task, 'list', 'link');
common::printIcon('testreport', 'browse', "objectID=$task->product&objectType=product&extra=$task->id", $task, 'list','flag');
common::printIcon('testreport', 'browse', "objectID=$task->product&objectType=product&extra=$task->id", $task, 'list', 'flag');
echo '</div>';
common::printIcon('testtask', 'view', "taskID=$task->id", '', 'list', 'list-alt','','iframe',true, "data-width='90%'");
common::printIcon('testtask', 'view', "taskID=$task->id", '', 'list', 'list-alt', '', 'iframe', true, "data-width='90%'");
common::printIcon('testtask', 'edit', "taskID=$task->id", $task, 'list');
if(common::hasPriv('testtask', 'delete', $task))
{
+1 -1
View File
@@ -36,7 +36,7 @@
</tr>
<tr>
<th><?php echo $lang->testtask->build;?></th>
<td class='w-p35-f'><span id='buildBox'><?php echo html::select('build', $builds, $task->build, "class='form-control chosen'");?></span></td>
<td class='w-p35-f'><span id='buildBox'><?php echo html::select('build', $builds, $task->build, "class='form-control chosen' onchange='loadTestReports(this.value)'");?></span></td>
<td></td>
</tr>
<tr>
+13 -1
View File
@@ -27,4 +27,16 @@ $config->todo->custom = new stdclass();
$config->todo->custom->batchCreateFields = 'type,pri,desc,beginAndEnd';
$config->todo->custom->batchEditFields = 'pri,beginAndEnd,status';
$config->todo->moduleList = array('bug', 'task', 'story', 'issue', 'risk', 'review', 'testtask', 'opportunity');
$config->todo->moduleList = array('bug', 'task', 'story', 'testtask');
$config->todo->getUserObjectsMethod = array();
$config->todo->getUserObjectsMethod['bug'] = 'ajaxGetUserBugs';
$config->todo->getUserObjectsMethod['task'] = 'ajaxGetUserTasks';
$config->todo->getUserObjectsMethod['story'] = 'ajaxGetUserStories';
$config->todo->getUserObjectsMethod['testtask'] = 'ajaxGetUserTestTasks';
$config->todo->objectList = array();
$config->todo->objectList['bug'] = 'bugs';
$config->todo->objectList['task'] = 'tasks';
$config->todo->objectList['story'] = 'stories';
$config->todo->objectList['testtask'] = 'testtasks';
+4 -4
View File
@@ -24,7 +24,6 @@ class todo extends control
$this->loadModel('task');
$this->loadModel('bug');
$this->loadModel('my')->setMenu();
if(!isset($this->config->qcVersion)) unset($this->lang->todo->typeList['review']);
}
/**
@@ -194,16 +193,17 @@ class todo extends control
$bugs = $this->bug->getUserBugPairs($account);
$tasks = $this->task->getUserTaskPairs($account, $status);
$storys = $this->loadModel('story')->getUserStoryPairs($account);
if(isset($this->config->bizVersion) or isset($this->config->maxVersion)) $this->view->feedbacks = $this->loadModel('feedback')->getUserFeedbackPairs($account);
if(isset($this->config->maxVersion))
{
$issues = $this->loadModel('issue')->getUserIssuePairs($account);
$risks = $this->loadmodel('risk')->getuserriskpairs($account);
$risks = $this->loadmodel('risk')->getUserRiskPairs($account);
$opportunities = $this->loadmodel('opportunity')->getUserOpportunityPairs($account);
}
$testtasks = $this->loadModel('testtask')->getUserTestTaskPairs($account);
$reviews = array();
if(isset($this->config->qcVersion)) $reviews = $this->loadModel('review')->getUserReviewPairs($account);
if(isset($this->config->qcVersion) or isset($this->config->maxVersion)) $reviews = $this->loadModel('review')->getUserReviewPairs($account);
$allTodos = $this->todo->getList($type, $account, $status);
if($this->post->todoIDList) $todoIDList = $this->post->todoIDList;
@@ -394,7 +394,7 @@ class todo extends control
$this->view->from = $from;
$this->view->projects = $projects;
$this->view->executions = $this->loadModel('execution')->getPairs($this->session->project);
$this->view->products = $this->loadModel('product')->getPairs();
$this->view->products = $todo->type == 'opportunity' ? $this->loadModel('product')->getPairsByProjectModel('waterfall') : $this->loadModel('product')->getPairs();
$this->view->projectProducts = $this->loadModel('product')->getProductPairsByProject($this->session->project);
$this->display();
+3 -35
View File
@@ -25,44 +25,12 @@ function loadList(type, id)
var param = 'userID=' + userID;
if(id) param += '&id=' + id;
if(type == 'bug')
if(moduleList.indexOf(type) !== -1)
{
link = createLink('bug', 'ajaxGetUserBugs', param);
}
else if(type == 'task')
{
link = createLink('task', 'ajaxGetUserTasks', param);
}
else if(type == 'story')
{
link = createLink('story', 'ajaxGetUserStories', param);
}
else if(type == 'issue')
{
link = createLink('issue', 'ajaxGetUserIssues', param);
}
else if(type == 'risk')
{
link = createLink('risk', 'ajaxGetUserRisks', param);
}
else if(type == 'opportunity')
{
link = createLink('opportunity', 'ajaxGetUseropportunities', param);
}
else if(type == 'testtask')
{
link = createLink('testtask', 'ajaxGetUserTestTasks', param);
}
else if(type == 'review')
{
link = createLink('review', 'ajaxGetUserReviews', param);
}
else if(type == 'feedback')
{
link = createLink('feedback', 'ajaxGetUserFeedback', param);
link = createLink(type, objectsMethod[type], param);
}
if(type == 'bug' || type == 'task' || type == 'story' || type == 'issue' || type == 'risk' || type == 'testtask' || type == 'review' || type == 'feedback' || type == 'opportunity')
if(moduleList.indexOf(type) !== -1)
{
$.get(link, function(data, status)
{
-1
View File
@@ -97,7 +97,6 @@ $lang->todo->typeList['cycle'] = 'Wiederkehrend';
$lang->todo->typeList['bug'] = 'Bug';
$lang->todo->typeList['task'] = 'Aufgabe';
$lang->todo->typeList['story'] = 'Story';
$lang->todo->typeList['review'] = 'Review';
$lang->todo->typeList['testtask'] = 'Testtask';
$lang->todo->confirmDelete = "Möchten Sie diesen ToDo löschen?";
-1
View File
@@ -98,7 +98,6 @@ $lang->todo->typeList['cycle'] = 'Recur';
$lang->todo->typeList['bug'] = 'Bug';
$lang->todo->typeList['task'] = 'Task';
$lang->todo->typeList['story'] = 'Story';
$lang->todo->typeList['review'] = 'Review';
$lang->todo->typeList['testtask'] = 'Testtask';
$lang->todo->confirmDelete = "Do you want to delete this todo?";
-1
View File
@@ -97,7 +97,6 @@ $lang->todo->typeList['cycle'] = 'Récur';
$lang->todo->typeList['bug'] = 'Bug';
$lang->todo->typeList['task'] = 'Tâche';
$lang->todo->typeList['story'] = 'Story';
$lang->todo->typeList['review'] = 'Review';
$lang->todo->typeList['testtask'] = 'Testtask';
$lang->todo->confirmDelete = "Voulez-vous supprimer cette entrée de l'agenda ?";
-1
View File
@@ -97,7 +97,6 @@ $lang->todo->typeList['cycle'] = 'Lặp lại';
$lang->todo->typeList['bug'] = 'Bug';
$lang->todo->typeList['task'] = 'Nhiệm vụ';
$lang->todo->typeList['story'] = 'Câu chuyện';
$lang->todo->typeList['review'] = 'Review';
$lang->todo->typeList['testtask'] = 'Testtask';
$lang->todo->confirmDelete = "Bạn có muốn xóa việc này?";
-1
View File
@@ -98,7 +98,6 @@ $lang->todo->typeList['cycle'] = '周期';
$lang->todo->typeList['bug'] = 'Bug';
$lang->todo->typeList['task'] = '任务';
$lang->todo->typeList['story'] = $lang->SRCommon;
$lang->todo->typeList['review'] = '评审';
$lang->todo->typeList['testtask'] = '测试单';
$lang->todo->confirmDelete = "您确定要删除这条待办吗?";
+18 -55
View File
@@ -39,44 +39,9 @@ class todoModel extends model
->remove(implode(',', $this->config->todo->moduleList) . ',uid')
->get();
if(!isset($todo->pri))
if(!isset($todo->pri) and in_array($this->post->type, $this->config->todo->moduleList) and $this->post->type !== 'review' and $this->post->type !== 'feedback')
{
if($this->post->type == 'task')
{
$todo->pri = $this->dao->select('pri')->from(TABLE_TASK)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
elseif($this->post->type == 'bug')
{
$todo->pri = $this->dao->select('pri')->from(TABLE_BUG)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
elseif($this->post->type == 'story')
{
$todo->pri = $this->dao->select('pri')->from(TABLE_STORY)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
elseif($this->post->type == 'testtask')
{
$todo->pri = $this->dao->select('pri')->from(TABLE_TESTTASK)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
elseif($this->post->type == 'issue' and isset($this->config->maxVersion))
{
$todo->pri = $this->dao->select('pri')->from(TABLE_ISSUE)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
elseif($this->post->type == 'risk' and isset($this->config->maxVersion))
{
$todo->pri = $this->dao->select('pri')->from(TABLE_RISK)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
elseif($this->post->type == 'review' and isset($this->config->maxVersion))
{
$todo->pri = $this->dao->select('pri')->from(TABLE_REVIEW)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
elseif($this->post->type == 'feedback' and isset($this->config->maxVersion))
{
$todo->pri = $this->dao->select('pri')->from(TABLE_FEEDBACK)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
elseif($this->post->type == 'opportunity' and isset($this->config->maxVersion))
{
$todo->pri = $this->dao->select('pri')->from(TABLE_OPPORTUNITY)->where('id')->eq($this->post->idvalue)->fetch('pri');
}
$todo->pri = $this->dao->select('pri')->from($this->config->objectTables[$this->post->type])->where('id')->eq($this->post->idvalue)->fetch('pri');
if($todo->pri == 'high') $todo->pri = 1;
if($todo->pri == 'middle') $todo->pri = 2;
@@ -155,7 +120,17 @@ class todoModel extends model
$validTodos = array();
for($i = 0; $i < $this->config->todo->batchCreate; $i++)
{
if($todos->names[$i] != '' || isset($todos->bugs[$i + 1]) || isset($todos->tasks[$i + 1]) || isset($todos->stories[$i + 1]) || isset($todos->issues[$i + 1]) || isset($todos->risks[$i + 1]) || isset($todos->reviews[$i + 1]) || isset($todos->testtasks[$i + 1]) || isset($todos->opportunities[$i + 1]))
$isExist = false;
foreach($this->config->todo->objectList as $objects)
{
if(isset($todos->{$objects}[$i + 1]))
{
$isExist = true;
break;
}
}
if($todos->names[$i] != '' || $isExist)
{
$todo = new stdclass();
$todo->account = $this->app->user->account;
@@ -178,14 +153,7 @@ class todoModel extends model
$todo->private = 0;
$todo->idvalue = 0;
if($todo->type == 'bug') $todo->idvalue = isset($todos->bugs[$i + 1]) ? $todos->bugs[$i + 1] : 0;
if($todo->type == 'task') $todo->idvalue = isset($todos->tasks[$i + 1]) ? $todos->tasks[$i + 1] : 0;
if($todo->type == 'story') $todo->idvalue = isset($todos->stories[$i + 1]) ? $todos->stories[$i + 1] : 0;
if($todo->type == 'issue') $todo->idvalue = isset($todos->issues[$i + 1]) ? $todos->issues[$i + 1] : 0;
if($todo->type == 'risk') $todo->idvalue = isset($todos->risks[$i + 1]) ? $todos->risks[$i + 1] : 0;
if($todo->type == 'opportunity') $todo->idvalue = isset($todos->opportunities[$i + 1]) ? $todos->opportunities[$i + 1] : 0;
if($todo->type == 'review') $todo->idvalue = isset($todos->reviews[$i + 1]) ? $todos->reviews[$i + 1] : 0;
if($todo->type == 'testtask') $todo->idvalue = isset($todos->testtasks[$i + 1]) ? $todos->testtasks[$i + 1] : 0;
if(in_array($todo->type, $this->config->todo->moduleList)) $todo->idvalue = isset($todos->{$this->config->todo->objectList[$todo->type]}[$i + 1]) ? $todos->{$this->config->todo->objectList[$todo->type]}[$i + 1] : 0;
if($todo->type != 'custom' and $todo->idvalue)
{
@@ -329,16 +297,11 @@ class todoModel extends model
$todo->name = ($todo->type == 'custom' or $todo->type == 'cycle' or $todo->type == 'feedback') ? $data->names[$todoID] : '';
$todo->begin = isset($data->begins[$todoID]) ? $data->begins[$todoID] : 2400;
$todo->end = isset($data->ends[$todoID]) ? $data->ends[$todoID] : 2400;
if($todo->type == 'task') $todo->idvalue = isset($data->tasks[$todoID]) ? $data->tasks[$todoID] : 0;
if($todo->type == 'bug') $todo->idvalue = isset($data->bugs[$todoID]) ? $data->bugs[$todoID] : 0;
if($todo->type == 'story') $todo->idvalue = isset($data->storys[$todoID]) ? $data->storys[$todoID] : 0;
if($todo->type == 'issue') $todo->idvalue = isset($data->issues[$todoID]) ? $data->issues[$todoID] : 0;
if($todo->type == 'risk') $todo->idvalue = isset($data->risks[$todoID]) ? $data->risks[$todoID] : 0;
if($todo->type == 'opportunity') $todo->idvalue = isset($data->opportunities[$todoID]) ? $data->opportunities[$todoID] : 0;
if($todo->type == 'review') $todo->idvalue = isset($data->reviews[$todoID]) ? $data->reviews[$todoID] : 0;
if($todo->type == 'testtask') $todo->idvalue = isset($data->testtasks[$todoID]) ? $data->testtasks[$todoID] : 0;
if($todo->type == 'feedback') $todo->idvalue = isset($data->feedbacks[$todoID]) ? $data->feedbacks[$todoID] : 0;
if(in_array($todo->type, $this->config->todo->moduleList))
{
$todo->idvalue = isset($data->{$this->config->todo->objectList[$todo->type]}[$todoID]) ? $data->{$this->config->todo->objectList[$todo->type]}[$todoID] : 0;
}
if($todo->end < $todo->begin) die(js::alert(sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin)));
$todos[$todoID] = $todo;
+2 -1
View File
@@ -12,7 +12,8 @@
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/datepicker.html.php';?>
<?php js::set('moduleList', $config->todo->moduleList)?>
<?php js::set('objectsMethod', $config->todo->getUserObjectsMethod)?>
<form id='todoBatchAddForm' method='post' target='hiddenwin' action='<?php echo $this->createLink('todo', 'batchCreate');?>'>
<div id="mainContent">
<div class="main-header">
+7 -6
View File
@@ -12,6 +12,8 @@
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/datepicker.html.php';?>
<?php js::set('moduleList', $config->todo->moduleList)?>
<?php js::set('objectsMethod', $config->todo->getUserObjectsMethod)?>
<div id="mainContent" class="main-content">
<div class="main-header">
<h2><?php echo $lang->todo->common . $lang->colon . $lang->todo->batchEdit;?></h2>
@@ -71,11 +73,6 @@
{
echo html::input("names[$todo->id]", $todo->name, "class='form-control'");
}
elseif($todo->type == 'feedback')
{
echo html::input("names[$todo->id]", $todo->name, "class='form-control'");
echo html::hidden("feedbacks[$todo->id]", $todo->idvalue, "class='form-control'");
}
elseif($todo->type == 'task')
{
echo html::select("tasks[$todo->id]", $tasks, $todo->idvalue, 'class="form-control chosen"');
@@ -86,7 +83,7 @@
}
elseif($todo->type == 'story')
{
echo html::select("storys[$todo->id]", $storys, $todo->idvalue, 'class="form-control chosen"');
echo html::select("stories[$todo->id]", $storys, $todo->idvalue, 'class="form-control chosen"');
}
elseif($todo->type == 'issue')
{
@@ -108,6 +105,10 @@
{
echo html::select("opportunities[$todo->id]", $opportunities, $todo->idvalue, 'class="form-control chosen"');
}
elseif($todo->type == 'feedback')
{
echo html::select("feedbacks[$todo->id]", $feedbacks, $todo->idvalue, 'class="form-control chosen"');
}
?>
</div>
</td>
+2
View File
@@ -14,6 +14,8 @@
<?php include '../../common/view/datepicker.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::set('noTodo', $lang->todo->noTodo);?>
<?php js::set('moduleList', $config->todo->moduleList)?>
<?php js::set('objectsMethod', $config->todo->getUserObjectsMethod)?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
+2
View File
@@ -13,6 +13,8 @@
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/datepicker.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::set('moduleList', $config->todo->moduleList)?>
<?php js::set('objectsMethod', $config->todo->getUserObjectsMethod)?>
<?php if(common::checkNotCN()):?>
<style> label.col-sm-1{width:100px;} </style>
<?php endif;?>
+2
View File
@@ -23,6 +23,8 @@ class upgrade extends control
$upgradeFile = $this->app->wwwRoot . 'upgrade.php';
if(!file_exists($upgradeFile)) $this->locate($this->createLink('my', 'index'));
$this->upgrade->deleteTmpModel();
$systemMode = $this->loadModel('setting')->getItem('owner=system&module=common&section=global&key=mode');
if(empty($systemMode) && !isset($this->config->qcVersion))
{
+1 -4
View File
@@ -106,9 +106,6 @@ $lang->upgrade->mergeByProduct = "PRODUCT-BASED iterations: You can select m
$lang->upgrade->mergeByProject = "Independent iterations: You can select several iterations and merge them into one large project, or merge them independently";
$lang->upgrade->mergeByMoreLink = "Iteration that relates multiple products: select which product the iteration belongs to.";
$lang->upgrade->mergeRepoTips = "Merge the selected version library under the selected product.";
$lang->upgrade->needBuild4Add = 'Full text retrieval has been added in this upgrad. Please create an index.';
$lang->upgrade->needBuild4Adjust = 'Full text retrieval has been adjusted. Please create an index.';
$lang->upgrade->buildIndex = 'Create Index';
$lang->upgrade->needBuild4Add = 'Full text retrieval has been added in this upgrade. Need create index. Please go [Admin->System->BuildIndex] page to build index.';
include dirname(__FILE__) . '/version.php';
+1 -4
View File
@@ -106,9 +106,6 @@ $lang->upgrade->mergeByProduct = "以产品组织的迭代:可以选择多
$lang->upgrade->mergeByProject = "独立的迭代:可以选择若干迭代归并到一个项目中,也可以独立归并。";
$lang->upgrade->mergeByMoreLink = "关联多个产品的迭代:选择这个迭代归属于哪一个产品。";
$lang->upgrade->mergeRepoTips = "将选中的版本库归并到所选产品下。";
$lang->upgrade->needBuild4Add = '本次升级新增全文检索功能,需要创建索引。';
$lang->upgrade->needBuild4Adjust = '本次升级全文检索功能有调整,需要创建索引。';
$lang->upgrade->buildIndex = '创建索引';
$lang->upgrade->needBuild4Add = '本次升级需要创建索引。请到 [后台->系统->重建索引] 页面,重新创建索引。';
include dirname(__FILE__) . '/version.php';
+13 -17
View File
@@ -672,7 +672,6 @@ class upgradeModel extends model
case '15_0':
$this->saveLogs('Execute 15_0');
$this->execSQL($this->getUpgradeFile('15.0'));
$this->updateProjectLifeTime();
$this->adjustBugOfProject();
$this->processBuildTable();
$this->updateProductVersion();
@@ -985,6 +984,18 @@ class upgradeModel extends model
return $alterSQL;
}
/**
* Delete tmp model files.
*
* @access public
* @return void
*/
public function deleteTmpModel()
{
$tmpModelDir = $this->app->getTmpRoot() . 'model/';
foreach(glob($tmpModelDir . '/*.php') as $tmpModelFile) unlink($tmpModelFile);
}
/**
* Delete Useless Files
*
@@ -1019,8 +1030,6 @@ class upgradeModel extends model
return $result;
}
/**
* Update ubb code in bug table and user Templates table to html.
*
@@ -4964,19 +4973,6 @@ class upgradeModel extends model
return true;
}
/**
* Update the lifetime field default value of the zt_project table.
*
* @access public
* @return bool
*/
public function updateProjectLifeTime()
{
$this->dao->update(TABLE_PROJECT)->set('lifetime')->eq('')->where('lifetime')->eq('sprint')->exec();
return true;
}
/**
* Update the createdVersion field of the zt_product table.
*
@@ -4985,7 +4981,7 @@ class upgradeModel extends model
*/
public function updateProductVersion()
{
$this->dao->update(TABLE_PRODUCT)->set('createdVersion')->eq($this->config->version)->where('createdVersion')->eq('')->exec();
$this->dao->update(TABLE_PRODUCT)->set('createdVersion')->eq($this->config->version)->where('createdVersion')->eq('')->andWhere('createdDate')->gt('2020-01-01')->exec();
return true;
}
}
+2 -48
View File
@@ -87,57 +87,11 @@ function updateFile(link)
}
<?php endif;?>
</script>
<?php
if(isset($needProcess['search']))
{
$searchLink = $this->createLink('search', 'buildIndex');
$needBuild = $lang->upgrade->needBuild4Add;
}
?>
<?php if(isset($searchLink)):?>
<?php if(isset($needProcess['search'])):?>
<script>
$(function()
{
$('a#tohome').closest('.message').hide();
$('.col-md-6:first').append("<div id='resultBox' class='alert alert-info'><p><?php echo $needBuild;?><a href='<?php echo $searchLink;?>' id='execButton' class='btn btn-primary btn-sm'><?php echo $lang->upgrade->buildIndex;?></a></p></div>");
$('#execButton').click(function()
{
$('#execButton').hide();
$.get($(this).attr('href'), function(response)
{
try
{
response = $.parseJSON(response);
if(response.result == 'finished')
{
searchFinish = true;
$('#resultBox').append("<li class='text-success'>" + response.message + "</li>");
<?php
$condition = array();
foreach($needProcess as $processKey => $value) $condition[] = $processKey . 'Finish == true';
$condition = join(' && ', $condition);
?>
if(<?php echo $condition?>)
{
$.get('<?php echo inlink('afterExec', "fromVersion=$fromVersion&processed=yes")?>');
$('a#tohome').closest('.message').show();
}
return false;
}
else
{
$('#execButton').attr('href', response.next);
$('#resultBox').append("<li class='text-success'>" + response.message + "</li>");
return $('#execButton').click();
}
}
catch(error)
{
$('#resultBox').append("<li>" + response + "</li>");
}
});
return false;
});
$('.col-md-6:first').append("<div class='alert alert-info'><p><?php echo $lang->upgrade->needBuild4Add;?></p></div>");
})
</script>
<?php endif;?>
+1 -1
View File
@@ -89,7 +89,7 @@
</tr>
<tr class='programParams'>
<th><?php echo $lang->project->acl;?></th>
<td><?php echo nl2br(html::radio('acl', $lang->program->subAclList, 'open', '', 'block'));?></td>
<td><?php echo nl2br(html::radio('acl', $lang->project->subAclList, 'open', '', 'block'));?></td>
</tr>
</table>
<div class='table-foot text-center'><?php echo html::submitButton();?></div>
+13 -13
View File
@@ -27,12 +27,12 @@ $app = router::createApp('pms', dirname(dirname(__FILE__)), 'router');
$tester = $app->loadCommon();
$config->zendataRoot = dirname(__FILE__) . DS . 'zendata';
$config->zdPath = "/usr/local/zd/zd";
$config->zdPath = __DIR__ . "/runtime/zd/zd";
/**
* Save variable to $_result.
*
* @param mix $result
*
* @param mix $result
* @access public
* @return bool true
*/
@@ -45,9 +45,9 @@ function run($result)
/**
* Print expect data.
*
* @param string $key
* @param string $delimiter
*
* @param string $key
* @param string $delimiter
* @access public
* @return void
*/
@@ -65,13 +65,13 @@ function expect($key, $delimiter = ',')
{
$keyList = explode(',', $key);
$dimension = 1;
foreach($_result as $value)
foreach($_result as $value)
{
if(is_array($value) or is_object($value)) $dimension = 2;
}
if($dimension == 1) $_result = array($_result);
foreach($_result as $object)
foreach($_result as $object)
{
foreach($keyList as $key) $result .= zget($object, $key, '') . $delimiter;
}
@@ -83,10 +83,10 @@ function expect($key, $delimiter = ',')
/**
* Import data create by zendata to one table.
*
* @param string $table
* @param string $yaml
* @param int $count
*
* @param string $table
* @param string $yaml
* @param int $count
* @access public
* @return void
*/
+13 -17
View File
@@ -1,30 +1,26 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/init.php';
/**
[case]
title=测试 branchModel::getById();
cid=1
pid=1
[group]
1. 使用branchID获取一个存在的分支 >> `A`
2. 使用branchID和productID获取一个存在的分支 >> `B`
3. 使用account获取一个存在的用户 >> ``
[esac]
使用branchID获取一个存在的分支 >> A
使用branchID和productID获取一个存在的分支 >> B
使用branchID获取一个存在的分支 >>
*/
include dirname(dirname(__DIR__)) . '/init.php';
$branch = $tester->loadModel('branch');
$app->dbh->query("truncate zt_product");
zdImport(TABLE_PRODUCT, "zendata/product.yaml", 10);
$app->dbh->query("truncate zt_branch");
zdImport(TABLE_PRODUCT, "zendata/product.yaml", 10);
zdImport(TABLE_BRANCH, "zendata/branch.yaml", 10);
/* Step 1.*/
run($branch->getByID(1)) and expect('name');
/* Step 2.*/
run($branch->getByID(2, 2)) and expect('name');
/* Step 3.*/
run($branch->getByID(null)) and expect('name');
/* Group 1. */
run($branch->getByID(1)) and expect('name'); // Step 1.1.
run($branch->getByID(2, 2)) and expect('name'); // Step 1.2.
run($branch->getByID(null)) and expect('name'); // Step 1.3.
+4 -5
View File
@@ -1,15 +1,14 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/init.php';
/**
[case]
title=测试 trainplanModel::getById();
cid=1
pid=1
[group]
1. 获取到的project是21,lecturer是hufangzhou >> `21,hufangzhou`
[esac]
获取到的project是21,lecturer是hufangzhou >> `21,hufangzhou`
*/
include dirname(dirname(dirname(__FILE__))) . '/init.php';
$trainplan = $tester->loadModel('trainplan');
$app->dbh->query("truncate zt_trainplan");
+4 -4
View File
@@ -4,11 +4,11 @@ author: automated export
version: "1.0"
fields:
- field: id
range: 1-10
range: 1-100000
- field: project
range: 21
range: 1-100
- field: name
range: 1-10
range: 1-100000
format: "培训计划_%d"
- field: begin
range: "(-1M)-(+1w):60"
@@ -30,7 +30,7 @@ fields:
- field: type
range: outside,inside
- field: status
range: wait
range: wait,done
- field: summary
range: ' '
- field: createdBy