Merge branch 'sprint/170' of https://gitlab.zcorp.cc/easycorp/zentaopms into 170
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* The doclibs entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class doclibsEntry extends Entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$type = $this->param('type', 0);
|
||||
$objectID = $this->param('objectID', 0);
|
||||
|
||||
$libs = $this->loadModel('doc')->getLibs($type, $this->param('extra', ''), $this->param('appendLibs', ''), $objectID);
|
||||
$result = array();
|
||||
foreach($libs as $libID => $libName)
|
||||
{
|
||||
$lib = new stdclass();
|
||||
$lib->id = $libID;
|
||||
$lib->name = $libName;
|
||||
$result[] = $lib;
|
||||
}
|
||||
krsort($result);
|
||||
|
||||
$lib = new stdclass();
|
||||
$lib->id = 'files';
|
||||
$lib->name = $this->lang->doclib->files;
|
||||
$result[] = $lib;
|
||||
|
||||
return $this->send(200, array('libs' => array_values($result)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* The docs entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class docsEntry extends Entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($libID = 0)
|
||||
{
|
||||
if(empty($libID)) $libID = $this->param('lib', 0);
|
||||
if(empty($libID)) return $this->sendError(400, 'Need lib id.');
|
||||
|
||||
$docTree = $this->loadModel('doc')->getDocTree($libID);
|
||||
|
||||
foreach($docTree as $i => $module)
|
||||
{
|
||||
if(empty($module->id))
|
||||
{
|
||||
unset($docTree[$i]);
|
||||
foreach($module->children as $doc) $docTree[] = $doc;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->send(200, array('docs' => array_values($docTree)));
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,14 @@ class executionEntry extends Entry
|
||||
}
|
||||
|
||||
$execution = $this->format($data->data->execution, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,begin:date,end:date,realBegan:date,realEnd:date,deleted:bool');
|
||||
|
||||
$this->loadModel('testcase');
|
||||
$execution->caseReview = ($this->config->testcase->needReview or !empty($this->config->testcase->forceReview));
|
||||
|
||||
if(!$fields) $this->send(200, $execution);
|
||||
|
||||
/* Set other fields. */
|
||||
$fields = explode(',', $fields);
|
||||
$fields = explode(',', strtolower($fields));
|
||||
foreach($fields as $field)
|
||||
{
|
||||
switch($field)
|
||||
@@ -50,6 +54,19 @@ class executionEntry extends Entry
|
||||
$execution->modules = $data->data->tree;
|
||||
}
|
||||
break;
|
||||
case 'moduleoptionmenu':
|
||||
$execution->moduleOptionMenu = $this->loadModel('tree')->getTaskOptionMenu($executionID, 0, 0, 'allModule');
|
||||
break;
|
||||
case 'members':
|
||||
$execution->members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');;
|
||||
unset($execution->members['']);
|
||||
break;
|
||||
case 'stories':
|
||||
$stories = $this->loadModel('story')->getExecutionStories($executionID);
|
||||
foreach($stories as $storyID => $story) $stories[$storyID] = $this->filterFields($story, 'id,title,module,pri,status,stage,estimate');
|
||||
|
||||
$execution->stories = array_values($stories);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* The execution bugs entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class executionBugsEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($executionID = 0)
|
||||
{
|
||||
if(!$executionID) $executionID = $this->param('execution', 0);
|
||||
if(empty($executionID)) return $this->sendError(400, 'Need execution id.');
|
||||
|
||||
$control = $this->loadController('execution', 'bug');
|
||||
$control->bug($executionID, $this->param('product', 0), $this->param('order', 'status,id_desc'), $this->param('build', 0), $this->param('status', 'all'), 0, 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$bugs = $data->data->bugs;
|
||||
$pager = $data->data->pager;
|
||||
$result = array();
|
||||
foreach($bugs as $bug)
|
||||
{
|
||||
$status = array('code' => $bug->status, 'name' => $this->lang->bug->statusList[$bug->status]);
|
||||
if($bug->status == 'active' and $bug->confirmed) $status = array('code' => 'confirmed', 'name' => $this->lang->bug->labelConfirmed);
|
||||
if($bug->resolution == 'postponed') $status = array('code' => 'postponed', 'name' => $this->lang->bug->labelPostponed);
|
||||
if(!empty($bug->delay)) $status = array('code' => 'delay', 'name' => $this->lang->bug->overdueBugs);
|
||||
$bug->status = $status;
|
||||
|
||||
$result[$bug->id] = $this->format($bug, 'activatedDate:time,openedDate:time,assignedDate:time,resolvedDate:time,closedDate:time,lastEditedDate:time,deadline:date,deleted:bool');
|
||||
}
|
||||
|
||||
$storyChangeds = $this->dao->select('t1.id')->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story=t2.id')
|
||||
->where('t1.id')->in(array_keys($result))
|
||||
->andWhere('t1.story')->ne('0')
|
||||
->andWhere('t1.storyVersion != t2.version')
|
||||
->fetchPairs('id', 'id');
|
||||
foreach($storyChangeds as $bugID)
|
||||
{
|
||||
$status = array('code' => 'storyChanged', 'name' => $this->lang->bug->changed);
|
||||
$result[$bugID]->status = $status;
|
||||
}
|
||||
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'bugs' => array_values($result)));
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* The execution cases entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class executionCasesEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($executionID = 0)
|
||||
{
|
||||
if(!$executionID) $executionID = $this->param('execution', 0);
|
||||
if(empty($executionID)) return $this->sendError(400, 'Need execution id.');
|
||||
|
||||
$control = $this->loadController('execution', 'testcase');
|
||||
$control->testcase($executionID, $this->param('status', 'all'), $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$cases = $data->data->cases;
|
||||
$pager = $data->data->pager;
|
||||
$result = array();
|
||||
foreach($cases as $case)
|
||||
{
|
||||
$case->status = array('code' => $case->status, 'name' => $this->lang->testcase->statusList[$case->status]);
|
||||
$result[] = $this->format($case, 'openedDate:time,reviewedDate:date,lastEditedDate:time,lastRunDate:time');
|
||||
}
|
||||
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'cases' => $result));
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
}
|
||||
}
|
||||
@@ -20,23 +20,55 @@ class executionsEntry extends entry
|
||||
*/
|
||||
public function get($projectID = 0)
|
||||
{
|
||||
$control = $this->loadController('execution', 'all');
|
||||
$control->all($this->param('status', 'all'), $this->param('project', $projectID), $this->param('order', 'id_desc'), 0, $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
$appendFields = $this->param('fields', '');
|
||||
$withProject = $this->param('withProject', '');
|
||||
if(strpos(strtolower(",{$appendFields},"), ',dropmenu,') !== false) return $this->getDropMenu();
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
if($projectID)
|
||||
{
|
||||
$pager = $data->data->pager;
|
||||
$result = array();
|
||||
foreach($data->data->executionStats as $execution)
|
||||
{
|
||||
$result[] = $this->format($execution, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,begin:date,end:date,realBegan:date,realEnd:date,deleted:bool');
|
||||
}
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'executions' => $result));
|
||||
}
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
$control = $this->loadController('project', 'execution');
|
||||
$control->execution($this->param('status', 'undone'), $projectID, $this->param('order', 'id_desc'), $this->param('product', 0), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
/* Response */
|
||||
$data = $this->getData();
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$executions = $data->data->executionStats;
|
||||
$pager = $data->data->pager;
|
||||
$projects = $data->data->projects;
|
||||
}
|
||||
else
|
||||
{
|
||||
$control = $this->loadController('execution', 'all');
|
||||
$control->all($this->param('status', 'all'), $this->param('project', $projectID), $this->param('order', 'id_desc'), 0, 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$executions = $data->data->executionStats;
|
||||
$pager = $data->data->pager;
|
||||
$projects = $data->data->projects;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach($data->data->executionStats as $execution)
|
||||
{
|
||||
foreach($execution->hours as $field => $value) $execution->$field = $value;
|
||||
|
||||
$execution = $this->filterFields($execution, 'id,name,project,code,type,parent,begin,end,status,openedBy,openedDate,delay,progress,' . $appendFields);
|
||||
$result[] = $this->format($execution, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,begin:date,end:date,realBegan:date,realEnd:date,deleted:bool');
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$data['page'] = $pager->pageID;
|
||||
$data['total'] = $pager->recTotal;
|
||||
$data['limit'] = $pager->recPerPage;
|
||||
$data['executions'] = $result;
|
||||
if(!empty($withProject)) $data['projects'] = $projects;
|
||||
|
||||
return $this->send(200, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,4 +102,50 @@ class executionsEntry extends entry
|
||||
|
||||
$this->send(201, $this->format($execution, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get drop menu.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getDropMenu()
|
||||
{
|
||||
$control = $this->loadController('execution', 'ajaxGetDropMenu');
|
||||
$control->ajaxGetDropMenu($this->request('executionID', 0), $this->request('module', 'execution'), $this->request('method', 'task'), $this->request('extra', ''));
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$account = $this->app->user->account;
|
||||
$projects = $data->data->projects;
|
||||
$dropMenu = array('involved' => array(), 'other' => array(), 'closed' => array());
|
||||
foreach($data->data->executions as $projectID => $projectExecutions)
|
||||
{
|
||||
foreach($projectExecutions as $execution)
|
||||
{
|
||||
if(helper::diffDate(date('Y-m-d'), $execution->end) > 0) $execution->delay = true;
|
||||
$teams = $execution->teams;
|
||||
$execution = $this->filterFields($execution, 'id,project,model,type,name,code,status,PM,delay');
|
||||
|
||||
$projectName = zget($projects, $execution->project, '');
|
||||
if($projectName) $execution->name = $projectName . '/' . $execution->name;
|
||||
|
||||
if($execution->status == 'closed')
|
||||
{
|
||||
$dropMenu['closed'][] = $execution;
|
||||
}
|
||||
elseif($execution->status != 'done' and $execution->status != 'closed' and ($execution->PM == $account or isset($teams->$account)))
|
||||
{
|
||||
$dropMenu['involved'][] = $execution;
|
||||
}
|
||||
else
|
||||
{
|
||||
$dropMenu['other'][] = $execution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->send(200, $dropMenu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class executionStoriesEntry extends entry
|
||||
class executionStoriesEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
@@ -20,8 +20,11 @@ class executionStoriesEntry extends entry
|
||||
*/
|
||||
public function get($executionID)
|
||||
{
|
||||
if(empty($executionID)) $this->param('execution', 0);
|
||||
if(empty($executionID)) return $this->sendError(400, 'Need execution id.');
|
||||
|
||||
$control = $this->loadController('execution', 'story');
|
||||
$control->story($executionID, $this->param('order', ''), $this->param('type', 'all'), 0, $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1));
|
||||
$control->story($executionID, $this->param('order', 'id_desc'), $this->param('status', 'all'), 0, 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
@@ -33,13 +36,10 @@ class executionStoriesEntry extends entry
|
||||
{
|
||||
$result[] = $this->format($story, 'openedDate:time,assignedDate:time,reviewedDate:time,lastEditedDate:time,closedDate:time');
|
||||
}
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'executionStories' => $result));
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'stories' => $result));
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail')
|
||||
{
|
||||
return $this->sendError(400, $data->message);
|
||||
}
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* The file entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class fileEntry extends Entry
|
||||
{
|
||||
/**
|
||||
* PUT method.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function put($fileID)
|
||||
{
|
||||
$uid = $this->param('uid', '');
|
||||
$action = $this->param('action', '');
|
||||
if($action == 'remove') unset($_SESSION['album']['used'][$uid][$fileID]);
|
||||
|
||||
$this->send(200, array('id' => $fileID));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* The files entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class filesEntry extends Entry
|
||||
{
|
||||
/**
|
||||
* POST method.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function post()
|
||||
{
|
||||
$uid = $this->param('uid', '');
|
||||
|
||||
$control = $this->loadController('file', 'ajaxUpload');
|
||||
$control->ajaxUpload($uid);
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
if(!$data or !isset($data->status)) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'error')
|
||||
{
|
||||
return isset($data->code) and $data->code == 404 ? $this->send404() : $this->sendError(400, $data->message);
|
||||
}
|
||||
|
||||
$this->send(200, array('id' => $data->id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* The langs entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class langsEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$modules = $this->param('modules', '');
|
||||
$language = $this->param('lang', '');
|
||||
|
||||
if($language and !isset($this->config->langs[$language])) return $this->sendError(400, 'Error lang parameter');
|
||||
if(empty($modules)) return $this->sendError(400, 'Need modules');
|
||||
|
||||
if(empty($language)) $language = 'zh-cn';
|
||||
$this->app->setClientLang($language);
|
||||
|
||||
$modules = explode(',', $modules);
|
||||
foreach($modules as $module)
|
||||
{
|
||||
if($module == 'all')
|
||||
{
|
||||
foreach(glob($this->app->getModuleRoot() . '*') as $modulePath)
|
||||
{
|
||||
if(!is_dir($modulePath)) continue;
|
||||
|
||||
$moduleName = basename($modulePath);
|
||||
$this->app->loadLang($moduleName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$this->app->loadLang($module);
|
||||
}
|
||||
|
||||
return $this->send(200, $this->lang);
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,17 @@ class productEntry extends Entry
|
||||
}
|
||||
|
||||
$product = $this->format($data->data->product, 'createdDate:time');
|
||||
|
||||
$users = $data->data->users;
|
||||
$product->PO = $this->formatUser($product->PO, $users);
|
||||
$product->QD = $this->formatUser($product->QD, $users);
|
||||
$product->RD = $this->formatUser($product->RD, $users);
|
||||
$product->createdBy = $this->formatUser($product->createdBy, $users);
|
||||
if(isset($product->feedback)) $product->feedback = $this->formatUser($product->feedback, $users);
|
||||
if(!$fields) return $this->send(200, $product);
|
||||
|
||||
/* Set other fields. */
|
||||
$fields = explode(',', $fields);
|
||||
$fields = explode(',', strtolower($fields));
|
||||
foreach($fields as $field)
|
||||
{
|
||||
switch($field)
|
||||
@@ -50,6 +57,29 @@ class productEntry extends Entry
|
||||
$product->modules = $data->data->tree;
|
||||
}
|
||||
break;
|
||||
case 'actions':
|
||||
$product->addComment = common::hasPriv('action', 'comment') ? true : false;
|
||||
|
||||
$actions = $data->data->actions;
|
||||
$product->actions = $this->loadModel('action')->processActionForAPI($actions, $users, $this->lang->product);
|
||||
break;
|
||||
case 'lastexecution':
|
||||
$execution = $this->dao->select('t2.id,t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
|
||||
->where('t2.deleted')->eq(0)
|
||||
->andWhere('t1.product')->eq($productID)
|
||||
->andWhere('t2.type')->in('sprint,stage')
|
||||
->orderBy('t2.id desc')
|
||||
->limit(1)
|
||||
->fetch();
|
||||
if($execution)
|
||||
{
|
||||
$workhour = $this->loadModel('project')->computerProgress(array($execution->id => $execution));
|
||||
if(isset($workhour[$execution->id])) $execution->progress = $workhour[$execution->id]->progress;
|
||||
}
|
||||
|
||||
$product->lastExecution = $execution;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,10 @@ class productplansEntry extends entry
|
||||
{
|
||||
$result = array();
|
||||
$plans = $data->data->plans;
|
||||
$pager = $data->data->pager;
|
||||
foreach($plans as $plan) $result[] = $plan;
|
||||
|
||||
return $this->send(200, array('plans' => $result));
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'plans' => array_values($result)));
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
+161
-17
@@ -20,23 +20,24 @@ class productsEntry extends entry
|
||||
*/
|
||||
public function get($programID = 0)
|
||||
{
|
||||
$fields = $this->param('fields', '');
|
||||
if(strpos(strtolower(",{$fields},"), ',dropmenu,') !== false) return $this->getDropMenu();
|
||||
|
||||
if(!$programID) $programID = $this->param('program', 0);
|
||||
$mergeChildren = $this->param('mergeChildren', '');
|
||||
|
||||
if($programID)
|
||||
{
|
||||
$control = $this->loadController('program', 'product');
|
||||
$control->product($programID, $this->param('status', 'all'), $this->param('order', 'order_asc'), 0, 10000);
|
||||
$control->product($programID, $this->param('status', 'all'), $this->param('order', 'order_asc'), 0, $this->param('limit', '20'), $this->param('page', '1'));
|
||||
|
||||
/* Response */
|
||||
$data = $this->getData();
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$result = array();
|
||||
$products = $data->data->products;
|
||||
foreach($products as $product) $result[] = $this->format($product, 'createdDate:time');
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$products = $data->data->products;
|
||||
|
||||
return $this->send(200, array('products' => $result));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -45,19 +46,51 @@ class productsEntry extends entry
|
||||
|
||||
/* Response */
|
||||
$data = $this->getData();
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$result = array();
|
||||
$products = $data->data->productStats;
|
||||
foreach($products as $product) $result[] = $this->format($product, 'createdDate:time');
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
return $this->send(200, array('products' => $result));
|
||||
}
|
||||
$products = $data->data->productStats;
|
||||
if($mergeChildren) $products = $data->data->productStructure;
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
$result = array();
|
||||
if($mergeChildren)
|
||||
{
|
||||
$programs = $this->mergeChildren($products);
|
||||
return $this->send(200, $programs);
|
||||
}
|
||||
else
|
||||
{
|
||||
$accounts = array();
|
||||
foreach($products as $product)
|
||||
{
|
||||
$accounts[$product->PO] = $product->PO;
|
||||
$accounts[$product->QD] = $product->QD;
|
||||
$accounts[$product->RD] = $product->RD;
|
||||
$accounts[$product->createdBy] = $product->createdBy;
|
||||
if(isset($product->feedback)) $accounts[$product->feedback] = $product->feedback;
|
||||
if(!empty($product->mailto))
|
||||
{
|
||||
foreach(explode(',', $product->mailto) as $account)
|
||||
{
|
||||
$account = trim($account);
|
||||
if(empty($account)) continue;
|
||||
$accounts[$account] = $account;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
$result[] = $this->format($product, 'createdDate:time');
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$data['total'] = count($result);
|
||||
$data['products'] = $result;
|
||||
|
||||
$withUser = $this->param('withUser', '');
|
||||
if(!empty($withUser)) $data['users'] = $this->loadModel('user')->getListByAccounts($accounts, 'account');
|
||||
|
||||
return $this->send(200, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,4 +121,115 @@ class productsEntry extends entry
|
||||
|
||||
$this->send(200, $product);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dropmenu.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getDropMenu()
|
||||
{
|
||||
$control = $this->loadController('product', 'ajaxGetDropMenu');
|
||||
$control->ajaxGetDropMenu($this->request('productID', 0), $this->request('module', 'product'), $this->request('method', 'browse'), $this->request('extra', ''), $this->request('from', ''));
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$dropMenu = array('owner' => array(), 'other' => array(), 'closed' => array());
|
||||
foreach($data->data->products as $programID => $products)
|
||||
{
|
||||
foreach($products as $product)
|
||||
{
|
||||
$product = $this->filterFields($product, 'id,program,name,code,status,PO');
|
||||
|
||||
if($product->status == 'closed')
|
||||
{
|
||||
$dropMenu['closed'][] = $product;
|
||||
}
|
||||
elseif($product->PO == $this->app->user->account)
|
||||
{
|
||||
$dropMenu['owner'][] = $product;
|
||||
}
|
||||
else
|
||||
{
|
||||
$dropMenu['other'][] = $product;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->send(200, $dropMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge children products.
|
||||
*
|
||||
* @param array $products
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function mergeChildren($products)
|
||||
{
|
||||
$programs = array();
|
||||
foreach($products as $programID => $program)
|
||||
{
|
||||
$programs[$programID] = new stdclass();
|
||||
if(!empty($programID))
|
||||
{
|
||||
$programs[$programID]->id = $programID;
|
||||
$programs[$programID]->name = $program->programName;
|
||||
$programs[$programID]->type = 'program';
|
||||
}
|
||||
|
||||
$unclosedTotal = 0;
|
||||
foreach($program as $field => $value)
|
||||
{
|
||||
if(!isset($programs[$programID]->children)) $programs[$programID]->children = array();
|
||||
if(isset($value->products))
|
||||
{
|
||||
$lineID = $field;
|
||||
if(empty($lineID))
|
||||
{
|
||||
foreach($value->products as $product)
|
||||
{
|
||||
unset($product->desc);
|
||||
$programs[$programID]->children[$product->id] = $product;
|
||||
if($product->status != 'closed') $unclosedTotal += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$line = new stdclass();
|
||||
$line->id = $lineID;
|
||||
$line->name = $value->lineName;
|
||||
$line->type = 'line';
|
||||
|
||||
$line->children = array();
|
||||
foreach($value->products as $product)
|
||||
{
|
||||
unset($product->desc);
|
||||
$line->children[$product->id] = $product;
|
||||
if($product->status != 'closed') $unclosedTotal += 1;
|
||||
}
|
||||
if(isset($line->children)) $line->children = array_values($line->children);
|
||||
|
||||
$programs[$programID]->children[$lineID] = $line;
|
||||
}
|
||||
if(isset($programs[$programID]->children)) $programs[$programID]->children = array_values($programs[$programID]->children);
|
||||
$programs[$programID]->unclosedTotal = $unclosedTotal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$topProducts = array();
|
||||
if(isset($programs[0]))
|
||||
{
|
||||
$topProducts = $programs[0]->children;
|
||||
unset($programs[0]);
|
||||
}
|
||||
|
||||
$programs = array_values($programs);
|
||||
foreach($topProducts as $product) $programs[] = $product;
|
||||
|
||||
return $programs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class ProgramEntry extends Entry
|
||||
class programEntry extends Entry
|
||||
{
|
||||
}
|
||||
|
||||
+75
-10
@@ -9,7 +9,7 @@
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class ProgramsEntry extends Entry
|
||||
class programsEntry extends Entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
@@ -19,25 +19,90 @@ class ProgramsEntry extends Entry
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$_COOKIE['showClosed'] = $this->param('showClosed', 0);
|
||||
$mergeChildren = $this->param('mergeChildren', 0);
|
||||
|
||||
$fields = $this->param('fields', '');
|
||||
if(stripos(strtolower(",{$fields},"), ",dropmenu,") !== false) return $this->getDropMenu();
|
||||
|
||||
$program = $this->loadController('program', 'browse');
|
||||
$program->browse($this->param('status', 'all'), $this->param('order', 'order_asc'));
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$programs = (array)$data->data->programs;
|
||||
$progressList = (array)$data->data->progressList;
|
||||
$users = $data->data->users;
|
||||
$result = array();
|
||||
foreach($programs as $program)
|
||||
{
|
||||
$programs = $data->data->programs;
|
||||
$result = array();
|
||||
foreach($programs as $program)
|
||||
if(isset($progressList[$program->id])) $program->progress = $progressList[$program->id];
|
||||
$param = $this->format($program, 'begin:date,end:date,realBegan:date,realEnd:date,openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,deleted:bool');
|
||||
|
||||
if($mergeChildren)
|
||||
{
|
||||
$result[] = $this->format($program, 'begin:date,end:date,realBegan:date,realEnd:date,openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,deleted:bool');
|
||||
unset($program->desc);
|
||||
$program->openedBy = zget($users, $program->openedBy);
|
||||
$program->closedBy = zget($users, $program->closedBy);
|
||||
$program->canceledBy = zget($users, $program->canceledBy);
|
||||
$program->PO = zget($users, $program->PO);
|
||||
$program->PM = zget($users, $program->PM);
|
||||
$program->QD = zget($users, $program->QD);
|
||||
$program->RD = zget($users, $program->RD);
|
||||
$program->end = $program->end == LONG_TIME ? $this->lang->program->longTime : $program->end;
|
||||
|
||||
$programBudget = in_array($this->app->getClientLang(), array('zh-cn','zh-tw')) ? round((float)$program->budget / 10000, 2) . $this->lang->project->tenThousand : round((float)$program->budget, 2);
|
||||
$program->labelBudget = $program->budget != 0 ? zget($this->lang->project->currencySymbol, $program->budgetUnit) . ' ' . $programBudget : $this->lang->project->future;
|
||||
|
||||
if(empty($program->parent)) $result[$program->id] = $program;
|
||||
if(isset($programs[$program->parent]))
|
||||
{
|
||||
$parentProgram = $programs[$program->parent];
|
||||
if(!isset($parentProgram->children)) $parentProgram->children = array();
|
||||
$parentProgram->children[] = $program;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$result[] = $program;
|
||||
}
|
||||
return $this->send(200, array('programs' => $result));
|
||||
}
|
||||
if(isset($data->status) and $data->status == 'fail')
|
||||
return $this->send(200, array('programs' => array_values($result)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get drop menu.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getDropMenu()
|
||||
{
|
||||
|
||||
$programs = $this->dao->select('id,name,parent,path,grade,`order`')->from(TABLE_PROJECT)
|
||||
->where('deleted')->eq('0')
|
||||
->andWhere('type')->eq('program')
|
||||
->andWhere('id')->in($this->app->user->view->programs)
|
||||
->beginIF(empty($_COOKIE['showClosed']))->andWhere('status')->ne('closed')->fi()
|
||||
->orderBy('grade desc, `order`')
|
||||
->fetchAll('id');
|
||||
|
||||
$dropMenu = array();
|
||||
foreach($programs as $programID => $program)
|
||||
{
|
||||
return $this->sendError(400, $data->message);
|
||||
if(empty($program->parent))
|
||||
{
|
||||
$dropMenu[] = $program;
|
||||
}
|
||||
elseif(isset($programs[$program->parent]))
|
||||
{
|
||||
if(!isset($programs[$program->parent]->children)) $programs[$program->parent]->children = array();
|
||||
$programs[$program->parent]->children[] = $program;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
$this->send(200, $dropMenu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,20 +20,64 @@ class projectEntry extends entry
|
||||
*/
|
||||
public function get($projectID)
|
||||
{
|
||||
$fields = strtolower($this->param('fields'));
|
||||
|
||||
$control = $this->loadController('project', 'view');
|
||||
$control->view($projectID);
|
||||
|
||||
$data = $this->getData();
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
|
||||
if(isset($data->status) and $data->status == 'success') return $this->send(200, $this->format($data->data->project, 'begin:date,end:date,realBegan:date,realEnd:date,openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,deleted:bool'));
|
||||
if(isset($data->status) and $data->status == 'fail')
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$project = $this->format($data->data->project, 'begin:date,end:date,realBegan:date,realEnd:date,openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,deleted:bool');
|
||||
if(empty($fields)) return $this->send(200, $project);
|
||||
|
||||
/* Set other fields. */
|
||||
$fields = explode(',', $fields);
|
||||
foreach($fields as $field)
|
||||
{
|
||||
if(isset($data->code) and $data->code == 404) $this->send404();
|
||||
return $this->sendError(400, $data->message);
|
||||
switch($field)
|
||||
{
|
||||
case 'team':
|
||||
$teams = array();
|
||||
$accounts = array();
|
||||
foreach($data->data->teamMembers as $account => $team)
|
||||
{
|
||||
$team = $this->filterFields($team, "account,role,join,realname");
|
||||
|
||||
$teams[$account] = $team;
|
||||
$accounts[$account] = $account;
|
||||
}
|
||||
$users = $this->loadModel('user')->getListByAccounts($accounts, 'account');
|
||||
foreach($teams as $account => $team)
|
||||
{
|
||||
$user = zget($users, $account, '');
|
||||
$team->avatar = $user->avatar;
|
||||
}
|
||||
|
||||
$project->teams = $teams;
|
||||
break;
|
||||
case "stat":
|
||||
$project->stat = $data->data->statData;
|
||||
break;
|
||||
case "workhour":
|
||||
$workhour = $data->data->workhour;
|
||||
$workhour->progress = ($workhour->totalConsumed + $workhour->totalLeft) ? floor($workhour->totalConsumed / ($workhour->totalConsumed + $workhour->totalLeft) * 1000) / 1000 * 100 : 0;
|
||||
$project->workhour = $workhour;
|
||||
break;
|
||||
case "actions":
|
||||
$actions = $data->data->actions;
|
||||
$project->actions = $this->loadModel('action')->processActionForAPI($actions, (array)$data->data->users, $this->lang->project);
|
||||
break;
|
||||
case "dynamics":
|
||||
$dynamics = $data->data->dynamics;
|
||||
$project->dynamics = $this->loadModel('action')->processDynamicForAPI($dynamics);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->sendError(400, 'error');
|
||||
return $this->send(200, $project);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* The project bugs entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class projectBugsEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($projectID = 0)
|
||||
{
|
||||
if(!$projectID) $projectID = $this->param('project', 0);
|
||||
if(empty($projectID)) return $this->sendError(400, 'Need project id.');
|
||||
|
||||
$control = $this->loadController('project', 'bug');
|
||||
$control->bug($projectID, $this->param('product', 0), $this->param('order', 'status,id_desc'), $this->param('build', 0), $this->param('status', 'all'), 0, 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$bugs = $data->data->bugs;
|
||||
$pager = $data->data->pager;
|
||||
$result = array();
|
||||
foreach($bugs as $bug)
|
||||
{
|
||||
$status = array('code' => $bug->status, 'name' => $this->lang->bug->statusList[$bug->status]);
|
||||
if($bug->status == 'active' and $bug->confirmed) $status = array('code' => 'confirmed', 'name' => $this->lang->bug->labelConfirmed);
|
||||
if($bug->resolution == 'postponed') $status = array('code' => 'postponed', 'name' => $this->lang->bug->labelPostponed);
|
||||
if(!empty($bug->delay)) $status = array('code' => 'delay', 'name' => $this->lang->bug->overdueBugs);
|
||||
$bug->status = $status;
|
||||
|
||||
$result[$bug->id] = $this->format($bug, 'activatedDate:time,openedDate:time,assignedDate:time,resolvedDate:time,closedDate:time,lastEditedDate:time,deadline:date,deleted:bool');
|
||||
}
|
||||
|
||||
$storyChangeds = $this->dao->select('t1.id')->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story=t2.id')
|
||||
->where('t1.id')->in(array_keys($result))
|
||||
->andWhere('t1.story')->ne('0')
|
||||
->andWhere('t1.storyVersion != t2.version')
|
||||
->fetchPairs('id', 'id');
|
||||
foreach($storyChangeds as $bugID)
|
||||
{
|
||||
$status = array('code' => 'storyChanged', 'name' => $this->lang->bug->changed);
|
||||
$result[$bugID]->status = $status;
|
||||
}
|
||||
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'bugs' => array_values($result)));
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
}
|
||||
}
|
||||
@@ -21,26 +21,50 @@ class projectsEntry extends entry
|
||||
public function get($programID = 0)
|
||||
{
|
||||
if(!$programID) $programID = $this->param('program', 0);
|
||||
$appendFields = $this->param('fields', '');
|
||||
if(stripos(strtolower(",{$appendFields},"), ',dropmenu,') !== false) return $this->getDropMenu();
|
||||
|
||||
$control = $this->loadController('project', 'browse');
|
||||
$control->browse($programID, $this->param('status', 'all'), 0, $this->param('order', 'order_asc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
$_COOKIE['involved'] = $this->param('involved', 0);
|
||||
|
||||
if($programID)
|
||||
{
|
||||
$control = $this->loadController('program', 'project');
|
||||
$control->project($programID, $this->param('status', 'all'), $this->param('order', 'order_asc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
}
|
||||
else
|
||||
{
|
||||
$control = $this->loadController('project', 'browse');
|
||||
$control->browse($programID, $this->param('status', 'all'), 0, $this->param('order', 'order_asc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$pager = $data->data->pager;
|
||||
$users = $data->data->users;
|
||||
$result = array();
|
||||
foreach($data->data->projectStats as $project)
|
||||
{
|
||||
foreach($project->hours as $field => $value) $project->$field = $value;
|
||||
|
||||
$project = $this->filterFields($project, 'id,name,code,model,type,budget,budgetUnit,parent,begin,end,status,openedBy,openedDate,PM,delay,progress,' . $appendFields);
|
||||
$result[] = $this->format($project, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time');
|
||||
}
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => (int)$pager->recPerPage, 'projects' => $result));
|
||||
|
||||
$data = array();
|
||||
$data['page'] = $pager->pageID;
|
||||
$data['total'] = $pager->recTotal;
|
||||
$data['limit'] = (int)$pager->recPerPage;
|
||||
$data['projects'] = $result;
|
||||
|
||||
$withUser = $this->param('withUser', '');
|
||||
if(!empty($withUser)) $data['users'] = $users;
|
||||
|
||||
return $this->send(200, $data);
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail')
|
||||
{
|
||||
return $this->sendError(400, $data->message);
|
||||
}
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
// TODO There is no handle for 401.
|
||||
return $this->sendError(400, 'error');
|
||||
@@ -57,6 +81,7 @@ class projectsEntry extends entry
|
||||
$fields = 'name,begin,end,products';
|
||||
$this->batchSetPost($fields);
|
||||
|
||||
$this->setPost('code', $this->request('code', ''));
|
||||
$this->setPost('acl', $this->request('acl', 'private'));
|
||||
$this->setPost('parent', $this->request('program', 0));
|
||||
$this->setPost('whitelist', $this->request('whitelist', array()));
|
||||
@@ -64,7 +89,7 @@ class projectsEntry extends entry
|
||||
$this->setPost('model', $this->request('model', 'scrum'));
|
||||
|
||||
$control = $this->loadController('project', 'create');
|
||||
$this->requireFields('name,begin,end,products');
|
||||
$this->requireFields('name,code,begin,end,products');
|
||||
|
||||
$control->create($this->request('model', 'scrum'));
|
||||
|
||||
@@ -76,4 +101,43 @@ class projectsEntry extends entry
|
||||
|
||||
$this->send(201, $this->format($project, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get drop menu.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getDropMenu()
|
||||
{
|
||||
$control = $this->loadController('project', 'ajaxGetDropMenu');
|
||||
$control->ajaxGetDropMenu($this->request('projectID', 0), $this->request('module', 'project'), $this->request('method', 'browse'));
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$dropMenu = array('owner' => array(), 'other' => array(), 'closed' => array());
|
||||
foreach($data->data->projects as $programID => $projects)
|
||||
{
|
||||
foreach($projects as $project)
|
||||
{
|
||||
if(helper::diffDate(date('Y-m-d'), $project->end) > 0) $project->delay = true;
|
||||
$project = $this->filterFields($project, 'id,model,type,name,code,parent,status,PM,delay');
|
||||
|
||||
if($project->status == 'closed')
|
||||
{
|
||||
$dropMenu['closed'][] = $project;
|
||||
}
|
||||
elseif($project->PM == $this->app->user->account)
|
||||
{
|
||||
$dropMenu['owner'][] = $project;
|
||||
}
|
||||
else
|
||||
{
|
||||
$dropMenu['other'][] = $project;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->send(200, $dropMenu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* The project entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package project
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class projectStoriesEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($projectID)
|
||||
{
|
||||
if(!$projectID) $projectID = $this->param('project');
|
||||
if(!$projectID) return $this->sendError(400, 'Need product id.');
|
||||
|
||||
$control = $this->loadController('projectstory', 'story');
|
||||
$control->story($projectID, $this->param('product', 0), $this->param('branch', ''), $this->param('status', 'unclosed'), 0, 'story', $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$stories = $data->data->stories;
|
||||
$pager = $data->data->pager;
|
||||
$result = array();
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$result[] = $this->format($story, 'openedDate:time,assignedDate:time,reviewedDate:time,lastEditedDate:time,closedDate:time');
|
||||
}
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'stories' => $result));
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
<?php
|
||||
/**
|
||||
* The reports entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class reportsEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$fields = $this->param('fields', '');
|
||||
$dept = $this->param('dept', 0);
|
||||
$account = $this->param('account', '');
|
||||
$year = $this->param('year', date('Y'));
|
||||
if(empty($fields)) return $this->send(400, 'Need fields param for report.');
|
||||
|
||||
$accounts = array();
|
||||
if($account) $accounts = array($account => $account);
|
||||
if(empty($accounts) and $dept) $accounts = array_keys($this->loadModel('dept')->getDeptUserPairs($dept));
|
||||
if(empty($accounts) and empty($dept)) $accounts = array_keys($this->loadModel('user')->getPairs('noclosed'));
|
||||
|
||||
$fields = explode(',', strtolower($fields));
|
||||
$report = array();
|
||||
foreach($fields as $field)
|
||||
{
|
||||
$field = trim($field);
|
||||
if(empty($field)) continue;
|
||||
|
||||
if($field == 'projectoverview')
|
||||
{
|
||||
$report['projectOverview'] = $this->projectOverview($accounts);
|
||||
}
|
||||
elseif($field == 'radar')
|
||||
{
|
||||
$report['radar'] = $this->radar($accounts, $year);
|
||||
}
|
||||
elseif($field == 'projectprogress')
|
||||
{
|
||||
$report['projectProgress'] = $this->projectProgress();
|
||||
}
|
||||
elseif($field == 'executionprogress')
|
||||
{
|
||||
$report['executionProgress'] = $this->executionProgress();
|
||||
}
|
||||
elseif($field == 'productprogress')
|
||||
{
|
||||
$report['productProgress'] = $this->productProgress();
|
||||
}
|
||||
elseif($field == 'bugprogress')
|
||||
{
|
||||
$report['bugProgress'] = $this->bugProgress();
|
||||
}
|
||||
elseif($field == 'bugprogress')
|
||||
{
|
||||
$report['bugProgress'] = $this->bugProgress();
|
||||
}
|
||||
elseif($field == 'output')
|
||||
{
|
||||
$report['output'] = $this->loadModel('report')->getOutput4API($accounts, $year);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->send(200, $report);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project overview by status.
|
||||
*
|
||||
* @param array $accounts
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function projectOverview($accounts)
|
||||
{
|
||||
$statusOverview = $this->loadModel('report')->getProjectStatusOverview($accounts);
|
||||
|
||||
$this->app->loadLang('project');
|
||||
$total = 0;
|
||||
$overview = array();
|
||||
foreach($statusOverview as $status => $count)
|
||||
{
|
||||
$total += $count;
|
||||
$statusName = zget($this->lang->project->statusList, $status);
|
||||
|
||||
$overview[$status] = array();
|
||||
$overview[$status]['code'] = $status;
|
||||
$overview[$status]['name'] = $statusName;
|
||||
$overview[$status]['total'] = $count;
|
||||
}
|
||||
|
||||
$projectOverview = array();
|
||||
$projectOverview['total'] = $total;
|
||||
$projectOverview['overview'] = array_values($overview);
|
||||
|
||||
return $projectOverview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radar data. include product, execution, qa, devel and other.
|
||||
*
|
||||
* @param array $accounts
|
||||
* @param string $year
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function radar($accounts, $year)
|
||||
{
|
||||
$contributions = $this->loadModel('report')->getUserYearContributions($accounts, $year);
|
||||
$annualDataConfig = $this->config->report->annualData;
|
||||
|
||||
$radarData = array('product' => 0, 'execution' => 0, 'devel' => 0, 'qa' => 0, 'other' => 0);
|
||||
foreach($contributions as $objectType => $objectContributions)
|
||||
{
|
||||
foreach($objectContributions as $actionName => $count)
|
||||
{
|
||||
$radarTypes = isset($annualDataConfig['radar'][$objectType][$actionName]) ? $annualDataConfig['radar'][$objectType][$actionName] : array('other');
|
||||
foreach($radarTypes as $radarType) $radarData[$radarType] += $count;
|
||||
}
|
||||
}
|
||||
|
||||
$radar = array();
|
||||
foreach($radarData as $radarType => $total)
|
||||
{
|
||||
$radar[$radarType]['code'] = $radarType;
|
||||
$radar[$radarType]['name'] = $this->lang->report->annualData->radarItems[$radarType];
|
||||
$radar[$radarType]['total'] = $total;
|
||||
}
|
||||
|
||||
return array_values($radar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project progress.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function projectProgress()
|
||||
{
|
||||
$projects = $this->loadModel('program')->getProjectStats(0, 'all');
|
||||
$this->app->loadLang('project');
|
||||
|
||||
$processedProjects = array();
|
||||
$statusList['all']['total'] = 0;
|
||||
$statusList['doing']['total'] = 0;
|
||||
$statusList['wait']['total'] = 0;
|
||||
$statusList['closed']['total'] = 0;
|
||||
foreach($projects as $project)
|
||||
{
|
||||
$newProject = new stdclass();
|
||||
$newProject->id = $project->id;
|
||||
$newProject->name = $project->name;
|
||||
$newProject->status = $project->status;
|
||||
$newProject->progress = round($project->hours->progress, 1);
|
||||
$newProject->totalConsumed = round($project->hours->totalConsumed, 1);
|
||||
$newProject->totalLeft = round($project->hours->totalLeft, 1);
|
||||
if(isset($project->delay)) $newProject->delay = $project->delay;
|
||||
|
||||
$statusList['all']['total'] += 1;
|
||||
if(isset($statusList[$project->status])) $statusList[$project->status]['total'] += 1;
|
||||
|
||||
$processedProjects[$project->id] = $newProject;
|
||||
}
|
||||
|
||||
foreach(array_keys($statusList) as $status)
|
||||
{
|
||||
$statusName = zget($this->lang->project->statusList, $status);
|
||||
if($status == 'all') $statusName = $this->lang->project->featureBar['all'];
|
||||
|
||||
$statusList[$status]['code'] = $status;
|
||||
$statusList[$status]['name'] = $statusName;
|
||||
}
|
||||
|
||||
return array('statusList' => $statusList, 'projects' => array_values($processedProjects));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get execution progress.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function executionProgress()
|
||||
{
|
||||
$executions = $this->loadModel('project')->getStats(0, 'all', 0, 0, 30, 'id_desc');
|
||||
$this->app->loadLang('execution');
|
||||
|
||||
$processedExecutions = array();
|
||||
$statusList['all']['total'] = 0;
|
||||
$statusList['doing']['total'] = 0;
|
||||
$statusList['wait']['total'] = 0;
|
||||
$statusList['closed']['total'] = 0;
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
$newExecution = new stdclass();
|
||||
$newExecution->id = $execution->id;
|
||||
$newExecution->name = $execution->name;
|
||||
$newExecution->status = $execution->status;
|
||||
$newExecution->progress = round($execution->hours->progress, 1);
|
||||
$newExecution->totalConsumed = round($execution->hours->totalConsumed, 1);
|
||||
$newExecution->totalLeft = round($execution->hours->totalLeft, 1);
|
||||
if(isset($execution->delay)) $newExecution->delay = $execution->delay;
|
||||
|
||||
$statusList['all']['total'] += 1;
|
||||
if(isset($statusList[$execution->status])) $statusList[$execution->status]['total'] += 1;
|
||||
|
||||
$processedExecutions[$execution->id] = $newExecution;
|
||||
}
|
||||
|
||||
foreach(array_keys($statusList) as $status)
|
||||
{
|
||||
$statusName = zget($this->lang->execution->statusList, $status);
|
||||
if($status == 'all') $statusName = $this->lang->execution->allTasks;
|
||||
|
||||
$statusList[$status]['code'] = $status;
|
||||
$statusList[$status]['name'] = $statusName;
|
||||
}
|
||||
|
||||
return array('statusList' => $statusList, 'executions' => array_values($processedExecutions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product progress with story.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function productProgress()
|
||||
{
|
||||
$this->app->loadLang('product');
|
||||
$this->app->loadLang('story');
|
||||
|
||||
$storyStatusStat = $this->dao->select('t1.product,t2.name,t2.status,t1.status as storyStatus,count(*) as storyCount')->from(TABLE_STORY)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')
|
||||
->where('t2.deleted')->eq(0)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->products)->fi()
|
||||
->groupBy('t1.product,t1.status')
|
||||
->orderBy('t1.product_desc,t1.status')
|
||||
->fetchAll();
|
||||
|
||||
$productStatusList['all']['total'] = 0;
|
||||
$productStatusList['normal']['total'] = 0;
|
||||
$productStatusList['closed']['total'] = 0;
|
||||
|
||||
$processedProducts = array();
|
||||
$productStoryStat = array();
|
||||
foreach($storyStatusStat as $product)
|
||||
{
|
||||
$productStoryStat[$product->product][$product->storyStatus] = $product->storyCount;
|
||||
|
||||
if(isset($processedProducts[$product->product])) continue;
|
||||
|
||||
$newProduct = new stdclass();
|
||||
$newProduct->id = $product->product;
|
||||
$newProduct->name = $product->name;
|
||||
$newProduct->status = $product->status;
|
||||
|
||||
$processedProducts[$product->product] = $newProduct;
|
||||
$productStatusList['all']['total'] += 1;
|
||||
if(isset($productStatusList[$product->status])) $productStatusList[$product->status]['total'] += 1;
|
||||
}
|
||||
|
||||
/* Set story status statistics integrate into product. */
|
||||
$storyStatusList = array('draft' => array(), 'active' => array(), 'changed' => array(), 'closed' => array());
|
||||
foreach($processedProducts as $productID => $product)
|
||||
{
|
||||
$product->storyStat = array();
|
||||
foreach(array_keys($storyStatusList) as $storyStatus) $product->storyStat[$storyStatus] = isset($productStoryStat[$productID][$storyStatus]) ? $productStoryStat[$productID][$storyStatus] : 0;
|
||||
$product->progress = $product->storyStat['closed'] == 0 ? 0 : round($product->storyStat['closed'] / array_sum($product->storyStat) * 100, 1);
|
||||
}
|
||||
|
||||
foreach(array_keys($productStatusList) as $status)
|
||||
{
|
||||
$statusName = zget($this->lang->product->statusList, $status);
|
||||
if($status == 'all') $statusName = $this->lang->product->allStory;
|
||||
if($status == 'normal') $statusName = $this->lang->product->unclosed;
|
||||
|
||||
$productStatusList[$status]['code'] = $status;
|
||||
$productStatusList[$status]['name'] = $statusName;
|
||||
}
|
||||
|
||||
foreach(array_keys($storyStatusList) as $status)
|
||||
{
|
||||
$statusName = zget($this->lang->story->statusList, $status);
|
||||
|
||||
$storyStatusList[$status]['code'] = $status;
|
||||
$storyStatusList[$status]['name'] = $statusName;
|
||||
}
|
||||
|
||||
return array('productStatusList' => $productStatusList, 'products' => array_values($processedProducts), 'storyStatusList' => $storyStatusList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bug progress by product.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function bugProgress()
|
||||
{
|
||||
$this->app->loadLang('product');
|
||||
$this->app->loadLang('bug');
|
||||
|
||||
$bugStatusStat = $this->dao->select('t1.product,t2.name,t2.status,t1.status as bugStatus,count(*) as bugCount')->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')
|
||||
->where('t2.deleted')->eq(0)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->products)->fi()
|
||||
->groupBy('t1.product,t1.status')
|
||||
->orderBy('t1.product_desc,t1.status')
|
||||
->fetchAll();
|
||||
|
||||
$productStatusList['all']['total'] = 0;
|
||||
$productStatusList['normal']['total'] = 0;
|
||||
$productStatusList['closed']['total'] = 0;
|
||||
|
||||
$processedProducts = array();
|
||||
$productBugStat = array();
|
||||
foreach($bugStatusStat as $product)
|
||||
{
|
||||
$productBugStat[$product->product][$product->bugStatus] = $product->bugCount;
|
||||
|
||||
if(isset($processedProducts[$product->product])) continue;
|
||||
|
||||
$newProduct = new stdclass();
|
||||
$newProduct->id = $product->product;
|
||||
$newProduct->name = $product->name;
|
||||
$newProduct->status = $product->status;
|
||||
|
||||
$processedProducts[$product->product] = $newProduct;
|
||||
$productStatusList['all']['total'] += 1;
|
||||
if(isset($productStatusList[$product->status])) $productStatusList[$product->status]['total'] += 1;
|
||||
}
|
||||
|
||||
/* Set bug status statistics integrate into product. */
|
||||
$bugStatusList = array('active' => array(), 'resolved' => array(), 'closed' => array());
|
||||
foreach($processedProducts as $productID => $product)
|
||||
{
|
||||
$product->bugStat = array();
|
||||
foreach(array_keys($bugStatusList) as $bugStatus) $product->bugStat[$bugStatus] = isset($productBugStat[$productID][$bugStatus]) ? $productBugStat[$productID][$bugStatus] : 0;
|
||||
}
|
||||
|
||||
foreach(array_keys($productStatusList) as $status)
|
||||
{
|
||||
$statusName = zget($this->lang->product->statusList, $status);
|
||||
if($status == 'all') $statusName = $this->lang->product->allStory;
|
||||
if($status == 'normal') $statusName = $this->lang->product->unclosed;
|
||||
|
||||
$productStatusList[$status]['code'] = $status;
|
||||
$productStatusList[$status]['name'] = $statusName;
|
||||
}
|
||||
|
||||
foreach(array_keys($bugStatusList) as $status)
|
||||
{
|
||||
$statusName = zget($this->lang->bug->statusList, $status);
|
||||
|
||||
$bugStatusList[$status]['code'] = $status;
|
||||
$bugStatusList[$status]['name'] = $statusName;
|
||||
}
|
||||
|
||||
return array('productStatusList' => $productStatusList, 'bugs' => array_values($processedProducts), 'bugStatusList' => $bugStatusList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/**
|
||||
* The stakeholder entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class stakeholdersEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $programID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($programID = 0)
|
||||
{
|
||||
if(!$programID) $programID = $this->param('program', 0);
|
||||
|
||||
if($programID)
|
||||
{
|
||||
$control = $this->loadController('program', 'stakeholder');
|
||||
$control->stakeholder($programID, $this->param('order', 't1.id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$this->app->loadLang('stakeholder');
|
||||
$pager = $data->data->pager;
|
||||
$users = $data->data->users;
|
||||
$result = array();
|
||||
foreach($data->data->stakeholders as $stakeholder)
|
||||
{
|
||||
$stakeholder->roleName = zget($this->lang->user->roleList, $stakeholder->role, '');
|
||||
$stakeholder->typeName = zget($this->lang->stakeholder->fromList, $stakeholder->from, '');
|
||||
|
||||
$result[] = $stakeholder;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$data['page'] = $pager->pageID;
|
||||
$data['total'] = $pager->recTotal;
|
||||
$data['limit'] = (int)$pager->recPerPage;
|
||||
$data['stakeholders'] = $result;
|
||||
|
||||
$withUser = $this->param('withUser', '');
|
||||
if(!empty($withUser)) $data['users'] = $users;
|
||||
|
||||
return $this->send(200, $data);
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
// TODO There is no handle for 401.
|
||||
return $this->sendError(400, 'error');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST method.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function post()
|
||||
{
|
||||
$fields = 'name,begin,end,products';
|
||||
$this->batchSetPost($fields);
|
||||
|
||||
$this->setPost('code', $this->request('code', ''));
|
||||
$this->setPost('acl', $this->request('acl', 'private'));
|
||||
$this->setPost('parent', $this->request('program', 0));
|
||||
$this->setPost('whitelist', $this->request('whitelist', array()));
|
||||
$this->setPost('PM', $this->request('PM', ''));
|
||||
$this->setPost('model', $this->request('model', 'scrum'));
|
||||
|
||||
$control = $this->loadController('project', 'create');
|
||||
$this->requireFields('name,code,begin,end,products');
|
||||
|
||||
$control->create($this->request('model', 'scrum'));
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
if(!isset($data->result)) return $this->sendError(400, 'error');
|
||||
|
||||
$project = $this->loadModel('project')->getByID($data->id);
|
||||
|
||||
$this->send(201, $this->format($project, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get drop menu.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getDropMenu()
|
||||
{
|
||||
$control = $this->loadController('project', 'ajaxGetDropMenu');
|
||||
$control->ajaxGetDropMenu($this->request('projectID', 0), $this->request('module', 'project'), $this->request('method', 'browse'));
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$dropMenu = array('owner' => array(), 'other' => array(), 'closed' => array());
|
||||
foreach($data->data->projects as $programID => $projects)
|
||||
{
|
||||
foreach($projects as $project)
|
||||
{
|
||||
if(helper::diffDate(date('Y-m-d'), $project->end) > 0) $project->delay = true;
|
||||
$project = $this->filterFields($project, 'id,model,type,name,code,parent,status,PM,delay');
|
||||
|
||||
if($project->status == 'closed')
|
||||
{
|
||||
$dropMenu['closed'][] = $project;
|
||||
}
|
||||
elseif($project->PM == $this->app->user->account)
|
||||
{
|
||||
$dropMenu['owner'][] = $project;
|
||||
}
|
||||
else
|
||||
{
|
||||
$dropMenu['other'][] = $project;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->send(200, $dropMenu);
|
||||
}
|
||||
}
|
||||
+16
-33
@@ -9,53 +9,36 @@
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class storiesEntry extends entry
|
||||
class storiesEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($productID = 0, $projectID = 0)
|
||||
public function get($productID = 0)
|
||||
{
|
||||
if(!$productID) $productID = $this->param('product');
|
||||
if(!$projectID) $projectID = $this->param('project');
|
||||
if(!$productID and !$projectID) return $this->sendError(400, 'Need product or project id.');
|
||||
if(!$productID) return $this->sendError(400, 'Need product id.');
|
||||
|
||||
if($projectID)
|
||||
{
|
||||
$control = $this->loadController('projectstory', 'story');
|
||||
$control->story($projectID, $productID, $this->param('branch', 0), $this->param('type', ''), 0, 'story', $this->param('order', ''), $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
}
|
||||
else
|
||||
{
|
||||
$control = $this->loadController('product', 'browse');
|
||||
$control->browse($productID, $this->param('branch', 0), $this->param('type', ''), 0, 'story', $this->param('order', ''), $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
}
|
||||
$control = $this->loadController('product', 'browse');
|
||||
$control->browse($productID, $this->param('branch', ''), $this->param('status', 'unclosed'), 0, $this->param('type', 'story'), $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
{
|
||||
$stories = $data->data->stories;
|
||||
$pager = $data->data->pager;
|
||||
$result = array();
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$result[] = $this->format($story, 'openedDate:time,assignedDate:time,reviewedDate:time,lastEditedDate:time,closedDate:time,deleted:bool');
|
||||
}
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'stories' => $result));
|
||||
}
|
||||
$data = $this->getData();
|
||||
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail')
|
||||
$stories = $data->data->stories;
|
||||
$pager = $data->data->pager;
|
||||
$result = array();
|
||||
foreach($stories as $story)
|
||||
{
|
||||
return $this->sendError(400, $data->message);
|
||||
if(isset($story->children)) $story->children = array_values((array)$story->children);
|
||||
$result[] = $this->format($story, 'openedDate:time,assignedDate:time,reviewedDate:time,lastEditedDate:time,closedDate:time,deleted:bool');
|
||||
}
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'stories' => $result));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +66,7 @@ class storiesEntry extends entry
|
||||
$this->requireFields('title,spec,pri,category');
|
||||
|
||||
$control->create($productID);
|
||||
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
if(isset($data->result) and !isset($data->id)) return $this->sendError(400, $data->message);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* The tabs entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class tabsEntry extends baseEntry
|
||||
{
|
||||
/**
|
||||
* Get tabs.
|
||||
*
|
||||
* @param string $moduleName work|
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($moduleName)
|
||||
{
|
||||
$menus = array();
|
||||
if($moduleName == 'work')
|
||||
{
|
||||
$this->app->loadLang('my');
|
||||
$tabs = array('calendar', 'task', 'bug', 'story', 'issue', 'risk', 'myMeeting');
|
||||
|
||||
foreach($tabs as $menuKey)
|
||||
{
|
||||
if(!common::hasPriv('my', $menuKey)) continue;
|
||||
$label = $this->lang->my->$menuKey;
|
||||
if($menuKey == 'calendar') $label = $this->lang->my->calendarAction;
|
||||
|
||||
$menu = new stdclass();
|
||||
$menu->code = $menuKey;
|
||||
$menu->name = $label;
|
||||
|
||||
$menus[] = $menu;
|
||||
}
|
||||
}
|
||||
elseif($moduleName == 'product')
|
||||
{
|
||||
$this->app->loadLang('product');
|
||||
$tabs = array('story', 'plan', 'project', 'release', 'requirement', 'doc', 'view');
|
||||
|
||||
foreach($tabs as $menuKey)
|
||||
{
|
||||
if($menuKey == 'requirement' and empty($this->config->URAndSR)) continue;
|
||||
if(isset($this->lang->product->menu->$menuKey))
|
||||
{
|
||||
list($label, $module, $method) = explode('|', $this->lang->product->menu->$menuKey['link']);
|
||||
if(!common::hasPriv($module, $method)) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!common::hasPriv('product', $menuKey)) continue;
|
||||
}
|
||||
|
||||
$label = zget($this->lang->product, $menuKey, '');
|
||||
if($menuKey == 'view') $label = $this->lang->overview;
|
||||
if($menuKey == 'doc') $label = $this->lang->doc->common;
|
||||
if($menuKey == 'project') $label = $this->lang->project->common;
|
||||
if($menuKey == 'story') $label = $this->lang->createObjects['story'];
|
||||
if($menuKey == 'requirement') $label = $this->lang->URCommon;
|
||||
|
||||
$menu = new stdclass();
|
||||
$menu->code = $menuKey;
|
||||
$menu->name = $label;
|
||||
|
||||
$menus[] = $menu;
|
||||
}
|
||||
}
|
||||
|
||||
$this->send(200, array('tabs' => $menus));
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class tasksEntry extends entry
|
||||
class tasksEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
@@ -31,7 +31,7 @@ class tasksEntry extends entry
|
||||
{
|
||||
/* Get tasks by execution. */
|
||||
$control = $this->loadController('execution', 'task');
|
||||
$control->task($executionID, $this->param('status', 'all'), 0, $this->param('order', ''), $this->param('total', 0), $this->param('limit', 100), $this->param('page', 1));
|
||||
$control->task($executionID, $this->param('status', 'all'), 0, $this->param('order', 'id_desc'), 0, $this->param('limit', 100), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ class tasksEntry extends entry
|
||||
$result = array();
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if(isset($task->children)) $task->children = array_values((array)$task->children);
|
||||
$result[] = $this->format($task, 'openedDate:time,assignedDate:time,realStarted:time,finishedDate:time,canceledDate:time,closedDate:time,lastEditedDate:time,deleted:bool');
|
||||
}
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'tasks' => $result));
|
||||
@@ -61,7 +62,7 @@ class tasksEntry extends entry
|
||||
*/
|
||||
public function post($executionID)
|
||||
{
|
||||
$fields = 'name,type,assignedTo,estimate,story,parent,execution,module,pri,desc,estStarted,deadline,mailto';
|
||||
$fields = 'name,type,assignedTo,estimate,story,execution,project,module,pri,desc,estStarted,deadline,mailto,team,teamEstimate,multiple,uid';
|
||||
$this->batchSetPost($fields);
|
||||
|
||||
$assignedTo = $this->request('assignedTo');
|
||||
@@ -71,7 +72,7 @@ class tasksEntry extends entry
|
||||
$this->requireFields('name,assignedTo,type,estStarted,deadline');
|
||||
|
||||
$control->create($executionID, $this->request('storyID', 0), $this->request('moduleID', 0), $this->request('copyTaskID', 0), $this->request('copyTodoID', 0));
|
||||
|
||||
|
||||
$data = $this->getData();
|
||||
if(!isset($data->id)) return $this->sendError(400, $data->message);
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* The todo activate entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class todoActivateEntry extends Entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $taskID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($todoID)
|
||||
{
|
||||
$control = $this->loadController('todo', 'activate');
|
||||
$control->activate($todoID);
|
||||
|
||||
$data = $this->getData();
|
||||
if($data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$todo = $this->loadModel('todo')->getByID($todoID);
|
||||
$this->send(200, $this->format($todo, 'assignedDate:time,finishedDate:time,closedDate:time'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* The todo finish entry point of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package entries
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class todoFinishEntry extends Entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
*
|
||||
* @param int $taskID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function get($todoID)
|
||||
{
|
||||
$control = $this->loadController('todo', 'finish');
|
||||
$control->finish($todoID);
|
||||
|
||||
$data = $this->getData();
|
||||
if($data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$todo = $this->loadModel('todo')->getByID($todoID);
|
||||
$this->send(200, $this->format($todo, 'assignedDate:time,finishedDate:time,closedDate:time'));
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class todosEntry extends entry
|
||||
public function get()
|
||||
{
|
||||
$control = $this->loadController('my', 'todo');
|
||||
$control->todo($this->param('date', 'all'), $this->param('user', ''), $this->param('status', 'all'), $this->param('order', 'date_desc'), 0, $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1));
|
||||
$control->todo($this->param('type', 'all'), $this->param('userID', ''), $this->param('status', 'all'), $this->param('order', 'date_desc,status,begin'), $this->param('total', 0), $this->param('limit', 100), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
|
||||
if(!isset($data->status)) return $this->sendError(400, 'error');
|
||||
@@ -63,7 +63,7 @@ class todosEntry extends entry
|
||||
if(isset($data->result) and !isset($data->id)) return $this->sendError(400, $data->message);
|
||||
|
||||
$todo = $this->loadModel('todo')->getByID($data->id);
|
||||
|
||||
|
||||
$this->send(201, $this->format($todo, 'assignedDate:time,finishedDate:time,closedDate:time'));
|
||||
}
|
||||
}
|
||||
|
||||
+189
-16
@@ -52,12 +52,13 @@ class userEntry extends Entry
|
||||
unset($profile->password);
|
||||
|
||||
$info->profile = $this->format($profile, 'last:time,locked:time,birthday:date,join:date');
|
||||
$info->profile->role = array('code' => $info->profile->role, 'name' => $this->lang->user->roleList[$info->profile->role]);
|
||||
$info->profile->role = array('code' => $info->profile->role, 'name' => $this->lang->user->roleList[$info->profile->role]);
|
||||
$info->profile->admin = strpos($this->app->company->admins, ",{$profile->account},") !== false;
|
||||
|
||||
if(!$fields) return $this->send(200, $info);
|
||||
|
||||
/* Set other fields. */
|
||||
$fields = explode(',', $fields);
|
||||
$fields = explode(',', strtolower($fields));
|
||||
|
||||
$this->loadModel('my');
|
||||
foreach($fields as $field)
|
||||
@@ -67,13 +68,23 @@ class userEntry extends Entry
|
||||
case 'product':
|
||||
$info->product = array('total' => 0, 'products' => array());
|
||||
|
||||
$products = $this->my->getProducts();
|
||||
$products = $this->my->getProducts('ownbyme');
|
||||
if($products)
|
||||
{
|
||||
$info->product['total'] = $products->allCount;
|
||||
$info->product['total'] = $products->unclosedCount;
|
||||
$info->product['products'] = $products->products;
|
||||
}
|
||||
break;
|
||||
case 'undoneproduct':
|
||||
$info->undoneProduct = array('total' => 0, 'products' => array());
|
||||
|
||||
$products = $this->my->getProducts('undone');
|
||||
if($products)
|
||||
{
|
||||
$info->undoneProduct['total'] = $products->allCount;
|
||||
$info->undoneProduct['products'] = $products->products;
|
||||
}
|
||||
break;
|
||||
case 'project':
|
||||
$info->project = array('total' => 0, 'projects' => array());
|
||||
|
||||
@@ -84,11 +95,96 @@ class userEntry extends Entry
|
||||
$info->project['projects'] = $projects->projects;
|
||||
}
|
||||
break;
|
||||
case 'lastproject':
|
||||
$info->lastProject = array('total' => 0, 'projects' => array());
|
||||
|
||||
$control = $this->loadController('project', 'ajaxGetDropMenu');
|
||||
$control->ajaxGetDropMenu(0, 'project', 'index');
|
||||
$data = $this->getData();
|
||||
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$myProjects['owner'] = array();
|
||||
$myProjects['other'] = array();
|
||||
foreach($data->data->projects as $programID => $programProjects)
|
||||
{
|
||||
foreach($programProjects as $project)
|
||||
{
|
||||
if($project->status == 'closed') continue;
|
||||
|
||||
$project = $this->filterFields($project, 'id,model,type,name,code,parent,status,PM');
|
||||
if($project->PM == $this->app->user->account)
|
||||
{
|
||||
$myProjects['owner'][] = $project;
|
||||
}
|
||||
else
|
||||
{
|
||||
$myProjects['other'][] = $project;
|
||||
}
|
||||
}
|
||||
}
|
||||
$lastProjects = array_merge($myProjects['owner'], $myProjects['other']);
|
||||
$lastProjects = array_slice($lastProjects, 0, 3);
|
||||
|
||||
$info->lastProject['total'] = count($lastProjects);
|
||||
$info->lastProject['projects'] = $lastProjects;
|
||||
}
|
||||
break;
|
||||
case 'execution':
|
||||
$info->execution = array('total' => 0, 'executions' => array());
|
||||
if(!common::hasPriv('my', 'execution')) break;
|
||||
|
||||
$control = $this->loadController('my', 'execution');
|
||||
$control->execution($this->param('type', 'undone'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 5), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$info->execution['total'] = $data->data->pager->recTotal;
|
||||
$info->execution['executions'] = array_values((array)$data->data->executions);
|
||||
}
|
||||
break;
|
||||
case 'lastexecution':
|
||||
$info->lastExecution = array('total' => 0, 'executions' => array());
|
||||
|
||||
$control = $this->loadController('execution', 'ajaxGetDropMenu');
|
||||
$control->ajaxGetDropMenu(0, 'execution', 'browse', '');
|
||||
$data = $this->getData();
|
||||
|
||||
$account = $this->app->user->account;
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$myExecutions['owner'] = array();
|
||||
$myExecutions['other'] = array();
|
||||
foreach($data->data->executions as $projectID => $projectExecutions)
|
||||
{
|
||||
foreach($projectExecutions as $execution)
|
||||
{
|
||||
if($execution->status == 'done' or $execution->status == 'closed') continue;
|
||||
|
||||
if($execution->PM == $account or isset($execution->teams->$account))
|
||||
{
|
||||
$myExecutions['owner'][] = $this->filterFields($execution, 'id,model,type,name,code,parent,status,PM');
|
||||
}
|
||||
else
|
||||
{
|
||||
$myExecutions['other'][] = $this->filterFields($execution, 'id,model,type,name,code,parent,status,PM');
|
||||
}
|
||||
}
|
||||
}
|
||||
$lastExecutions = array_merge($myExecutions['owner'], $myExecutions['other']);
|
||||
$lastExecutions = array_slice($lastExecutions, 0, 3);
|
||||
|
||||
$info->lastExecution['total'] = count($lastExecutions);
|
||||
$info->lastExecution['executions'] = $lastExecutions;
|
||||
}
|
||||
break;
|
||||
case 'actions':
|
||||
$info->actions = $this->my->getActions();
|
||||
break;
|
||||
case 'task':
|
||||
$info->task = array('total' => 0, 'tasks' => array());
|
||||
if(!common::hasPriv('my', 'task')) break;
|
||||
|
||||
$control = $this->loadController('my', 'task');
|
||||
$control->task($this->param('type', 'assignedTo'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 5), $this->param('page', 1));
|
||||
@@ -97,12 +193,13 @@ class userEntry extends Entry
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$info->task['total'] = $data->data->pager->recTotal;
|
||||
$info->task['tasks'] = $data->data->tasks;
|
||||
$info->task['tasks'] = array_values((array)$data->data->tasks);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'bug':
|
||||
$info->bug = array('total' => 0, 'bugs' => array());
|
||||
if(!common::hasPriv('my', 'bug')) break;
|
||||
|
||||
$control = $this->loadController('my', 'bug');
|
||||
$control->bug($this->param('type', 'assignedTo'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 5), $this->param('page', 1));
|
||||
@@ -110,54 +207,120 @@ class userEntry extends Entry
|
||||
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$bugs = array();
|
||||
foreach($data->data->bugs as $bug)
|
||||
{
|
||||
$status = array('code' => $bug->status, 'name' => $this->lang->bug->statusList[$bug->status]);
|
||||
if($bug->status == 'active' and $bug->confirmed) $status = array('code' => 'confirmed', 'name' => $this->lang->bug->labelConfirmed);
|
||||
if($bug->resolution == 'postponed') $status = array('code' => 'postponed', 'name' => $this->lang->bug->labelPostponed);
|
||||
if(!empty($bug->delay)) $status = array('code' => 'delay', 'name' => $this->lang->bug->overdueBugs);
|
||||
$bug->status = $status;
|
||||
|
||||
$bugs[$bug->id] = $bug;
|
||||
}
|
||||
|
||||
$storyChangeds = $this->dao->select('t1.id')->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story=t2.id')
|
||||
->where('t1.id')->in(array_keys($bugs))
|
||||
->andWhere('t1.story')->ne('0')
|
||||
->andWhere('t1.storyVersion != t2.version')
|
||||
->fetchPairs('id', 'id');
|
||||
foreach($storyChangeds as $bugID)
|
||||
{
|
||||
$status = array('code' => 'storyChanged', 'name' => $this->lang->bug->changed);
|
||||
$bugs[$bugID]->status = $status;
|
||||
}
|
||||
|
||||
$info->bug['total'] = $data->data->pager->recTotal;
|
||||
$info->bug['bugs'] = $data->data->bugs;
|
||||
$info->bug['bugs'] = array_values($bugs);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'todo':
|
||||
$info->todo = array('total' => 0, 'todos' => array());
|
||||
if(!common::hasPriv('my', 'todo')) break;
|
||||
|
||||
$control = $this->loadController('my', 'todo');
|
||||
$control->todo($this->param('date', 'all'), '', 'all', 'date_desc', 0, 0, $this->param('limit', 10), 1);
|
||||
$control->todo($this->param('date', 'all'), '', 'all', 'date_desc', 0, 0, $this->param('limit', 5), 1);
|
||||
$data = $this->getData();
|
||||
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$info->todo['total'] = $data->data->pager->recTotal;
|
||||
$info->todo['todos'] = $data->data->todos;
|
||||
$info->todo['todos'] = array_values((array)$data->data->todos);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'story':
|
||||
$info->story = array('total' => 0, 'stories' => array());
|
||||
if(!common::hasPriv('my', 'story')) break;
|
||||
|
||||
$control = $this->loadController('my', 'story');
|
||||
$control->story($this->param('type', 'assignedTo'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 5), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$stories = array();
|
||||
foreach($data->data->stories as $story)
|
||||
{
|
||||
$story->status = array('code' => $story->status, 'name' => $this->lang->story->statusList[$story->status]);
|
||||
$stories[$story->id] = $story;
|
||||
}
|
||||
|
||||
$info->story['total'] = $data->data->pager->recTotal;
|
||||
$info->story['stories'] = array_values($stories);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'issue':
|
||||
$info->issue = array('total' => 0, 'issues' => array());
|
||||
if(!common::hasPriv('my', 'issue')) break;
|
||||
|
||||
if(!empty($this->config->maxVersion))
|
||||
{
|
||||
$info->issue = array('total' => 0, 'issues' => array());
|
||||
|
||||
$control = $this->loadController('my', 'issue');
|
||||
$control->issue('createdBy', 'id_desc', 0, $this->param('limit', 10), 1);
|
||||
$control->issue('createdBy', 'id_desc', 0, $this->param('limit', 5), 1);
|
||||
$data = $this->getData();
|
||||
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$info->issue['total'] = $data->data->pager->recTotal;
|
||||
$info->issue['issues'] = $data->data->issues;
|
||||
$info->issue['issues'] = array_values((array)$data->data->issues);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'risk':
|
||||
$info->risk = array('total' => 0, 'risks' => array());
|
||||
if(!common::hasPriv('my', 'risk')) break;
|
||||
|
||||
if(!empty($this->config->maxVersion))
|
||||
{
|
||||
$info->risk = array('total' => 0, 'risks' => array());
|
||||
|
||||
$control = $this->loadController('my', 'risk');
|
||||
$control->risk('createdBy', 'id_desc', 0, $this->param('limit', 10), 1);
|
||||
$control->risk('createdBy', 'id_desc', 0, $this->param('limit', 5), 1);
|
||||
$data = $this->getData();
|
||||
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$info->risk['total'] = $data->data->pager->recTotal;
|
||||
$info->risk['risks'] = $data->data->risks;
|
||||
$info->risk['risks'] = array_values((array)$data->data->risks);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'meeting':
|
||||
$info->meeting = array('total' => 0, 'meetings' => array());
|
||||
if(!common::hasPriv('my', 'myMeeting')) break;
|
||||
|
||||
if(!empty($this->config->maxVersion))
|
||||
{
|
||||
$control = $this->loadController('my', 'myMeeting');
|
||||
$control->myMeeting('all', 'id_desc', 0, $this->param('limit', 5), 1);
|
||||
$data = $this->getData();
|
||||
|
||||
if($data->status == 'success')
|
||||
{
|
||||
$info->meeting['total'] = $data->data->pager->recTotal;
|
||||
$info->meeting['meetings'] = array_values((array)$data->data->meetings);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -167,6 +330,16 @@ class userEntry extends Entry
|
||||
case 'contribute':
|
||||
$info->contribute = $this->my->getContribute();
|
||||
break;
|
||||
case 'rights':
|
||||
$inAdminGroup = $this->dao->select('t1.*')->from(TABLE_USERGROUP)->alias('t1')
|
||||
->leftJoin(TABLE_GROUP)->alias('t2')->on('t1.group=t2.id')
|
||||
->where('t1.account')->eq($info->profile->account)
|
||||
->andWhere('t2.role')->eq('admin')
|
||||
->fetch();
|
||||
|
||||
$info->rights = array();
|
||||
$info->rights['admin'] = (!empty($inAdminGroup) or $this->app->user->admin);
|
||||
$info->rights['rights'] = $this->app->user->rights['rights'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-12
@@ -9,7 +9,7 @@
|
||||
* @version 1
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
class usersEntry extends entry
|
||||
class usersEntry extends entry
|
||||
{
|
||||
/**
|
||||
* GET method.
|
||||
@@ -19,22 +19,32 @@ class usersEntry extends entry
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$control = $this->loadController('company', 'browse');
|
||||
$control->browse('inside', 0, $this->param('type', 'bydept'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
if(!common::hasPriv('company', 'browse')) return $this->sendError(400, 'error: no company-browse priv.');
|
||||
|
||||
if(isset($data->status) and $data->status == 'success')
|
||||
$appendFields = $this->param('fileds', '');
|
||||
$type = $this->param('type', 'bydept');
|
||||
$limit = (int)$this->param('limit', 20);
|
||||
|
||||
$pager = null;
|
||||
if($limit)
|
||||
{
|
||||
$users = $data->data->users;
|
||||
$pager = $data->data->pager;
|
||||
$result = array();
|
||||
foreach($users as $user) $result[] = $this->format($user, 'locked:time');
|
||||
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'users' => $result));
|
||||
$this->app->loadClass('pager', $static = true);
|
||||
$pager = pager::init(0, $limit, $this->param('page', 1));
|
||||
}
|
||||
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
|
||||
$users = $this->loadModel('company')->getUsers($this->param('browse', 'inside'), $type, 0, 0, $this->param('order', 'id_desc'), $pager);
|
||||
$result = array();
|
||||
foreach($users as $user)
|
||||
{
|
||||
$user = $this->filterFields($user, 'id,dept,account,realname,role,pinyin,email,' . $appendFields);
|
||||
$result[] = $this->format($user, 'locked:time');
|
||||
}
|
||||
|
||||
return $this->sendError(400, 'error');
|
||||
$pageID = $pager ? $pager->pageID : 1;
|
||||
$total = $pager ? $pager->recTotal : count($result);
|
||||
$limit = $pager ? $pager->recPerPage : $total;
|
||||
|
||||
return $this->send(200, array('page' => $pageID, 'total' => $total, 'limit' => $limit, 'users' => $result));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+31
-9
@@ -4,7 +4,14 @@
|
||||
*/
|
||||
$routes = array();
|
||||
|
||||
$routes['/tokens'] = 'tokens';
|
||||
$routes['/tokens'] = 'tokens';
|
||||
$routes['/langs'] = 'langs';
|
||||
$routes['/comments'] = 'comments';
|
||||
|
||||
$routes['/tabs/:module'] = 'tabs';
|
||||
|
||||
$routes['/files'] = 'files';
|
||||
$routes['/files/:id'] = 'file';
|
||||
|
||||
$routes['/configurations'] = 'configs';
|
||||
$routes['/configurations/:name'] = 'config';
|
||||
@@ -27,13 +34,16 @@ $routes['/releases/:id'] = 'release';
|
||||
|
||||
$routes['/stories'] = 'stories';
|
||||
$routes['/products/:id/stories'] = 'stories';
|
||||
$routes['/projects/:id/stories'] = 'projectStories';
|
||||
$routes['/executions/:id/stories'] = 'executionStories';
|
||||
$routes['/stories/:id'] = 'story';
|
||||
$routes['/stories/:id/change'] = 'storyChange';
|
||||
|
||||
$routes['/products/:id/bugs'] = 'bugs';
|
||||
$routes['/bugs'] = 'bugs';
|
||||
$routes['/bugs/:id'] = 'bug';
|
||||
$routes['/products/:id/bugs'] = 'bugs';
|
||||
$routes['/projects/:id/bugs'] = 'projectBugs';
|
||||
$routes['/executions/:id/bugs'] = 'executionBugs';
|
||||
$routes['/bugs'] = 'bugs';
|
||||
$routes['/bugs/:id'] = 'bug';
|
||||
|
||||
$routes['/programs/:id/projects'] = 'projects';
|
||||
$routes['/projects'] = 'projects';
|
||||
@@ -57,21 +67,26 @@ $routes['/user'] = 'user';
|
||||
$routes['/programs'] = 'programs';
|
||||
$routes['/programs/:id'] = 'program';
|
||||
|
||||
$routes['/programs/:id/stakeholders'] = 'stakeholders';
|
||||
|
||||
$routes['/products/:productID/issues'] = 'productIssues';
|
||||
$routes['/projects/:projectID/issues'] = 'issues';
|
||||
$routes['/issues'] = 'issues';
|
||||
$routes['/issues/:issueID'] = 'issue';
|
||||
|
||||
$routes['/todos'] = 'todos';
|
||||
$routes['/todos/:id'] = 'todo';
|
||||
$routes['/todos'] = 'todos';
|
||||
$routes['/todos/:id'] = 'todo';
|
||||
$routes['/todos/:id/finish'] = 'todoFinish';
|
||||
$routes['/todos/:id/activate'] = 'todoActivate';
|
||||
|
||||
$routes['/projects/:projectID/builds'] = 'builds';
|
||||
$routes['/builds'] = 'builds';
|
||||
$routes['/builds/:id'] = 'build';
|
||||
|
||||
$routes['/products/:id/testcases'] = 'testcases';
|
||||
$routes['/testcases'] = 'testcases';
|
||||
$routes['/testcases/:id'] = 'testcase';
|
||||
$routes['/products/:id/testcases'] = 'testcases';
|
||||
$routes['/executions/:id/testcases'] = 'executioncases';
|
||||
$routes['/testcases'] = 'testcases';
|
||||
$routes['/testcases/:id'] = 'testcase';
|
||||
|
||||
$routes['/projects/:projectID/testtasks'] = 'testtasks';
|
||||
$routes['/testtasks'] = 'testtasks';
|
||||
@@ -84,6 +99,13 @@ $routes['/risks/:id'] = 'risk';
|
||||
$routes['/departments'] = 'departments';
|
||||
$routes['/departments/:id'] = 'department';
|
||||
|
||||
$routes['/doclibs'] = 'doclibs';
|
||||
$routes['/doclibs/:id'] = 'docs';
|
||||
$routes['/docs'] = 'docs';
|
||||
$routes['/docs/:id'] = 'doc';
|
||||
|
||||
$routes['/reports'] = 'reports';
|
||||
|
||||
$routes['/z/folders'] = 'zfolders';
|
||||
$routes['/z/folders/:id'] = 'zfolder';
|
||||
$routes['/z/files/:id'] = 'zfile';
|
||||
|
||||
@@ -486,6 +486,8 @@ class baseEntry
|
||||
$type = $field[1];
|
||||
$isArray = false;
|
||||
|
||||
if(!isset($object->$key)) continue;
|
||||
|
||||
$pos = strpos($type, ']');
|
||||
if($pos !== FALSE)
|
||||
{
|
||||
@@ -519,6 +521,48 @@ class baseEntry
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter fields.
|
||||
*
|
||||
* @param object $object
|
||||
* @param array $filters
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function filterFields($object, $allowable = '')
|
||||
{
|
||||
if(empty($allowable)) return $object;
|
||||
if(is_string($allowable)) $allowable = explode(',', $allowable);
|
||||
|
||||
$filtered = new stdclass();
|
||||
foreach($allowable as $field)
|
||||
{
|
||||
$field = trim($field);
|
||||
if(empty($field)) continue;
|
||||
if(!isset($object->$field)) continue;
|
||||
$filtered->$field = $object->$field;
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format user.
|
||||
*
|
||||
* @param string $account
|
||||
* @param array $users
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function formatUser($account, $users)
|
||||
{
|
||||
$user = array();
|
||||
$user['account'] = $account;
|
||||
$user['realname'] = zget($users, $account);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型转换.
|
||||
* Typecasting.
|
||||
@@ -589,7 +633,7 @@ class baseEntry
|
||||
{
|
||||
$module = $this->app->getModuleName();
|
||||
$method = $this->app->getMethodName();
|
||||
if($module and $method and !commonModel::hasPriv($module, $method))
|
||||
if($module and $method and !$this->loadModel('common')->isOpenMethod($module, $method) and !commonModel::hasPriv($module, $method))
|
||||
{
|
||||
$this->send(403, array('error' => 'Access not allowed'));
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class action extends control
|
||||
$actionID = $this->action->create($objectType, $objectID, 'Commented', $this->post->comment);
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api')
|
||||
{
|
||||
die(array('status' => 'success', 'data' => $actionID));
|
||||
return $this->send(array('status' => 'success', 'data' => $actionID));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+83
-3
@@ -69,7 +69,7 @@ class actionModel extends model
|
||||
|
||||
|
||||
$this->dao->insert(TABLE_ACTION)->data($action)->autoCheck()->exec();
|
||||
$actionID = $this->dbh->lastInsertID();
|
||||
$actionID = $this->dao->lastInsertID();
|
||||
|
||||
if($this->post->uid) $this->file->updateObjectID($this->post->uid, $objectID, $objectType);
|
||||
|
||||
@@ -983,7 +983,7 @@ class actionModel extends model
|
||||
$action->date = date(DT_MONTHTIME2, strtotime($action->date));
|
||||
$action->actionLabel = isset($this->lang->$objectType->$actionType) ? $this->lang->$objectType->$actionType : $action->action;
|
||||
$action->actionLabel = isset($this->lang->action->label->$actionType) ? $this->lang->action->label->$actionType : $action->actionLabel;
|
||||
$action->objectLabel = $this->getObjectLabel($objectType, $action->objectID, $requirements);
|
||||
$action->objectLabel = $this->getObjectLabel($objectType, $action->objectID, $actionType, $requirements);
|
||||
|
||||
/* If action type is login or logout, needn't link. */
|
||||
if($actionType == 'svncommited' or $actionType == 'gitcommited') $action->actor = zget($commiters, $action->actor);
|
||||
@@ -1110,11 +1110,12 @@ class actionModel extends model
|
||||
*
|
||||
* @param string $objectType
|
||||
* @param int $objectID
|
||||
* @param string $actionType
|
||||
* @param array $requirements
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getObjectLabel($objectType, $objectID, $requirements)
|
||||
public function getObjectLabel($objectType, $objectID, $actionType, $requirements)
|
||||
{
|
||||
$actionObjectLabel = $objectType;
|
||||
if(isset($this->lang->action->label->$objectType))
|
||||
@@ -1579,4 +1580,83 @@ class actionModel extends model
|
||||
echo $actionType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process action for API.
|
||||
*
|
||||
* @param array $actions
|
||||
* @param array $users
|
||||
* @param array $objectLang
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function processActionForAPI($actions, $users = array(), $objectLang = array())
|
||||
{
|
||||
$actions = (array)$actions;
|
||||
foreach($actions as $action)
|
||||
{
|
||||
$action->actor = zget($users, $action->actor);
|
||||
if($action->action == 'assigned') $action->extra = zget($users, $action->extra);
|
||||
if(strpos($action->actor, ':') !== false) $action->actor = substr($action->actor, strpos($action->actor, ':') + 1);
|
||||
|
||||
ob_start();
|
||||
$this->printAction($action);
|
||||
$action->desc = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
if($action->history)
|
||||
{
|
||||
foreach($action->history as $i => $history)
|
||||
{
|
||||
$history->fieldName = zget($objectLang, $history->field);
|
||||
$action->history[$i] = $history;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_values($actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process dynamic for API.
|
||||
*
|
||||
* @param array $dynamics
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function processDynamicForAPI($dynamics)
|
||||
{
|
||||
$users = $this->loadModel('user')->getList();
|
||||
$simplifyUsers = array();
|
||||
foreach($users as $user)
|
||||
{
|
||||
$simplifyUser = new stdclass();
|
||||
$simplifyUser->id = $user->id;
|
||||
$simplifyUser->account = $user->account;
|
||||
$simplifyUser->realname = $user->realname;
|
||||
$simplifyUser->avatar = $user->avatar;
|
||||
$simplifyUsers[$user->account] = $simplifyUser;
|
||||
}
|
||||
|
||||
$actions = array();
|
||||
foreach($dynamics as $key => $dynamic)
|
||||
{
|
||||
if($dynamic->objectType == 'user') continue;
|
||||
|
||||
$simplifyUser = zget($simplifyUsers, $dynamic->actor, '');
|
||||
$actor = $simplifyUser;
|
||||
if(empty($simplifyUser))
|
||||
{
|
||||
$actor = new stdclass();
|
||||
$actor->id = 0;
|
||||
$actor->account = $dynamic->actor;
|
||||
$actor->realname = $dynamic->actor;
|
||||
$actor->avatar = '';
|
||||
}
|
||||
|
||||
$dynamic->actor = $actor;
|
||||
$actions[] = $dynamic;
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,3 +585,20 @@ $lang->block->flowchart['project'] = array('项目经理', '创建' . $lang->exe
|
||||
if($config->systemMode == 'new') $lang->block->flowchart['project'] = array('项目经理', '创建项目、' . $lang->executionCommon, '维护团队', "关联需求", '分解任务', '跟踪进度');
|
||||
$lang->block->flowchart['dev'] = array('研发人员', '领取任务和Bug', '设计实现方案', '更新状态', '完成任务和Bug', '提交代码');
|
||||
$lang->block->flowchart['tester'] = array('测试人员', '撰写用例', '执行用例', '提交Bug', '验证Bug', '关闭Bug');
|
||||
|
||||
$lang->block->zentaoapp = new stdclass();
|
||||
$lang->block->zentaoapp->thisYearInvestment = '今年投入';
|
||||
$lang->block->zentaoapp->sinceTotalInvestment = '从使用至今,总投入';
|
||||
$lang->block->zentaoapp->myStory = '我的需求';
|
||||
$lang->block->zentaoapp->allStorySum = '需求总数';
|
||||
$lang->block->zentaoapp->storyCompleteRate = '需求完成率';
|
||||
$lang->block->zentaoapp->latestExecution = '近期执行';
|
||||
$lang->block->zentaoapp->involvedExecution = '我参与的执行';
|
||||
$lang->block->zentaoapp->mangedProduct = '负责产品';
|
||||
$lang->block->zentaoapp->involvedProject = '参与项目';
|
||||
$lang->block->zentaoapp->customIndexCard = '定制首页卡片';
|
||||
$lang->block->zentaoapp->createStory = '提需求';
|
||||
$lang->block->zentaoapp->createEffort = '记日志';
|
||||
$lang->block->zentaoapp->createDoc = '建文档';
|
||||
$lang->block->zentaoapp->createTodo = '建待办';
|
||||
$lang->block->zentaoapp->workbench = '工作台';
|
||||
|
||||
@@ -9,13 +9,14 @@ $lang->branch->manageTitle = '%s Management';
|
||||
$lang->branch->all = 'All ';
|
||||
$lang->branch->main = 'Main';
|
||||
|
||||
$lang->branch->edit = 'Edit';
|
||||
$lang->branch->editAction = 'Edit %s';
|
||||
$lang->branch->edit = 'Edit %s';
|
||||
$lang->branch->editAction = 'Edit Branch';
|
||||
$lang->branch->activate = 'Activate';
|
||||
$lang->branch->activateAction = 'Activate Branch';
|
||||
$lang->branch->close = 'Close';
|
||||
$lang->branch->closeAction = 'Close Branch';
|
||||
$lang->branch->create = 'Create %s';
|
||||
$lang->branch->createAction = 'Create Branch';
|
||||
$lang->branch->merge = 'Merge';
|
||||
$lang->branch->batchEdit = 'Batch Edit';
|
||||
$lang->branch->defaultBranch = 'Default Branch';
|
||||
|
||||
@@ -9,13 +9,14 @@ $lang->branch->manageTitle = '%s管理';
|
||||
$lang->branch->all = '所有';
|
||||
$lang->branch->main = '主干';
|
||||
|
||||
$lang->branch->edit = '编辑';
|
||||
$lang->branch->editAction = '编辑%s';
|
||||
$lang->branch->edit = '编辑%s';
|
||||
$lang->branch->editAction = '编辑分支';
|
||||
$lang->branch->activate = '激活';
|
||||
$lang->branch->activateAction = '激活分支';
|
||||
$lang->branch->close = '关闭';
|
||||
$lang->branch->closeAction = '关闭分支';
|
||||
$lang->branch->create = '新增%s';
|
||||
$lang->branch->createAction = '新增分支';
|
||||
$lang->branch->merge = '合并';
|
||||
$lang->branch->batchEdit = '批量编辑';
|
||||
$lang->branch->defaultBranch = '默认分支';
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<?php include '../../common/view/header.html.php';?>
|
||||
<div id='mainContent' class='main-content'>
|
||||
<div class='main-header'>
|
||||
<h2><?php echo sprintf($lang->branch->editAction, $lang->product->branchName[$product->type]);?></h2>
|
||||
<h2><?php echo sprintf($lang->branch->edit, $lang->product->branchName[$product->type]);?></h2>
|
||||
</div>
|
||||
<form class='main-form' method='post' target='hiddenwin'>
|
||||
<table class='table table-form'>
|
||||
|
||||
+60
-10
@@ -433,10 +433,10 @@ class bug extends control
|
||||
}
|
||||
|
||||
/* Get product, then set menu. */
|
||||
$productID = $this->product->saveState($productID, $this->products);
|
||||
$product = $this->product->getById($productID);
|
||||
$productID = $this->product->saveState($productID, $this->products);
|
||||
$productInfo = $this->product->getById($productID);
|
||||
$branches = $productInfo->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($productID, 'active');
|
||||
if($branch === '') $branch = (int)$this->cookie->preBranch;
|
||||
$branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($productID);
|
||||
|
||||
/* Init vars. */
|
||||
$projectID = 0;
|
||||
@@ -522,7 +522,6 @@ class bug extends control
|
||||
$builds = $this->loadModel('build')->getProductBuildPairs($productID, $branch, 'noempty,noterminate,nodone');
|
||||
$stories = $this->story->getProductStoryPairs($productID, $branch);
|
||||
}
|
||||
$builds[''] = '';
|
||||
|
||||
$moduleOwner = $this->bug->getModuleOwner($moduleID, $productID);
|
||||
|
||||
@@ -631,7 +630,7 @@ class bug extends control
|
||||
$this->view->keywords = $keywords;
|
||||
$this->view->severity = $severity;
|
||||
$this->view->type = $type;
|
||||
$this->view->product = $product;
|
||||
$this->view->productInfo = $productInfo;
|
||||
$this->view->branch = $branch;
|
||||
$this->view->branches = $branches;
|
||||
$this->view->blockID = $blockID;
|
||||
@@ -739,7 +738,7 @@ class bug extends control
|
||||
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, $branch === 'all' ? 0 : $branch);
|
||||
$this->view->moduleID = $moduleID;
|
||||
$this->view->branch = $branch;
|
||||
$this->view->branches = $this->loadModel('branch')->getPairs($productID);
|
||||
$this->view->branches = $this->loadModel('branch')->getPairs($productID, 'active');
|
||||
$this->display();
|
||||
}
|
||||
|
||||
@@ -1014,12 +1013,38 @@ class bug extends control
|
||||
$plans = $this->loadModel('productplan')->getPairs($productID, $branch);
|
||||
$plans = array('' => '', 'ditto' => $this->lang->bug->ditto) + $plans;
|
||||
|
||||
/* Set branches and modules. */
|
||||
$branches = array();
|
||||
$modules = array();
|
||||
if($product->type != 'normal')
|
||||
{
|
||||
$branches = $this->loadModel('branch')->getPairs($productID);
|
||||
if($branch === 'all')
|
||||
{
|
||||
$modules[0] = $this->tree->getOptionMenu($productID, 'bug', 0, 0);
|
||||
foreach($branches as $branchID => $branchName)
|
||||
{
|
||||
$modules[$branchID] = $this->tree->getOptionMenu($productID, 'bug', 0, $branchID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$modules[$branch] = $this->tree->getOptionMenu($productID, 'bug', 0, $branch);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$modules[0] = $this->tree->getOptionMenu($productID, 'bug', 0, 0);
|
||||
}
|
||||
|
||||
/* Set product menu. */
|
||||
$this->qa->setMenu($this->products, $productID, $branch);
|
||||
|
||||
$this->view->title = $product->name . $this->lang->colon . "BUG" . $this->lang->bug->batchEdit;
|
||||
$this->view->position[] = html::a($this->createLink('bug', 'browse', "productID=$productID&branch=$branch"), $this->products[$productID]);
|
||||
$this->view->plans = $plans;
|
||||
$this->view->branches = $product->type == 'normal' ? array() : array('' => '', 'ditto' => $this->lang->bug->ditto) + $this->loadModel('branch')->getPairs($product->id);
|
||||
$this->view->branches = $branches;
|
||||
$this->view->modules = $modules;
|
||||
}
|
||||
/* The bugs of my. */
|
||||
else
|
||||
@@ -1147,10 +1172,35 @@ class bug extends control
|
||||
{
|
||||
if($this->post->bugIDList)
|
||||
{
|
||||
$bugIDList = $this->post->bugIDList;
|
||||
$bugIDList = array_unique($bugIDList);
|
||||
$bugIDList = $this->post->bugIDList;
|
||||
$bugIDList = array_unique($bugIDList);
|
||||
$oldBugs = $this->bug->getByList($bugIDList);
|
||||
$skipBugIDList = '';
|
||||
unset($_POST['bugIDList']);
|
||||
$allChanges = $this->bug->batchChangeBranch($bugIDList, $branchID);
|
||||
|
||||
/* Remove condition mismatched bugs. */
|
||||
foreach($bugIDList as $key => $bugID)
|
||||
{
|
||||
$oldBug = $oldBugs[$bugID];
|
||||
if($branchID == $oldBug->branch)
|
||||
{
|
||||
unset($bugIDList[$key]);
|
||||
continue;
|
||||
}
|
||||
elseif($branchID != $oldBug->branch and !empty($oldBug->module))
|
||||
{
|
||||
$skipBugIDList .= '[' . $bugID . ']';
|
||||
unset($bugIDList[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($skipBugIDList))
|
||||
{
|
||||
echo js::alert(sprintf($this->lang->bug->noSwitchBranch, $skipBugIDList));
|
||||
}
|
||||
|
||||
$allChanges = $this->bug->batchChangeBranch($bugIDList, $branchID, $oldBugs);
|
||||
if(dao::isError()) die(js::error(dao::getError()));
|
||||
foreach($allChanges as $bugID => $changes)
|
||||
{
|
||||
|
||||
@@ -45,3 +45,4 @@ html[lang='en'] #deadlineTd .input-group-addon {padding: 5px 18px;}
|
||||
#osBox .required:after {right: 1px;}
|
||||
#osBox {width: 190px;}
|
||||
#projectBox .required:after {right: 1px;}
|
||||
#branch_chosen {min-width: 90px;}
|
||||
|
||||
@@ -6,42 +6,6 @@ $(function()
|
||||
if($titleCol.width() < 150) $titleCol.width(150);
|
||||
})
|
||||
|
||||
/**
|
||||
* Set branch related.
|
||||
*
|
||||
* @param int $branchID
|
||||
* @param int $productID
|
||||
* @param int $num
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function setBranchRelated(branchID, productID, num)
|
||||
{
|
||||
moduleLink = createLink('tree', 'ajaxGetModules', 'productID=' + productID + '&viewType=bug&branch=' + branchID + '&num=' + num);
|
||||
$.get(moduleLink, function(modules)
|
||||
{
|
||||
if(!modules) modules = '<select id="modules' + num + '" name="modules[' + num + ']" class="form-control"></select>';
|
||||
$('#modules' + num).replaceWith(modules);
|
||||
$("#modules" + num + "_chosen").remove();
|
||||
$("#modules" + num).next('.picker').remove();
|
||||
$("#modules" + num).chosen();
|
||||
});
|
||||
|
||||
executionLink = createLink('product', 'ajaxGetExecutions', 'productID=' + productID + '&projectID=0&branch=' + branchID + '&num=' + num);
|
||||
$.get(executionLink, function(executions)
|
||||
{
|
||||
if(!executions) executions = '<select id="executions' + num + '" name="executions[' + num + ']" class="form-control"></select>';
|
||||
$('#executions' + num).replaceWith(executions);
|
||||
$("#executions" + num + "_chosen").remove();
|
||||
$("#executions" + num).next('.picker').remove();
|
||||
$("#executions" + num).chosen();
|
||||
});
|
||||
|
||||
buildLink = createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + "&varName=openedBuilds&build=&branch=" + branchID + "&index=" + num);
|
||||
|
||||
setOpenedBuilds(buildLink, num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set opened builds.
|
||||
*
|
||||
|
||||
@@ -612,3 +612,52 @@ function notice()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set branch related.
|
||||
*
|
||||
* @param int $branchID
|
||||
* @param int $productID
|
||||
* @param int $num
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function setBranchRelated(branchID, productID, num)
|
||||
{
|
||||
moduleLink = createLink('tree', 'ajaxGetModules', 'productID=' + productID + '&viewType=bug&branch=' + branchID + '&num=' + num);
|
||||
$.get(moduleLink, function(modules)
|
||||
{
|
||||
if(!modules) modules = '<select id="modules' + num + '" name="modules[' + num + ']" class="form-control"></select>';
|
||||
$('#modules' + num).replaceWith(modules);
|
||||
$("#modules" + num + "_chosen").remove();
|
||||
$("#modules" + num).next('.picker').remove();
|
||||
$("#modules" + num).chosen();
|
||||
});
|
||||
|
||||
executionLink = createLink('product', 'ajaxGetExecutions', 'productID=' + productID + '&projectID=0&branch=' + branchID + '&num=' + num);
|
||||
$.get(executionLink, function(executions)
|
||||
{
|
||||
if(!executions) executions = '<select id="executions' + num + '" name="executions[' + num + ']" class="form-control"></select>';
|
||||
$('#executions' + num).replaceWith(executions);
|
||||
$("#executions" + num + "_chosen").remove();
|
||||
$("#executions" + num).next('.picker').remove();
|
||||
$("#executions" + num).chosen();
|
||||
});
|
||||
|
||||
buildLink = createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + "&varName=openedBuilds&build=&branch=" + branchID + "&index=" + num);
|
||||
|
||||
/* If the branch of the current row is inconsistent with the one below, clear the module and execution of the nex row. */
|
||||
if(config.currentMethod == 'batchCreate')
|
||||
{
|
||||
var nextBranchID = $('#branch' + (num + 1)).val();
|
||||
if(nextBranchID != branchID)
|
||||
{
|
||||
$('#modules' + (num + 1)).find("option[value='ditto']").remove();
|
||||
$('#modules' + (num + 1)).trigger("chosen:updated");
|
||||
|
||||
$('#executions' + (num + 1)).find("option[value='ditto']").remove();
|
||||
$('#executions' + (num + 1)).trigger("chosen:updated");
|
||||
}
|
||||
setOpenedBuilds(buildLink, num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +192,7 @@ $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?";
|
||||
$lang->bug->noSwitchBranch = 'The linked module of Bug%s is not in the current branch. It will be omitted.';
|
||||
|
||||
/* Template. */
|
||||
$lang->bug->tplStep = "<p>[Steps]</p><br/>";
|
||||
|
||||
@@ -151,12 +151,16 @@ $lang->bug->assignToMeAB = '指派给我';
|
||||
$lang->bug->openedByMeAB = '由我创建';
|
||||
$lang->bug->resolvedByMeAB = '由我解决';
|
||||
|
||||
$lang->bug->ditto = '同上';
|
||||
$lang->bug->dittoNotice = '该bug与上一bug不属于同一产品!';
|
||||
$lang->bug->noAssigned = '未指派';
|
||||
$lang->bug->noBug = '暂时没有Bug。';
|
||||
$lang->bug->noModule = '<div>您现在还没有模块信息</div><div>请维护测试模块</div>';
|
||||
$lang->bug->delayWarning = " <strong class='text-danger'> 延期%s天 </strong>";
|
||||
$lang->bug->ditto = '同上';
|
||||
$lang->bug->dittoNotice = '该bug与上一bug不属于同一产品!';
|
||||
$lang->bug->noAssigned = '未指派';
|
||||
$lang->bug->noBug = '暂时没有Bug。';
|
||||
$lang->bug->noModule = '<div>您现在还没有模块信息</div><div>请维护测试模块</div>';
|
||||
$lang->bug->delayWarning = " <strong class='text-danger'> 延期%s天 </strong>";
|
||||
$lang->bug->labelConfirmed = '已确认';
|
||||
$lang->bug->labelPostponed = '被延期';
|
||||
$lang->bug->changed = '已变动';
|
||||
$lang->bug->storyChanged = '需求变动';
|
||||
|
||||
/* 页面标签。*/
|
||||
$lang->bug->lblAssignedTo = '当前指派';
|
||||
@@ -192,6 +196,7 @@ $lang->bug->skipClose = 'Bug %s 不是已解决状态,不能关闭
|
||||
$lang->bug->executionAccessDenied = "您无权访问该Bug所属的{$lang->executionCommon}!";
|
||||
$lang->bug->stepsNotEmpty = "重现步骤不能为空。";
|
||||
$lang->bug->confirmUnlinkBuild = "更换解决版本将取消与旧版本的关联,您确定取消该bug与%s的关联吗?";
|
||||
$lang->bug->noSwitchBranch = 'Bug%s所属模块不在当前分支下,将自动忽略。';
|
||||
|
||||
/* 模板。*/
|
||||
$lang->bug->tplStep = "<p>[步骤]</p><br/>";
|
||||
|
||||
@@ -623,7 +623,7 @@ class bugModel extends model
|
||||
|
||||
$now = helper::now();
|
||||
$bug = fixer::input('post')
|
||||
->cleanInt('product,module,severity,execution,story,task,branch')
|
||||
->cleanInt('product,module,severity,project,execution,story,task,branch')
|
||||
->stripTags($this->config->bug->editor->edit['id'], $this->config->allowedTags)
|
||||
->setDefault('product,module,execution,story,task,duplicateBug,branch', 0)
|
||||
->setDefault('openedBuild', '')
|
||||
@@ -742,6 +742,7 @@ class bugModel extends model
|
||||
$bug->title = $data->titles[$bugID];
|
||||
$bug->plan = empty($data->plans[$bugID]) ? 0 : $data->plans[$bugID];
|
||||
$bug->branch = empty($data->branches[$bugID]) ? 0 : $data->branches[$bugID];
|
||||
$bug->module = $data->modules[$bugID];
|
||||
$bug->assignedTo = $data->assignedTos[$bugID];
|
||||
$bug->deadline = $data->deadlines[$bugID];
|
||||
$bug->resolvedBy = $data->resolvedBys[$bugID];
|
||||
@@ -1043,18 +1044,17 @@ class bugModel extends model
|
||||
*
|
||||
* @param array $bugIDList
|
||||
* @param int $branchID
|
||||
* @param array $oldBugs
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function batchChangeBranch($bugIDList, $branchID)
|
||||
public function batchChangeBranch($bugIDList, $branchID, $oldBugs)
|
||||
{
|
||||
$now = helper::now();
|
||||
$allChanges = array();
|
||||
$oldBugs = $this->getByList($bugIDList);
|
||||
foreach($bugIDList as $bugID)
|
||||
{
|
||||
$oldBug = $oldBugs[$bugID];
|
||||
if($branchID == $oldBug->branch) continue;
|
||||
|
||||
$bug = new stdclass();
|
||||
$bug->lastEditedBy = $this->app->user->account;
|
||||
@@ -1313,7 +1313,7 @@ class bugModel extends model
|
||||
$this->config->bug->search['params']['module']['values'] = $modules;
|
||||
$this->config->bug->search['params']['execution']['values'] = $this->loadModel('product')->getExecutionPairsByProduct($productID, 0, 'id_desc', $projectID);
|
||||
$this->config->bug->search['params']['severity']['values'] = array(0 => '') + $this->lang->bug->severityList; //Fix bug #939.
|
||||
$this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getProductBuildPairs($productID, 0, $params = '');
|
||||
$this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getProductBuildPairs($productID, 0, 'withbranch');
|
||||
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values'];
|
||||
if($this->session->currentProductType == 'normal')
|
||||
{
|
||||
@@ -1514,6 +1514,8 @@ class bugModel extends model
|
||||
->beginIF(!empty($productID))->andWhere('product')->eq($productID)->fi()
|
||||
->beginIF($type == 'unresolved')->andWhere('status')->eq('active')->fi()
|
||||
->beginIF($type == 'noclosed')->andWhere('status')->ne('closed')->fi()
|
||||
->beginIF($type == 'assignedtome')->andWhere('assignedTo')->eq($this->app->user->account)->fi()
|
||||
->beginIF($type == 'openedbyme')->andWhere('openedBy')->eq($this->app->user->account)->fi()
|
||||
->beginIF($build)->andWhere("CONCAT(',', openedBuild, ',') like '%,$build,%'")->fi()
|
||||
->beginIF($excludeBugs)->andWhere('id')->notIN($excludeBugs)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
|
||||
@@ -51,10 +51,11 @@
|
||||
<th class='c-type<?php echo zget($visibleFields, 'type', ' hidden') . zget($requiredFields, 'type', '', ' required');?>'><?php echo $lang->bug->type;?></th>
|
||||
<th class='c-severity<?php echo zget($visibleFields, 'severity', ' hidden') . zget($requiredFields, 'severity', '', ' required');?>'><?php echo $lang->bug->severity;?></th>
|
||||
<th class='c-pri<?php echo zget($visibleFields, 'pri', ' hidden') . zget($requiredFields, 'pri', '', ' required');?>'><?php echo $lang->bug->pri;?></th>
|
||||
<th class="required <?php if(count($visibleFields) >= 10) echo ' c-title';?>"><?php echo $lang->bug->title;?></th>
|
||||
<th class="required <?php if(count($visibleFields) >= 9) echo ' c-title';?>"><?php echo $lang->bug->title;?></th>
|
||||
<?php if($branchProduct):?>
|
||||
<th class='c-branch<?php echo zget($visibleFields, 'branch', ' hidden')?>'><?php echo $lang->bug->branch;?></th>
|
||||
<th class='c-branch'><?php echo $lang->bug->branch;?></th>
|
||||
<?php endif;?>
|
||||
<th class='c-module<?php echo zget($requiredFields, 'module', '', ' required');?>'> <?php echo $lang->bug->module;?></th>
|
||||
<th class='c-plan<?php echo zget($visibleFields, 'productplan', ' hidden') . zget($requiredFields, 'productplan', '', ' required');?>'><?php echo $lang->bug->productplan;?></th>
|
||||
<th class='c-assigned<?php echo zget($visibleFields, 'assignedTo', ' hidden') . zget($requiredFields, 'assignedTo', '', ' required');?>'><?php echo $lang->bug->assignedTo;?></th>
|
||||
<th class='c-date<?php echo zget($visibleFields, 'deadline', ' hidden') . zget($requiredFields, 'deadline', '', ' required');?>'><?php echo $lang->bug->deadline;?></th>
|
||||
@@ -78,14 +79,14 @@
|
||||
{
|
||||
$product = $this->product->getByID($bug->product);
|
||||
|
||||
$plans = $this->loadModel('productplan')->getPairs($bug->product, $branch);
|
||||
$plans = array('' => '', 'ditto' => $this->lang->bug->ditto) + $plans;
|
||||
$bugBranch = isset($bug->branch) ? $bug->branch : 0;
|
||||
$plans = $this->loadModel('productplan')->getPairs($bug->product, $branch);
|
||||
|
||||
$branches = $product->type == 'normal' ? array('' => '') : $this->loadModel('branch')->getPairs($product->id);
|
||||
if($product->type != 'normal')
|
||||
{
|
||||
foreach($branches as $branchID => $branchName) $branches[$branchID] = '/' . $product->name . '/' . $branchName;
|
||||
$branches = array('ditto' => $this->lang->story->ditto) + $branches;
|
||||
$modules[$bugBranch] = $this->tree->getOptionMenu($bug->product, $viewType = 'case', 0, $bugBranch);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -109,12 +110,13 @@
|
||||
<div>
|
||||
</td>
|
||||
<?php if($branchProduct):?>
|
||||
<td class='<?php echo zget($visibleFields, 'branch', ' hidden')?>' style='overflow:visible'>
|
||||
<td style='overflow:visible'>
|
||||
<?php $branchProductID = $productID ? $productID : $product->id;?>
|
||||
<?php $disabled = (isset($product) and $product->type == 'normal') ? "disabled='disabled'" : '';?>
|
||||
<?php echo html::select("branches[$bugID]", $branches, $bug->branch, "class='form-control chosen' $disabled");?>
|
||||
<?php echo html::select("branches[$bugID]", $branches, $bug->branch, "class='form-control chosen' $disabled onchange='setBranchRelated(this.value, $bug->product, $bug->id)'");?>
|
||||
</td>
|
||||
<?php endif;?>
|
||||
<td><?php echo html::select("modules[$bugID]", $modules[$bug->branch], $bug->module, "class='form-control chosen'");?></td>
|
||||
<td class='<?php echo zget($visibleFields, 'productplan', ' hidden')?>' style='overflow:visible'><?php echo html::select("plans[$bugID]", $plans, $bug->plan, "class='form-control chosen'");?></td>
|
||||
<td class='<?php echo zget($visibleFields, 'assignedTo', ' hidden')?>' style='overflow:visible'><?php echo html::select("assignedTos[$bugID]", $users, $bug->assignedTo, "class='form-control chosen'");?></td>
|
||||
<td class='<?php echo zget($visibleFields, 'deadline', ' hidden')?>' style='overflow:visible'><?php echo html::input("deadlines[$bugID]", $bug->deadline, "class='form-control form-date'");?></td>
|
||||
|
||||
@@ -50,7 +50,7 @@ js::set('moduleID', $moduleID);
|
||||
<td>
|
||||
<div class='input-group'>
|
||||
<?php echo html::select('product', $products, $productID, "onchange='loadAll(this.value);' class='form-control chosen control-product'");?>
|
||||
<?php if($product->type != 'normal' and isset($products[$productID])):?>
|
||||
<?php if($productInfo->type != 'normal' and isset($products[$productID])):?>
|
||||
<?php echo html::select('branch', $branches, $branch, "onchange='loadBranch()' class='form-control chosen control-branch'");?>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ js::set('changeProductConfirmed' , false);
|
||||
js::set('changeExecutionConfirmed' , false);
|
||||
js::set('confirmChangeProduct' , $lang->bug->confirmChangeProduct);
|
||||
js::set('planID' , $bug->plan);
|
||||
js::set('oldExecutionID' , $bug->execution);
|
||||
js::set('oldProjectID' , $bug->project);
|
||||
js::set('oldStoryID' , $bug->story);
|
||||
js::set('oldTaskID' , $bug->task);
|
||||
js::set('oldOpenedBuild' , $bug->openedBuild);
|
||||
|
||||
@@ -71,7 +71,7 @@ class build extends control
|
||||
$products = array();
|
||||
|
||||
/* Set branches and products. */
|
||||
if($productGroups[$productID]->type != 'normal' and isset($branchGroups[$productID]))
|
||||
if(isset($productGroups[$productID]) and $productGroups[$productID]->type != 'normal' and isset($branchGroups[$productID]))
|
||||
{
|
||||
foreach($branchGroups[$productID] as $branchID => $branch)
|
||||
{
|
||||
@@ -350,7 +350,6 @@ class build extends control
|
||||
*/
|
||||
public function ajaxGetProductBuilds($productID, $varName, $build = '', $branch = 0, $index = 0, $type = 'normal')
|
||||
{
|
||||
$branch = $branch ? "0,$branch" : $branch;
|
||||
$isJsonView = $this->app->getViewType() == 'json';
|
||||
if($varName == 'openedBuild' )
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ function loadBranches(productID)
|
||||
oldBranch = productGroups[productID]['branches'];
|
||||
}
|
||||
|
||||
executionID = $('#execution').val();
|
||||
$.get(createLink('branch', 'ajaxGetBranches', 'productID=' + productID + '&oldBranch=0¶m=&projectID=' + executionID), function(data)
|
||||
{
|
||||
if(data)
|
||||
|
||||
@@ -227,7 +227,6 @@ class buildModel extends model
|
||||
*/
|
||||
public function getExecutionBuildPairs($executionID, $productID, $branch = 0, $params = '', $buildIdList = '')
|
||||
{
|
||||
$branch = str_replace('0,', '', $branch);
|
||||
if($branch == 'all') $branch = 0;
|
||||
$sysBuilds = array();
|
||||
$selectedBuilds = array();
|
||||
@@ -271,7 +270,6 @@ class buildModel extends model
|
||||
*/
|
||||
public function getProductBuildPairs($products, $branch = 0, $params = 'noterminate, nodone', $replace = true)
|
||||
{
|
||||
$branch = str_replace('0,', '', $branch);
|
||||
if($branch == 'all') $branch = 0;
|
||||
$sysBuilds = array();
|
||||
if(strpos($params, 'noempty') === false) $sysBuilds = array('' => '');
|
||||
@@ -292,7 +290,8 @@ class buildModel extends model
|
||||
{
|
||||
if(empty($build->releaseID) and (strpos($params, 'nodone') !== false) and ($build->executionStatus === 'done')) continue;
|
||||
if((strpos($params, 'noterminate') !== false) and ($build->releaseStatus === 'terminate')) continue;
|
||||
$builds[$key] = ((strpos($params, 'withbranch') !== false and $build->branchName) ? $build->branchName . '/' : '') . $build->name;
|
||||
$branchName = $build->branchName ? $build->branchName : $this->lang->branch->main;
|
||||
$builds[$key] = (strpos($params, 'withbranch') !== false ? $branchName . '/' : '') . $build->name;
|
||||
}
|
||||
|
||||
if(!$builds) return $sysBuilds;
|
||||
@@ -306,7 +305,11 @@ class buildModel extends model
|
||||
->beginIF($branch)->andWhere('branch')->in("0,$branch")->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetchPairs();
|
||||
foreach($releases as $buildID => $releaseName) $builds[$buildID] = ((strpos($params, 'withbranch') !== false and $productBuilds[$buildID]->branchName) ? $productBuilds[$buildID]->branchName . '/' : '') . $releaseName;
|
||||
foreach($releases as $buildID => $releaseName)
|
||||
{
|
||||
$branchName = $productBuilds[$buildID]->branchName ? $productBuilds[$buildID]->branchName : $this->lang->branch->main;
|
||||
$builds[$buildID] = (strpos($params, 'withbranch') !== false ? $branchName . '/' : '') . $releaseName;
|
||||
}
|
||||
}
|
||||
|
||||
return $sysBuilds + $builds;
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
<?php
|
||||
if(!empty($product) and $product->type != 'normal')
|
||||
{
|
||||
$branches = $branches[$product->id];
|
||||
echo "<span class='input-group-addon fix-padding fix-border'></span>" . html::select('branch', $branches, key($product->branches), "class='form-control chosen'");
|
||||
}
|
||||
?>
|
||||
@@ -94,5 +93,4 @@
|
||||
</div>
|
||||
</div>
|
||||
<?php js::set('productGroups', $productGroups)?>
|
||||
<?php js::set('executionID', $executionID)?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
@@ -34,6 +34,7 @@ $lang->logout = '退出';
|
||||
$lang->login = '登录';
|
||||
$lang->help = '帮助';
|
||||
$lang->aboutZenTao = '关于禅道';
|
||||
$lang->ztWebsite = '禅道系统网址';
|
||||
$lang->profile = '个人档案';
|
||||
$lang->changePassword = '修改密码';
|
||||
$lang->unfoldMenu = '展开导航';
|
||||
@@ -104,9 +105,10 @@ $lang->customField = '自定义表单项';
|
||||
$lang->lineNumber = '行号';
|
||||
$lang->tutorialConfirm = '检测到你尚未退出新手教程模式,是否现在退出?';
|
||||
|
||||
$lang->preShortcutKey = '[快捷键:←]';
|
||||
$lang->nextShortcutKey = '[快捷键:→]';
|
||||
$lang->backShortcutKey = '[快捷键:Alt+↑]';
|
||||
$lang->preShortcutKey = '[快捷键:←]';
|
||||
$lang->nextShortcutKey = '[快捷键:→]';
|
||||
$lang->backShortcutKey = '[快捷键:Alt+↑]';
|
||||
$lang->shortcutOperation = '快捷操作';
|
||||
|
||||
$lang->select = '选择';
|
||||
$lang->selectAll = '全选';
|
||||
@@ -307,7 +309,8 @@ $lang->createObjects['program'] = '项目集';
|
||||
$lang->createObjects['doc'] = '文档';
|
||||
|
||||
/* 语言 */
|
||||
$lang->lang = 'Language';
|
||||
$lang->lang = 'Language';
|
||||
$lang->setLang = '语言设置';
|
||||
|
||||
/* 风格列表。*/
|
||||
$lang->theme = '主题';
|
||||
|
||||
@@ -103,7 +103,8 @@ class commonModel extends model
|
||||
->orderBy('id_desc')
|
||||
->fetchPairs();
|
||||
|
||||
$this->dao->update(TABLE_PROGRAM)->set('status')->eq('doing')->where('id')->in($waitList)->exec();
|
||||
$now = helper::now();
|
||||
$this->dao->update(TABLE_PROGRAM)->set('status')->eq('doing')->set('realBegan')->eq($now)->where('id')->in($waitList)->exec();
|
||||
foreach($waitList as $programID)
|
||||
{
|
||||
$this->loadModel('action')->create('program', $programID, 'syncprogram');
|
||||
|
||||
@@ -52,7 +52,7 @@ $uid = uniqid('');
|
||||
cssData: 'html,body {background: none}.article-content{overflow:visible}.article-content, .article-content table td, .article-content table th {line-height: 1.3846153846; font-size: 13px;}.article-content .table-auto {width: auto!important; max-width: 100%;}',
|
||||
placeholder: <?php echo json_encode($lang->noticePasteImg);?>,
|
||||
placeholderStyle: {fontSize: '13px', color: '#888'},
|
||||
pasteImage: {postUrl: createLink('file', 'ajaxPasteImage', 'uid=' + kuid)},
|
||||
pasteImage: {postUrl: createLink('file', 'ajaxPasteImg', 'uid=' + kuid)},
|
||||
syncAfterBlur: true,
|
||||
allowFileManager: false,
|
||||
spellcheck: false
|
||||
@@ -90,7 +90,7 @@ $uid = uniqid('');
|
||||
{
|
||||
items: editorTool,
|
||||
placeholder: $editor.attr('placeholder') || options.placeholder || '',
|
||||
pasteImage: {postUrl: createLink('file', 'ajaxPasteImage', 'uid=' + kuid), placeholder: $editor.attr('placeholder') || <?php echo json_encode($lang->noticePasteImg);?>},
|
||||
pasteImage: {postUrl: createLink('file', 'ajaxPasteImg', 'uid=' + kuid), placeholder: $editor.attr('placeholder') || <?php echo json_encode($lang->noticePasteImg);?>},
|
||||
});
|
||||
|
||||
try
|
||||
|
||||
@@ -78,6 +78,7 @@ class docModel extends model
|
||||
->where('deleted')->eq(0)
|
||||
->beginIF($type)->andWhere('type')->eq($type)->fi()
|
||||
->beginIF(!$type)->andWhere('type')->ne('api')->fi()
|
||||
->beginIF($objectID and strpos(',product,project,execution,', ",$type,") !== false)->andWhere($type)->eq($objectID)->fi()
|
||||
->orderBy('`order`, id desc')->query();
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ class docModel extends model
|
||||
$executions = $this->loadModel('execution')->getPairs();
|
||||
|
||||
$libPairs = array();
|
||||
while ($lib = $stmt->fetch())
|
||||
while($lib = $stmt->fetch())
|
||||
{
|
||||
if($lib->product != 0 and !isset($products[$lib->product])) continue;
|
||||
if($lib->execution != 0 and !isset($executions[$lib->execution])) continue;
|
||||
@@ -108,7 +109,7 @@ class docModel extends model
|
||||
if(!empty($appendLibs))
|
||||
{
|
||||
$stmt = $this->dao->select('*')->from(TABLE_DOCLIB)->where('id')->in($appendLibs)->orderBy('`order`, id desc')->query();
|
||||
while ($lib = $stmt->fetch())
|
||||
while($lib = $stmt->fetch())
|
||||
{
|
||||
if(!isset($libPairs[$lib->id]) and $this->checkPrivLib($lib, $extra)) $libPairs[$lib->id] = $lib->name;
|
||||
}
|
||||
@@ -1621,10 +1622,7 @@ class docModel extends model
|
||||
static $docGroups;
|
||||
if(empty($docGroups))
|
||||
{
|
||||
$docs = $this->dao->select('*')->from(TABLE_DOC)
|
||||
->where('lib')->eq((int)$libID)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetchAll();
|
||||
$docs = $this->dao->select('*')->from(TABLE_DOC)->where('lib')->eq((int)$libID)->andWhere('deleted')->eq(0)->fetchAll();
|
||||
$docGroups = array();
|
||||
foreach($docs as $doc)
|
||||
{
|
||||
@@ -1647,6 +1645,7 @@ class docModel extends model
|
||||
$docItem->type = 'doc';
|
||||
$docItem->id = $doc->id;
|
||||
$docItem->title = $doc->title;
|
||||
$docItem->acl = $doc->acl;
|
||||
$docItem->url = helper::createLink('doc', 'view', "doc=$doc->id");
|
||||
|
||||
$buttons = '';
|
||||
|
||||
@@ -3070,7 +3070,7 @@ class executionModel extends model
|
||||
foreach($products as $product)
|
||||
{
|
||||
$productModules = $this->loadModel('tree')->getOptionMenu($product->id);
|
||||
$productBuilds = $this->loadModel('build')->getProductBuildPairs($product->id, 0, $params = 'noempty|notrunk');
|
||||
$productBuilds = $this->loadModel('build')->getProductBuildPairs($product->id, 0, $params = 'noempty|notrunk|withbranch');
|
||||
foreach($productModules as $moduleID => $moduleName)
|
||||
{
|
||||
$modules[$moduleID] = ((count($products) >= 2 and $moduleID) ? $product->name : '') . $moduleName;
|
||||
|
||||
@@ -78,8 +78,9 @@ class file extends control
|
||||
if($uid) $_SESSION['album'][$uid][] = $fileID;
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api')
|
||||
{
|
||||
if($uid) $_SESSION['album']['used'][$uid][$fileID] = $fileID;
|
||||
$_SERVER['SCRIPT_NAME'] = 'index.php';
|
||||
die(json_encode(array('status' => 'success', 'data' => commonModel::getSysURL() . $this->config->webRoot . $url)));
|
||||
return $this->send(array('status' => 'success', 'id' => $fileID, 'data' => commonModel::getSysURL() . $this->config->webRoot . $url));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -91,7 +92,7 @@ class file extends control
|
||||
$error = strip_tags(sprintf($this->lang->file->errorCanNotWrite, $this->file->savePath, $this->file->savePath));
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api')
|
||||
{
|
||||
die(json_encode(array('status' => 'error', 'message' => $error)));
|
||||
return $this->send(array('status' => 'error', 'message' => $error));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -99,7 +100,7 @@ class file extends control
|
||||
}
|
||||
}
|
||||
}
|
||||
die(json_encode(array('status' => 'error', 'message' => $this->lang->file->uploadImagesExplain)));
|
||||
return $this->send(array('status' => 'error', 'message' => $this->lang->file->uploadImagesExplain));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +333,7 @@ class file extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxPasteImage($uid = '')
|
||||
public function ajaxPasteImg($uid = '')
|
||||
{
|
||||
if($_POST) die($this->file->pasteImage($this->post->editor, $uid));
|
||||
}
|
||||
|
||||
@@ -834,7 +834,7 @@ class fileModel extends model
|
||||
$data = new stdclass();
|
||||
$data->objectID = $objectID;
|
||||
$data->objectType = $objectType;
|
||||
$data->extra = 'editor';
|
||||
if(!defined('RUN_MODE') OR RUN_MODE != 'api') $data->extra = 'editor';
|
||||
if(isset($_SESSION['album']['used'][$uid]) and $_SESSION['album']['used'][$uid])
|
||||
{
|
||||
$this->dao->update(TABLE_FILE)->data($data)->where('id')->in($_SESSION['album']['used'][$uid])->exec();
|
||||
|
||||
@@ -421,7 +421,7 @@ $lang->product->methodOrder[105] = 'unbindWhitelist';
|
||||
/* Branch. */
|
||||
$lang->resource->branch = new stdclass();
|
||||
$lang->resource->branch->manage = 'manage';
|
||||
$lang->resource->branch->create = 'create';
|
||||
$lang->resource->branch->create = 'createAction';
|
||||
$lang->resource->branch->edit = 'editAction';
|
||||
$lang->resource->branch->close = 'closeAction';
|
||||
$lang->resource->branch->activate = 'activateAction';
|
||||
|
||||
@@ -372,7 +372,10 @@ class groupModel extends model
|
||||
$dynamic = array();
|
||||
foreach($actions['actions'] as $moduleName => $moduleActions)
|
||||
{
|
||||
if($moduleName != 'todo' and isset($actions['views']) and !in_array($this->lang->navGroup->$moduleName, $actions['views'])) continue;
|
||||
$groupName = $moduleName;
|
||||
if(isset($this->lang->navGroup->$moduleName)) $groupName = $this->lang->navGroup->$moduleName;
|
||||
if($moduleName == 'case') $groupName = $this->lang->navGroup->testcase;
|
||||
if($groupName != 'my' and isset($actions['views']) and !in_array($groupName, $actions['views'])) continue;
|
||||
|
||||
$dynamic[$moduleName] = $moduleActions;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<?php foreach($lang->mainNav as $menuKey => $menu):?>
|
||||
<?php if(!is_string($menu)) continue;?>
|
||||
<?php list($moduleName, $module) = explode('|', $menu);?>
|
||||
<?php if($module == 'my') continue;?>
|
||||
<?php if($menuKey == 'my') continue;?>
|
||||
<?php $moduleName = strip_tags($moduleName);?>
|
||||
<div class='group-item'>
|
||||
<div class='checkbox-primary'>
|
||||
|
||||
@@ -5,7 +5,10 @@ $(function()
|
||||
$('#subNavbar li[data-id=' + mode + ']').addClass('active');
|
||||
if(typeof rawMethod === 'string' && rawMethod == 'work') $('#subNavbar li[data-id=' + mode + '] a').append('<span class="label label-light label-badge">' + total + '</span>');
|
||||
}
|
||||
var scp = $('[data-id="changePassword"] a');
|
||||
var sign = config.requestType == 'GET' ? '&' : '?';
|
||||
scp.attr('href', scp.attr('href') + sign + 'onlybody=yes').modalTrigger({width:500, type:'iframe'});
|
||||
var $scp = $('[data-id="changePassword"] a');
|
||||
if($scp.length > 0)
|
||||
{
|
||||
var sign = config.requestType == 'GET' ? '&' : '?';
|
||||
$scp.attr('href', $scp.attr('href') + sign + 'onlybody=yes').modalTrigger({width:500, type:'iframe'});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ global $config;
|
||||
|
||||
/* 方法列表。*/
|
||||
$lang->my->index = '首页';
|
||||
$lang->my->data = '我的数据';
|
||||
$lang->my->todo = '我的待办';
|
||||
$lang->my->calendar = '日程';
|
||||
$lang->my->work = '待处理';
|
||||
|
||||
+127
-73
@@ -60,49 +60,75 @@ class myModel extends model
|
||||
/**
|
||||
* Get my charged products.
|
||||
*
|
||||
* @param string $type undone|ownbyme
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function getProducts()
|
||||
public function getProducts($type = 'undone')
|
||||
{
|
||||
$products = $this->dao->select('t1.id as id,t1.*')->from(TABLE_PRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
|
||||
->where('t1.deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->products)->fi()
|
||||
->orderBy('t1.order_asc')
|
||||
->fetchAll('id');
|
||||
$products = $this->dao->select('t1.*, t2.name as programName')->from(TABLE_PRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
|
||||
->where('t1.deleted')->eq(0)
|
||||
->beginIF($type == 'undone')->andWhere('t1.status')->eq('normal')->fi()
|
||||
->beginIF($type == 'ownbyme')->andWhere('t1.PO')->eq($this->app->user->account)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->products)->fi()
|
||||
->orderBy('t1.order_asc')
|
||||
->fetchAll('id');
|
||||
$productKeys = array_keys($products);
|
||||
|
||||
$stories = $this->dao->select('product, sum(estimate) AS estimateCount')
|
||||
->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productKeys)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
$plans = $this->dao->select('product, count(*) AS count')
|
||||
->from(TABLE_PRODUCTPLAN)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productKeys)
|
||||
->andWhere('end')->gt(helper::now())
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
$releases = $this->dao->select('product, count(*) AS count')
|
||||
->from(TABLE_RELEASE)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productKeys)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
$executions = $this->dao->select('t1.product,t2.id,t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
|
||||
->where('t1.product')->in($productKeys)
|
||||
->andWhere('t2.type')->in('stage,sprint')
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->orderBy('t1.project')
|
||||
->fetchAll('product');
|
||||
foreach($executions as $key => $execuData)
|
||||
$storyGroups = $this->dao->select('id,product,status,stage,estimate')
|
||||
->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productKeys)
|
||||
->groupBy('product')
|
||||
->fetchGroup('product', 'id');
|
||||
$summaryStories = array();
|
||||
foreach($storyGroups as $productID => $stories)
|
||||
{
|
||||
$execution = $this->loadModel('execution')->getById($execuData->id);
|
||||
$executions[$key]->progress = ($execution->totalConsumed + $execution->totalLeft) ? floor($execution->totalConsumed / ($execution->totalConsumed + $execution->totalLeft) * 1000) / 1000 * 100 : 0;
|
||||
$summaryStory = new stdclass();
|
||||
$summaryStory->total = count($stories);
|
||||
|
||||
$finishedTotal = 0;
|
||||
$leftTotal = 0;
|
||||
$estimateCount = 0;
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$estimateCount += $story->estimate;
|
||||
($story->status == 'closed' or $story->stage == 'released' or $story->stage == 'closed') ? $finishedTotal ++ : $leftTotal ++;
|
||||
}
|
||||
|
||||
$summaryStory->finishedTotal = $finishedTotal;
|
||||
$summaryStory->leftTotal = $leftTotal;
|
||||
$summaryStory->estimateCount = $estimateCount;
|
||||
$summaryStory->finishedRate = $summaryStory->total == 0 ? 0 : ($finishedTotal / $summaryStory->total) * 100;
|
||||
$summaryStories[$productID] = $summaryStory;
|
||||
}
|
||||
|
||||
$plans = $this->dao->select('product, count(*) AS count')
|
||||
->from(TABLE_PRODUCTPLAN)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productKeys)
|
||||
->andWhere('end')->gt(helper::now())
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
$releases = $this->dao->select('product, count(*) AS count')
|
||||
->from(TABLE_RELEASE)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productKeys)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
$executions = $this->dao->select('t1.product,t2.id,t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
|
||||
->where('t1.product')->in($productKeys)
|
||||
->andWhere('t2.type')->in('stage,sprint')
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->orderBy('t1.project')
|
||||
->fetchAll('product');
|
||||
$this->loadModel('execution');
|
||||
foreach($executions as $productID => $execution)
|
||||
{
|
||||
$execution = $this->execution->getById($execution->id);
|
||||
$executions[$productID]->progress = ($execution->totalConsumed + $execution->totalLeft) ? floor($execution->totalConsumed / ($execution->totalConsumed + $execution->totalLeft) * 1000) / 1000 * 100 : 0;
|
||||
}
|
||||
|
||||
$allCount = count($products);
|
||||
@@ -112,7 +138,12 @@ class myModel extends model
|
||||
$product->plans = isset($plans[$product->id]) ? $plans[$product->id] : 0;
|
||||
$product->releases = isset($releases[$product->id]) ? $releases[$product->id] : 0;
|
||||
if(isset($executions[$product->id])) $product->executions = $executions[$product->id];
|
||||
$product->storyEstimateCount = isset($stories[$product->id]) ? $stories[$product->id] : 0;
|
||||
$product->storyEstimateCount = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->estimateCount : 0;
|
||||
$product->storyTotal = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->total : 0;
|
||||
$product->storyFinishedTotal = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->finishedTotal : 0;
|
||||
$product->storyLeftTotal = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->leftTotal : 0;
|
||||
$product->storyFinishedRate = isset($summaryStories[$product->id]) ? $summaryStories[$product->id]->finishedRate : 0;
|
||||
$product->latestExecution = isset($executions[$product->id]) ? $executions[$product->id] : '';
|
||||
if($product->status != 'closed') $unclosedCount ++;
|
||||
if($product->status == 'closed') unset($products[$key]);
|
||||
}
|
||||
@@ -137,22 +168,37 @@ class myModel extends model
|
||||
public function getDoingProjects()
|
||||
{
|
||||
$data = new stdClass();
|
||||
$doingProjects = array();
|
||||
$projects = $this->loadModel('project')->getOverviewList('byStatus', 'all', 'id_desc');
|
||||
$doingProjects = $this->loadModel('project')->getOverviewList('byStatus', 'doing', 'id_desc');
|
||||
$maxCount = 5;
|
||||
foreach($projects as $key => $project)
|
||||
$myProjects = array();
|
||||
foreach($doingProjects as $key => $project)
|
||||
{
|
||||
if($project->status == 'doing')
|
||||
if($project->PM == $this->app->user->account)
|
||||
{
|
||||
$workhour = $this->project->getWorkhour($project->id);
|
||||
$projects[$key]->progress = ($workhour->totalConsumed + $workhour->totalLeft) ? floor($workhour->totalConsumed / ($workhour->totalConsumed + $workhour->totalLeft) * 1000) / 1000 * 100 : 0;
|
||||
$doingProjects[] = $projects[$key];
|
||||
if(count($doingProjects) >= $maxCount) break;
|
||||
$myProjects[$key] = $project;
|
||||
unset($doingProjects[$key]);
|
||||
}
|
||||
if(count($myProjects) >= $maxCount) break;
|
||||
}
|
||||
if(count($myProjects) < $maxCount and !empty($doingProjects))
|
||||
{
|
||||
foreach($doingProjects as $key => $project)
|
||||
{
|
||||
$myProjects[$key] = $project;
|
||||
if(count($myProjects) >= $maxCount) break;
|
||||
}
|
||||
}
|
||||
|
||||
$data->doingCount = count($doingProjects);
|
||||
$data->projects = $doingProjects;
|
||||
foreach($myProjects as $key => $project)
|
||||
{
|
||||
$workhour = $this->project->getWorkhour($project->id);
|
||||
$project->progress = ($workhour->totalConsumed + $workhour->totalLeft) ? floor($workhour->totalConsumed / ($workhour->totalConsumed + $workhour->totalLeft) * 1000) / 1000 * 100 : 0;
|
||||
$project->delay = (helper::diffDate(helper::today(), $project->end) > 0);
|
||||
$project->link = common::hasPriv('project', 'view') ? helper::createLink('project', 'view', "projectID={$project->id}") : '';
|
||||
}
|
||||
|
||||
$data->doingCount = count($myProjects);
|
||||
$data->projects = array_values($myProjects);
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -164,20 +210,36 @@ class myModel extends model
|
||||
*/
|
||||
public function getOverview()
|
||||
{
|
||||
$allConsumed = 0;
|
||||
$thisYearConsumed = 0;
|
||||
$inAdminGroup = $this->dao->select('t1.*')->from(TABLE_USERGROUP)->alias('t1')
|
||||
->leftJoin(TABLE_GROUP)->alias('t2')->on('t1.group=t2.id')
|
||||
->where('t1.account')->eq($this->app->user->account)
|
||||
->andWhere('t2.role')->eq('admin')
|
||||
->fetch();
|
||||
|
||||
$projects = $this->loadModel('project')->getOverviewList('byStatus', 'all', 'id_desc');
|
||||
$projectsConsumed = $this->project->getProjectsConsumed(array_keys($projects), 'THIS_YEAR');
|
||||
foreach($projects as $project)
|
||||
$overview = new stdclass();
|
||||
if(!empty($inAdminGroup) or $this->app->user->admin)
|
||||
{
|
||||
$allConsumed += $project->consumed;
|
||||
$thisYearConsumed += $projectsConsumed[$project->id]->totalConsumed;
|
||||
}
|
||||
$allConsumed = 0;
|
||||
$thisYearConsumed = 0;
|
||||
|
||||
$overview->projectTotal = count($projects);
|
||||
$overview->allConsumed = $allConsumed;
|
||||
$overview->thisYearConsumed = $thisYearConsumed;
|
||||
$projects = $this->loadModel('project')->getOverviewList('byStatus', 'all', 'id_desc', 0);
|
||||
$projectsConsumed = $this->project->getProjectsConsumed(array_keys($projects), 'THIS_YEAR');
|
||||
foreach($projects as $project)
|
||||
{
|
||||
$allConsumed += $project->consumed;
|
||||
$thisYearConsumed += $projectsConsumed[$project->id]->totalConsumed;
|
||||
}
|
||||
|
||||
$overview->projectTotal = count($projects);
|
||||
$overview->allConsumed = round($allConsumed, 1);
|
||||
$overview->thisYearConsumed = round($thisYearConsumed, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
$overview->myTaskTotal = (int)$this->dao->select('count(*) AS count')->from(TABLE_TASK)->where('assignedTo')->eq($this->app->user->account)->andWhere('deleted')->eq(0)->fetch('count');
|
||||
$overview->myStoryTotal = (int)$this->dao->select('count(*) AS count')->from(TABLE_STORY)->where('assignedTo')->eq($this->app->user->account)->andWhere('deleted')->eq(0)->andWhere('type')->eq('story')->fetch('count');
|
||||
$overview->myBugTotal = (int)$this->dao->select('count(*) AS count')->from(TABLE_BUG)->where('assignedTo')->eq($this->app->user->account)->andWhere('deleted')->eq(0)->fetch('count');
|
||||
}
|
||||
|
||||
return $overview;
|
||||
}
|
||||
@@ -223,7 +285,10 @@ class myModel extends model
|
||||
*/
|
||||
public function getActions()
|
||||
{
|
||||
$actions = $this->loadModel('action')->getDynamic('all', 'today', 'date_desc');
|
||||
$this->app->loadClass('pager', $static = true);
|
||||
$pager = new pager(0, 50, 1);
|
||||
|
||||
$actions = $this->loadModel('action')->getDynamic('all', 'all', 'date_desc', $pager);
|
||||
$users = $this->loadModel('user')->getList();
|
||||
|
||||
$simplifyUsers = array();
|
||||
@@ -237,20 +302,9 @@ class myModel extends model
|
||||
$simplifyUsers[$user->account] = $simplifyUser;
|
||||
}
|
||||
|
||||
foreach($actions as $key => $action)
|
||||
{
|
||||
$simplifyUser = zget($simplifyUsers, $action->actor, '');
|
||||
$actionActor = $simplifyUser;
|
||||
if(empty($simplifyUser))
|
||||
{
|
||||
$actionActor = new stdclass();
|
||||
$actionActor->id = 0;
|
||||
$actionActor->account = $action->actor;
|
||||
$actionActor->realname = $action->actor;
|
||||
$actionActor->avatar = '';
|
||||
}
|
||||
$actions[$key]->actor = $actionActor;
|
||||
}
|
||||
$maxCount = 5;
|
||||
$actions = $this->action->processDynamicForAPI($actions);
|
||||
$actions = array_slice($actions, 0, $maxCount);
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
@@ -928,7 +928,7 @@ class product extends control
|
||||
public function ajaxGetProjects($productID, $branch = 0, $projectID = 0)
|
||||
{
|
||||
$projects = array('' => '');
|
||||
$projects += $this->product->getProjectPairsByProduct($productID, $branch ? "0,$branch" : $branch);
|
||||
$projects += $this->product->getProjectPairsByProduct($productID, $branch);
|
||||
if($this->app->getViewType() == 'json') die(json_encode($projects));
|
||||
|
||||
die(html::select('project', $projects, $projectID, "class='form-control' onchange='loadProductExecutions({$productID}, this.value)'"));
|
||||
@@ -947,7 +947,7 @@ class product extends control
|
||||
*/
|
||||
public function ajaxGetExecutions($productID, $projectID = 0, $branch = 0, $number = '', $executionID = 0)
|
||||
{
|
||||
$executions = $this->product->getExecutionPairsByProduct($productID, $branch ? "0,$branch" : $branch, 'id_desc', $projectID);
|
||||
$executions = $this->product->getExecutionPairsByProduct($productID, $branch, 'id_desc', $projectID);
|
||||
if($this->app->getViewType() == 'json') die(json_encode($executions));
|
||||
|
||||
if($number === '')
|
||||
|
||||
@@ -145,14 +145,16 @@ class productModel extends model
|
||||
*/
|
||||
public function saveState($productID, $products)
|
||||
{
|
||||
if($productID > 0) $this->session->set('product', (int)$productID);
|
||||
if($productID == 0 and $this->cookie->preProductID) $this->session->set('product', (int)$this->cookie->preProductID);
|
||||
if($productID == 0 and $this->session->product == '') $this->session->set('product', key($products));
|
||||
if($productID == 0 and $this->cookie->preProductID) $productID = $this->cookie->preProductID;
|
||||
if($productID == 0 and $this->session->product == '') $productID = key($products);
|
||||
$this->session->set('product', (int)$productID, $this->app->tab);
|
||||
|
||||
if(!isset($products[$this->session->product]))
|
||||
{
|
||||
$product = $this->getById($productID);
|
||||
if(empty($product)) $this->session->set('product', key($products));
|
||||
if($productID && strpos(",{$this->app->user->view->products},", ",{$this->session->product},") === false) $this->accessDenied();
|
||||
if(empty($product)) $productID = key($products);
|
||||
$this->session->set('product', (int)$productID, $this->app->tab);
|
||||
if($productID && strpos(",{$this->app->user->view->products},", ",{$productID},") === false) $this->accessDenied();
|
||||
|
||||
setcookie('preProductID', $productID, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, true);
|
||||
}
|
||||
@@ -1410,6 +1412,10 @@ class productModel extends model
|
||||
$product->bugs = $bugs ? $bugs->count : 0;
|
||||
$product->docs = $docs ? $docs->count : 0;
|
||||
|
||||
$closedTotal = $this->dao->select('count(id) AS count')->from(TABLE_STORY)->where('deleted')->eq(0)->andWhere('status')->eq('closed')->andWhere('product')->eq($productID)->fetch('count');
|
||||
$allTotal = $this->dao->select('count(id) AS count')->from(TABLE_STORY)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch('count');
|
||||
$product->progress = empty($closedTotal) ? 0 : round($closedTotal / $allTotal * 100, 1);
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
@@ -1547,6 +1553,16 @@ class productModel extends model
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
$this->app->loadClass('date', true);
|
||||
$weekDate = date::getThisWeek();
|
||||
$thisWeekBugs = $this->dao->select('product,count(*) AS count')
|
||||
->from(TABLE_BUG)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('openedDate')->between($weekDate['begin'], $weekDate['end'])
|
||||
->andWhere('product')->in($productKeys)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
$assignToNull = $this->dao->select('product,count(*) AS count')
|
||||
->from(TABLE_BUG)
|
||||
->where('deleted')->eq(0)
|
||||
@@ -1557,7 +1573,7 @@ class productModel extends model
|
||||
|
||||
if(empty($programID))
|
||||
{
|
||||
$programKeys = array(0=>0);
|
||||
$programKeys = array(0 => 0);
|
||||
foreach($products as $product) $programKeys[] = $product->program;
|
||||
$programs = $this->dao->select('id,name')->from(TABLE_PROGRAM)
|
||||
->where('id')->in(array_unique($programKeys))
|
||||
@@ -1578,7 +1594,13 @@ class productModel extends model
|
||||
$product->unResolved = isset($unResolved[$product->id]) ? $unResolved[$product->id] : 0;
|
||||
$product->closedBugs = isset($closedBugs[$product->id]) ? $closedBugs[$product->id] : 0;
|
||||
$product->fixedBugs = isset($fixedBugs[$product->id]) ? $fixedBugs[$product->id] : 0;
|
||||
$product->thisWeekBugs = isset($thisWeekBugs[$product->id]) ? $thisWeekBugs[$product->id] : 0;
|
||||
$product->assignToNull = isset($assignToNull[$product->id]) ? $assignToNull[$product->id] : 0;
|
||||
|
||||
$closedTotal = $product->stories['closed'] + $product->requirements['closed'];
|
||||
$allTotal = array_sum($product->stories) + array_sum($product->requirements);
|
||||
$product->progress = empty($closedTotal) ? 0 : round($closedTotal / $allTotal * 100, 1);
|
||||
|
||||
$stats[] = $product;
|
||||
}
|
||||
|
||||
|
||||
@@ -485,9 +485,10 @@ $projectIDParam = $isProjectStory ? "projectID=$projectID&" : '';
|
||||
<?php
|
||||
foreach($plans as $planID => $plan)
|
||||
{
|
||||
$searchKey = $withSearch ? ('data-key="' . zget($plansPinYin, $plan, '') . '"') : '';
|
||||
$planTitle = strip_tags($plan);
|
||||
$searchKey = $withSearch ? ('data-key="' . zget($plansPinYin, $plan, '') . '"') : '';
|
||||
$actionLink = $this->createLink('story', 'batchChangePlan', "planID=$planID");
|
||||
echo html::a('#', $plan, '', "$searchKey title='{$plan}' onclick=\"setFormAction('$actionLink', 'hiddenwin', '#productStoryForm')\"");
|
||||
echo html::a('#', $plan, '', "$searchKey title='{$planTitle}' onclick=\"setFormAction('$actionLink', 'hiddenwin', '#productStoryForm')\"");
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
@@ -346,7 +346,7 @@ class productplan extends control
|
||||
}
|
||||
else
|
||||
{
|
||||
$plans = $this->productplan->getPairs($productID, $branch, $expired);
|
||||
$plans = $this->productplan->getPairs($productID, $branch);
|
||||
}
|
||||
|
||||
$planName = $number === '' ? 'plan' : "plan[$number]";
|
||||
|
||||
@@ -181,25 +181,27 @@ class productplanModel extends model
|
||||
public function getPairs($product = 0, $branch = '', $expired = '', $skipParent = false)
|
||||
{
|
||||
$date = date('Y-m-d');
|
||||
$plans = $this->dao->select('id,title,parent,begin,end')->from(TABLE_PRODUCTPLAN)
|
||||
->where('product')->in($product)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF($branch !== '')->andWhere('branch')->eq($branch)->fi()
|
||||
->beginIF($expired == 'unexpired')->andWhere('end')->ge($date)->fi()
|
||||
->beginIF($skipParent)->andWhere('parent')->ne(-1)->fi()
|
||||
->orderBy('begin desc')
|
||||
$plans = $this->dao->select('t1.id,t1.title,t1.parent,t1.begin,t1.end,t2.name as branchName')->from(TABLE_PRODUCTPLAN)->alias('t1')
|
||||
->leftJoin(TABLE_BRANCH)->alias('t2')->on('t2.id=t1.branch')
|
||||
->where('t1.product')->in($product)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->beginIF($branch !== '')->andWhere('t1.branch')->eq($branch)->fi()
|
||||
->beginIF($expired == 'unexpired')->andWhere('t1.end')->ge($date)->fi()
|
||||
->beginIF($skipParent)->andWhere('t1.parent')->ne(-1)->fi()
|
||||
->orderBy('t1.begin desc')
|
||||
->fetchAll('id');
|
||||
|
||||
if($expired == 'unexpired')
|
||||
{
|
||||
$plans += $this->dao->select('id,title,parent,begin,end')->from(TABLE_PRODUCTPLAN)
|
||||
->where('product')->in($product)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('end')->lt($date)
|
||||
->beginIF($branch)->andWhere("branch")->in("0,$branch")->fi()
|
||||
->beginIF($plans)->andWhere("id")->notIN(array_keys($plans))->fi()
|
||||
->beginIF($skipParent)->andWhere('parent')->ne(-1)->fi()
|
||||
->orderBy('begin desc')
|
||||
$plans += $this->dao->select('t1.id,t1.title,t1.parent,t1.begin,t1.end,t2.name as branchName')->from(TABLE_PRODUCTPLAN)->alias('t1')
|
||||
->leftJoin(TABLE_BRANCH)->alias('t2')->on('t2.id=t1.branch')
|
||||
->where('t1.product')->in($product)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->andWhere('t1.end')->lt($date)
|
||||
->beginIF($branch)->andWhere("t1.branch")->in("0,$branch")->fi()
|
||||
->beginIF($plans)->andWhere("t1.id")->notIN(array_keys($plans))->fi()
|
||||
->beginIF($skipParent)->andWhere('t1.parent')->ne(-1)->fi()
|
||||
->orderBy('t1.begin desc')
|
||||
->limit(5)
|
||||
->fetchAll('id');
|
||||
}
|
||||
@@ -207,11 +209,12 @@ class productplanModel extends model
|
||||
$plans = $this->reorder4Children($plans);
|
||||
$planPairs = array();
|
||||
$parentTitle = array();
|
||||
$this->app->loadLang('branch');
|
||||
foreach($plans as $plan)
|
||||
{
|
||||
if($plan->parent == '-1') $parentTitle[$plan->id] = $plan->title;
|
||||
if($plan->parent > 0 and isset($parentTitle[$plan->parent])) $plan->title = $parentTitle[$plan->parent] . ' /' . $plan->title;
|
||||
$planPairs[$plan->id] = $plan->title . " [{$plan->begin} ~ {$plan->end}]";
|
||||
$planPairs[$plan->id] = '<span class="label label-outline label-badge">' . ($plan->branchName ? $plan->branchName : $this->lang->branch->main) . '</span> ' . $plan->title . " [{$plan->begin} ~ {$plan->end}]";
|
||||
if($plan->begin == '2030-01-01' and $plan->end == '2030-01-01') $planPairs[$plan->id] = $plan->title . ' ' . $this->lang->productplan->future;
|
||||
}
|
||||
return array('' => '') + $planPairs;
|
||||
|
||||
@@ -443,7 +443,8 @@ class programModel extends model
|
||||
$projectList = $this->dao->select('*')->from(TABLE_PROJECT)
|
||||
->where('deleted')->eq('0')
|
||||
->beginIF($this->config->systemMode == 'new')->andWhere('type')->eq('project')->fi()
|
||||
->beginIF($browseType != 'all')->andWhere('status')->eq($browseType)->fi()
|
||||
->beginIF($browseType != 'all' and $browseType != 'undone')->andWhere('status')->eq($browseType)->fi()
|
||||
->beginIF($browseType == 'undone')->andWhere('status')->in('wait,doing')->fi()
|
||||
->beginIF($path)->andWhere('path')->like($path . '%')->fi()
|
||||
->beginIF(!$queryAll and !$this->app->user->admin and $this->config->systemMode == 'new')->andWhere('id')->in($this->app->user->view->projects)->fi()
|
||||
->beginIF(!$queryAll and !$this->app->user->admin and $this->config->systemMode == 'classic')->andWhere('id')->in($this->app->user->view->sprints)->fi()
|
||||
@@ -507,7 +508,7 @@ class programModel extends model
|
||||
*/
|
||||
public function getStakeholders($programID = 0, $orderBy, $pager = null)
|
||||
{
|
||||
return $this->dao->select('t2.account,t2.realname,t2.role,t2.qq,t2.mobile,t2.phone,t2.weixin,t2.email,t1.id,t1.type,t1.key')->from(TABLE_STAKEHOLDER)->alias('t1')
|
||||
return $this->dao->select('t2.account,t2.realname,t2.role,t2.qq,t2.mobile,t2.phone,t2.weixin,t2.email,t1.id,t1.type,t1.from,t1.key')->from(TABLE_STAKEHOLDER)->alias('t1')
|
||||
->leftJoin(TABLE_USER)->alias('t2')->on('t1.user=t2.account')
|
||||
->where('t1.objectID')->eq($programID)
|
||||
->andWhere('t1.objectType')->eq('program')
|
||||
|
||||
@@ -1081,12 +1081,21 @@ class project extends control
|
||||
/* Set project builds. */
|
||||
$projectBuilds = array();
|
||||
$productList = $this->product->getProducts($projectID);
|
||||
$this->app->loadLang('branch');
|
||||
if(!empty($builds))
|
||||
{
|
||||
foreach($builds as $build)
|
||||
{
|
||||
/* If product is normal, unset branch name. */
|
||||
if(isset($productList[$build->product]) and $productList[$build->product]->type == 'normal') $build->branchName = '';
|
||||
if(isset($productList[$build->product]) and $productList[$build->product]->type == 'normal')
|
||||
{
|
||||
$build->branchName = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$build->branchName = isset($build->branchName) ? $build->branchName : $this->lang->branch->main;
|
||||
}
|
||||
|
||||
$projectBuilds[$build->product][] = $build;
|
||||
}
|
||||
}
|
||||
|
||||
+60
-38
@@ -131,7 +131,7 @@ class projectModel extends model
|
||||
$moduleName = $this->app->getModuleName();
|
||||
$methodName = $this->app->getMethodName();
|
||||
$this->loadModel('common')->resetProjectPriv($this->session->project);
|
||||
if(!commonModel::hasPriv($moduleName, $methodName)) $this->common->deny($moduleName, $methodName, false);
|
||||
if(!$this->common->isOpenMethod($moduleName, $methodName) and !commonModel::hasPriv($moduleName, $methodName)) $this->common->deny($moduleName, $methodName, false);
|
||||
|
||||
return $this->session->project;
|
||||
}
|
||||
@@ -284,20 +284,22 @@ class projectModel extends model
|
||||
->andWhere('type')->eq('project')
|
||||
->groupBy('root')->fetchPairs();
|
||||
|
||||
$hours = $this->dao->select('t2.parent as project, sum(t1.consumed) as consumed, sum(t1.estimate) as estimate')->from(TABLE_TASK)->alias('t1')
|
||||
$hours = $this->dao->select('t2.parent as project, ROUND(SUM(t1.consumed), 1) AS consumed, ROUND(SUM(t1.estimate), 1) AS estimate')->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution = t2.id')
|
||||
->where('t2.parent')->in($projectIdList)
|
||||
->where('t2.project')->in($projectIdList)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->andWhere('t1.parent')->lt(1)
|
||||
->groupBy('t2.parent')
|
||||
->groupBy('t2.project')
|
||||
->fetchAll('project');
|
||||
|
||||
$leftTasks = $this->dao->select('t2.parent as project, count(*) as tasks')->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution = t2.id')
|
||||
->where('t2.parent')->in($projectIdList)
|
||||
->where('t2.project')->in($projectIdList)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->andWhere('t1.status')->in('wait,doing,pause')
|
||||
->groupBy('t2.parent')
|
||||
->groupBy('t2.project')
|
||||
->fetchPairs();
|
||||
|
||||
$this->loadModel('product');
|
||||
@@ -367,32 +369,38 @@ class projectModel extends model
|
||||
*/
|
||||
public function getWorkhour($projectID)
|
||||
{
|
||||
$executions = $this->loadModel('execution')->getPairs($projectID);
|
||||
|
||||
$total = $this->dao->select('
|
||||
ROUND(SUM(estimate), 1) AS totalEstimate,
|
||||
ROUND(SUM(`left`), 2) AS totalLeft')
|
||||
->from(TABLE_TASK)
|
||||
->where('execution')->in(array_keys($executions))
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('parent')->lt(1)
|
||||
$total = $this->dao->select('ROUND(SUM(estimate), 1) AS totalEstimate, ROUND(SUM(`left`), 2) AS totalLeft')->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution = t2.id')
|
||||
->where('t2.project')->in($projectID)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->andWhere('t1.parent')->lt(1)
|
||||
->fetch();
|
||||
|
||||
$totalConsumed = $this->dao->select('ROUND(SUM(consumed), 1) AS totalConsumed')->from(TABLE_TASK)
|
||||
->where('execution')->in(array_keys($executions))
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('parent')->lt(1)
|
||||
$totalConsumed = $this->dao->select('ROUND(SUM(consumed), 1) AS totalConsumed')->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution = t2.id')
|
||||
->where('t2.project')->in($projectID)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->andWhere('t1.parent')->lt(1)
|
||||
->fetch('totalConsumed');
|
||||
|
||||
$closedTotalLeft = $this->dao->select('ROUND(SUM(`left`), 2) AS totalLeft')->from(TABLE_TASK)
|
||||
->where('execution')->in(array_keys($executions))
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('parent')->lt(1)
|
||||
->andWhere('status')->in('closed,cancel')
|
||||
$closedTotalLeft = $this->dao->select('ROUND(SUM(`left`), 2) AS totalLeft')->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution = t2.id')
|
||||
->where('t2.project')->in($projectID)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->andWhere('t1.parent')->lt(1)
|
||||
->andWhere('t1.status')->in('closed,cancel')
|
||||
->fetch('totalLeft');
|
||||
|
||||
$workhour = new stdclass();
|
||||
$workhour->totalHours = $this->dao->select('sum(days * hours) AS totalHours')->from(TABLE_TEAM)->where('root')->in(array_keys($executions))->andWhere('type')->eq('project')->fetch('totalHours');
|
||||
$workhour->totalHours = $this->dao->select('sum(t1.days * t1.hours) AS totalHours')->from(TABLE_TEAM)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.root=t2.id')
|
||||
->where('t2.project')->in($projectID)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->andWhere('t1.type')->eq('project')
|
||||
->fetch('totalHours');
|
||||
$workhour->totalEstimate = $total->totalEstimate;
|
||||
$workhour->totalConsumed = $totalConsumed;
|
||||
$workhour->totalLeft = round($total->totalLeft - $closedTotalLeft, 1);
|
||||
@@ -412,13 +420,13 @@ class projectModel extends model
|
||||
{
|
||||
$projects = array();
|
||||
|
||||
$totalConsumeds = $this->dao->select('project,ROUND(SUM(consumed), 1) AS totalConsumed')
|
||||
->from(TABLE_TASK)
|
||||
->where('project')->in($projectIdList)
|
||||
->beginIF($time == 'THIS_YEAR')->andWhere('realStarted')->ge(date("Y-01-01 00:00:00"))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('parent')->lt(1)
|
||||
->groupBy('project')
|
||||
$totalConsumeds = $this->dao->select('t2.project,ROUND(SUM(t1.consumed), 1) AS totalConsumed')->from(TABLE_TASKESTIMATE)->alias('t1')
|
||||
->leftJoin(TABLE_TASK)->alias('t2')->on('t1.task=t2.id')
|
||||
->where('t2.project')->in($projectIdList)
|
||||
->beginIF($time == 'THIS_YEAR')->andWhere('LEFT(t1.`date`, 4)')->eq(date('Y'))->fi()
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->andWhere('t2.parent')->lt(1)
|
||||
->groupBy('t2.project')
|
||||
->fetchAll('project');
|
||||
|
||||
foreach($projectIdList as $projectID)
|
||||
@@ -447,13 +455,27 @@ class projectModel extends model
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->fetch('storyCount');
|
||||
|
||||
$taskCount = $this->dao->select('count(id) as taskCount')->from(TABLE_TASK)->where('project')->in(array_keys($executions))->andWhere('deleted')->eq(0)->fetch('taskCount');
|
||||
$bugCount = $this->dao->select('count(id) as bugCount')->from(TABLE_BUG)->where('project')->in(array_keys($executions))->andWhere('deleted')->eq(0)->fetch('bugCount');
|
||||
$taskCount = $this->dao->select('count(id) as taskCount')->from(TABLE_TASK)->where('execution')->in(array_keys($executions))->andWhere('deleted')->eq(0)->fetch('taskCount');
|
||||
$bugCount = $this->dao->select('count(id) as bugCount')->from(TABLE_BUG)->where('project')->in($projectID)->andWhere('deleted')->eq(0)->fetch('bugCount');
|
||||
|
||||
$statusPairs = $this->dao->select('status,count(id) as count')->from(TABLE_TASK)->where('execution')->in(array_keys($executions))->andWhere('deleted')->eq(0)->groupBy('status')->fetchPairs('status', 'count');
|
||||
$finishedCount = $this->dao->select('count(id) as taskCount')->from(TABLE_TASK)->where('execution')->in(array_keys($executions))->andWhere('finishedBy')->ne('')->andWhere('deleted')->eq(0)->fetch('taskCount');
|
||||
$delayedCount = $this->dao->select('count(id) as count')->from(TABLE_TASK)
|
||||
->where('execution')->in(array_keys($executions))
|
||||
->andWhere('deadline')->ne('0000-00-00')
|
||||
->andWhere('deadline')->lt(helper::today())
|
||||
->andWhere('status')->in('wait,doing')
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetch('count');
|
||||
|
||||
$statData = new stdclass();
|
||||
$statData->storyCount = $storyCount;
|
||||
$statData->taskCount = $taskCount;
|
||||
$statData->bugCount = $bugCount;
|
||||
$statData->storyCount = $storyCount;
|
||||
$statData->taskCount = $taskCount;
|
||||
$statData->bugCount = $bugCount;
|
||||
$statData->waitCount = zget($statusPairs, 'wait', 0);
|
||||
$statData->doingCount = zget($statusPairs, 'doing', 0);
|
||||
$statData->finishedCount = $finishedCount;
|
||||
$statData->delayedCount = $delayedCount;
|
||||
|
||||
return $statData;
|
||||
}
|
||||
@@ -1953,7 +1975,7 @@ class projectModel extends model
|
||||
$hour->totalConsumed = round($hour->totalConsumed, 1);
|
||||
$hour->totalLeft = round($hour->totalLeft, 1);
|
||||
$hour->totalReal = $hour->totalConsumed + $hour->totalLeft;
|
||||
$hour->progress = $hour->totalReal ? round($hour->totalConsumed / $hour->totalReal, 3) * 100 : 0;
|
||||
$hour->progress = $hour->totalReal ? round($hour->totalConsumed / $hour->totalReal * 100, 2) : 0;
|
||||
}
|
||||
|
||||
return $hours;
|
||||
|
||||
@@ -54,3 +54,11 @@ $config->report->annualData['month']['story'] = array('opened' => 'create', 'act
|
||||
$config->report->annualData['month']['task'] = array('opened' => 'create', 'started' => 'start', 'finished' => 'finish', 'paused' => 'pause', 'activated' => 'activate', 'canceled' => 'cancel', 'closed' => 'close');
|
||||
$config->report->annualData['month']['bug'] = array('opened' => 'create', 'bugconfirmed' => 'confirm', 'activated' => 'activate', 'resolved' => 'resolve', 'closed' => 'close');
|
||||
$config->report->annualData['month']['case'] = array('opened' => 'create', 'run' => 'run', 'createBug' => 'createBug');
|
||||
|
||||
$config->report->outputData['story'] = array('opened' => 'create', 'changed' => 'change', 'reviewed' => 'review', 'closed' => 'close');
|
||||
$config->report->outputData['productplan'] = array('opened' => 'create');
|
||||
$config->report->outputData['release'] = array('opened' => 'create', 'stoped' => 'stop', 'activated' => 'activate');
|
||||
$config->report->outputData['execution'] = array('opened' => 'create', 'started' => 'start', 'delayed' => 'putoff', 'suspended' => 'suspend', 'closed' => 'close');
|
||||
$config->report->outputData['task'] = array('opened' => 'create', 'assigned' => 'assign', 'finished' => 'finish', 'activated' => 'activate', 'closed' => 'close');
|
||||
$config->report->outputData['bug'] = array('opened' => 'create', 'resolved' => 'resolve', 'activated' => 'activate', 'closed' => 'close');
|
||||
$config->report->outputData['case'] = array('opened' => 'create', 'run' => 'run', 'createBug' => 'createBug');
|
||||
|
||||
@@ -58,8 +58,8 @@ section.active {border: 1px solid rgb(0, 166, 255);}
|
||||
#actionData .ratio {display: inline-block; width: 400px;}
|
||||
#actionData .ratio .item {display: inline-block; text-align: center;}
|
||||
|
||||
#radar {width: 350px; height: 330px;}
|
||||
#radarCanvas {width: 310px; height: 310px; margin: 0 auto;}
|
||||
#radar {width: 350px; height: 330px; padding-left:10px;}
|
||||
#radarCanvas {width: 330px; height: 310px; margin: 0 auto;}
|
||||
|
||||
#executionData, #productData {width: 633px; height: 350px; margin-top: 20px; position: relative;}
|
||||
#executionData div.has-table, #productData div.has-table {margin-top: 30px; height: 285px; overflow: auto;}
|
||||
|
||||
@@ -193,6 +193,9 @@ $lang->report->annualData->actionList['assign'] = '指派';
|
||||
$lang->report->annualData->actionList['activate'] = '激活';
|
||||
$lang->report->annualData->actionList['resolve'] = '解决';
|
||||
$lang->report->annualData->actionList['run'] = '执行';
|
||||
$lang->report->annualData->actionList['stop'] = '停止维护';
|
||||
$lang->report->annualData->actionList['putoff'] = '延期';
|
||||
$lang->report->annualData->actionList['suspend'] = '挂起';
|
||||
$lang->report->annualData->actionList['change'] = '变更';
|
||||
$lang->report->annualData->actionList['pause'] = '暂停';
|
||||
$lang->report->annualData->actionList['cancel'] = '取消';
|
||||
@@ -203,8 +206,25 @@ $lang->report->annualData->todoStatus['all'] = '所有待办';
|
||||
$lang->report->annualData->todoStatus['undone'] = '未完成';
|
||||
$lang->report->annualData->todoStatus['done'] = '已完成';
|
||||
|
||||
$lang->report->annualData->radarItems['product'] = '产品';
|
||||
$lang->report->annualData->radarItems['execution'] = $lang->executionCommon;
|
||||
$lang->report->annualData->radarItems['product'] = '产品管理';
|
||||
$lang->report->annualData->radarItems['execution'] = '项目管理';
|
||||
$lang->report->annualData->radarItems['devel'] = "研发";
|
||||
$lang->report->annualData->radarItems['qa'] = "测试";
|
||||
$lang->report->annualData->radarItems['other'] = "其他";
|
||||
|
||||
$lang->report->companyRadar = "公司能力雷达图";
|
||||
$lang->report->outputData = "产出数据";
|
||||
$lang->report->outputTotal = "产出总数";
|
||||
$lang->report->storyOutput = "需求产出";
|
||||
$lang->report->planOutput = "计划产出";
|
||||
$lang->report->releaseOutput = "发布产出";
|
||||
$lang->report->executionOutput = "执行产出";
|
||||
$lang->report->taskOutput = "任务产出";
|
||||
$lang->report->bugOutput = "Bug产出";
|
||||
$lang->report->caseOutput = "用例产出";
|
||||
$lang->report->bugProgress = "Bug进展";
|
||||
$lang->report->productProgress = "产品进展";
|
||||
$lang->report->executionProgress = "执行进展";
|
||||
$lang->report->projectProgress = "项目进展";
|
||||
$lang->report->yearProjectOverview = "年度项目总览";
|
||||
$lang->report->projectOverview = "截止目前项目总览";
|
||||
|
||||
+148
-15
@@ -578,18 +578,39 @@ class reportModel extends model
|
||||
*/
|
||||
public function getUserYearContributions($accounts, $year)
|
||||
{
|
||||
$actionGroups = array();
|
||||
foreach($this->config->report->annualData['contributions'] as $objectType => $actions)
|
||||
$stmt = $this->dao->select('*')->from(TABLE_ACTION)
|
||||
->where('LEFT(date, 4)')->eq($year)
|
||||
->andWhere('objectType')->in(array_keys($this->config->report->annualData['contributions']))
|
||||
->beginIF($accounts)->andWhere('actor')->in($accounts)->fi()
|
||||
->orderBy('objectType,objectID,id')
|
||||
->query();
|
||||
|
||||
$filterActions = array();
|
||||
$objectIdList = array();
|
||||
while($action = $stmt->fetch())
|
||||
{
|
||||
$table = $this->config->objectTables[$objectType];
|
||||
$actionGroups[$objectType] = $this->dao->select('t1.*')->from(TABLE_ACTION)->alias('t1')
|
||||
->leftJoin($table)->alias('t2')->on("t1.objectType='$objectType' && t1.objectID=t2.id")
|
||||
->where('LEFT(t1.date, 4)')->eq($year)
|
||||
->andWhere('t1.objectType')->eq($objectType)
|
||||
->andWhere('t1.action')->in(array_keys($actions))
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->beginIF($accounts)->andWhere('t1.actor')->in($accounts)->fi()
|
||||
->fetchAll('id');
|
||||
$objectType = $action->objectType;
|
||||
$objectID = $action->objectID;
|
||||
$lowerAction = strtolower($action->action);
|
||||
if(!isset($this->config->report->annualData['contributions'][$objectType][$lowerAction])) continue;
|
||||
|
||||
$objectIdList[$objectType][$objectID] = $objectID;
|
||||
$filterActions[$objectType][$objectID][$action->id] = $action;
|
||||
}
|
||||
|
||||
foreach($objectIdList as $objectType => $idList)
|
||||
{
|
||||
$deletedIdList = $this->dao->select('id')->from($this->config->objectTables[$objectType])->where('deleted')->eq(1)->andWhere('id')->in($idList)->fetchPairs('id', 'id');
|
||||
foreach($deletedIdList as $id) unset($filterActions[$objectType][$id]);
|
||||
}
|
||||
|
||||
$actionGroups = array();
|
||||
foreach($filterActions as $objectType => $objectActions)
|
||||
{
|
||||
foreach($objectActions as $objectID => $actions)
|
||||
{
|
||||
foreach($actions as $action) $actionGroups[$objectType][$action->id] = $action;
|
||||
}
|
||||
}
|
||||
|
||||
$contributions = array();
|
||||
@@ -598,9 +619,7 @@ class reportModel extends model
|
||||
foreach($actions as $action)
|
||||
{
|
||||
$lowerAction = strtolower($action->action);
|
||||
if(!isset($this->config->report->annualData['contributions'][$objectType][$lowerAction])) continue;
|
||||
|
||||
$actionName = $this->config->report->annualData['contributions'][$objectType][$lowerAction];
|
||||
$actionName = $this->config->report->annualData['contributions'][$objectType][$lowerAction];
|
||||
|
||||
$type = ($actionName == 'svnCommit' or $actionName == 'gitCommit') ? 'repo' : $objectType;
|
||||
if(!isset($contributions[$type][$actionName])) $contributions[$type][$actionName] = 0;
|
||||
@@ -999,7 +1018,7 @@ class reportModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status vverview.
|
||||
* Get status overview.
|
||||
*
|
||||
* @param string $objectType
|
||||
* @param array $statusStat
|
||||
@@ -1029,6 +1048,120 @@ class reportModel extends model
|
||||
|
||||
return $overview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project status overview.
|
||||
*
|
||||
* @param array $accounts
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getProjectStatusOverview($accounts = array())
|
||||
{
|
||||
$projectStatus = $this->dao->select('t1.id,t1.status')->from(TABLE_PROJECT)->alias('t1')
|
||||
->leftJoin(TABLE_TEAM)->alias('t2')->on("t1.id=t2.root && t2.type='project'")
|
||||
->where('t1.type')->in($this->config->systemMode == 'classic' ? 'sprint,stage' : 'project')
|
||||
->beginIF(!empty($accounts))->andWhere('t2.account')->in($accounts)->fi()
|
||||
->fetchPairs('id', 'status');
|
||||
|
||||
$statusOverview = array();
|
||||
foreach($projectStatus as $projectID => $status)
|
||||
{
|
||||
if(!isset($statusOverview[$status])) $statusOverview[$status] = 0;
|
||||
$statusOverview[$status] ++;
|
||||
}
|
||||
|
||||
return $statusOverview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output data for API.
|
||||
*
|
||||
* @param array $accounts
|
||||
* @param string $year
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getOutput4API($accounts, $year)
|
||||
{
|
||||
$stmt = $this->dao->select('id,objectType,objectID,action,extra')->from(TABLE_ACTION)
|
||||
->where('objectType')->in(array_keys($this->config->report->outputData))
|
||||
->andWhere('LEFT(date, 4)')->eq($year)
|
||||
->beginIF($accounts)->andWhere('actor')->in($accounts)->fi()
|
||||
->query();
|
||||
|
||||
$outputData = array();
|
||||
$actionGroup = array();
|
||||
$objectIdList = array();
|
||||
while($action = $stmt->fetch())
|
||||
{
|
||||
if($action->objectType == 'release' and $action->action == 'changestatus')
|
||||
{
|
||||
if($action->extra == 'terminate') $action->action = 'stoped';
|
||||
if($action->extra == 'normal') $action->action = 'activated';
|
||||
}
|
||||
unset($action->extra);
|
||||
|
||||
if(!isset($this->config->report->outputData[$action->objectType][$action->action])) continue;
|
||||
|
||||
if(!isset($outputData[$action->objectType][$action->action])) $outputData[$action->objectType][$action->action] = 0;
|
||||
$objectIdList[$action->objectType][$action->objectID] = $action->objectID;
|
||||
$actionGroup[$action->objectType][$action->id] = $action;
|
||||
}
|
||||
|
||||
foreach($actionGroup as $objectType => $actions)
|
||||
{
|
||||
$deletedIdList = $this->dao->select('id')->from($this->config->objectTables[$objectType])->where('deleted')->eq(1)->andWhere('id')->in($objectIdList[$objectType])->fetchPairs('id', 'id');
|
||||
|
||||
foreach($actions as $action)
|
||||
{
|
||||
if(isset($deletedIdList[$action->objectID])) continue;
|
||||
$outputData[$action->objectType][$action->action] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->dao->select('t1.*')->from(TABLE_ACTION)->alias('t1')
|
||||
->leftJoin(TABLE_BUG)->alias('t2')->on('t1.objectID=t2.id')
|
||||
->where('t1.objectType')->eq('bug')
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->andWhere('LEFT(t1.date, 4)')->eq($year)
|
||||
->andWhere('t1.action')->eq('opened')
|
||||
->andWhere('t2.case')->ne('0')
|
||||
->beginIF($accounts)->andWhere('t1.actor')->in($accounts)->fi()
|
||||
->query();
|
||||
while($action = $stmt->fetch())
|
||||
{
|
||||
if(!isset($outputData['case']['createBug'])) $outputData['case']['createBug'] = 0;
|
||||
$outputData['case']['createBug'] += 1;
|
||||
}
|
||||
|
||||
$outputData['case']['run'] = $this->dao->select('count(*) as count')->from(TABLE_TESTRESULT)->alias('t1')
|
||||
->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case=t2.id')
|
||||
->where('LEFT(t1.date, 4)')->eq($year)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->beginIF($accounts)->andWhere('t1.lastRunner')->in($accounts)->fi()
|
||||
->fetch('count');
|
||||
|
||||
$processedOutput = array();
|
||||
foreach($this->config->report->outputData as $objectType => $actions)
|
||||
{
|
||||
if(!isset($outputData[$objectType])) continue;
|
||||
|
||||
$objectActions = $outputData[$objectType];
|
||||
$processedOutput[$objectType]['total'] = array_sum($objectActions);
|
||||
|
||||
foreach($actions as $action => $langCode)
|
||||
{
|
||||
if(empty($objectActions[$action])) continue;
|
||||
|
||||
$processedOutput[$objectType]['actions'][$langCode]['code'] = $langCode;
|
||||
$processedOutput[$objectType]['actions'][$langCode]['name'] = $this->lang->report->annualData->actionList[$langCode];
|
||||
$processedOutput[$objectType]['actions'][$langCode]['total'] = $objectActions[$action];
|
||||
}
|
||||
}
|
||||
|
||||
return $processedOutput;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -328,7 +328,7 @@ class stakeholderModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($parents)) return false;
|
||||
if(empty($parents)) return array();
|
||||
|
||||
/* Get all parent stakeholders.*/
|
||||
$parentStakeholders = $this->dao->select('objectID, user')->from(TABLE_STAKEHOLDER)->where('objectID')->in(array_keys($parents))->andWhere('deleted')->eq('0')->fetchAll();
|
||||
|
||||
+36
-10
@@ -721,14 +721,35 @@ class story extends control
|
||||
$product = $this->product->getByID($productID);
|
||||
$branchProduct = $product->type == 'normal' ? false : true;
|
||||
|
||||
/* Set modules. */
|
||||
$modules = array('ditto' => $this->lang->story->ditto) + $this->tree->getOptionMenu($productID, $viewType = 'story', 0, $branch);
|
||||
/* Set branches and modules. */
|
||||
$branches = array();
|
||||
$modules = array();
|
||||
if($product->type != 'normal')
|
||||
{
|
||||
$branches = $this->loadModel('branch')->getPairs($productID);
|
||||
if($branch === 'all')
|
||||
{
|
||||
$modules[0] = $this->tree->getOptionMenu($productID, $viewType = 'story', 0, $branch);
|
||||
foreach($branches as $branchID => $branchName)
|
||||
{
|
||||
$modules[$branchID] = $this->tree->getOptionMenu($productID, $viewType = 'story', 0, $branchID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$modules[$branch] = $this->tree->getOptionMenu($productID, $viewType = 'story', 0, $branch);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$modules[0] = $this->tree->getOptionMenu($productID, $viewType = 'story', 0, $branch);
|
||||
}
|
||||
|
||||
$this->view->modules = $modules;
|
||||
$this->view->branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($product->id);
|
||||
$this->view->plans = $this->productplan->getBranchPlanPairs($productID);
|
||||
$this->view->position[] = html::a($this->createLink('product', 'browse', "product=$product->id&branch=$branch"), $product->name);
|
||||
$this->view->title = $product->name . $this->lang->colon . $this->lang->story->batchEdit;
|
||||
$this->view->modules = $modules;
|
||||
$this->view->branches = $branches;
|
||||
$this->view->plans = $this->productplan->getBranchPlanPairs($productID);
|
||||
$this->view->position[] = html::a($this->createLink('product', 'browse', "product=$product->id&branch=$branch"), $product->name);
|
||||
$this->view->title = $product->name . $this->lang->colon . $this->lang->story->batchEdit;
|
||||
}
|
||||
elseif($executionID)
|
||||
{
|
||||
@@ -1817,15 +1838,20 @@ class story extends control
|
||||
* AJAX: get stories of a product in html select.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $branch
|
||||
* @param int $moduleID
|
||||
* @param int $storyID
|
||||
* @param string $onlyOption
|
||||
* @param string $status
|
||||
* @param int $limit
|
||||
* @param string $type
|
||||
* @param bool $hasParent
|
||||
* @param int $executionID
|
||||
* @param int $number
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxGetProductStories($productID, $branch = 0, $moduleID = 0, $storyID = 0, $onlyOption = 'false', $status = '', $limit = 0, $type = 'full', $hasParent = 1, $executionID = 0)
|
||||
public function ajaxGetProductStories($productID, $branch = 0, $moduleID = 0, $storyID = 0, $onlyOption = 'false', $status = '', $limit = 0, $type = 'full', $hasParent = 1, $executionID = 0, $number = '')
|
||||
{
|
||||
if($moduleID)
|
||||
{
|
||||
@@ -1847,11 +1873,11 @@ class story extends control
|
||||
}
|
||||
else
|
||||
{
|
||||
$stories = $this->story->getProductStoryPairs($productID, $branch ? "0,$branch" : $branch, $moduleID, $storyStatus, 'id_desc', $limit, $type, 'story', $hasParent);
|
||||
$stories = $this->story->getProductStoryPairs($productID, $branch, $moduleID, $storyStatus, 'id_desc', $limit, $type, 'story', $hasParent);
|
||||
}
|
||||
|
||||
$storyID = isset($stories[$storyID]) ? $storyID : 0;
|
||||
$select = html::select('story', empty($stories) ? array('' => '') : $stories, $storyID, "class='form-control'");
|
||||
$select = html::select('story' . $number, empty($stories) ? array('' => '') : $stories, $storyID, "class='form-control'");
|
||||
|
||||
/* If only need options, remove select wrap. */
|
||||
if($onlyOption == 'true') die(substr($select, strpos($select, '>') + 1, -10));
|
||||
|
||||
@@ -67,6 +67,17 @@ function setModuleAndPlan(branchID, productID, num)
|
||||
$("#plan" + num).next('.picker').remove();
|
||||
$("#plan" + num).chosen();
|
||||
});
|
||||
|
||||
/* If the branch of the current row is inconsistent with the one below, clear the module and plan of the nex row. */
|
||||
var nextBranchID = $('#branch' + (num + 1)).val();
|
||||
if(nextBranchID != branchID)
|
||||
{
|
||||
$('#module' + (num + 1)).find("option[value='ditto']").remove();
|
||||
$('#module' + (num + 1)).trigger("chosen:updated");
|
||||
|
||||
$('#plan' + (num + 1)).find("option[value='ditto']").remove();
|
||||
$('#plan' + (num + 1)).trigger("chosen:updated");
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy story title as story spec. */
|
||||
|
||||
@@ -2228,13 +2228,12 @@ class storyModel extends model
|
||||
*/
|
||||
public function getProductStoryPairs($productID = 0, $branch = 0, $moduleIdList = 0, $status = 'all', $order = 'id_desc', $limit = 0, $type = 'full', $storyType = 'story', $hasParent = true)
|
||||
{
|
||||
if($branch) $branch = "0,$branch";//Fix bug 1059.
|
||||
$stories = $this->dao->select('t1.id, t1.title, t1.module, t1.pri, t1.estimate, t2.name AS product')
|
||||
->from(TABLE_STORY)->alias('t1')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id')
|
||||
->where('1=1')
|
||||
->beginIF($productID)->andWhere('t1.product')->in($productID)->fi()
|
||||
->beginIF($moduleIdList)->andWhere('t1.module')->in($moduleIdList)->fi()
|
||||
->beginIF($branch)->andWhere('t1.branch')->in($branch)->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('t1.branch')->in($branch)->fi()
|
||||
->beginIF(!$hasParent)->andWhere('t1.parent')->ge(0)->fi()
|
||||
->beginIF($status and $status != 'all')->andWhere('t1.status')->in($status)->fi()
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
@@ -2673,7 +2672,7 @@ class storyModel extends model
|
||||
->where('t1.project')->eq((int)$executionID)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->beginIF($productID)->andWhere('t2.product')->eq((int)$productID)->fi()
|
||||
->beginIF($branch)->andWhere('t2.branch')->in("0,$branch")->fi()
|
||||
->beginIF($branch != 'all')->andWhere('t2.branch')->eq($branch)->fi()
|
||||
->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi()
|
||||
->beginIF($status == 'unclosed')->andWhere('t2.status')->ne('closed')->fi()
|
||||
->orderBy('t1.`order` desc')
|
||||
|
||||
@@ -76,15 +76,12 @@ foreach(explode(',', $showFields) as $field)
|
||||
if($product->type != 'normal')
|
||||
{
|
||||
foreach($branches as $branchID => $branchName) $branches[$branchID] = '/' . $product->name . '/' . $branchName;
|
||||
$branches = array('ditto' => $this->lang->story->ditto) + $branches;
|
||||
}
|
||||
|
||||
$modules = $this->tree->getOptionMenu($story->product, $viewType = 'story', 0, $story->branch);
|
||||
foreach($modules as $moduleID => $moduleName) $modules[$moduleID] = '/' . $product->name . $moduleName;
|
||||
$modules = array('ditto' => $this->lang->story->ditto) + $modules;
|
||||
if(!isset($modules[$story->branch])) $modules[$story->branch] = $this->tree->getOptionMenu($story->product, 'story', 0, $story->branch);
|
||||
foreach($modules[$story->branch] as $moduleID => $moduleName) $modules[$story->branch][$moduleID] = '/' . $product->name . $moduleName;
|
||||
|
||||
$productPlans = $this->productplan->getPairs($story->product, $branch);
|
||||
$productPlans = array('' => '', 'ditto' => $this->lang->story->ditto) + $productPlans;
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
@@ -97,7 +94,7 @@ foreach(explode(',', $showFields) as $field)
|
||||
</td>
|
||||
<?php endif;?>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'module')?>'>
|
||||
<?php echo html::select("modules[$storyID]", $modules, $story->module, "class='form-control chosen'");?>
|
||||
<?php echo html::select("modules[$storyID]", $modules[$story->branch], $story->module, "class='form-control chosen'");?>
|
||||
</td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'plan', ' hidden')?>'>
|
||||
<?php
|
||||
|
||||
@@ -138,7 +138,7 @@ class task extends control
|
||||
$this->executeHooks($taskID);
|
||||
|
||||
/* Return task id when call the API. */
|
||||
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $taskID));
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $taskID));
|
||||
|
||||
/* If link from no head then reload. */
|
||||
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
|
||||
@@ -300,7 +300,7 @@ class task extends control
|
||||
foreach($mails as $mail) $taskIDList[] = $mail->taskID;
|
||||
|
||||
/* Return task id list when call the API. */
|
||||
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'idList' => $taskIDList));
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'idList' => $taskIDList));
|
||||
|
||||
/* Locate the browser. */
|
||||
if(!empty($iframe)) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
|
||||
@@ -410,7 +410,7 @@ class task extends control
|
||||
}
|
||||
}
|
||||
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api')
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api'))
|
||||
{
|
||||
return $this->send(array('status' => 'success', 'data' => $taskID));
|
||||
}
|
||||
@@ -594,7 +594,7 @@ class task extends control
|
||||
|
||||
if(dao::isError())
|
||||
{
|
||||
if($this->viewType == 'json') return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
die(js::error(dao::getError()));
|
||||
}
|
||||
|
||||
@@ -603,7 +603,7 @@ class task extends control
|
||||
|
||||
$this->executeHooks($taskID);
|
||||
|
||||
if($this->viewType == 'json') return $this->send(array('result' => 'success'));
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success'));
|
||||
if(isonlybody()) die(js::closeModal('parent.parent', 'this'));
|
||||
die(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
|
||||
}
|
||||
@@ -798,7 +798,7 @@ class task extends control
|
||||
|
||||
if(dao::isError())
|
||||
{
|
||||
if($this->viewType == 'json') return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
die(js::error(dao::getError()));
|
||||
}
|
||||
|
||||
@@ -827,7 +827,7 @@ class task extends control
|
||||
}
|
||||
}
|
||||
|
||||
if($this->viewType == 'json') return $this->send(array('result' => 'success'));
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success'));
|
||||
if(isonlybody()) die(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
|
||||
die(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
|
||||
}
|
||||
@@ -961,7 +961,7 @@ class task extends control
|
||||
$changes = $this->task->finish($taskID);
|
||||
if(dao::isError())
|
||||
{
|
||||
if($this->viewType == 'json') return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
die(js::error(dao::getError()));
|
||||
}
|
||||
$files = $this->loadModel('file')->saveUpload('task', $taskID);
|
||||
|
||||
@@ -22,7 +22,6 @@ class taskModel extends model
|
||||
*/
|
||||
public function create($executionID)
|
||||
{
|
||||
|
||||
if($this->post->estimate < 0)
|
||||
{
|
||||
dao::$errors[] = $this->lang->task->error->recordMinus;
|
||||
|
||||
+31
-12
@@ -529,8 +529,9 @@ class testcase extends control
|
||||
$this->app->tab == 'project' ? $this->loadModel('project')->setMenu($this->session->project) : $this->testcase->setMenu($this->products, $productID, $branch);
|
||||
|
||||
/* Set story list. */
|
||||
$story = $storyID ? $this->story->getByID($storyID) : '';
|
||||
$storyList = $storyID ? array($storyID => $story->id . ':' . $story->title) : array('');
|
||||
$story = $storyID ? $this->story->getByID($storyID) : '';
|
||||
$storyPairs = $this->loadModel('story')->getProductStoryPairs($productID, $branch === 'all' ? 0 : $branch);
|
||||
$storyPairs += $storyID ? array($storyID => $story->id . ':' . $story->title) : array('');
|
||||
|
||||
/* Set module option menu. */
|
||||
$moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch === 'all' ? 0 : $branch);
|
||||
@@ -569,12 +570,12 @@ class testcase extends control
|
||||
$this->view->product = $product;
|
||||
$this->view->productID = $productID;
|
||||
$this->view->story = $story;
|
||||
$this->view->storyList = $storyList;
|
||||
$this->view->storyPairs = $storyPairs;
|
||||
$this->view->productName = $this->products[$productID];
|
||||
$this->view->moduleOptionMenu = $moduleOptionMenu;
|
||||
$this->view->currentModuleID = $currentModuleID;
|
||||
$this->view->branch = $branch;
|
||||
$this->view->branches = $this->loadModel('branch')->getPairs($productID);
|
||||
$this->view->branches = $this->loadModel('branch')->getPairs($productID, 'active');
|
||||
$this->view->needReview = $this->testcase->forceNotReview() == true ? 0 : 1;
|
||||
|
||||
$this->display();
|
||||
@@ -882,7 +883,6 @@ class testcase extends control
|
||||
|
||||
/* Set modules. */
|
||||
$modules = $this->tree->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0, $branch);
|
||||
$modules = array('ditto' => $this->lang->testcase->ditto) + $modules;
|
||||
|
||||
$this->view->modules = $modules;
|
||||
$this->view->title = $libraries[$libID] . $this->lang->colon . $this->lang->testcase->batchEdit;
|
||||
@@ -894,11 +894,31 @@ class testcase extends control
|
||||
|
||||
if($product->type != 'normal') $branchProduct = true;
|
||||
|
||||
/* Set modules. */
|
||||
$modules = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch);
|
||||
$modules = array('ditto' => $this->lang->testcase->ditto) + $modules;
|
||||
/* Set branches and modules. */
|
||||
$branches = array();
|
||||
$modules = array();
|
||||
if($product->type != 'normal')
|
||||
{
|
||||
$branches = $this->loadModel('branch')->getPairs($productID);
|
||||
if($branch === 'all')
|
||||
{
|
||||
$modules[0] = $this->tree->getOptionMenu($productID, $viewType = 'case', 0, 0);
|
||||
foreach($branches as $branchID => $branchName)
|
||||
{
|
||||
$modules[$branchID] = $this->tree->getOptionMenu($productID, $viewType = 'case', 0, $branchID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$modules[$branch] = $this->tree->getOptionMenu($productID, $viewType = 'case', 0, $branch);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$modules[0] = $this->tree->getOptionMenu($productID, $viewType = 'case', 0, 0);
|
||||
}
|
||||
|
||||
$this->view->branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($product->id);
|
||||
$this->view->branches = $branches;
|
||||
$this->view->modules = $modules;
|
||||
$this->view->position[] = html::a($this->createLink('testcase', 'browse', "productID=$productID"), $this->products[$productID]);
|
||||
$this->view->title = $product->name . $this->lang->colon . $this->lang->testcase->batchEdit;
|
||||
@@ -923,7 +943,6 @@ class testcase extends control
|
||||
$this->view->position[] = html::a($this->server->http_referer, $this->lang->my->testCase);
|
||||
$this->view->title = $this->lang->testcase->batchEdit;
|
||||
|
||||
/* Set modules. */
|
||||
$productIdList = array();
|
||||
foreach($cases as $case) $productIdList[$case->product] = $case->product;
|
||||
|
||||
@@ -1328,7 +1347,7 @@ class testcase extends control
|
||||
{
|
||||
$cases = array();
|
||||
$orderBy = " ORDER BY " . str_replace(array('|', '^A', '_'), ' ', $orderBy);
|
||||
$stmt = $this->dbh->query($this->session->testcaseQueryCondition . $orderBy . ($this->post->limit ? ' LIMIT ' . $this->post->limit : ''));
|
||||
$stmt = $this->dao->query($this->session->testcaseQueryCondition . $orderBy . ($this->post->limit ? ' LIMIT ' . $this->post->limit : ''));
|
||||
while($row = $stmt->fetch())
|
||||
{
|
||||
$caseID = isset($row->case) ? $row->case : $row->id;
|
||||
@@ -1389,7 +1408,7 @@ class testcase extends control
|
||||
$result = isset($results[$case->id]) ? $results[$case->id] : array();
|
||||
|
||||
$case->real = '';
|
||||
if(!empty($result))
|
||||
if(!empty($result) and !isset($relatedSteps[$case->id]))
|
||||
{
|
||||
$firstStep = reset($result);
|
||||
$case->real = $firstStep['real'];
|
||||
|
||||
@@ -3,66 +3,6 @@ $(document).ready(function()
|
||||
removeDitto();//Remove 'ditto' in first row.
|
||||
if($('#batchCreateForm table thead tr th.c-title').width() < 150) $('#batchCreateForm table thead tr th.c-title').width('150');
|
||||
|
||||
$(document).on('mouseup', '.chosen-with-drop', function()
|
||||
{
|
||||
var select = $(this).prev('select');
|
||||
var id = $(select).attr('id');
|
||||
if(id.indexOf('story') != -1)
|
||||
{
|
||||
var index = id.substring(5);
|
||||
var moduleID = $('#module' + index).val();
|
||||
var branch = $('#branch' + index).length > 0 ? $('#branch' + index).val() : 0;
|
||||
if(moduleID == 'ditto')
|
||||
{
|
||||
for(var i = index - 1; i >=0; i--)
|
||||
{
|
||||
if($('#module' + i).val() != 'ditto')
|
||||
{
|
||||
moduleID = $('#module' + i).val();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var link = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&storyID='+ ($(select).val() || '0') + '&onlyOption=true&status=noclosed&limit=0&type=null&hasParent=0');
|
||||
var $story = $('#story' + index);
|
||||
if($story.data('loadLink') !== link)
|
||||
{
|
||||
$story.load(link, function(){$story.data('loadLink', link).trigger("chosen:updated");});
|
||||
}
|
||||
}
|
||||
if($(select).val() == 'ditto')
|
||||
{
|
||||
var index = $(select).closest('td').index();
|
||||
var row = $(select).closest('tr').index();
|
||||
var table = $(select).closest('tr').parent();
|
||||
var value = '';
|
||||
for(i = row - 1; i >= 0; i--)
|
||||
{
|
||||
value = $(table).find('tr').eq(i).find('td').eq(index).find('select').val();
|
||||
if(value != 'ditto') break;
|
||||
}
|
||||
$(select).val(value);
|
||||
$(select).trigger("chosen:updated");
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('mousedown', 'select', function()
|
||||
{
|
||||
if($(this).val() == 'ditto')
|
||||
{
|
||||
var index = $(this).closest('td').index();
|
||||
var row = $(this).closest('tr').index();
|
||||
var table = $(this).closest('tr').parent();
|
||||
var value = '';
|
||||
for(i = row - 1; i >= 0; i--)
|
||||
{
|
||||
value = $(table).find('tr').eq(i).find('td').eq(index).find('select').val();
|
||||
if(value != 'ditto') break;
|
||||
}
|
||||
$(this).val(value);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).keydown(function(event)
|
||||
{
|
||||
if(event.ctrlKey && event.keyCode == 38)
|
||||
@@ -106,6 +46,19 @@ function setModules(branchID, productID, num)
|
||||
$('#module' + num).replaceWith(modules);
|
||||
$("#module" + num + "_chosen").remove();
|
||||
$("#module" + num).next('.picker').remove();
|
||||
$("#module" + num).chosen();
|
||||
$("#module" + num).attr('onchange', "loadStories("+ productID + ", this.value, " + num + ")").chosen();
|
||||
});
|
||||
|
||||
loadStories(productID, 0, num);
|
||||
|
||||
/* If the branch of the current row is inconsistent with the one below, clear the module and story of the nex row. */
|
||||
var nextBranchID = $('#branch' + (num + 1)).val();
|
||||
if(nextBranchID != branchID)
|
||||
{
|
||||
$('#module' + (num + 1)).find("option[value='ditto']").remove();
|
||||
$('#module' + (num + 1)).trigger("chosen:updated");
|
||||
|
||||
$('#plan' + (num + 1)).find("option[value='ditto']").remove();
|
||||
$('#plan' + (num + 1)).trigger("chosen:updated");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,39 @@ function loadBranches(product, branch, caseID)
|
||||
if(!branch) branch = 0;
|
||||
|
||||
moduleLink = createLink('tree', 'ajaxGetOptionMenu', 'productID=' + product + '&viewtype=case&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=' + caseID + '&needManage=true');
|
||||
$('#modules' + caseID).parent('td').load(moduleLink, function(){$('#modules' + caseID).chosen();})
|
||||
$('#modules' + caseID).parent('td').load(moduleLink, function()
|
||||
{
|
||||
$("#modules" + caseID).attr('onchange', "loadStories("+ product + ", this.value, " + caseID + ")").chosen();
|
||||
});
|
||||
|
||||
loadStories(product, 0, caseID);
|
||||
}
|
||||
|
||||
$(function()
|
||||
{
|
||||
removeDitto(); //Remove 'ditto' in first row.
|
||||
$('#subNavbar li[data-id="testcase"]').addClass('active');
|
||||
if(hasStory)
|
||||
{
|
||||
$("[name^='story']").each(function()
|
||||
{
|
||||
var id = $(this).attr('id');
|
||||
var num = id.substring(5);
|
||||
var moduleID = $('#modules' + num).val();
|
||||
var branchID = $('#branches' + num).val();
|
||||
var storyID = $("#story" + num).val();
|
||||
var storyLink = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&branch=' + branchID + '&moduleID=' + moduleID + '&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1&executionID=0&number=' + num);
|
||||
$.get(storyLink, function(stories)
|
||||
{
|
||||
if(!stories) modules = '<select id="story' + num + '" name="story[' + num + ']" class="form-control"></select>';
|
||||
$('#story' + num).replaceWith(stories);
|
||||
$('#story' + num + "_chosen").remove();
|
||||
$('#story' + num).next('.picker').remove();
|
||||
$('#story' + num).attr('name', 'story[' + num + ']').chosen();
|
||||
$('#story' + num).val(storyID).trigger('chosen:updated');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.chosen-with-drop', function(){oldValue = $(this).prev('select').val();})//Save old value.
|
||||
|
||||
@@ -343,3 +343,28 @@ function updateStepID()
|
||||
var i = 1;
|
||||
$('.stepID').each(function(){$(this).html(i ++)});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stories.
|
||||
*
|
||||
* @param int productID
|
||||
* @param int moduleID
|
||||
* @param int num
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadStories(productID, moduleID, num)
|
||||
{
|
||||
var branchIDName = config.currentMethod == 'batchcreate' ? '#branch' : '#branches';
|
||||
var branchID = $(branchIDName + num).val();
|
||||
var storyLink = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&branch=' + branchID + '&moduleID=' + moduleID + '&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1&executionID=0&number=' + num);
|
||||
$.get(storyLink, function(stories)
|
||||
{
|
||||
if(!stories) modules = '<select id="story' + num + '" name="story[' + num + ']" class="form-control"></select>';
|
||||
$('#story' + num).replaceWith(stories);
|
||||
$('#story' + num + "_chosen").remove();
|
||||
$('#story' + num).next('.picker').remove();
|
||||
$('#story' + num).attr('name', 'story[' + num + ']');
|
||||
$('#story' + num).chosen();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -306,12 +306,27 @@ class testcaseModel extends model
|
||||
* @param int $executionID
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param string $browseType
|
||||
* @param string $browseType all|wait|needconfirm
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getExecutionCases($executionID, $orderBy = 'id_desc', $pager = null, $browseType = '')
|
||||
{
|
||||
if($browseType == 'needconfirm')
|
||||
{
|
||||
return $this->dao->select('distinct t1.*, t2.*')->from(TABLE_PROJECTCASE)->alias('t1')
|
||||
->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case=t2.id')
|
||||
->leftJoin(TABLE_STORY)->alias('t3')->on('t1.story = t3.id')
|
||||
->where('t1.project')->eq((int)$executionID)
|
||||
->beginIF($browseType != 'all')->andWhere('t2.status')->eq($browseType)->fi()
|
||||
->andWhere('t2.deleted')->eq('0')
|
||||
->andWhere('t3.version > t2.storyVersion')
|
||||
->andWhere("t3.status")->eq('active')
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
}
|
||||
|
||||
return $this->dao->select('distinct t1.*, t2.*')->from(TABLE_PROJECTCASE)->alias('t1')
|
||||
->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case=t2.id')
|
||||
->where('t1.project')->eq((int)$executionID)
|
||||
@@ -863,13 +878,13 @@ class testcaseModel extends model
|
||||
if($data->pris[$caseID] == 'ditto') $data->pris[$caseID] = isset($prev['pri']) ? $prev['pri'] : 3;
|
||||
if($data->branches[$caseID] == 'ditto') $data->branches[$caseID] = isset($prev['branch']) ? $prev['branch'] : 0;
|
||||
if($data->modules[$caseID] == 'ditto') $data->modules[$caseID] = isset($prev['module']) ? $prev['module'] : 0;
|
||||
if($data->stories[$caseID] == 'ditto') $data->stories[$caseID] = isset($prev['story']) ? $prev['story'] : 0;
|
||||
if($data->story[$caseID] == 'ditto') $data->story[$caseID] = isset($prev['story']) ? $prev['story'] : 0;
|
||||
if($data->types[$caseID] == 'ditto') $data->types[$caseID] = isset($prev['type']) ? $prev['type'] : '';
|
||||
if($data->stories[$caseID] == '') $data->stories[$caseID] = 0;
|
||||
if($data->story[$caseID] == '') $data->story[$caseID] = 0;
|
||||
|
||||
$prev['pri'] = $data->pris[$caseID];
|
||||
$prev['type'] = $data->types[$caseID];
|
||||
$prev['story'] = $data->stories[$caseID];
|
||||
$prev['story'] = $data->story[$caseID];
|
||||
$prev['branch'] = $data->branches[$caseID];
|
||||
$prev['module'] = $data->modules[$caseID];
|
||||
}
|
||||
@@ -885,7 +900,7 @@ class testcaseModel extends model
|
||||
$case->branch = $data->branches[$caseID];
|
||||
$case->module = $data->modules[$caseID];
|
||||
$case->status = $data->statuses[$caseID];
|
||||
$case->story = $data->stories[$caseID];
|
||||
$case->story = $data->story[$caseID];
|
||||
$case->color = $data->color[$caseID];
|
||||
$case->title = $data->title[$caseID];
|
||||
$case->precondition = $data->precondition[$caseID];
|
||||
@@ -1707,6 +1722,13 @@ class testcaseModel extends model
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary cases
|
||||
*
|
||||
* @param array $cases
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function summary($cases)
|
||||
{
|
||||
$executed = 0;
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
<tr>
|
||||
<td class="text-center"><?php echo $i+1;?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, $product->type, ' hidden')?>'><?php echo html::select("branch[$i]", $branches, $branch, "class='form-control' onchange='setModules(this.value, $productID, $i)'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'module', ' hidden')?>' style='overflow:visible'><?php echo html::select("module[$i]", $moduleOptionMenu, $currentModuleID, "class='form-control chosen'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'story', ' hidden'); echo $hiddenStory;?>' style='overflow:visible'> <?php echo html::select("story[$i]", $storyList, $story ? $story->id : '', 'class="form-control chosen"');?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'module', ' hidden')?>' style='overflow:visible'><?php echo html::select("module[$i]", $moduleOptionMenu, $currentModuleID, "class='form-control chosen' onchange='loadStories($productID, this.value, $i)'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'story', ' hidden'); echo $hiddenStory;?>' style='overflow:visible'> <?php echo html::select("story[$i]", $storyPairs, $story ? $story->id : '', 'class="form-control chosen"');?></td>
|
||||
<td style='overflow:visible'>
|
||||
<div class="input-control has-icon-right">
|
||||
<?php echo html::input("title[$i]", '', "class='form-control title-import'");?>
|
||||
@@ -124,7 +124,7 @@
|
||||
<tr>
|
||||
<td class="text-center">%s</td>
|
||||
<td class='text-left<?php echo zget($visibleFields, $product->type, ' hidden')?>'><?php echo html::select("branch[%s]", $branches, $branch, "class='form-control chosen' onchange='setModules(this.value, $productID, \"%s\")'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'module', ' hidden')?>' style='overflow:visible'><?php echo html::select("module[%s]", $moduleOptionMenu, $currentModuleID, "class='form-control chosen'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'module', ' hidden')?>' style='overflow:visible'><?php echo html::select("module[%s]", $moduleOptionMenu, $currentModuleID, "class='form-control chosen' onchange='loadStories($productID, this.value, \"%s\")'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'story', ' hidden'); echo $hiddenStory;?>' style='overflow:visible'> <?php echo html::select("story[%s]", '', '', 'class="form-control chosen"');?></td>
|
||||
<td style='overflow:visible'>
|
||||
<div class="input-control has-icon-right">
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
?>
|
||||
<?php include '../../common/view/header.html.php';?>
|
||||
<?php js::set('dittoNotice', $this->lang->testcase->dittoNotice);?>
|
||||
<?php js::set('productID', $productID);?>
|
||||
<div id="mainContent" class="main-content">
|
||||
<div class="main-header">
|
||||
<h2><?php echo $lang->testcase->common . $lang->colon . $lang->testcase->batchEdit;?></h2>
|
||||
@@ -67,6 +68,7 @@
|
||||
<?php foreach($caseIDList as $caseID):?>
|
||||
<?php
|
||||
if(!isset($cases[$caseID])) continue;
|
||||
$caseBranch = isset($cases[$caseID]->branch) ? $cases[$caseID]->branch : 0;
|
||||
if((!$productID and !$cases[$caseID]->lib) or $app->tab != 'qa')
|
||||
{
|
||||
$product = $this->product->getByID($cases[$caseID]->product);
|
||||
@@ -74,10 +76,9 @@
|
||||
if($product->type != 'normal')
|
||||
{
|
||||
foreach($branches as $branchID => $branchName) $branches[$branchID] = '/' . $product->name . '/' . $branchName;
|
||||
$branches = array('ditto' => $this->lang->story->ditto) + $branches;
|
||||
}
|
||||
|
||||
$modules = $this->tree->getOptionMenu($cases[$caseID]->product, $viewType = 'case', 0, $cases[$caseID]->branch);
|
||||
$modules[$caseBranch] = $this->tree->getOptionMenu($cases[$caseID]->product, $viewType = 'case', 0, $caseBranch);
|
||||
}
|
||||
?>
|
||||
<tr class='text-center'>
|
||||
@@ -103,8 +104,8 @@
|
||||
<?php echo html::select("branches[$caseID]", $branches, $cases[$caseID]->branch, "class='form-control chosen' onchange='loadBranches($branchProductID, this.value, $caseID)', $disabled");?>
|
||||
</td>
|
||||
<?php endif;?>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'module', ' hidden')?>' style='overflow:visible'><?php echo html::select("modules[$caseID]", $modules, $cases[$caseID]->module, "class='form-control chosen'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'story', ' hidden')?>' style='overflow:visible'><?php echo html::select("stories[$caseID]", $stories, $cases[$caseID]->story, "class='form-control chosen'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'module', ' hidden')?>' style='overflow:visible'><?php echo html::select("modules[$caseID]", $modules[$caseBranch], $cases[$caseID]->module, "class='form-control chosen' onchange='loadStories($productID, this.value, $caseID)'");?></td>
|
||||
<td class='text-left<?php echo zget($visibleFields, 'story', ' hidden')?>' style='overflow:visible'><?php echo html::select("story[$caseID]", $stories, $cases[$caseID]->story, "class='form-control chosen'");?></td>
|
||||
<td style='overflow:visible' title='<?php echo $cases[$caseID]->title?>'>
|
||||
<div class='input-group'>
|
||||
<div class="input-control has-icon-right">
|
||||
@@ -140,4 +141,5 @@
|
||||
</form>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<?php js::set('hasStory', isset($visibleFields['story']));?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<?php js::set('lblBefore', $lang->testcase->insertBefore);?>
|
||||
<?php js::set('lblAfter', $lang->testcase->insertAfter);?>
|
||||
<?php js::set('caseID', $case->id);?>
|
||||
<?php js::set('executionID', $case->execution);?>
|
||||
<div id='mainContent' class='main-content'>
|
||||
<div class='main-header'>
|
||||
<h2>
|
||||
|
||||
@@ -312,6 +312,7 @@ class todo extends control
|
||||
{
|
||||
$todo = $this->todo->getById($todoID);
|
||||
if($todo->status == 'done' or $todo->status == 'closed') $this->todo->activate($todoID);
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
|
||||
if(isonlybody()) die(js::reload('parent.parent'));
|
||||
die(js::reload('parent'));
|
||||
}
|
||||
@@ -476,8 +477,10 @@ class todo extends control
|
||||
if($todo->type == 'task') $app = 'execution';
|
||||
if($todo->type == 'story') $app = 'product';
|
||||
$cancelURL = $this->server->HTTP_REFERER;
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success', 'message' => sprintf($this->lang->todo->$confirmNote, $todo->idvalue), 'locate' => $confirmURL));
|
||||
die(js::confirm(sprintf($this->lang->todo->$confirmNote, $todo->idvalue), $confirmURL, $cancelURL, $okTarget, 'parent', $app));
|
||||
}
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
|
||||
if(isonlybody())die(js::reload('parent.parent'));
|
||||
die(js::reload('parent'));
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ $lang->todo->lblClickCreate = "点击添加待办";
|
||||
$lang->todo->noTodo = '该类型没有待办事务';
|
||||
$lang->todo->noAssignedTo = '被指派人不能为空';
|
||||
$lang->todo->unfinishedTodo = '待办ID %s 不是完成状态,不能关闭。';
|
||||
$lang->todo->today = '今日待办';
|
||||
|
||||
$lang->todo->periods['all'] = '所有';
|
||||
$lang->todo->periods['before'] = '未完';
|
||||
|
||||
+20
-10
@@ -186,8 +186,9 @@ $lang->user->personalData['createdIssues'] = '创建的问题数';
|
||||
$lang->user->personalData['resolvedIssues'] = '解决的问题数';
|
||||
$lang->user->personalData['createdDocs'] = '创建的文档数';
|
||||
|
||||
$lang->user->keepLogin['on'] = '保持登录';
|
||||
$lang->user->loginWithDemoUser = '使用demo帐号登录:';
|
||||
$lang->user->keepLogin['on'] = '保持登录';
|
||||
$lang->user->loginWithDemoUser = '使用demo帐号登录:';
|
||||
$lang->user->scanToLogin = '扫一扫登录';
|
||||
|
||||
$lang->user->tpl = new stdclass();
|
||||
$lang->user->tpl->type = '类型';
|
||||
@@ -199,12 +200,16 @@ $lang->usertpl = new stdclass();
|
||||
$lang->usertpl->title = '模板名称';
|
||||
|
||||
$lang->user->placeholder = new stdclass();
|
||||
$lang->user->placeholder->account = '英文、数字和下划线的组合,三位以上';
|
||||
$lang->user->placeholder->password1 = '六位以上';
|
||||
$lang->user->placeholder->role = '职位影响内容和用户列表的顺序。';
|
||||
$lang->user->placeholder->group = '分组决定用户的权限列表。';
|
||||
$lang->user->placeholder->commiter = '版本控制系统(subversion)中的帐号';
|
||||
$lang->user->placeholder->verify = '请输入您的系统登录密码';
|
||||
$lang->user->placeholder->account = '英文、数字和下划线的组合,三位以上';
|
||||
$lang->user->placeholder->password1 = '六位以上';
|
||||
$lang->user->placeholder->role = '职位影响内容和用户列表的顺序。';
|
||||
$lang->user->placeholder->group = '分组决定用户的权限列表。';
|
||||
$lang->user->placeholder->commiter = '版本控制系统(subversion)中的帐号';
|
||||
$lang->user->placeholder->verify = '请输入您的系统登录密码';
|
||||
|
||||
$lang->user->placeholder->loginPassword = '请输入密码';
|
||||
$lang->user->placeholder->loginAccount = '请输入用户名';
|
||||
$lang->user->placeholder->loginUrl = '请输入禅道系统网址';
|
||||
|
||||
$lang->user->placeholder->passwordStrength[1] = '6位以上,包含大小写字母,数字。';
|
||||
$lang->user->placeholder->passwordStrength[2] = '10位以上,包含大小写字母,数字,特殊字符。';
|
||||
@@ -219,6 +224,8 @@ $lang->user->error->reserved = "【ID %s】的用户名已被系统预留"
|
||||
$lang->user->error->weakPassword = "【ID %s】的密码强度小于系统设定。";
|
||||
$lang->user->error->dangerPassword = "【ID %s】的密码不能使用【%s】这些常用若口令。";
|
||||
|
||||
$lang->user->error->url = "网址不正确,请联系管理员";
|
||||
$lang->user->error->verify = "用户名或密码错误";
|
||||
$lang->user->error->verifyPassword = "验证失败,请检查您的系统登录密码是否正确";
|
||||
$lang->user->error->originalPassword = "原密码不正确";
|
||||
$lang->user->error->companyEmpty = "公司名称不能为空!";
|
||||
@@ -267,16 +274,19 @@ $lang->user->process4DB = "检测到您可能在使用一键安装包环境,
|
||||
$lang->user->mkdirWin = <<<EOT
|
||||
<html><head><meta charset='utf-8'></head>
|
||||
<body><table align='center' style='width:700px; margin-top:100px; border:1px solid gray; font-size:14px;'><tr><td style='padding:8px'>
|
||||
<div style='margin-bottom:8px;'>不能创建临时目录,请确认目录<strong style='color:#ed980f'>%s</strong>是否存在并有操作权限。</div>
|
||||
<div style='margin-bottom:8px;'>不能创建临时目录,请确认目录<strong style='color:#ed980f'>%s</strong>是否存在并有操作权限。</div>
|
||||
<div>Can't create tmp directory, make sure the directory <strong style='color:#ed980f'>%s</strong> exists and has permission to operate.</div>
|
||||
</td></tr></table></body></html>
|
||||
EOT;
|
||||
$lang->user->mkdirLinux = <<<EOT
|
||||
<html><head><meta charset='utf-8'></head>
|
||||
<body><table align='center' style='width:700px; margin-top:100px; border:1px solid gray; font-size:14px;'><tr><td style='padding:8px'>
|
||||
<div style='margin-bottom:8px;'>不能创建临时目录,请确认目录<strong style='color:#ed980f'>%s</strong>是否存在并有操作权限。</div>
|
||||
<div style='margin-bottom:8px;'>不能创建临时目录,请确认目录<strong style='color:#ed980f'>%s</strong>是否存在并有操作权限。</div>
|
||||
<div style='margin-bottom:8px;'>命令为:<strong style='color:#ed980f'>chmod o=rwx -R %s</strong>。</div>
|
||||
<div>Can't create tmp directory, make sure the directory <strong style='color:#ed980f'>%s</strong> exists and has permission to operate.</div>
|
||||
<div style='margin-bottom:8px;'>Commond: <strong style='color:#ed980f'>chmod o=rwx -R %s</strong>.</div>
|
||||
</td></tr></table></body></html>
|
||||
EOT;
|
||||
|
||||
$lang->user->zentaoapp = new stdclass();
|
||||
$lang->user->zentaoapp->logout = '退出登录';
|
||||
|
||||
@@ -58,7 +58,7 @@ class userModel extends model
|
||||
{
|
||||
if(empty($accounts)) return array();
|
||||
|
||||
return $this->dao->select('id,account,realname,role')->from(TABLE_USER)
|
||||
return $this->dao->select('id,account,realname,avatar,role')->from(TABLE_USER)
|
||||
->where('account')->in($accounts)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('type')->eq('inside')
|
||||
@@ -1082,9 +1082,9 @@ class userModel extends model
|
||||
|
||||
/* Get all tasks and compute totalConsumed, totalLeft, totalWait, progress according to them. */
|
||||
$hours = array();
|
||||
$emptyHour = array('totalConsumed' => 0, 'totalLeft' => 0, 'progress' => 0, 'waitTasks' => 0, 'assignedToMeTasks' => 0);
|
||||
$emptyHour = array('totalConsumed' => 0, 'totalLeft' => 0, 'progress' => 0, 'waitTasks' => 0, 'assignedToMeTasks' => 0, 'doneTasks' => 0, 'taskTotal' => 0);
|
||||
$searchField = $type == 'project' ? 'project' : 'execution';
|
||||
$tasks = $this->dao->select('id, project, execution, consumed, `left`, status, assignedTo')
|
||||
$tasks = $this->dao->select('id, project, execution, consumed, `left`, status, assignedTo,finishedBy')
|
||||
->from(TABLE_TASK)
|
||||
->where('parent')->lt(1)
|
||||
->andWhere($searchField)->in($objectIdList)->fi()
|
||||
@@ -1095,9 +1095,11 @@ class userModel extends model
|
||||
foreach($tasks as $objectID => $objectTasks)
|
||||
{
|
||||
$hour = (object)$emptyHour;
|
||||
$hour->taskTotal = count($objectTasks);
|
||||
foreach($objectTasks as $task)
|
||||
{
|
||||
if($task->status == 'wait') $hour->waitTasks += 1;
|
||||
if($task->finishedBy != '') $hour->doneTasks += 1;
|
||||
if($task->status != 'cancel') $hour->totalConsumed += $task->consumed;
|
||||
if($task->status != 'cancel' and $task->status != 'closed') $hour->totalLeft += $task->left;
|
||||
if($task->assignedTo == $account) $hour->assignedToMeTasks += 1;
|
||||
@@ -1128,6 +1130,9 @@ class userModel extends model
|
||||
/* Process the hours. */
|
||||
$object->progress = isset($hours[$object->id]) ? $hours[$object->id]->progress : 0;
|
||||
$object->waitTasks = isset($hours[$object->id]) ? $hours[$object->id]->waitTasks : 0;
|
||||
$object->doneTasks = isset($hours[$object->id]) ? $hours[$object->id]->doneTasks : 0;
|
||||
$object->taskTotal = isset($hours[$object->id]) ? $hours[$object->id]->taskTotal : 0;
|
||||
$object->totalConsumed = isset($hours[$object->id]) ? $hours[$object->id]->totalConsumed : 0;
|
||||
$object->assignedToMeTasks = isset($hours[$object->id]) ? $hours[$object->id]->assignedToMeTasks : 0;
|
||||
|
||||
if($object->project)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
|
||||
|
||||
/**
|
||||
|
||||
title=获取项目列表;
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
获取条目的project和type字段 >> 0,project
|
||||
获取不存在的项目 >> 404 Not found
|
||||
获取错误ID的项目 >> 404 Not found
|
||||
|
||||
*/
|
||||
$token = $rest->post('/tokens', array('account' => 'admin', 'password' => '123456'));
|
||||
$project = $rest->get('/projects/712', array("Token" => $token->body->token));
|
||||
$zeroIdError = $rest->get('/projects/0', array("Token" => $token->body->token));
|
||||
$stringIdError = $rest->get('/projects/test', array("Token" => $token->body->token));
|
||||
|
||||
r($project) && c('200') && p('project,type', ',') && e('0,project'); // 获取条目的project和type字段
|
||||
r($zeroIdError) && c('404') && p('error') && e('404 Not found'); // 获取不存在的项目
|
||||
r($stringIdError) && c('404') && p('error') && e('404 Not found'); // 获取错误ID的项目
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
|
||||
|
||||
/**
|
||||
|
||||
title=获取项目列表;
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
获取条目的project和type字段 >> 0,project
|
||||
|
||||
*/
|
||||
global $token;
|
||||
$projects = $rest->get('/projects', array("Token" => $token));
|
||||
|
||||
$project = array(reset($projects->body->projects));
|
||||
r($project) && p('project,type', ',') && e('0,project'); // 获取条目的project和type字段
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
|
||||
|
||||
/**
|
||||
|
||||
title=创建项目;
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
创建失败,没有name字段 >> `『项目名称』`
|
||||
创建失败,没有code字段 >> `『项目代号』`
|
||||
创建失败,没有end字段 >> `『计划完成』`
|
||||
创建成功,获取创建的name和code字段 >> test111,test222
|
||||
创建失败,获取错误信息 >> `『test111』`
|
||||
|
||||
*/
|
||||
global $token;
|
||||
|
||||
$postData = array();
|
||||
$postData['parent'] = '0';
|
||||
$postData['name'] = 'test111';
|
||||
$postData['code'] = '';
|
||||
$postData['PM'] = '';
|
||||
$postData['budget'] = '';
|
||||
$postData['budgetUnit'] = 'CNY';
|
||||
$postData['begin'] = date('Y-m-d');
|
||||
$postData['end'] = date('Y-m-d', time() + 10 * 24 * 3600);
|
||||
$postData['days'] = '10';
|
||||
$postData['acl'] = 'private';
|
||||
$postData['auth'] = 'extend';
|
||||
$postData['model'] = 'scrum';
|
||||
$postData['products'][] = 1;
|
||||
$postData['plan'][] = '';
|
||||
$postData['whitelist'][] = '';
|
||||
|
||||
$postData['name'] = '';
|
||||
$postData['code'] = 'test222';
|
||||
$noNameError = $rest->post('/projects', $postData, array("Token" => $token));
|
||||
$noNameError = $noNameError->body->error->name[0];
|
||||
|
||||
$postData['name'] = 'test111';
|
||||
$postData['code'] = '';
|
||||
$noCodeError = $rest->post('/projects', $postData, array("Token" => $token));
|
||||
$noCodeError = $noCodeError->body->error->code[0];
|
||||
|
||||
$postData['code'] = 'test222';
|
||||
$postData['end'] = '';
|
||||
$noEndError = $rest->post('/projects', $postData, array("Token" => $token));
|
||||
$noEndError = $noEndError->body->error->end[0];
|
||||
|
||||
$postData['end'] = date('Y-m-d', time() + 10 * 24 * 3600);
|
||||
$project = $rest->post('/projects', $postData, array("Token" => $token));
|
||||
$error = $rest->post('/projects', $postData, array("Token" => $token));
|
||||
$error = $error->body->error->name[0];
|
||||
|
||||
r($noNameError) && p('error') && e('`『项目名称』`'); // 创建失败,没有name字段
|
||||
r($noCodeError) && p('error') && e('`『项目代号』`'); // 创建失败,没有code字段
|
||||
r($noEndError) && p('error') && e('`『计划完成』`'); // 创建失败,没有end字段
|
||||
r($project) && c('201') && p('name,code', ',') && e('test111,test222'); // 创建成功,获取创建的name和code字段
|
||||
r($error) && p('error') && e('`『test111』`'); // 创建失败,获取错误信息
|
||||
Reference in New Issue
Block a user