-

branch->editAction, $lang->product->branchName[$product->type]);?>

+

branch->edit, $lang->product->branchName[$product->type]);?>

diff --git a/module/bug/lang/zh-cn.php b/module/bug/lang/zh-cn.php index 0aa5536766..12dc4b85fb 100644 --- a/module/bug/lang/zh-cn.php +++ b/module/bug/lang/zh-cn.php @@ -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 = '
您现在还没有模块信息
请维护测试模块
'; -$lang->bug->delayWarning = " 延期%s天 "; +$lang->bug->ditto = '同上'; +$lang->bug->dittoNotice = '该bug与上一bug不属于同一产品!'; +$lang->bug->noAssigned = '未指派'; +$lang->bug->noBug = '暂时没有Bug。'; +$lang->bug->noModule = '
您现在还没有模块信息
请维护测试模块
'; +$lang->bug->delayWarning = " 延期%s天 "; +$lang->bug->labelConfirmed = '已确认'; +$lang->bug->labelPostponed = '被延期'; +$lang->bug->changed = '已变动'; +$lang->bug->storyChanged = '需求变动'; /* 页面标签。*/ $lang->bug->lblAssignedTo = '当前指派'; diff --git a/module/bug/model.php b/module/bug/model.php index 3c1baa04d9..8542debbf5 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -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(); diff --git a/module/build/control.php b/module/build/control.php index aa1dc47bef..d1d4ffbcf2 100644 --- a/module/build/control.php +++ b/module/build/control.php @@ -16,6 +16,7 @@ class build extends control * * @param int $executionID * @param int $productID + * @param int $projectID * @access public * @return void */ @@ -62,11 +63,17 @@ class build extends control $execution = $this->execution->getByID($executionID); $executions = $this->execution->getPairs($execution->project); } + + $executionList = $this->execution->getByIdList(array_keys($executions)); + foreach($executionList as $execution) + { + if($execution->lifetime == 'ops') unset($executions[$execution->id]); + } $productGroups = $this->loadModel('product')->getProducts($executionID); $productID = $productID ? $productID : key($productGroups); $branchGroups = $this->loadModel('project')->getBranchesByProject($executionID); - $branchPairs = $this->loadModel('branch')->getPairs($productID); + $branchPairs = $this->loadModel('branch')->getPairs($productID, 'active'); $branches = array(); $products = array(); @@ -75,7 +82,7 @@ class build extends control { foreach($branchGroups[$productID] as $branchID => $branch) { - $branches[$branchID] = $branchPairs[$branchID]; + if(isset($branchPairs[$branchID])) $branches[$branchID] = $branchPairs[$branchID]; } } diff --git a/module/build/js/common.js b/module/build/js/common.js index 71897d5bdc..e92d9470ad 100644 --- a/module/build/js/common.js +++ b/module/build/js/common.js @@ -16,7 +16,7 @@ function loadBranches(productID) } executionID = $('#execution').val(); - $.get(createLink('branch', 'ajaxGetBranches', 'productID=' + productID + '&oldBranch=0¶m=&projectID=' + executionID), function(data) + $.get(createLink('branch', 'ajaxGetBranches', 'productID=' + productID + '&oldBranch=0¶m=active&projectID=' + executionID), function(data) { if(data) { diff --git a/module/build/model.php b/module/build/model.php index 1170b9fbe9..b2625dfedc 100644 --- a/module/build/model.php +++ b/module/build/model.php @@ -286,6 +286,7 @@ class buildModel extends model /* Set builds and filter done executions and terminate releases. */ $builds = array(); + $this->app->loadLang('branch'); foreach($productBuilds as $key => $build) { if(empty($build->releaseID) and (strpos($params, 'nodone') !== false) and ($build->executionStatus === 'done')) continue; diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index af2a8e6031..608bfe8a11 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.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 = '主题'; diff --git a/module/common/view/kindeditor.html.php b/module/common/view/kindeditor.html.php index b155782660..19d2831692 100755 --- a/module/common/view/kindeditor.html.php +++ b/module/common/view/kindeditor.html.php @@ -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: 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') || noticePasteImg);?>}, + pasteImage: {postUrl: createLink('file', 'ajaxPasteImg', 'uid=' + kuid), placeholder: $editor.attr('placeholder') || noticePasteImg);?>}, }); try diff --git a/module/doc/model.php b/module/doc/model.php index deea0fb09b..ef211b0820 100644 --- a/module/doc/model.php +++ b/module/doc/model.php @@ -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 = ''; diff --git a/module/execution/view/testcase.html.php b/module/execution/view/testcase.html.php index 002f050ade..8864d62792 100644 --- a/module/execution/view/testcase.html.php +++ b/module/execution/view/testcase.html.php @@ -49,7 +49,7 @@ version";?> task";?> - + diff --git a/module/file/control.php b/module/file/control.php index ba4318d1d4..b25ccae783 100644 --- a/module/file/control.php +++ b/module/file/control.php @@ -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)); } diff --git a/module/file/model.php b/module/file/model.php index a27c80b173..a287289f3e 100644 --- a/module/file/model.php +++ b/module/file/model.php @@ -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(); diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index f08660dd09..786d2cf8b8 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -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'; diff --git a/module/group/model.php b/module/group/model.php index 32f14009c9..379decbf3b 100644 --- a/module/group/model.php +++ b/module/group/model.php @@ -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; } diff --git a/module/group/view/manageview.html.php b/module/group/view/manageview.html.php index 77b2cb578f..ddaa99e4e2 100644 --- a/module/group/view/manageview.html.php +++ b/module/group/view/manageview.html.php @@ -26,7 +26,7 @@ mainNav as $menuKey => $menu):?> - +
diff --git a/module/my/js/common.js b/module/my/js/common.js index 60242230cc..ad12c9cff0 100644 --- a/module/my/js/common.js +++ b/module/my/js/common.js @@ -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('' + total + ''); } - 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'}); + } }); diff --git a/module/my/lang/zh-cn.php b/module/my/lang/zh-cn.php index 6eef0eb79e..6c5335819b 100644 --- a/module/my/lang/zh-cn.php +++ b/module/my/lang/zh-cn.php @@ -3,6 +3,7 @@ global $config; /* 方法列表。*/ $lang->my->index = '首页'; +$lang->my->data = '我的数据'; $lang->my->todo = '我的待办'; $lang->my->calendar = '日程'; $lang->my->work = '待处理'; diff --git a/module/my/model.php b/module/my/model.php index ce75ba9b1e..57ea22b967 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -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; } diff --git a/module/product/model.php b/module/product/model.php index 0ac6d5f248..5072f1fe60 100644 --- a/module/product/model.php +++ b/module/product/model.php @@ -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; } diff --git a/module/productplan/control.php b/module/productplan/control.php index aa837d46df..008a027c55 100644 --- a/module/productplan/control.php +++ b/module/productplan/control.php @@ -63,6 +63,7 @@ class productplan extends control $this->commonAction($productID, $branchID); $lastPlan = $this->productplan->getLast($productID, $branchID, $parent); + $product = $this->loadModel('product')->getById($productID); if($lastPlan) { @@ -83,6 +84,7 @@ class productplan extends control $this->view->productID = $productID; $this->view->lastPlan = $lastPlan; $this->view->branch = $branchID; + $this->view->branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($productID, 'active'); $this->view->parent = $parent; $this->display(); } diff --git a/module/program/model.php b/module/program/model.php index 8ab2027d4a..62762b3917 100644 --- a/module/program/model.php +++ b/module/program/model.php @@ -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') diff --git a/module/project/model.php b/module/project/model.php index c52659a543..ebf1b70f3e 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -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; diff --git a/module/report/config.php b/module/report/config.php index 9bd447e755..ef44d7a72f 100644 --- a/module/report/config.php +++ b/module/report/config.php @@ -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'); diff --git a/module/report/css/annualdata.css b/module/report/css/annualdata.css index 2001f5f069..aad549989b 100644 --- a/module/report/css/annualdata.css +++ b/module/report/css/annualdata.css @@ -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;} diff --git a/module/report/lang/zh-cn.php b/module/report/lang/zh-cn.php index 428efdce3a..b884bf3de9 100644 --- a/module/report/lang/zh-cn.php +++ b/module/report/lang/zh-cn.php @@ -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 = "截止目前项目总览"; diff --git a/module/report/model.php b/module/report/model.php index b2952578a1..668819e56c 100644 --- a/module/report/model.php +++ b/module/report/model.php @@ -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; + } } /** diff --git a/module/stakeholder/model.php b/module/stakeholder/model.php index 18b6e47045..f400c73c09 100644 --- a/module/stakeholder/model.php +++ b/module/stakeholder/model.php @@ -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(); diff --git a/module/story/control.php b/module/story/control.php index b50212f9c3..84ced15b3a 100644 --- a/module/story/control.php +++ b/module/story/control.php @@ -522,7 +522,7 @@ class story extends control $this->view->spec = $spec; $this->view->type = $type; $this->view->branch = $branch; - $this->view->branches = $this->loadModel('branch')->getPairs($productID); + $this->view->branches = $this->loadModel('branch')->getPairs($productID, 'active'); /* When the user is product owner or add story in project or not set review, the default is not to review. */ $this->view->needReview = ($this->app->user->account == $product->PO || $executionID > 0 || $this->config->story->needReview == 0) ? 0 : 1; diff --git a/module/task/control.php b/module/task/control.php index dd882c45c4..6b3a8016bd 100644 --- a/module/task/control.php +++ b/module/task/control.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); diff --git a/module/task/model.php b/module/task/model.php index 6b7616902c..5be7174d2b 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -22,7 +22,6 @@ class taskModel extends model */ public function create($executionID) { - if($this->post->estimate < 0) { dao::$errors[] = $this->lang->task->error->recordMinus; diff --git a/module/testcase/control.php b/module/testcase/control.php index 1acfb49869..d1d80274ea 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -448,6 +448,7 @@ class testcase extends control $stories = $this->story->getExecutionStoryPairs($projectID, $productID, $branch, $modules); } if($storyID and !isset($stories[$storyID])) $stories = $this->story->formatStories(array($storyID => $story)) + $stories;//Fix bug #2406. + $productInfo = $this->loadModel('product')->getById($productID); /* Set custom. */ foreach(explode(',', $this->config->testcase->customCreateFields) as $field) $customFields[$field] = $this->lang->testcase->$field; @@ -473,6 +474,7 @@ class testcase extends control $this->view->projectID = isset($projectID) ? $projectID : 0; $this->view->productID = $productID; $this->view->executionID = $executionID; + $this->view->productInfo = $productInfo; $this->view->productName = $this->products[$productID]; $this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch === 'all' ? 0 : $branch); $this->view->currentModuleID = $currentModuleID; @@ -1383,7 +1385,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; @@ -1444,7 +1446,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']; diff --git a/module/testcase/model.php b/module/testcase/model.php index 3a0186770e..c8e4a53949 100644 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -307,12 +307,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) @@ -1536,7 +1551,7 @@ class testcaseModel extends model break; case 'title': $showBranch = isset($this->config->testcase->browse->showBranch) ? $this->config->testcase->browse->showBranch : 1; - if(isset($branches[$case->branch]) and $showBranch) echo "{$branches[$case->branch]} "; + if(isset($branches[$case->branch]) and $showBranch) echo "{$branches[$case->branch]} "; if($modulePairs and $case->module and isset($modulePairs[$case->module])) echo "{$modulePairs[$case->module]} "; echo $canView ? ($fromCaseID ? html::a($caseLink, $case->title, null, "style='color: $case->color' data-app='{$this->app->tab}'") . html::a(helper::createLink('testcase', 'view', "caseID=$fromCaseID"), "[#$fromCaseID]", '', "data-app='{$this->app->tab}'") : html::a($caseLink, $case->title, null, "style='color: $case->color' data-app='{$this->app->tab}'")) : "$case->title"; break; @@ -1721,6 +1736,13 @@ class testcaseModel extends model return false; } + /** + * Summary cases + * + * @param array $cases + * @access public + * @return string + */ public function summary($cases) { $executed = 0; diff --git a/module/testtask/control.php b/module/testtask/control.php index b3b8d0bcc9..6cbf9bcdb2 100644 --- a/module/testtask/control.php +++ b/module/testtask/control.php @@ -1255,7 +1255,7 @@ class testtask extends control { $this->loadModel('qa')->setMenu($this->products, $productID, $taskID); } - $this->view->moduleOptionMenu = $this->loadModel('tree')->getOptionMenu($productID, 'case'); + $this->view->moduleOptionMenu = $this->loadModel('tree')->getOptionMenu($productID, 'case', 0, 'all'); $cases = $this->dao->select('*')->from(TABLE_CASE)->where('id')->in($caseIDList)->fetchAll('id'); } diff --git a/module/todo/control.php b/module/todo/control.php index 0746c82942..d8dcb8f1c3 100644 --- a/module/todo/control.php +++ b/module/todo/control.php @@ -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')); } diff --git a/module/todo/lang/zh-cn.php b/module/todo/lang/zh-cn.php index f709d457b2..400422870f 100644 --- a/module/todo/lang/zh-cn.php +++ b/module/todo/lang/zh-cn.php @@ -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'] = '未完'; diff --git a/module/user/lang/zh-cn.php b/module/user/lang/zh-cn.php index 2c4e33c843..b221554de6 100644 --- a/module/user/lang/zh-cn.php +++ b/module/user/lang/zh-cn.php @@ -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 = <<
pri?>' title='testcase->priList, $case->pri, $case->pri);?>'>testcase->priList, $case->pri, $case->pri)?> createLink('testcase', 'view', $params, '', '', $case->project), $case->title, null, "style='color: $case->color' data-app='{$this->app->tab}'");?>createLink('testcase', 'view', $params, '', '', $case->project), $case->title, null, "style='color: $case->color' data-app='{$this->app->tab}'");?> testcase->typeList, $case->type);?> openedBy);?> lastRunner);?>
-
不能创建临时目录,请确认目录%s是否存在并有操作权限。
+
不能创建临时目录,请确认目录%s是否存在并有操作权限。
Can't create tmp directory, make sure the directory %s exists and has permission to operate.
EOT; $lang->user->mkdirLinux = <<
-
不能创建临时目录,请确认目录%s是否存在并有操作权限。
+
不能创建临时目录,请确认目录%s是否存在并有操作权限。
命令为:chmod o=rwx -R %s。
Can't create tmp directory, make sure the directory %s exists and has permission to operate.
Commond: chmod o=rwx -R %s.
EOT; + +$lang->user->zentaoapp = new stdclass(); +$lang->user->zentaoapp->logout = '退出登录'; diff --git a/module/user/model.php b/module/user/model.php index 3533390f11..c79cc18c01 100644 --- a/module/user/model.php +++ b/module/user/model.php @@ -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) diff --git a/test/api/project/get.php b/test/api/project/get.php new file mode 100644 index 0000000000..677e970957 --- /dev/null +++ b/test/api/project/get.php @@ -0,0 +1,23 @@ +#!/usr/bin/env php +> 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的项目 diff --git a/test/api/projects/get.php b/test/api/projects/get.php new file mode 100644 index 0000000000..70367f0912 --- /dev/null +++ b/test/api/projects/get.php @@ -0,0 +1,18 @@ +#!/usr/bin/env php +> 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字段 diff --git a/test/api/projects/post.php b/test/api/projects/post.php new file mode 100644 index 0000000000..3c8222de44 --- /dev/null +++ b/test/api/projects/post.php @@ -0,0 +1,61 @@ +#!/usr/bin/env php +> `『项目名称』` +创建失败,没有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』`'); // 创建失败,获取错误信息