Merge branch 'master' of https://gitlab.zcorp.cc/easycorp/zentaopms into sprint/zentaopms271_lanzongjun
This commit is contained in:
@@ -57,4 +57,4 @@ html[lang='de'] #keywordsAddonLabel, .task {width: 73px;}
|
||||
#openedBuildLabel {display: inline; position: relative; width: 50%; left: 14%;}
|
||||
#bugTypeInputGroup > .table-col {width: 40% !important;}
|
||||
|
||||
.pri-text > .label-pri {padding: 1px 5px;}
|
||||
.pri-text > .label-pri {padding: 2px 6px;}
|
||||
|
||||
@@ -1579,6 +1579,7 @@ class execution extends control
|
||||
|
||||
$execution = $this->commonAction($executionID);
|
||||
$executionID = $execution->id;
|
||||
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
|
||||
|
||||
$title = $execution->name . $this->lang->colon . $this->lang->execution->team;
|
||||
$position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
|
||||
@@ -1586,7 +1587,7 @@ class execution extends control
|
||||
|
||||
$this->view->title = $title;
|
||||
$this->view->position = $position;
|
||||
$this->view->deptUsers = $this->loadModel('dept')->getDeptUserPairs($this->app->user->dept, 'id');
|
||||
$this->view->deptUsers = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
|
||||
$this->view->canBeChanged = common::canModify('execution', $execution); // Determines whether an object is editable.
|
||||
|
||||
$this->display();
|
||||
|
||||
@@ -351,8 +351,8 @@ $lang->execution->howToUpdateBurn = "<a href='https://api.zentao.net/goto.p
|
||||
$lang->execution->whyNoStories = "看起来没有{$lang->SRCommon}可以关联。请检查下{$lang->executionCommon}关联的{$lang->productCommon}中有没有{$lang->SRCommon},而且要确保它们已经审核通过。";
|
||||
$lang->execution->projectNoStories = "看起来没有{$lang->SRCommon}可以关联。请检查下项目中有没有{$lang->SRCommon},而且要确保它们已经审核通过。";
|
||||
$lang->execution->productStories = "{$lang->executionCommon}关联的{$lang->SRCommon}是{$lang->productCommon}{$lang->SRCommon}的子集,并且只有评审通过的{$lang->SRCommon}才能关联。请<a href='%s'>关联{$lang->SRCommon}</a>。";
|
||||
$lang->execution->haveBranchDraft = "有%s条草稿状态或非本{$lang->executionCommon}关联分支的{$lang->SRCommon}无法关联到该{$lang->executionCommon}";
|
||||
$lang->execution->haveDraft = "有%s条草稿状态的{$lang->SRCommon}无法关联到该{$lang->executionCommon}";
|
||||
$lang->execution->haveBranchDraft = "有%s条非激活状态或不是{$lang->executionCommon}关联分支的{$lang->SRCommon}无法导入";
|
||||
$lang->execution->haveDraft = "有%s条非激活状态的{$lang->SRCommon}无法导入";
|
||||
$lang->execution->doneExecutions = '已结束';
|
||||
$lang->execution->selectDept = '选择部门';
|
||||
$lang->execution->selectDeptTitle = '选择一个部门的成员';
|
||||
|
||||
@@ -1610,11 +1610,12 @@ class project extends control
|
||||
$this->project->setMenu($projectID);
|
||||
|
||||
$project = $this->project->getById($projectID);
|
||||
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
|
||||
|
||||
$this->view->title = $project->name . $this->lang->colon . $this->lang->project->team;
|
||||
$this->view->projectID = $projectID;
|
||||
$this->view->teamMembers = $this->project->getTeamMembers($projectID);
|
||||
$this->view->deptUsers = $this->loadModel('dept')->getDeptUserPairs($this->app->user->dept, 'id');
|
||||
$this->view->deptUsers = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
|
||||
$this->view->canBeChanged = common::canModify('project', $project);
|
||||
|
||||
$this->display();
|
||||
|
||||
@@ -1583,7 +1583,7 @@ class projectModel extends model
|
||||
->exec();
|
||||
if(dao::isError()) return false;
|
||||
|
||||
if(!$oldProject->hasProduct and ($oldProject->name != $project->name or $oldProject->parent != $project->parent)) $this->updateShadowProduct($project);
|
||||
if(!$oldProject->hasProduct and ($oldProject->name != $project->name or $oldProject->parent != $project->parent or $oldProject->acl != $project->acl)) $this->updateShadowProduct($project);
|
||||
|
||||
/* Get team and language item. */
|
||||
$this->loadModel('user');
|
||||
@@ -1744,7 +1744,7 @@ class projectModel extends model
|
||||
|
||||
if(!dao::isError())
|
||||
{
|
||||
if(!$oldProject->hasProduct and ($oldProject->name != $project->name or $oldProject->parent != $project->parent)) $this->updateShadowProduct($project);
|
||||
if(!$oldProject->hasProduct and ($oldProject->name != $project->name or $oldProject->parent != $project->parent or $oldProject->acl != $project->acl)) $this->updateShadowProduct($project);
|
||||
|
||||
if(isset($project->parent))
|
||||
{
|
||||
@@ -2048,7 +2048,7 @@ class projectModel extends model
|
||||
{
|
||||
$product = $this->dao->select('product')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($project->id)->fetch('product');
|
||||
$topProgram = !empty($project->parent) ? $this->loadModel('program')->getTopByID($project->parent) : 0;
|
||||
$this->dao->update(TABLE_PRODUCT)->set('name')->eq($project->name)->set('program')->eq($topProgram)->where('id')->eq($product)->exec();
|
||||
$this->dao->update(TABLE_PRODUCT)->set('name')->eq($project->name)->set('program')->eq($topProgram)->set('acl')->eq($project->acl)->where('id')->eq($product)->exec();
|
||||
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
@@ -249,13 +249,14 @@ class projectrelease extends control
|
||||
if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
|
||||
|
||||
$storyPager = new pager($type == 'story' ? $recTotal : 0, $recPerPage, $type == 'story' ? $pageID : 1);
|
||||
$stories = $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder")->from(TABLE_STORY)
|
||||
->where('id')->in($release->stories)
|
||||
->andWhere('deleted')->eq(0)
|
||||
$stories = $this->dao->select("t1.*,t2.id as buildID, t2.name as buildName,IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder")->from(TABLE_STORY)->alias('t1')
|
||||
->leftJoin(TABLE_BUILD)->alias('t2')->on("FIND_IN_SET(t1.id, t2.stories)")
|
||||
->where('t1.id')->in($release->stories)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->beginIF($type == 'story')->orderBy($sort)->fi()
|
||||
->page($storyPager)
|
||||
->fetchAll('id');
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story');
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false);
|
||||
|
||||
$stages = $this->dao->select('*')->from(TABLE_STORYSTAGE)->where('story')->in(array_keys($stories))->andWhere('branch')->eq($release->branch)->fetchPairs('story', 'stage');
|
||||
foreach($stages as $storyID => $stage) $stories[$storyID]->stage = $stage;
|
||||
@@ -304,6 +305,7 @@ class projectrelease extends control
|
||||
$this->view->storyPager = $storyPager;
|
||||
$this->view->bugPager = $bugPager;
|
||||
$this->view->leftBugPager = $leftBugPager;
|
||||
$this->view->builds = $this->loadModel('build')->getBuildPairs($release->product, 'all', 'withbranch|hasproject', 0, 'execution', '', false);
|
||||
$this->display();
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
</th>
|
||||
<th class='c-pri' title='<?php echo $lang->pri;?>'><?php common::printOrderLink('pri', $orderBy, $vars, $lang->priAB);?></th>
|
||||
<th class="text-left"><?php common::printOrderLink('title', $orderBy, $vars, $lang->story->title);?></th>
|
||||
<th class='c-build w-200px text-left'><?php echo $lang->build->common;?></th>
|
||||
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
|
||||
<th class='w-80px text-right' style='padding-right:20px'><?php common::printOrderLink('estimate', $orderBy, $vars, $lang->story->estimateAB);?></th>
|
||||
<th class='w-90px'> <?php common::printOrderLink('status', $orderBy, $vars, $lang->statusAB);?></th>
|
||||
@@ -98,6 +99,7 @@
|
||||
echo html::a($storyLink,$story->title, '', "class='preview'");
|
||||
?>
|
||||
</td>
|
||||
<td class='text-left' title='<?php echo $story->buildName?>'><?php echo $story->buildName?></td>
|
||||
<td><?php echo zget($users, $story->openedBy);?></td>
|
||||
<td class='text-right' style='padding-right:25px' title="<?php echo $story->estimate . ' ' . $lang->hourCommon;?>"><?php echo $story->estimate . $config->hourUnit;?></td>
|
||||
<td>
|
||||
@@ -167,6 +169,7 @@
|
||||
</th>
|
||||
<th class='text-left'><?php common::printOrderLink('title', $orderBy, $vars, $lang->bug->title);?></th>
|
||||
<th class='w-100px'> <?php common::printOrderLink('status', $orderBy, $vars, $lang->bug->status);?></th>
|
||||
<th class='c-build w-200px text-left'><?php echo $lang->bug->resolvedBuild;?></th>
|
||||
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
|
||||
<th class='w-date'> <?php common::printOrderLink('openedDate', $orderBy, $vars, $lang->bug->openedDateAB);?></th>
|
||||
<th class='c-user'> <?php common::printOrderLink('resolvedBy', $orderBy, $vars, $lang->bug->resolvedByAB);?></th>
|
||||
@@ -191,6 +194,8 @@
|
||||
<td>
|
||||
<span class='status-bug status-<?php echo $bug->status?>'><?php echo $this->processStatus('bug', $bug);?></span>
|
||||
</td>
|
||||
<?php $resolvedBuildName = zget($builds, $bug->resolvedBuild, '');?>
|
||||
<td class='text-left' title='<?php echo $resolvedBuildName?>'><?php echo $resolvedBuildName;?></td>
|
||||
<td><?php echo zget($users, $bug->openedBy);?></td>
|
||||
<td><?php echo helper::isZeroDate($bug->openedDate) ? '' : substr($bug->openedDate, 5, 11);?></td>
|
||||
<td><?php echo zget($users, $bug->resolvedBy);?></td>
|
||||
@@ -257,6 +262,7 @@
|
||||
<th class='w-80px' title=<?php echo $lang->bug->severity;?>><?php common::printOrderLink('severity', $orderBy, $vars, $lang->bug->severityAB);?></th>
|
||||
<th class='text-left'> <?php common::printOrderLink('title', $orderBy, $vars, $lang->bug->title);?></th>
|
||||
<th class='w-100px'> <?php common::printOrderLink('status', $orderBy, $vars, $lang->bug->status);?></th>
|
||||
<th class='c-build w-200px text-left'><?php echo $lang->bug->openedBuild;?></th>
|
||||
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
|
||||
<th class='w-150px'> <?php common::printOrderLink('openedDate', $orderBy, $vars, $lang->bug->openedDateAB);?></th>
|
||||
<th class='w-50px'> <?php echo $lang->actions;?></th>
|
||||
@@ -295,6 +301,11 @@
|
||||
</td>
|
||||
<td class='text-left nobr' title='<?php echo $bug->title?>'><?php echo html::a($bugLink, $bug->title, '', "class='preview'");?></td>
|
||||
<td><span class='status-<?php echo $bug->status?>'> <?php echo $this->processStatus('bug', $bug);?></span></td>
|
||||
<?php
|
||||
$openedBuildName = '';
|
||||
foreach(explode(',', $bug->openedBuild) as $buildID) $openedBuildName .= zget($builds, $buildID, '') . ' ';
|
||||
?>
|
||||
<td class='text-left' title='<?php echo $openedBuildName?>'><?php echo $openedBuildName;?></td>
|
||||
<td><?php echo zget($users, $bug->openedBy);?></td>
|
||||
<td><?php echo $bug->openedDate?></td>
|
||||
<td class='c-actions'>
|
||||
|
||||
@@ -185,14 +185,15 @@ class release extends control
|
||||
if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
|
||||
|
||||
$storyPager = new pager($type == 'story' ? $recTotal : 0, $recPerPage, $type == 'story' ? $pageID : 1);
|
||||
$stories = $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder")->from(TABLE_STORY)
|
||||
->where('id')->in($release->stories)
|
||||
->andWhere('deleted')->eq(0)
|
||||
$stories = $this->dao->select("t1.*,t2.id as buildID, t2.name as buildName, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder")->from(TABLE_STORY)->alias('t1')
|
||||
->leftJoin(TABLE_BUILD)->alias('t2')->on("FIND_IN_SET(t1.id, t2.stories)")
|
||||
->where('t1.id')->in($release->stories)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->beginIF($type == 'story')->orderBy($sort)->fi()
|
||||
->page($storyPager)
|
||||
->fetchAll('id');
|
||||
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story');
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false);
|
||||
$stages = $this->dao->select('*')->from(TABLE_STORYSTAGE)->where('story')->in(array_keys($stories))->andWhere('branch')->eq($release->branch)->fetchPairs('story', 'stage');
|
||||
foreach($stages as $storyID => $stage) $stories[$storyID]->stage = $stage;
|
||||
|
||||
@@ -238,6 +239,7 @@ class release extends control
|
||||
$this->view->storyPager = $storyPager;
|
||||
$this->view->bugPager = $bugPager;
|
||||
$this->view->leftBugPager = $leftBugPager;
|
||||
$this->view->builds = $this->loadModel('build')->getBuildPairs($release->product, 'all', 'withbranch|hasproject', 0, 'execution', '', false);
|
||||
|
||||
if($this->app->getViewType() == 'json')
|
||||
{
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
</th>
|
||||
<th class='c-pri' title=<?php echo $lang->pri;?>><?php common::printOrderLink('pri', $orderBy, $vars, $lang->priAB);?></th>
|
||||
<th class="text-left"><?php common::printOrderLink('title', $orderBy, $vars, $lang->story->title);?></th>
|
||||
<th class='c-build w-200px text-left'><?php echo $lang->build->common;?></th>
|
||||
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
|
||||
<th class='w-80px text-right'><?php common::printOrderLink('estimate', $orderBy, $vars, $lang->story->estimateAB);?></th>
|
||||
<th class='w-90px text-center'> <?php common::printOrderLink('status', $orderBy, $vars, $lang->statusAB);?></th>
|
||||
@@ -100,6 +101,7 @@
|
||||
echo html::a($storyLink,$story->title, '', "class='preview'");
|
||||
?>
|
||||
</td>
|
||||
<td class='text-left' title='<?php echo $story->buildName?>'><?php echo $story->buildName?></td>
|
||||
<td><?php echo zget($users, $story->openedBy);?></td>
|
||||
<td class='text-right' title="<?php echo $story->estimate . ' ' . $lang->hourCommon;?>"><?php echo $story->estimate . $config->hourUnit;?></td>
|
||||
<td class='text-center'>
|
||||
@@ -169,6 +171,7 @@
|
||||
</th>
|
||||
<th class='text-left'><?php common::printOrderLink('title', $orderBy, $vars, $lang->bug->title);?></th>
|
||||
<th class='w-100px'> <?php common::printOrderLink('status', $orderBy, $vars, $lang->bug->status);?></th>
|
||||
<th class='c-build w-200px text-left'><?php echo $lang->bug->resolvedBuild;?></th>
|
||||
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
|
||||
<th class='w-date'> <?php common::printOrderLink('openedDate', $orderBy, $vars, $lang->bug->openedDateAB);?></th>
|
||||
<th class='c-user'> <?php common::printOrderLink('resolvedBy', $orderBy, $vars, $lang->bug->resolvedByAB);?></th>
|
||||
@@ -193,6 +196,8 @@
|
||||
<td>
|
||||
<span class='status-bug status-<?php echo $bug->status?>'><?php echo $this->processStatus('bug', $bug);?></span>
|
||||
</td>
|
||||
<?php $resolvedBuildName = zget($builds, $bug->resolvedBuild, '');?>
|
||||
<td class='text-left' title='<?php echo $resolvedBuildName?>'><?php echo $resolvedBuildName;?></td>
|
||||
<td><?php echo zget($users, $bug->openedBy);?></td>
|
||||
<td><?php echo substr($bug->openedDate, 5, 11)?></td>
|
||||
<td><?php echo zget($users, $bug->resolvedBy);?></td>
|
||||
@@ -259,6 +264,7 @@
|
||||
<th class='w-80px' title=<?php echo $lang->bug->severity;?>><?php common::printOrderLink('severity', $orderBy, $vars, $lang->bug->severityAB);?></th>
|
||||
<th class='text-left'> <?php common::printOrderLink('title', $orderBy, $vars, $lang->bug->title);?></th>
|
||||
<th class='w-100px'> <?php common::printOrderLink('status', $orderBy, $vars, $lang->bug->status);?></th>
|
||||
<th class='c-build w-200px text-left'><?php echo $lang->bug->openedBuild;?></th>
|
||||
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
|
||||
<th class='w-150px'> <?php common::printOrderLink('openedDate', $orderBy, $vars, $lang->bug->openedDateAB);?></th>
|
||||
<th class='w-50px'> <?php echo $lang->actions;?></th>
|
||||
@@ -297,6 +303,11 @@
|
||||
</td>
|
||||
<td class='text-left nobr' title='<?php echo $bug->title?>'><?php echo html::a($bugLink, $bug->title, '', "class='preview'");?></td>
|
||||
<td><span class='status-<?php echo $bug->status?>'> <?php echo $this->processStatus('bug', $bug);?></span></td>
|
||||
<?php
|
||||
$openedBuildName = '';
|
||||
foreach(explode(',', $bug->openedBuild) as $buildID) $openedBuildName .= zget($builds, $buildID, '') . ' ';
|
||||
?>
|
||||
<td class='text-left' title='<?php echo $openedBuildName?>'><?php echo $openedBuildName;?></td>
|
||||
<td><?php echo zget($users, $bug->openedBy);?></td>
|
||||
<td><?php echo $bug->openedDate?></td>
|
||||
<td class='c-actions'>
|
||||
|
||||
@@ -89,7 +89,7 @@ class testsuite extends control
|
||||
$privateNum++;
|
||||
}
|
||||
}
|
||||
$suitesNum = !empty(count($suites, 0)) ? count($suites, 0) : 0;
|
||||
$suitesNum = !empty($suites) ? count($suites) : 0;
|
||||
$publicNum = $suitesNum - $privateNum;
|
||||
$summary = str_replace(array('%total%', '%public%', '%private%'), array($suitesNum, $publicNum, $privateNum), $this->lang->testsuite->summary);
|
||||
|
||||
|
||||
@@ -756,6 +756,9 @@ class upgradeModel extends model
|
||||
case 'max3_3':
|
||||
$this->addReviewIssusApprovalData();
|
||||
break;
|
||||
case 'max4_0_beta1':
|
||||
$this->initReviewEfforts();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7969,4 +7972,31 @@ class upgradeModel extends model
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init review efforts.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function initReviewEfforts()
|
||||
{
|
||||
$nodes = $this->dao->select('t1.id,t3.id as reviewID,t3.title,t2.id as approvalID,t1.extra as consumed')->from(TABLE_APPROVALNODE)->alias('t1')
|
||||
->leftJoin(TABLE_APPROVAL)->alias('t2')->on("t1.approval=t2.id")
|
||||
->leftJoin(TABLE_REVIEW)->alias('t3')->on("t2.objectID=t3.id")
|
||||
->where('t3.deleted')->eq('0')
|
||||
->andWhere('t2.deleted')->eq('0')
|
||||
->andWhere('t2.objectType')->eq('review')
|
||||
->andWhere('t1.extra')->ne('')
|
||||
->andWhere('t1.extra')->ne(0)
|
||||
->orderBy('t1.approval,t1.id')
|
||||
->fetchAll('id');
|
||||
$this->loadModel('effort');
|
||||
foreach($nodes as $node)
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_EFFORT)->where('objectType')->eq('review')->andWhere('objectID')->eq($node->reviewID)->exec();
|
||||
$this->effort->create('review', $node->reviewID, (int)$node->consumed, $node->title, $node->approvalID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,11 +219,11 @@ tbody tr td.c-actions .dividing-line {width: 1px; height: 16px; display: inline-
|
||||
|
||||
.pri-text > .label-pri {padding: 1px 5px;}
|
||||
.pri-text > .label-pri, .pri-text > .label-severity {height: 20px;}
|
||||
.pri-text > .label-severity {background-size: 80%; background-position: 2px -130px;}
|
||||
.pri-text > .label-severity[data-severity="1"] {color: #FB2B2B; background-position: 2px 1px;}
|
||||
.pri-text > .label-severity[data-severity="2"] {color: #FF8058; background-position: 2px -31px;}
|
||||
.pri-text > .label-severity[data-severity="3"] {color: #FAAE1A; background-position: 2px -64px;}
|
||||
.pri-text > .label-severity[data-severity="4"] {color: #B89664; background-position: 2px -97px;}
|
||||
.pri-text > .label-severity {background-size: 85%; background-position: 1px -139px;}
|
||||
.pri-text > .label-severity[data-severity="1"] {color: #FB2B2B; background-position: 2px 0px;}
|
||||
.pri-text > .label-severity[data-severity="2"] {color: #FF8058; background-position: 2px -34px;}
|
||||
.pri-text > .label-severity[data-severity="3"] {color: #FAAE1A; background-position: 1px -69px;}
|
||||
.pri-text > .label-severity[data-severity="4"] {color: #B89664; background-position: 1px -104px;}
|
||||
|
||||
.flow-block .footerbar span.label-success {background-color: #0DBB7D;}
|
||||
.flow-block .footerbar span.label-default {background-color: #9EA3B0;}
|
||||
|
||||
@@ -0,0 +1,991 @@
|
||||
<?php
|
||||
class zentaoBot extends xuanBot
|
||||
{
|
||||
/**
|
||||
* Bot name.
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $name = 'ZenTao';
|
||||
|
||||
/**
|
||||
* Bot codename.
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $code = 'zentao';
|
||||
|
||||
/**
|
||||
* Command list, inits in init() since all commands are internal commands.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $commands = array();
|
||||
|
||||
/**
|
||||
* Construct function, load lang of zentao and setup commands.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if(isset($_SESSION['clientLang']))
|
||||
{
|
||||
if($_SESSION['clientLang'] == 'zh-cn') $this->name = '禅道';
|
||||
if($_SESSION['clientLang'] == 'zh-tw') $this->name = '禪道';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init bot, load model.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->lang = $this->im->lang->im->bot->zentaoBot;
|
||||
|
||||
foreach(array('view', 'start', 'close', 'finish') as $command)
|
||||
{
|
||||
$this->commands[] = array('command' => $command, 'alias' => $this->lang->commands->$command->alias, 'description' => $this->lang->commands->$command->description, 'internal' => true);
|
||||
}
|
||||
|
||||
$this->im->loadModel('task');
|
||||
$this->im->loadModel('user');
|
||||
$this->im->app->loadClass('pager', $static = true);
|
||||
|
||||
$this->users = array_filter($this->im->user->getPairs('noclosed|noletter'));
|
||||
$this->taskStatusList = array_filter($this->im->lang->task->statusList);
|
||||
$this->help = $this->lang->help;
|
||||
|
||||
$this->inited = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close command.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @access public
|
||||
* @return object|string
|
||||
*/
|
||||
public function close($args = array())
|
||||
{
|
||||
$args = $this->removeEqualSign($args);
|
||||
foreach($args as $key => $value)
|
||||
{
|
||||
if(in_array(strtolower($value), $this->lang->condKeywords['task'])) unset($args[$key]);
|
||||
}
|
||||
$task = $this->closeTask($args);
|
||||
return $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish command.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @access public
|
||||
* @return object|string
|
||||
*/
|
||||
public function finish($args = array())
|
||||
{
|
||||
$originArgs = $args;
|
||||
$args = array_filter(explode('=', implode('=', $args)));
|
||||
foreach($args as $key => $value)
|
||||
{
|
||||
if(in_array(strtolower($value), $this->lang->condKeywords['task'])) unset($args[$key]);
|
||||
}
|
||||
return $this->finishTask($args, $originArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* View command.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @param int $userID
|
||||
* @param object $user
|
||||
* @access public
|
||||
* @return object|string
|
||||
*/
|
||||
public function view($args = array(), $userID = 0, $user = null)
|
||||
{
|
||||
$pager = pager::init(0, 10, 1);
|
||||
|
||||
$pageSearchRegex = $this->lang->pageSearchRegex;
|
||||
$pageIDKeywords = $this->lang->condKeywords['pageID'];
|
||||
|
||||
$args = array_filter($args, function($arg) use ($pager, $pageSearchRegex, $pageIDKeywords)
|
||||
{
|
||||
$matches = array();
|
||||
preg_match($pageSearchRegex, $arg, $matches);
|
||||
if(count($matches) > 2)
|
||||
{
|
||||
if(in_array(strtolower($matches[1]), $pageIDKeywords))
|
||||
{
|
||||
$pager->pageID = $matches[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
$pager->recPerPage = $matches[2];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
$originArgs = $args;
|
||||
|
||||
$args = $this->removeEqualSign($args);
|
||||
|
||||
foreach($args as $key => $value)
|
||||
{
|
||||
if(in_array(strtolower($value), $this->lang->condKeywords['task']))
|
||||
{
|
||||
unset($args[$key]);
|
||||
$tasks = $this->viewTask($args, $user, $pager);
|
||||
return $this->renderTask($tasks, $originArgs, $pager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse bot command arguments.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @param array $keys
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function parseArguments($args = array(), $keys = array())
|
||||
{
|
||||
$assignedToList = '';
|
||||
$priList = '';
|
||||
$statusList = '';
|
||||
$idList = '';
|
||||
$taskName = '';
|
||||
$comment = '';
|
||||
while(!empty($args))
|
||||
{
|
||||
$arg = array_shift($args);
|
||||
if(isset($keys['pri']))
|
||||
{
|
||||
if(in_array(strtolower($arg), $this->lang->condKeywords['pri']))
|
||||
{
|
||||
$pris = array_shift($args);
|
||||
$priList = $this->buildCondParamByModifier($pris, $priList, 'p');
|
||||
continue;
|
||||
}
|
||||
if(preg_match('/p[1-4]/i', $arg) == 1)
|
||||
{
|
||||
$priList = $this->buildCondParamByModifier($arg, $priList, 'p');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(isset($keys['id']))
|
||||
{
|
||||
if(in_array(strtolower($arg), $this->lang->condKeywords['id']))
|
||||
{
|
||||
$ids = array_shift($args);
|
||||
$idList .= $this->buildCondParamByModifier($ids, $idList, '#');
|
||||
continue;
|
||||
}
|
||||
if(preg_match('/#\d+/', $arg) == 1)
|
||||
{
|
||||
$idList .= $this->buildCondParamByModifier($arg, $idList, '#');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(isset($keys['status']))
|
||||
{
|
||||
if(in_array(strtolower($arg), $this->lang->condKeywords['status']))
|
||||
{
|
||||
$statuses = array_shift($args);
|
||||
$statusList = $this->buildCondParam($statuses, $statusList, $this->taskStatusList);
|
||||
continue;
|
||||
}
|
||||
if($this->checkCondType($arg, $this->taskStatusList))
|
||||
{
|
||||
$statusList = $this->buildCondParam($arg, $statusList, $this->taskStatusList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(isset($keys['assignTo']))
|
||||
{
|
||||
if(in_array(strtolower($arg), $this->lang->condKeywords['assignTo']))
|
||||
{
|
||||
$usernames = array_shift($args);
|
||||
$assignedToList = $this->buildCondParam($usernames, $assignedToList, $this->users);
|
||||
continue;
|
||||
}
|
||||
if($this->checkCondType($arg, $this->users))
|
||||
{
|
||||
$assignedToList = $this->buildCondParam($arg, $assignedToList, $this->users);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(isset($keys['comment']))
|
||||
{
|
||||
if(in_array(strtolower($arg), $this->lang->condKeywords['comment']))
|
||||
{
|
||||
$comment = array_shift($args);
|
||||
}
|
||||
else {
|
||||
$comment = $arg;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(isset($keys['taskName']))
|
||||
{
|
||||
if(in_array(strtolower($arg), $this->lang->condKeywords['taskName']))
|
||||
{
|
||||
$taskName = array_shift($args);
|
||||
}
|
||||
else
|
||||
{
|
||||
$taskName = $arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$conds = new stdClass();
|
||||
if(isset($keys['pri'])) $conds->priList = trim($priList, ',');
|
||||
if(isset($keys['assignTo'])) $conds->assignedToList = trim($assignedToList, ',');
|
||||
if(isset($keys['status'])) $conds->statusList = trim($statusList, ',');
|
||||
if(isset($keys['id'])) $conds->idList = trim($idList, ',');
|
||||
if(isset($keys['taskName'])) $conds->taskName = $taskName;
|
||||
if(isset($keys['comment'])) $conds->comment = $comment;
|
||||
return $conds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check condition type.
|
||||
*
|
||||
* @param string $arg
|
||||
* @param array $list
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function checkCondType($arg, $list)
|
||||
{
|
||||
$args = explode(',', $arg);
|
||||
$array_inter = array_intersect($args, $list);
|
||||
if(!empty($array_inter)) return true;
|
||||
|
||||
$list = array_flip($list);
|
||||
$array_inter = array_intersect($args, $list);
|
||||
if(!empty($array_inter)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build condition param.
|
||||
*
|
||||
* @param string $arg bot command arguments
|
||||
* @param string $condParam
|
||||
* @param array $condArr
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function buildCondParam($arg, $condParam, $condArr)
|
||||
{
|
||||
$arr = explode(',', $arg);
|
||||
foreach($arr as $cond)
|
||||
{
|
||||
if(array_key_exists($cond, $condArr))
|
||||
{
|
||||
$condParam .= ',' . $cond;
|
||||
}
|
||||
else
|
||||
{
|
||||
$key = array_search($cond, $condArr);
|
||||
if($key !== false) $condParam .= ',' . $key;
|
||||
}
|
||||
}
|
||||
return $condParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove equal sign from bot command arguments.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function removeEqualSign($args)
|
||||
{
|
||||
return array_filter(explode('=', implode('=', $args)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build condition param by modifier.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @param string $condParam
|
||||
* @param string $modifier
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function buildCondParamByModifier($arg, $condParam, $modifier)
|
||||
{
|
||||
$arg = strtolower($arg);
|
||||
$arg = str_replace($modifier, '', $arg);
|
||||
$condParam .= ',' . $arg;
|
||||
return $condParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* View task command.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @param object $user
|
||||
* @param object $pager
|
||||
* @access public
|
||||
* @return object|string
|
||||
*/
|
||||
public function viewTask($args = array(), $user = null, $pager = null)
|
||||
{
|
||||
if(empty($args))
|
||||
{
|
||||
$conds = new stdClass();
|
||||
$conds->assignedToList = is_object($user) ? $user->account : false;
|
||||
$conds->statusList = 'wait,doing,done,pause,cancel';
|
||||
return $this->im->task->getListByConds($conds, 'status_asc', 0, $pager);
|
||||
}
|
||||
|
||||
$keys = array();
|
||||
$keys['pri'] = true;
|
||||
$keys['id'] = true;
|
||||
$keys['status'] = true;
|
||||
$keys['assignTo'] = true;
|
||||
$keys['taskName'] = true;
|
||||
|
||||
$conds = $this->parseArguments($args, $keys);
|
||||
return $this->im->task->getListByConds($conds, 'status_asc', 0, $pager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close task command.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @access public
|
||||
* @return object|string
|
||||
*/
|
||||
public function closeTask($args)
|
||||
{
|
||||
$keys = array();
|
||||
$keys['id'] = true;
|
||||
$keys['comment'] = true;
|
||||
|
||||
$conds = $this->parseArguments($args, $keys);
|
||||
$tasks = array_filter(explode(',', $conds->idList));
|
||||
$taskID = array_pop($tasks);
|
||||
$comment = $conds->comment;
|
||||
|
||||
if(!is_numeric($taskID)) return $this->lang->errors->invalidCommand;
|
||||
|
||||
$task = $this->loadEntry('task', 'get', array('taskID'=> $taskID));
|
||||
if(empty($task)) return $this->lang->errors->emptyResult;
|
||||
|
||||
if($task->status != 'done' && $task->status != 'cancel') return sprintf($this->lang->errors->invalidStatus, $this->taskStatusList[$task->status]);
|
||||
|
||||
$task = $this->loadEntry('taskclose', 'post', array('taskID' => $taskID, 'comment' => $comment));
|
||||
if($task->result == 'fail') return $task->message;
|
||||
|
||||
$link = str_replace('x.php', 'index.php', helper::createLink('task', 'view', "taskID=$taskID", 'html'));
|
||||
$messages = new stdClass();
|
||||
$messages->type = 'url';
|
||||
$messages->url = common::getSysURL() . $link;
|
||||
return array($this->lang->success, $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish task.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array $originArgs
|
||||
* @access public
|
||||
* @return object|string
|
||||
*/
|
||||
private function finishTask($args, $originArgs)
|
||||
{
|
||||
$taskID = '';
|
||||
$params = array();
|
||||
while(!empty($args))
|
||||
{
|
||||
$arg = array_shift($args);
|
||||
if(in_array(strtolower($arg), $this->lang->condKeywords['id']))
|
||||
{
|
||||
$taskID = array_shift($args);
|
||||
$taskID = str_replace('#', '', $taskID);
|
||||
}
|
||||
elseif(preg_match('/#\d+/', $arg) == 1)
|
||||
{
|
||||
$taskID = str_replace('#', '', $arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_str(str_replace('amp;', '', end($originArgs)), $params);
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_numeric($taskID)) return $this->lang->errors->invalidCommand;
|
||||
|
||||
$task = $this->loadEntry('task', 'get', array('taskID'=> $taskID));
|
||||
if(!common::hasPriv('task', 'finish')) return $this->lang->errors->unauthorized;
|
||||
|
||||
if(empty($task) || isset($task->error)) return $this->lang->errors->emptyResult;
|
||||
if($task->status != 'wait' && $task->status != 'doing') return sprintf($this->lang->errors->invalidStatus, $this->taskStatusList[$task->status]);
|
||||
|
||||
$reply = new stdClass();
|
||||
$reply->messages = array();
|
||||
$reply->responses = array();
|
||||
|
||||
$realStarted = $this->formatDate($task, 'realStarted');
|
||||
$finishedDate = $this->formatDate($task, 'finishedDate');
|
||||
|
||||
if(empty($params))
|
||||
{
|
||||
$originCommand = rawurlencode($this->lang->finishTask . ' ' . implode(' ', $originArgs));
|
||||
|
||||
$json = (object)array
|
||||
(
|
||||
'title' => "#{$taskID} {$task->name}",
|
||||
'submitLabel' => $this->im->lang->task->finish,
|
||||
'inputs' =>
|
||||
array(
|
||||
(object)array
|
||||
(
|
||||
'name' => $this->im->lang->task->hasConsumed,
|
||||
'type' => 'input',
|
||||
'label' => $this->im->lang->task->hasConsumed,
|
||||
'value' => $task->consumed,
|
||||
'disabled' => true,
|
||||
'addon' => $this->im->lang->task->hour,
|
||||
),
|
||||
(object)array
|
||||
(
|
||||
'name' => $this->im->lang->task->currentConsumed,
|
||||
'type' => 'input',
|
||||
'label' => $this->im->lang->task->currentConsumed,
|
||||
'value' => 0,
|
||||
'addon' => $this->im->lang->task->hour,
|
||||
),
|
||||
(object)array
|
||||
(
|
||||
'name' => $this->im->lang->task->realStarted,
|
||||
'type' => 'datetime-local',
|
||||
'label' => $this->im->lang->task->realStarted,
|
||||
'value' => $realStarted,
|
||||
),
|
||||
(object)array
|
||||
(
|
||||
'name' => $this->im->lang->task->finishedDate,
|
||||
'type' => 'datetime-local',
|
||||
'label' => $this->im->lang->task->finishedDate,
|
||||
'value' => $finishedDate,
|
||||
),
|
||||
),
|
||||
);
|
||||
$json = rawurlencode(json_encode($json));
|
||||
|
||||
$reply->messages[] = $this->lang->finish->tip;
|
||||
$reply->messages[] = "[{$this->lang->finish->tipLinkTitle}](xxc://openFormAndSendToServerBySendbox/$json/$originCommand)";
|
||||
}
|
||||
else
|
||||
{
|
||||
$messageID = isset($params['messageId']) ? $params['messageId'] : '';
|
||||
unset($params['messageId']);
|
||||
|
||||
$currentConsumed = 0;
|
||||
|
||||
foreach($params as $key => $value)
|
||||
{
|
||||
if(in_array(strtolower($key), $this->lang->condKeywords['realStarted'])) $realStarted = $value;
|
||||
if(in_array(strtolower($key), $this->lang->condKeywords['finishedDate'])) $finishedDate = $value;
|
||||
if(in_array(strtolower($key), $this->lang->condKeywords['currentConsumed'])) $currentConsumed = $value;
|
||||
};
|
||||
|
||||
if(empty($realStarted) || empty($finishedDate)) return '';
|
||||
|
||||
$params = array
|
||||
(
|
||||
'currentConsumed' => $currentConsumed,
|
||||
'realStarted' => $realStarted,
|
||||
'finishedDate' => $finishedDate,
|
||||
);
|
||||
|
||||
$task = $this->loadEntry('taskfinish', 'post', array_merge(array('taskID' => $taskID, 'assignedTo' => '', 'comment' => ''), $params));
|
||||
if(isset($task->error))
|
||||
{
|
||||
foreach($task->error as $error) $reply->messages[] = is_array($error) ? implode(',', $error): $error;
|
||||
return $reply;
|
||||
}
|
||||
|
||||
$finishedDate = $this->formatDate($task, 'finishedDate', '', 'Y-m-d H:i:s');
|
||||
|
||||
$reply->messages[] = sprintf($this->lang->finish->done, $taskID, $finishedDate, $task->consumed);
|
||||
$reply->messages[] = $this->renderTaskTable(array($task));
|
||||
|
||||
$reply->responses[] = $this->disableMarkdownLinkInMessage($messageID);
|
||||
|
||||
if(!empty($task->fromBug))
|
||||
{
|
||||
$reply->messages[] = sprintf($this->lang->finish->bugTip, $taskID);
|
||||
$link = commonModel::getSysURL() . str_replace('x.php', 'index.php', helper::createLink('bug', 'view', "bugID={$task->fromBug}", 'html'));
|
||||
$href = urlencode($link);
|
||||
$reply->messages[] = "[{$this->lang->finish->bugTipLinkTitle}](xxc:openInApp/zentao-integrated/{$href})";
|
||||
}
|
||||
}
|
||||
|
||||
return $reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date.
|
||||
*
|
||||
* @param object $task
|
||||
* @param string $field
|
||||
* @param string $default
|
||||
* @param string $format
|
||||
* @return string
|
||||
*/
|
||||
private function formatDate($task, $field, $default = '', $format = 'Y-m-d\TH:i')
|
||||
{
|
||||
try
|
||||
{
|
||||
$datetime = new DateTime(empty($task->$field) ? 'now' : $task->$field, new DateTimeZone('UTC'));
|
||||
$datetime->setTimezone(new DateTimeZone(date_default_timezone_get()));
|
||||
return $datetime->format($format);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render tasks as table html.
|
||||
*
|
||||
* @param array $tasks
|
||||
* @param array $originArgs
|
||||
* @param pager $pager
|
||||
* @return string|array
|
||||
*/
|
||||
public function renderTask($tasks, $originArgs, $pager)
|
||||
{
|
||||
$lang = $this->im->lang;
|
||||
if(!$tasks || $pager->recTotal === 0) return $lang->task->noTask;
|
||||
|
||||
$sysURL = common::getSysURL();
|
||||
|
||||
if($pager->recTotal == 1)
|
||||
{
|
||||
$task = current($tasks);
|
||||
$link = str_replace('x.php', 'index.php', helper::createLink('task', 'view', "taskID=$task->id", 'html'));
|
||||
|
||||
$messages = new stdclass();
|
||||
$messages->type = 'url';
|
||||
$messages->url = $sysURL . $link;
|
||||
return array(sprintf($this->lang->tasksFound, $pager->recTotal), $messages);
|
||||
}
|
||||
|
||||
$messages = array();
|
||||
if($pager->pageID == 1)
|
||||
{
|
||||
$messages[] = sprintf($this->lang->tasksFound, $pager->recTotal);
|
||||
}
|
||||
|
||||
$taskTable = $this->renderTaskTable($tasks);
|
||||
|
||||
$originCommand = $this->lang->viewTask . ' ' . implode(' ', $originArgs);
|
||||
$paging = "$pager->pageID / $pager->pageTotal";
|
||||
if($pager->pageID != 1)
|
||||
{
|
||||
$previousPageCommand = $originCommand . " {$this->lang->condKeywords['pageID'][1]}=" . ($pager->pageID - 1) . " {$this->lang->condKeywords['recPerPage'][1]}=" . $pager->recPerPage;
|
||||
$previousPageCommand = rawurlencode($previousPageCommand);
|
||||
$paging .= " [{$this->lang->prevPage}](xxc://sendContentToServerBySendbox/{$previousPageCommand})";
|
||||
}
|
||||
|
||||
if($pager->pageID != $pager->pageTotal)
|
||||
{
|
||||
$nextPageCommand = $originCommand . " {$this->lang->condKeywords['pageID'][1]}=" . ($pager->pageID + 1) . " {$this->lang->condKeywords['recPerPage'][1]}=" . $pager->recPerPage;
|
||||
$nextPageCommand = rawurlencode($nextPageCommand);
|
||||
$paging .= " [{$this->lang->nextPage}](xxc://sendContentToServerBySendbox/{$nextPageCommand})";
|
||||
}
|
||||
$paging = "<div class=\"text-right\">$paging</div>";
|
||||
|
||||
$messages[] = $taskTable . $paging;
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render tasks as markdown table.
|
||||
*
|
||||
* @param array $tasks
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function renderTaskTable($tasks)
|
||||
{
|
||||
$lang = $this->im->lang;
|
||||
$sysURL = common::getSysURL();
|
||||
$headMap = array('id'=>'ID', 'pri' => $lang->task->pri, 'name' => $lang->task->name, 'assignedTo' => $lang->task->assignedTo, 'status' => $lang->task->status, 'estimate' => $lang->task->estimateAB, 'consumed' => $lang->task->consumedAB, 'left' => $lang->task->leftAB, 'actions' => $lang->actions);
|
||||
$thead = '';
|
||||
|
||||
foreach($headMap as $value) $thead .= "<th>{$value}</th>";
|
||||
|
||||
$thead = "<thead class='text-nowrap'><tr>{$thead}</tr></thead>";
|
||||
$tbody = '';
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
$tr = '';
|
||||
$link = str_replace('x.php', 'index.php', helper::createLink('task', 'view', "taskID=$task->id", 'html'));
|
||||
$href = urlencode($sysURL . $link);
|
||||
$isParent = $task->parent == -1;
|
||||
$isChild = $task->parent > 0;
|
||||
$isMulti = !empty($task->mode);
|
||||
foreach($headMap as $key => $value)
|
||||
{
|
||||
switch($key)
|
||||
{
|
||||
case 'name':
|
||||
$tr .= "<td>";
|
||||
if($isParent) $tr .= " <span class='label label-sm circle'>{$lang->task->parentAB}</span> ";
|
||||
if($isChild) $tr .= " <span class='label label-sm circle'>{$lang->task->childrenAB}</span> ";
|
||||
if($isMulti) $tr .= " <span class='label label-sm circle'>" . zget($lang->task->modeList, $task->mode) . "</span> ";
|
||||
$tr .= "<a href='xxc:openInApp/zentao-integrated/{$href}'>{$task->$key}</a>";
|
||||
$tr .= "</td>";
|
||||
break;
|
||||
case 'status':
|
||||
$tr .= "<td class='text-nowrap'>{$this->taskStatusList[$task->$key]}</td>";
|
||||
break;
|
||||
case 'assignedTo':
|
||||
if($task->mode == 'multi')
|
||||
{
|
||||
$tr .= "<td class='text-nowrap'>{$lang->task->team}</td>";
|
||||
}
|
||||
else if($task->$key == 'closed')
|
||||
{
|
||||
$tr .= "<td class='text-nowrap'>Closed</td>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$tr .= "<td class='text-nowrap'>{$this->users[$task->$key]}</td>";
|
||||
}
|
||||
break;
|
||||
case 'actions':
|
||||
$startUrl = 'xxc://sendContentToServerBySendbox/' . "{$this->lang->startTask} #$task->id";
|
||||
$finishUrl = 'xxc://sendContentToServerBySendbox/' . "{$this->lang->finishTask} #$task->id";
|
||||
$closeUrl = 'xxc://sendContentToServerBySendbox/' . "{$this->lang->closeTask} #$task->id";
|
||||
|
||||
$canStart = in_array($task->status, array('wait', 'pause'));
|
||||
$canFinish = in_array($task->status, array('wait', 'pause', 'doing'));
|
||||
$canClose = in_array($task->status, array('done', 'cancel'));
|
||||
|
||||
/* Disable all if task has children. */
|
||||
if($isParent)
|
||||
{
|
||||
$canStart = false;
|
||||
$canFinish = false;
|
||||
$canClose = false;
|
||||
}
|
||||
|
||||
$tr .= '<td><div class="flex gap-xs">';
|
||||
$tr .= $canStart ? "<a href='$startUrl'><i class='icon-zt-play'></i></a>" : "<div><i class='icon-zt-play disabled'></i></div>";
|
||||
$tr .= $canFinish ? "<a href='$finishUrl'><i class='icon-zt-checked'></i></a>" : "<div><i class='icon-zt-checked disabled'></i></div>";
|
||||
$tr .= $canClose ? "<a href='$closeUrl'><i class='icon-zt-off'></i></a>" : "<div><i class='icon-zt-off disabled'></i></div>";
|
||||
$tr .= '</div></td>';
|
||||
break;
|
||||
case 'estimate':
|
||||
case 'consumed':
|
||||
case 'left':
|
||||
$tr .= "<td class='text-nowrap'>{$task->$key}h</td>";
|
||||
break;
|
||||
default:
|
||||
$tr .= "<td class='text-nowrap'>{$task->$key}</td>";
|
||||
}
|
||||
}
|
||||
$tbody .= "<tr>{$tr}</tr>";
|
||||
}
|
||||
return "<table>{$thead}{$tbody}</table>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Start task command.
|
||||
*
|
||||
* @param array $args bot command arguments
|
||||
* @param int $userID
|
||||
* @access public
|
||||
* @return object|string
|
||||
*/
|
||||
public function start($args = array(), $userID = 0)
|
||||
{
|
||||
$originArgs = $args;
|
||||
foreach($args as $key => $value)
|
||||
{
|
||||
if(in_array(strtolower($value), $this->lang->condKeywords['task']))
|
||||
{
|
||||
unset($args[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if(count($args) == 0) return $this->lang->errors->taskIDRequired;
|
||||
|
||||
$taskID = 0;
|
||||
foreach($args as $key => $value)
|
||||
{
|
||||
if(strpos($value, '#') !== false)
|
||||
{
|
||||
$id = str_replace('#', '', $value);
|
||||
if($id && is_numeric($id))
|
||||
{
|
||||
$taskID = $id;
|
||||
unset($args[$key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if($args[$key + 1] && is_numeric($args[$key + 1]))
|
||||
{
|
||||
$taskID = $args[$key + 1];
|
||||
unset($args[$key]);
|
||||
unset($args[$key + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$taskID) return $this->lang->errors->taskIDRequired;
|
||||
|
||||
$task = $this->loadEntry('task', 'get', array('taskID' => $taskID));
|
||||
if(!$task) return $this->lang->errors->taskNotFound;
|
||||
if($task->status != 'wait') return sprintf($this->lang->errors->invalidStatus, $this->taskStatusList[$task->status]);
|
||||
|
||||
$reply = new stdClass();
|
||||
$reply->messages = array();
|
||||
$reply->responses = array();
|
||||
|
||||
if(count($args) == 0)
|
||||
{
|
||||
$originCommand = rawurlencode($this->lang->startTask . ' ' . implode(' ', $originArgs));
|
||||
|
||||
$realStarted = $this->formatDate($task, 'realStarted');
|
||||
|
||||
$json = (object)array
|
||||
(
|
||||
'title' => "#{$taskID} {$task->name}",
|
||||
'submitLabel' => $this->im->lang->task->start,
|
||||
'inputs' => array
|
||||
(
|
||||
(object)array
|
||||
(
|
||||
'name' => $this->im->lang->task->realStarted,
|
||||
'type' => 'datetime-local',
|
||||
'label' => $this->im->lang->task->realStarted,
|
||||
'value' => $realStarted,
|
||||
),
|
||||
(object)array
|
||||
(
|
||||
'name' => $this->im->lang->task->consumed,
|
||||
'type' => 'number',
|
||||
'label' => $this->im->lang->task->consumed,
|
||||
'value' => $task->consumed,
|
||||
'addon' => $this->im->lang->task->hour,
|
||||
),
|
||||
(object)array
|
||||
(
|
||||
'name' => $this->im->lang->task->left,
|
||||
'type' => 'number',
|
||||
'label' => $this->im->lang->task->left,
|
||||
'value' => $task->left,
|
||||
'addon' => $this->im->lang->task->hour,
|
||||
'helpText' => $this->lang->start->finishWithZeroLeft,
|
||||
)
|
||||
),
|
||||
);
|
||||
$json = rawurlencode(json_encode($json));
|
||||
|
||||
$reply->messages[] = sprintf($this->lang->start->tip, $taskID);
|
||||
$reply->messages[] = "[{$this->lang->start->tipLinkTitle}](xxc://openFormAndSendToServerBySendbox/$json/$originCommand)";
|
||||
}
|
||||
else
|
||||
{
|
||||
$hasFormArgs = false;
|
||||
|
||||
$left = $task->left;
|
||||
$consumed = $task->consumed;
|
||||
$realStarted = $task->realStarted;
|
||||
|
||||
foreach($args as $arg)
|
||||
{
|
||||
if(strpos($arg, 'messageId'))
|
||||
{
|
||||
$hasFormArgs = true;
|
||||
$formArgs = explode('&', $arg);
|
||||
$replyArgs = array();
|
||||
foreach($formArgs as $formArg)
|
||||
{
|
||||
$item = explode('=', $formArg);
|
||||
if(count($item) == 2)
|
||||
{
|
||||
$replyArgs[$item[0]] = $item[1];
|
||||
}
|
||||
}
|
||||
foreach($replyArgs as $key => $value)
|
||||
{
|
||||
if(in_array(strtolower($key), $this->lang->condKeywords['consumed'])) $consumed = $value;
|
||||
if(in_array(strtolower($key), $this->lang->condKeywords['realStarted'])) $realStarted = $value;
|
||||
if(in_array(strtolower($key), $this->lang->condKeywords['left'])) $left = $value;
|
||||
}
|
||||
if($consumed == 0 && $left == 0) return $this->im->lang->task->noticeTaskStart;
|
||||
|
||||
$task = $this->loadEntry('taskStart', 'post', array
|
||||
(
|
||||
'taskID' => $taskID,
|
||||
'left' => $left,
|
||||
'realStarted' => $realStarted,
|
||||
'consumed' => $consumed,
|
||||
));
|
||||
if($task->result and $task->result == 'fail')
|
||||
{
|
||||
$reply->messages[] = $task->message;
|
||||
}
|
||||
elseif(isset($task->error))
|
||||
{
|
||||
foreach($task->error as $error) $reply->messages[] = is_array($error) ? implode(',', $error): $error;
|
||||
}
|
||||
else
|
||||
{
|
||||
$reply->messages[] = sprintf($this->lang->effortRecorded, $taskID);
|
||||
$reply->messages[] = $this->renderTaskTable(array($task));
|
||||
|
||||
$reply->responses[] = $this->disableMarkdownLinkInMessage($replyArgs['messageId']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$hasFormArgs)
|
||||
{
|
||||
while(!empty($args))
|
||||
{
|
||||
$arg = array_shift($args);
|
||||
if(in_array(strtolower($arg), $this->lang->condKeywords['left']))
|
||||
{
|
||||
$left = array_shift($args);
|
||||
}
|
||||
elseif(in_array(strtolower($arg), $this->lang->condKeywords['consumed']))
|
||||
{
|
||||
$consumed = array_shift($args);
|
||||
}
|
||||
elseif(in_array(strtolower($arg), $this->lang->condKeywords['realStarted']))
|
||||
{
|
||||
$realStarted = array_shift($args);
|
||||
}
|
||||
}
|
||||
|
||||
$task = $this->loadEntry('taskStart', 'post', array
|
||||
(
|
||||
'taskID' => $taskID,
|
||||
'left' => $left,
|
||||
'realStarted' => $realStarted,
|
||||
'consumed' => $consumed,
|
||||
));
|
||||
if($task->result and $task->result == 'fail')
|
||||
{
|
||||
$reply->messages[] = $task->message;
|
||||
}
|
||||
elseif(isset($task->error))
|
||||
{
|
||||
foreach($task->error as $error) $reply->messages[] = is_array($error) ? implode(',', $error): $error;
|
||||
}
|
||||
else
|
||||
{
|
||||
$reply->messages[] = sprintf($this->lang->effortRecorded, $taskID);
|
||||
$reply->messages[] = $this->renderTaskTable(array($task));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a Markdown link in the message and generate a response.
|
||||
*
|
||||
* @param $messageID
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
private function disableMarkdownLinkInMessage($messageID)
|
||||
{
|
||||
if(!$messageID) return null;
|
||||
$message = $this->im->message->getList('', array($messageID));
|
||||
$message = current($message);
|
||||
if($message->user != 'xuanbot') return null;
|
||||
$message->content = preg_replace('/\[(.*?)\]\(.*?\)/', '$1', $message->content);
|
||||
$this->im->message->setMessage($message);
|
||||
|
||||
$userID = array_filter(explode('&', $message->cgid), function($item)
|
||||
{
|
||||
return is_numeric($item);
|
||||
});
|
||||
|
||||
$response = new stdclass();
|
||||
$response->result = 'success';
|
||||
$response->method = 'messageUpdate';
|
||||
$response->users = $userID;
|
||||
$response->data = array($message);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to call zentao's API.
|
||||
*
|
||||
* @param string $entry
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
* @param string $version
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function loadEntry($entry, $action, $params = array(), $version = 'v1')
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->im->loadModel('user');
|
||||
|
||||
$user = $this->im->user->getByID($this->im->app->input['userID'], 'id');
|
||||
/* Authorize him and save to session. */
|
||||
$user->rights = $this->im->user->authorize($user->account);
|
||||
$user->groups = $this->im->user->getGroups($user->account);
|
||||
$user->view = $this->im->user->grantUserView($user->account, $user->rights['acls'], $user->rights['projects']);
|
||||
$user->admin = strpos($this->im->app->company->admins, ",{$user->account},") !== false;
|
||||
|
||||
global $app;
|
||||
$app->action = $action;
|
||||
$app->user = $user;
|
||||
|
||||
include_once($this->im->app->appRoot . "framework/api/entry.class.php");
|
||||
include_once($this->im->app->appRoot . "api/{$version}/entries/" . strtolower($entry) . ".php");
|
||||
|
||||
$entryName = $entry . 'Entry';
|
||||
$entry = new $entryName();
|
||||
|
||||
if($action == 'post' || $action == 'put')
|
||||
{
|
||||
$entry->requestBody = (object)$params;
|
||||
}
|
||||
|
||||
$content = call_user_func_array(array($entry, $action), $params);
|
||||
return json_decode($content);
|
||||
}
|
||||
catch(EndResponseException $endResponseException)
|
||||
{
|
||||
return $endResponseException->getContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,3 +36,89 @@ $lang->im->xxd->aes = 'Server-side AES';
|
||||
$lang->im->xxdAESTip = 'This only affects server-side AES encryption between XXB and XXD.';
|
||||
$lang->im->aesOptions['on'] = 'Enabled';
|
||||
$lang->im->aesOptions['off'] = 'Disabled';
|
||||
|
||||
$lang->im->bot->zentaoBot = new stdclass();
|
||||
$lang->im->bot->zentaoBot->name = 'ZenTao';
|
||||
$lang->im->bot->zentaoBot->pageSearchRegex = '/(pageID|recPerPage)=(\d+)/';
|
||||
|
||||
$lang->im->bot->zentaoBot->commands = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->view = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->view->description = 'View task';
|
||||
$lang->im->bot->zentaoBot->commands->start = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->start->description = 'Start task';
|
||||
$lang->im->bot->zentaoBot->commands->close = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->close->description = 'Close task';
|
||||
$lang->im->bot->zentaoBot->commands->finish = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->finish->description = 'Finish task';
|
||||
|
||||
$lang->im->bot->zentaoBot->condKeywords = array();
|
||||
$lang->im->bot->zentaoBot->condKeywords['task'] = array('task');
|
||||
$lang->im->bot->zentaoBot->condKeywords['pri'] = array('pri');
|
||||
$lang->im->bot->zentaoBot->condKeywords['status'] = array('status');
|
||||
$lang->im->bot->zentaoBot->condKeywords['assignTo'] = array('assignto', 'user');
|
||||
$lang->im->bot->zentaoBot->condKeywords['id'] = array('id');
|
||||
$lang->im->bot->zentaoBot->condKeywords['taskName'] = array('taskname');
|
||||
$lang->im->bot->zentaoBot->condKeywords['comment'] = array('comment');
|
||||
$lang->im->bot->zentaoBot->condKeywords['left'] = array('left');
|
||||
$lang->im->bot->zentaoBot->condKeywords['consumed'] = array('consumed');
|
||||
$lang->im->bot->zentaoBot->condKeywords['realStarted'] = array('realStarted');
|
||||
$lang->im->bot->zentaoBot->condKeywords['pageID'] = array('pageID');
|
||||
$lang->im->bot->zentaoBot->condKeywords['recPerPage'] = array('recPerPage');
|
||||
$lang->im->bot->zentaoBot->condKeywords['finishedDate'] = array('finishedDate');
|
||||
$lang->im->bot->zentaoBot->condKeywords['currentConsumed'] = array('currentConsumed');
|
||||
|
||||
$lang->im->bot->zentaoBot->success = 'Command executed successfully.';
|
||||
$lang->im->bot->zentaoBot->tasksFound = 'Found %d tasks.';
|
||||
$lang->im->bot->zentaoBot->prevPage = 'Prev Page';
|
||||
$lang->im->bot->zentaoBot->nextPage = 'Next Page';
|
||||
$lang->im->bot->zentaoBot->effortRecorded = 'Effort recorded for task #%d.';
|
||||
|
||||
$lang->im->bot->zentaoBot->finishTask = 'finish';
|
||||
$lang->im->bot->zentaoBot->closeTask = 'close';
|
||||
$lang->im->bot->zentaoBot->startTask = 'start';
|
||||
$lang->im->bot->zentaoBot->viewTask = 'view';
|
||||
|
||||
$lang->im->bot->zentaoBot->errors = new stdclass();
|
||||
$lang->im->bot->zentaoBot->errors->emptyResult = 'No task found.';
|
||||
$lang->im->bot->zentaoBot->errors->invalidCommand = 'Invalid command.';
|
||||
$lang->im->bot->zentaoBot->errors->invalidStatus = 'Cannot perform such action on task with %s status.';
|
||||
$lang->im->bot->zentaoBot->errors->unauthorized = 'You are not authorized to perform this action.';
|
||||
$lang->im->bot->zentaoBot->errors->taskIDRequired = 'Task ID is required.';
|
||||
$lang->im->bot->zentaoBot->errors->taskNotFound = 'Task not found.';
|
||||
|
||||
$lang->im->bot->zentaoBot->finish = new stdclass();
|
||||
$lang->im->bot->zentaoBot->finish->tip = 'Click the link below to finish the task. Time consumed and starting time of this task are required.';
|
||||
$lang->im->bot->zentaoBot->finish->tipLinkTitle = 'Finish Task';
|
||||
$lang->im->bot->zentaoBot->finish->done = 'Task #%d is finished, ended at:%s, time consumed: %.1f hours.';
|
||||
$lang->im->bot->zentaoBot->finish->bugTip = 'Task #%d is associated with a bug, you may mark the bug as resolved by clicking the link below.';
|
||||
$lang->im->bot->zentaoBot->finish->bugTipLinkTitle = 'Resolve Bug';
|
||||
|
||||
$lang->im->bot->zentaoBot->start = new stdclass();
|
||||
$lang->im->bot->zentaoBot->start->tip = 'Click the link below to start task #%d.';
|
||||
$lang->im->bot->zentaoBot->start->tipLinkTitle = 'Start Task';
|
||||
$lang->im->bot->zentaoBot->start->finishWithZeroLeft = 'Hours left is 0 thus the task is finished.';
|
||||
|
||||
$lang->im->bot->zentaoBot->help = <<<EOT
|
||||
### 1. Task command
|
||||
|
||||
Command:`view task condition...`
|
||||
Example:`view task dev1 P1 doing` Displays tasks assigned to dev1, with priority P1 and status in progress
|
||||
|
||||
| Command | Description |
|
||||
| ---- | ---- |
|
||||
| view task | Show all open tasks under the current username |
|
||||
| view task Name Keyword | Show tasks that match the name keyword |
|
||||
| view task Assignor | Show tasks whose assignor is the entered value |
|
||||
| view task Priority | Show tasks with priority as entered |
|
||||
| view task Status | Show tasks with status as input |
|
||||
| view task ID | Show tasks with ID as input |
|
||||
|
||||
### 2. Task Edit command
|
||||
The Task Edit command supports making status changes to tasks.
|
||||
|
||||
| Command | Description |
|
||||
| ---- | ---- |
|
||||
| start task #ID | Start the task and record its consumption/remaining hours |
|
||||
| complete task #ID | Complete the task and record its consumption/remaining hours |
|
||||
| close task #ID | Close the task and record its consumption/remaining work hours |
|
||||
EOT;
|
||||
|
||||
@@ -36,3 +36,89 @@ $lang->im->xxd->aes = 'Server-side AES';
|
||||
$lang->im->xxdAESTip = 'This only affects server-side AES encryption between XXB and XXD.';
|
||||
$lang->im->aesOptions['on'] = 'Enabled';
|
||||
$lang->im->aesOptions['off'] = 'Disabled';
|
||||
|
||||
$lang->im->bot->zentaoBot = new stdclass();
|
||||
$lang->im->bot->zentaoBot->name = 'ZenTao';
|
||||
$lang->im->bot->zentaoBot->pageSearchRegex = '/(pageID|recPerPage)=(\d+)/';
|
||||
|
||||
$lang->im->bot->zentaoBot->commands = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->view = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->view->description = 'View task';
|
||||
$lang->im->bot->zentaoBot->commands->start = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->start->description = 'Start task';
|
||||
$lang->im->bot->zentaoBot->commands->close = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->close->description = 'Close task';
|
||||
$lang->im->bot->zentaoBot->commands->finish = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->finish->description = 'Finish task';
|
||||
|
||||
$lang->im->bot->zentaoBot->condKeywords = array();
|
||||
$lang->im->bot->zentaoBot->condKeywords['task'] = array('task');
|
||||
$lang->im->bot->zentaoBot->condKeywords['pri'] = array('pri');
|
||||
$lang->im->bot->zentaoBot->condKeywords['status'] = array('status');
|
||||
$lang->im->bot->zentaoBot->condKeywords['assignTo'] = array('assignto', 'user');
|
||||
$lang->im->bot->zentaoBot->condKeywords['id'] = array('id');
|
||||
$lang->im->bot->zentaoBot->condKeywords['taskName'] = array('taskname');
|
||||
$lang->im->bot->zentaoBot->condKeywords['comment'] = array('comment');
|
||||
$lang->im->bot->zentaoBot->condKeywords['left'] = array('left');
|
||||
$lang->im->bot->zentaoBot->condKeywords['consumed'] = array('consumed');
|
||||
$lang->im->bot->zentaoBot->condKeywords['realStarted'] = array('realStarted');
|
||||
$lang->im->bot->zentaoBot->condKeywords['pageID'] = array('pageID');
|
||||
$lang->im->bot->zentaoBot->condKeywords['recPerPage'] = array('recPerPage');
|
||||
$lang->im->bot->zentaoBot->condKeywords['finishedDate'] = array('finishedDate');
|
||||
$lang->im->bot->zentaoBot->condKeywords['currentConsumed'] = array('currentConsumed');
|
||||
|
||||
$lang->im->bot->zentaoBot->success = 'Command executed successfully.';
|
||||
$lang->im->bot->zentaoBot->tasksFound = 'Found %d tasks.';
|
||||
$lang->im->bot->zentaoBot->prevPage = 'Prev Page';
|
||||
$lang->im->bot->zentaoBot->nextPage = 'Next Page';
|
||||
$lang->im->bot->zentaoBot->effortRecorded = 'Effort recorded for task #%d.';
|
||||
|
||||
$lang->im->bot->zentaoBot->finishTask = 'finish';
|
||||
$lang->im->bot->zentaoBot->closeTask = 'close';
|
||||
$lang->im->bot->zentaoBot->startTask = 'start';
|
||||
$lang->im->bot->zentaoBot->viewTask = 'view';
|
||||
|
||||
$lang->im->bot->zentaoBot->errors = new stdclass();
|
||||
$lang->im->bot->zentaoBot->errors->emptyResult = 'No task found.';
|
||||
$lang->im->bot->zentaoBot->errors->invalidCommand = 'Invalid command.';
|
||||
$lang->im->bot->zentaoBot->errors->invalidStatus = 'Cannot perform such action on task with %s status.';
|
||||
$lang->im->bot->zentaoBot->errors->unauthorized = 'You are not authorized to perform this action.';
|
||||
$lang->im->bot->zentaoBot->errors->taskIDRequired = 'Task ID is required.';
|
||||
$lang->im->bot->zentaoBot->errors->taskNotFound = 'Task not found.';
|
||||
|
||||
$lang->im->bot->zentaoBot->finish = new stdclass();
|
||||
$lang->im->bot->zentaoBot->finish->tip = 'Click the link below to finish the task. Time consumed and starting time of this task are required.';
|
||||
$lang->im->bot->zentaoBot->finish->tipLinkTitle = 'Finish Task';
|
||||
$lang->im->bot->zentaoBot->finish->done = 'Task #%d is finished, ended at:%s, time consumed: %.1f hours.';
|
||||
$lang->im->bot->zentaoBot->finish->bugTip = 'Task #%d is associated with a bug, you may mark the bug as resolved by clicking the link below.';
|
||||
$lang->im->bot->zentaoBot->finish->bugTipLinkTitle = 'Resolve Bug';
|
||||
|
||||
$lang->im->bot->zentaoBot->start = new stdclass();
|
||||
$lang->im->bot->zentaoBot->start->tip = 'Click the link below to start task #%d.';
|
||||
$lang->im->bot->zentaoBot->start->tipLinkTitle = 'Start Task';
|
||||
$lang->im->bot->zentaoBot->start->finishWithZeroLeft = 'Hours left is 0 thus the task is finished.';
|
||||
|
||||
$lang->im->bot->zentaoBot->help = <<<EOT
|
||||
### 1. Task command
|
||||
|
||||
Command:`view task condition...`
|
||||
Example:`view task dev1 P1 doing` Displays tasks assigned to dev1, with priority P1 and status in progress
|
||||
|
||||
| Command | Description |
|
||||
| ---- | ---- |
|
||||
| view task | Show all open tasks under the current username |
|
||||
| view task Name Keyword | Show tasks that match the name keyword |
|
||||
| view task Assignor | Show tasks whose assignor is the entered value |
|
||||
| view task Priority | Show tasks with priority as entered |
|
||||
| view task Status | Show tasks with status as input |
|
||||
| view task ID | Show tasks with ID as input |
|
||||
|
||||
### 2. Task Edit command
|
||||
The Task Edit command supports making status changes to tasks.
|
||||
|
||||
| Command | Description |
|
||||
| ---- | ---- |
|
||||
| start task #ID | Start the task and record its consumption/remaining hours |
|
||||
| complete task #ID | Complete the task and record its consumption/remaining hours |
|
||||
| close task #ID | Close the task and record its consumption/remaining work hours |
|
||||
EOT;
|
||||
|
||||
@@ -36,3 +36,89 @@ $lang->im->xxd->aes = 'Server-side AES';
|
||||
$lang->im->xxdAESTip = 'This only affects server-side AES encryption between XXB and XXD.';
|
||||
$lang->im->aesOptions['on'] = 'Enabled';
|
||||
$lang->im->aesOptions['off'] = 'Disabled';
|
||||
|
||||
$lang->im->bot->zentaoBot = new stdclass();
|
||||
$lang->im->bot->zentaoBot->name = 'ZenTao';
|
||||
$lang->im->bot->zentaoBot->pageSearchRegex = '/(pageID|recPerPage)=(\d+)/';
|
||||
|
||||
$lang->im->bot->zentaoBot->commands = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->view = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->view->description = 'View task';
|
||||
$lang->im->bot->zentaoBot->commands->start = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->start->description = 'Start task';
|
||||
$lang->im->bot->zentaoBot->commands->close = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->close->description = 'Close task';
|
||||
$lang->im->bot->zentaoBot->commands->finish = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->finish->description = 'Finish task';
|
||||
|
||||
$lang->im->bot->zentaoBot->condKeywords = array();
|
||||
$lang->im->bot->zentaoBot->condKeywords['task'] = array('task');
|
||||
$lang->im->bot->zentaoBot->condKeywords['pri'] = array('pri');
|
||||
$lang->im->bot->zentaoBot->condKeywords['status'] = array('status');
|
||||
$lang->im->bot->zentaoBot->condKeywords['assignTo'] = array('assignto', 'user');
|
||||
$lang->im->bot->zentaoBot->condKeywords['id'] = array('id');
|
||||
$lang->im->bot->zentaoBot->condKeywords['taskName'] = array('taskname');
|
||||
$lang->im->bot->zentaoBot->condKeywords['comment'] = array('comment');
|
||||
$lang->im->bot->zentaoBot->condKeywords['left'] = array('left');
|
||||
$lang->im->bot->zentaoBot->condKeywords['consumed'] = array('consumed');
|
||||
$lang->im->bot->zentaoBot->condKeywords['realStarted'] = array('realStarted');
|
||||
$lang->im->bot->zentaoBot->condKeywords['pageID'] = array('pageID');
|
||||
$lang->im->bot->zentaoBot->condKeywords['recPerPage'] = array('recPerPage');
|
||||
$lang->im->bot->zentaoBot->condKeywords['finishedDate'] = array('finishedDate');
|
||||
$lang->im->bot->zentaoBot->condKeywords['currentConsumed'] = array('currentConsumed');
|
||||
|
||||
$lang->im->bot->zentaoBot->success = 'Command executed successfully.';
|
||||
$lang->im->bot->zentaoBot->tasksFound = 'Found %d tasks.';
|
||||
$lang->im->bot->zentaoBot->prevPage = 'Prev Page';
|
||||
$lang->im->bot->zentaoBot->nextPage = 'Next Page';
|
||||
$lang->im->bot->zentaoBot->effortRecorded = 'Effort recorded for task #%d.';
|
||||
|
||||
$lang->im->bot->zentaoBot->finishTask = 'finish';
|
||||
$lang->im->bot->zentaoBot->closeTask = 'close';
|
||||
$lang->im->bot->zentaoBot->startTask = 'start';
|
||||
$lang->im->bot->zentaoBot->viewTask = 'view';
|
||||
|
||||
$lang->im->bot->zentaoBot->errors = new stdclass();
|
||||
$lang->im->bot->zentaoBot->errors->emptyResult = 'No task found.';
|
||||
$lang->im->bot->zentaoBot->errors->invalidCommand = 'Invalid command.';
|
||||
$lang->im->bot->zentaoBot->errors->invalidStatus = 'Cannot perform such action on task with %s status.';
|
||||
$lang->im->bot->zentaoBot->errors->unauthorized = 'You are not authorized to perform this action.';
|
||||
$lang->im->bot->zentaoBot->errors->taskIDRequired = 'Task ID is required.';
|
||||
$lang->im->bot->zentaoBot->errors->taskNotFound = 'Task not found.';
|
||||
|
||||
$lang->im->bot->zentaoBot->finish = new stdclass();
|
||||
$lang->im->bot->zentaoBot->finish->tip = 'Click the link below to finish the task. Time consumed and starting time of this task are required.';
|
||||
$lang->im->bot->zentaoBot->finish->tipLinkTitle = 'Finish Task';
|
||||
$lang->im->bot->zentaoBot->finish->done = 'Task #%d is finished, ended at:%s, time consumed: %.1f hours.';
|
||||
$lang->im->bot->zentaoBot->finish->bugTip = 'Task #%d is associated with a bug, you may mark the bug as resolved by clicking the link below.';
|
||||
$lang->im->bot->zentaoBot->finish->bugTipLinkTitle = 'Resolve Bug';
|
||||
|
||||
$lang->im->bot->zentaoBot->start = new stdclass();
|
||||
$lang->im->bot->zentaoBot->start->tip = 'Click the link below to start task #%d.';
|
||||
$lang->im->bot->zentaoBot->start->tipLinkTitle = 'Start Task';
|
||||
$lang->im->bot->zentaoBot->start->finishWithZeroLeft = 'Hours left is 0 thus the task is finished.';
|
||||
|
||||
$lang->im->bot->zentaoBot->help = <<<EOT
|
||||
### 1. Task command
|
||||
|
||||
Command:`view task condition...`
|
||||
Example:`view task dev1 P1 doing` Displays tasks assigned to dev1, with priority P1 and status in progress
|
||||
|
||||
| Command | Description |
|
||||
| ---- | ---- |
|
||||
| view task | Show all open tasks under the current username |
|
||||
| view task Name Keyword | Show tasks that match the name keyword |
|
||||
| view task Assignor | Show tasks whose assignor is the entered value |
|
||||
| view task Priority | Show tasks with priority as entered |
|
||||
| view task Status | Show tasks with status as input |
|
||||
| view task ID | Show tasks with ID as input |
|
||||
|
||||
### 2. Task Edit command
|
||||
The Task Edit command supports making status changes to tasks.
|
||||
|
||||
| Command | Description |
|
||||
| ---- | ---- |
|
||||
| start task #ID | Start the task and record its consumption/remaining hours |
|
||||
| complete task #ID | Complete the task and record its consumption/remaining hours |
|
||||
| close task #ID | Close the task and record its consumption/remaining work hours |
|
||||
EOT;
|
||||
|
||||
@@ -36,3 +36,98 @@ $lang->im->xxd->aes = '服务端通信 AES';
|
||||
$lang->im->xxdAESTip = '该设置仅针对 XXB 和 XXD 之间的通讯加密,不影响客户端通讯加密。';
|
||||
$lang->im->aesOptions['on'] = '开启';
|
||||
$lang->im->aesOptions['off'] = '关闭';
|
||||
|
||||
$lang->im->bot->commonName = '阿道';
|
||||
$lang->im->bot->welcome->title = '哈喽~我是你的助手阿道';
|
||||
$lang->im->bot->upgradeWelcome->title = '哈喽~我是你的助手阿道';
|
||||
|
||||
$lang->im->bot->zentaoBot = new stdclass();
|
||||
$lang->im->bot->zentaoBot->name = '禅道';
|
||||
$lang->im->bot->zentaoBot->pageSearchRegex = '/(pageID|recPerPage|页码|每页数量|頁碼|每頁數量)=(\d+)/';
|
||||
|
||||
$lang->im->bot->zentaoBot->commands = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->view = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->view->description = '查看任务';
|
||||
$lang->im->bot->zentaoBot->commands->view->alias = array('查看', '搜索', '查询', '筛选');
|
||||
$lang->im->bot->zentaoBot->commands->start = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->start->description = '开始任务';
|
||||
$lang->im->bot->zentaoBot->commands->start->alias = array('开始', '开始任务');
|
||||
$lang->im->bot->zentaoBot->commands->close = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->close->description = '关闭任务';
|
||||
$lang->im->bot->zentaoBot->commands->close->alias = array('关闭', '关闭任务');
|
||||
$lang->im->bot->zentaoBot->commands->finish = new stdclass();
|
||||
$lang->im->bot->zentaoBot->commands->finish->description = '完成任务';
|
||||
$lang->im->bot->zentaoBot->commands->finish->alias = array('完成', '完成任务');
|
||||
|
||||
$lang->im->bot->zentaoBot->condKeywords = array();
|
||||
$lang->im->bot->zentaoBot->condKeywords['task'] = array('任务', 'task');
|
||||
$lang->im->bot->zentaoBot->condKeywords['pri'] = array('优先级', 'pri');
|
||||
$lang->im->bot->zentaoBot->condKeywords['status'] = array('状态', 'status');
|
||||
$lang->im->bot->zentaoBot->condKeywords['assignTo'] = array('指派人', '指派给', 'assignto', 'user');
|
||||
$lang->im->bot->zentaoBot->condKeywords['id'] = array('编号', 'id');
|
||||
$lang->im->bot->zentaoBot->condKeywords['taskName'] = array('任务名', '任务名称', 'taskname');
|
||||
$lang->im->bot->zentaoBot->condKeywords['comment'] = array('备注', 'comment');
|
||||
$lang->im->bot->zentaoBot->condKeywords['left'] = array('预计剩余', 'left');
|
||||
$lang->im->bot->zentaoBot->condKeywords['consumed'] = array('总计消耗', 'consumed');
|
||||
$lang->im->bot->zentaoBot->condKeywords['realStarted'] = array('实际开始', 'realStarted');
|
||||
$lang->im->bot->zentaoBot->condKeywords['pageID'] = array('pageID', '页码', '頁碼');
|
||||
$lang->im->bot->zentaoBot->condKeywords['recPerPage'] = array('recPerPage', '每页数量', '每頁數量');
|
||||
$lang->im->bot->zentaoBot->condKeywords['finishedDate'] = array('实际完成', 'finishedDate');
|
||||
$lang->im->bot->zentaoBot->condKeywords['currentConsumed'] = array('本次消耗', 'currentConsumed');
|
||||
|
||||
$lang->im->bot->zentaoBot->success = '指令执行完成';
|
||||
$lang->im->bot->zentaoBot->tasksFound = '为您匹配到 %d 项任务';
|
||||
$lang->im->bot->zentaoBot->prevPage = '上一页';
|
||||
$lang->im->bot->zentaoBot->nextPage = '下一页';
|
||||
$lang->im->bot->zentaoBot->effortRecorded = '任务 #%d 已完成工时信息填写';
|
||||
|
||||
$lang->im->bot->zentaoBot->finishTask = '完成任务';
|
||||
$lang->im->bot->zentaoBot->closeTask = '关闭任务';
|
||||
$lang->im->bot->zentaoBot->startTask = '开始任务';
|
||||
$lang->im->bot->zentaoBot->viewTask = '查看任务';
|
||||
|
||||
$lang->im->bot->zentaoBot->errors = new stdclass();
|
||||
$lang->im->bot->zentaoBot->errors->emptyResult = '未查询到相关匹配信息';
|
||||
$lang->im->bot->zentaoBot->errors->invalidCommand = '无法识别该指令';
|
||||
$lang->im->bot->zentaoBot->errors->invalidStatus = '检测到该任务为%s状态,无法实现指令操作';
|
||||
$lang->im->bot->zentaoBot->errors->unauthorized = '您无权操作此任务';
|
||||
$lang->im->bot->zentaoBot->errors->taskIDRequired = '请输入任务编号';
|
||||
$lang->im->bot->zentaoBot->errors->taskNotFound = '任务不存在';
|
||||
|
||||
$lang->im->bot->zentaoBot->finish = new stdclass();
|
||||
$lang->im->bot->zentaoBot->finish->tip = '完成任务指令需要填入工时与记录起始时间,请点击下方入口';
|
||||
$lang->im->bot->zentaoBot->finish->tipLinkTitle = '工时记录';
|
||||
$lang->im->bot->zentaoBot->finish->done = '任务 #%d 已完成,完成时间:%s,消耗:%.1f 小时';
|
||||
$lang->im->bot->zentaoBot->finish->bugTip = '检测到任务 #%d 关联相关 Bug,您可以点击以下链接进行处理';
|
||||
$lang->im->bot->zentaoBot->finish->bugTipLinkTitle = '关联 Bug 处理';
|
||||
|
||||
$lang->im->bot->zentaoBot->start = new stdclass();
|
||||
$lang->im->bot->zentaoBot->start->tip = '点击链接开始任务 #%d';
|
||||
$lang->im->bot->zentaoBot->start->tipLinkTitle = '开始任务';
|
||||
$lang->im->bot->zentaoBot->start->finishWithZeroLeft = '剩余工时为 0,任务将被标记为"已完成"';
|
||||
|
||||
$lang->im->bot->zentaoBot->help = <<<EOT
|
||||
### 1. 任务查询指令
|
||||
|
||||
指令:`查看 任务 条件a 值a1,值a2 条件b 值b1,值b2···条件n 值n1,值n2`
|
||||
示例:`查看 任务 阿道 P1 进行中` 显示指派给阿道、优先级为P1且状态为进行中的任务
|
||||
|
||||
| 命令 | 描述 |
|
||||
| ---- | ---- |
|
||||
| 查看 任务 | 显示当前用户名下所有未关闭的任务 |
|
||||
| 查看 任务 名称关键字 | 显示匹配到名称关键字的任务 |
|
||||
| 查看 任务 指派人 | 显示指派人为输入值的任务 |
|
||||
| 查看 任务 优先级 | 显示优先级为输入值的任务 |
|
||||
| 查看 任务 状态 | 显示状态为输入值的任务 |
|
||||
| 查看 任务 ID | 显示ID为输入值的任务 |
|
||||
|
||||
### 2. 任务编辑指令
|
||||
|
||||
任务编辑指令支持对任务进行状态变更。
|
||||
|
||||
| 命令 | 描述 |
|
||||
| ---- | ---- |
|
||||
| 开始 任务 #ID | 开始任务并记录其消耗/剩余工时 |
|
||||
| 完成 任务 #ID | 完成任务并记录其消耗/剩余工时 |
|
||||
| 关闭 任务 #ID | 关闭任务并记录其消耗/剩余工时 |
|
||||
EOT;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Get task list by conditions.
|
||||
*
|
||||
* @param object $conds
|
||||
* @param string $orderBy
|
||||
* @param int $limit
|
||||
* @param object $pager
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getListByConds($conds, $orderBy = 'id_desc', $limit = 0, $pager = null)
|
||||
{
|
||||
foreach(array('priList' => array(), 'assignedToList' => array(), 'statusList' => array(), 'idList' => array(), 'taskName' => '') as $condKey => $defaultValue)
|
||||
{
|
||||
if(!isset($conds->$condKey)) $conds->$condKey = $defaultValue;
|
||||
}
|
||||
|
||||
return $this->dao->select('*')->from(TABLE_TASK)
|
||||
->where('deleted')->eq(0)
|
||||
->beginIF(!empty($conds->priList))->andWhere('pri')->in($conds->priList)->fi()
|
||||
->beginIF(!empty($conds->assignedToList))->andWhere('assignedTo')->in($conds->assignedToList)->fi()
|
||||
->beginIF(!empty($conds->statusList))->andWhere('status')->in($conds->statusList)->fi()
|
||||
->beginIF(!empty($conds->idList))->andWhere('id')->in($conds->idList)->fi()
|
||||
->beginIF(!empty($conds->taskName))->andWhere('name')->like("%{$conds->taskName}%")
|
||||
->orderBy($orderBy)
|
||||
->beginIF($limit > 0)->limit($limit)->fi()
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Reference in New Issue
Block a user