From b559be298dbf4cb4d07c0faaceaa301675399aa9 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Fri, 5 May 2023 15:47:36 +0800 Subject: [PATCH 01/15] * adjust code for getProjectStatsByProduct method in productModel. --- module/product/control.php | 4 +- module/product/model.php | 62 +++---------------- module/product/zen.php | 20 +++--- module/program/model.php | 122 ++++++++++++++++++------------------- 4 files changed, 83 insertions(+), 125 deletions(-) diff --git a/module/product/control.php b/module/product/control.php index 524b2b87d5..2b0a0ba872 100755 --- a/module/product/control.php +++ b/module/product/control.php @@ -383,7 +383,7 @@ class product extends control if(!empty($_POST)) { $data = form::data($this->config->product->form->create); - $product = $this->productZen->prepareCreateExtras($data); + $product = $this->productZen->prepareCreateExtras($data, $this->post->acl, $this->post->uid); $productID = $this->product->create($product, $this->post->uid, zget($_POST, 'lineName', '')); if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -414,7 +414,7 @@ class product extends control if(!empty($_POST)) { $data = form::data($this->config->product->form->edit); - $product = $this->productZen->prepareEditExtras($data); + $product = $this->productZen->prepareEditExtras($data, $this->post->acl, $this->post->uid); $changes = $this->product->update($productID, $product, $this->post->uid); if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError())); diff --git a/module/product/model.php b/module/product/model.php index d48c7c96c5..f8a7958f8e 100755 --- a/module/product/model.php +++ b/module/product/model.php @@ -1214,7 +1214,7 @@ class productModel extends model * @param string $orderBy * @param object $pager * @access public - * @return array + * @return int[] */ public function getProjectStatsByProduct(int $productID, string $browseType = 'all', string $branch = '0', bool $involved = false, string $orderBy = 'order_desc', object|null $pager = null): array { @@ -1223,8 +1223,6 @@ class productModel extends model $projectKeys = array_keys($projects); $stats = array(); - $hours = array(); - $emptyHour = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'progress' => 0); /* Get all tasks and compute totalEstimate, totalConsumed, totalLeft, progress according to them. */ $tasks = $this->dao->select('id, project, estimate, consumed, `left`, status, closedReason') @@ -1233,59 +1231,17 @@ class productModel extends model ->andWhere('parent')->lt(1) ->andWhere('deleted')->eq(0) ->fetchGroup('project', 'id'); - - /* Compute totalEstimate, totalConsumed, totalLeft. */ - foreach($tasks as $projectID => $projectTasks) - { - $hour = (object)$emptyHour; - foreach($projectTasks as $task) - { - if($task->status != 'cancel') - { - $hour->totalEstimate += $task->estimate; - $hour->totalConsumed += $task->consumed; - } - if($task->status != 'cancel' and $task->status != 'closed') $hour->totalLeft += $task->left; - } - $hours[$projectID] = $hour; - } - - /* Compute totalReal and progress. */ - $progressList = $this->loadModel('project')->getWaterfallProgress(array_keys($hours)); - foreach($hours as $projectID => $hour) - { - $hour->totalEstimate = round($hour->totalEstimate, 1) ; - $hour->totalConsumed = round($hour->totalConsumed, 1); - $hour->totalLeft = round($hour->totalLeft, 1); - $hour->totalReal = $hour->totalConsumed + $hour->totalLeft; - $hour->progress = $projects[$projectID]->model == 'waterfall' ? $progressList[$projectID] : ($hour->totalReal ? round($hour->totalConsumed / $hour->totalReal, 2) * 100 : 0); - } + $hours = $this->loadModel('program')->computeProjectHours($tasks); /* Get the number of project teams. */ - $teams = $this->dao->select('root,count(*) as teams')->from(TABLE_TEAM) - ->where('root')->in($projectKeys) - ->andWhere('type')->eq('project') - ->groupBy('root') - ->fetchAll('root'); + $teams = $this->dao->select('t1.root,t1.account')->from(TABLE_TEAM)->alias('t1') + ->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account') + ->where('t1.root')->in($projectKeys) + ->andWhere('t1.type')->eq('project') + ->andWhere('t2.deleted')->eq(0) + ->fetchGroup('root', 'account'); - /* Process projects. */ - foreach($projects as $key => $project) - { - if($project->end == '0000-00-00') $project->end = ''; - - /* Judge whether the project is delayed. */ - if($project->status != 'done' and $project->status != 'closed' and $project->status != 'suspended') - { - $delay = helper::diffDate(helper::today(), $project->end); - if($delay > 0) $project->delay = $delay; - } - - /* Process the hours. */ - $project->hours = isset($hours[$project->id]) ? $hours[$project->id] : (object)$emptyHour; - - $project->teamCount = isset($teams[$project->id]) ? $teams[$project->id]->teams : 0; - $stats[] = $project; - } + $stats = $this->program->appendStatToProjects($projects, 'hours,teamCount', array('hours' => $hours, 'teams' => $teams)); return $stats; } diff --git a/module/product/zen.php b/module/product/zen.php index 70a270f87d..415cd0fde5 100644 --- a/module/product/zen.php +++ b/module/product/zen.php @@ -407,38 +407,42 @@ class productZen extends product * 追加创建信息,处理白名单、项目集字段,还有富文本内容处理。 * Prepare data for create. * - * @param form $data + * @param form $data + * @param string $acl + * @param string $uid * @access protected * @return object */ - protected function prepareCreateExtras(form $data): object + protected function prepareCreateExtras(form $data, string $acl, string $uid = ''): object { $product = $data->setDefault('createdBy', $this->app->user->account) ->setDefault('createdDate', helper::now()) ->setDefault('createdVersion', $this->config->version) ->setIF($this->config->systemMode == 'light', 'program', (int)zget($this->config->global, 'defaultProgram', 0)) - ->setIF($data->rawdata->acl == 'open', 'whitelist', '') + ->setIF($acl == 'open', 'whitelist', '') ->stripTags($this->config->product->editor->create['id'], $this->config->allowedTags) ->get(); - return $this->loadModel('file')->processImgURL($product, $this->config->product->editor->create['id'], $data->rawdata->uid); + return $this->loadModel('file')->processImgURL($product, $this->config->product->editor->create['id'], $uid); } /** * 处理白名单和富文本内容。 * Prepare data for edit. * - * @param form $data + * @param form $data + * @param string $acl + * @param string $uid * @access protected * @return object */ - protected function prepareEditExtras(form $data): object + protected function prepareEditExtras(form $data, string $acl, string $uid = ''): object { - $product = fixer::input('post')->setIF($data->rawdata->acl == 'open', 'whitelist', '') + $product = fixer::input('post')->setIF($acl == 'open', 'whitelist', '') ->stripTags($this->config->product->editor->edit['id'], $this->config->allowedTags) ->get(); - return $this->loadModel('file')->processImgURL($product, $this->config->product->editor->edit['id'], zget($data->rawdata, 'uid', '')); + return $this->loadModel('file')->processImgURL($product, $this->config->product->editor->edit['id'], $uid); } /** diff --git a/module/program/model.php b/module/program/model.php index b3da73ba3e..0be3f59eee 100644 --- a/module/program/model.php +++ b/module/program/model.php @@ -344,7 +344,7 @@ class programModel extends model ->andWhere('deleted')->eq(0) ->fetchGroup('execution', 'id'); - $hours = $this->computeProgress($tasks); + $hours = $this->computeProjectHours($tasks); $projectHours = $this->getProgressList(); /* Group data by product. */ @@ -517,22 +517,25 @@ class programModel extends model } /** - * Compute progress for project or execution. + * 根据按照项目分组的任务,统计各个项目的工时和进度 + * Compute hours and progress for project or execution. * * @param array $tasks * @access public - * @return void + * @return array */ - public function computeProgress($tasks) + public function computeProjectHours(array $tasks): array { $hours = array(); foreach($tasks as $projectID => $projectTasks) { + /* Init hour. */ $hour = new stdclass(); $hour->totalConsumed = 0; $hour->totalEstimate = 0; $hour->totalLeft = 0; + /* Compute totalEstimate, totalConsumed, totalLeft to them. */ foreach($projectTasks as $task) { $hour->totalConsumed += $task->consumed; @@ -542,18 +545,64 @@ class programModel extends model $hours[$projectID] = $hour; } - foreach($hours as $hour) + /* 计算进度。四舍五入totalEstimate, totalConsumed, totalLeft. */ + $progressList = $this->loadModel('project')->getWaterfallProgress(array_keys($hours)); + foreach($hours as $projectID => $hour) { $hour->totalEstimate = round($hour->totalEstimate, 1) ; $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, 2) * 100 : 0; + $hour->progress = zget($progressList, $projectID, ($hour->totalReal ? round($hour->totalConsumed / $hour->totalReal, 2) * 100 : 0)) ; } return $hours; } + /** + * 将工时、团队人数、剩余任务数、团队成员等统计信息,追加到对应的项目中。 + * Append statistics fields to projects. + * + * @param array $projects + * @param string $appendFields hours,teamCount,leftTasks,teamMembers + * @param array $data array keys are hours, teams and leftTasks. + * @access public + * @return array + */ + public function appendStatToProjects(array $projects, string $appendFields = '', array $data = array()): array + { + $appendFields = explode(',', $appendFields); + $emptyHour = json_decode(json_encode(array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'progress' => 0))); + /* Process projects. */ + $stats = array(); + foreach($projects as $projectID => $project) + { + if(helper::isZeroDate($project->end)) $project->end = ''; + + /* Judge whether the project is delayed. */ + if($project->status != 'done' and $project->status != 'closed' and $project->status != 'suspended') + { + $delay = empty($project->end) ? 0 : helper::diffDate(helper::today(), $project->end); + if($delay > 0) $project->delay = $delay; + } + + /* Merge the hours. */ + if(in_array('hours', $appendFields)) + { + $project->hours = $emptyHour; + if(isset($data['hours'])) $project->hours = zget($data['hours'], $project->id, $emptyHour); + } + + /* Merge the team and left tasks. */ + if(in_array('teamCount', $appendFields)) $project->teamCount = isset($data['teams'][$project->id]) ? count($data['teams'][$project->id]) : 0; + if(in_array('teamMembers', $appendFields)) $project->teamMembers = isset($data['teams'][$project->id]) ? array_keys($data['teams'][$project->id]) : array(); + if(in_array('leftTasks', $appendFields)) $project->leftTasks = isset($data['leftTasks'][$project->id]) ? $data['leftTasks'][$project->id]->tasks : '—'; + + $stats[$projectID] = $project; + } + return $stats; + } + /** * Get project list data. * @@ -1418,15 +1467,10 @@ class programModel extends model /* Init vars. */ $projects = $this->getProjectList($programID, $browseType, $queryID, $orderBy, $pager, $programTitle, $involved, $queryAll); - if(empty($projects)) return array(); $projectKeys = array_keys($projects); - $stats = array(); - $hours = array(); - $emptyHour = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'progress' => 0); $leftTasks = array(); - $teamMembers = array(); $executions = $this->dao->select('id')->from(TABLE_EXECUTION) ->where('deleted')->eq(0) @@ -1438,37 +1482,11 @@ class programModel extends model $tasks = $this->dao->select('id, project, estimate, consumed, `left`, status, closedReason, execution') ->from(TABLE_TASK) ->where('project')->in($projectKeys) + ->andWhere('execution')->in(array_keys($executions)) ->andWhere('parent')->lt(1) ->andWhere('deleted')->eq(0) ->fetchGroup('project', 'id'); - - /* Compute totalEstimate, totalConsumed, totalLeft. */ - foreach($tasks as $projectID => $projectTasks) - { - $hour = (object)$emptyHour; - foreach($projectTasks as $task) - { - if(isset($executions[$task->execution])) - { - $hour->totalConsumed += $task->consumed; - $hour->totalEstimate += $task->estimate; - - if(strpos('cancel,closed', $task->status) === false) $hour->totalLeft += $task->left; - } - } - $hours[$projectID] = $hour; - } - - /* Compute totalReal and progress. */ - $progressList = $this->loadModel('project')->getWaterfallProgress(array_keys($hours)); - foreach($hours as $projectID => $hour) - { - $hour->totalEstimate = round($hour->totalEstimate, 1) ; - $hour->totalConsumed = round($hour->totalConsumed, 1); - $hour->totalLeft = round($hour->totalLeft, 1); - $hour->totalReal = $hour->totalConsumed + $hour->totalLeft; - $hour->progress = $projects[$projectID]->model == 'waterfall' ? $progressList[$projectID] : ($hour->totalReal ? round($hour->totalConsumed / $hour->totalReal, 2) * 100 : 0); - } + $hours = $this->computeProjectHours($tasks); /* Get the number of left tasks. */ if($this->cookie->projectType and $this->cookie->projectType == 'bycard') @@ -1489,30 +1507,10 @@ class programModel extends model ->andWhere('t2.deleted')->eq(0) ->fetchGroup('root', 'account'); - /* Process projects. */ - foreach($projects as $key => $project) - { - if($project->end == '0000-00-00') $project->end = ''; + $stats = $this->appendStatToProjects($projects, 'hours,teamCount,teamMembers,leftTasks', array('hours' => $hours, 'teams' => $teamMembers, 'leftTasks' => $leftTasks)); + /* Convert predefined HTML entities to characters. */ + foreach($stats as $project) $project->name = htmlspecialchars_decode($project->name, ENT_QUOTES); - /* Judge whether the project is delayed. */ - if($project->status != 'done' and $project->status != 'closed' and $project->status != 'suspended') - { - $delay = helper::diffDate(helper::today(), $project->end); - if($delay > 0) $project->delay = $delay; - } - - /* Process the hours. */ - $project->hours = isset($hours[$project->id]) ? $hours[$project->id] : (object)$emptyHour; - - $project->teamCount = isset($teamMembers[$project->id]) ? count($teamMembers[$project->id]) : 0; - $project->leftTasks = isset($leftTasks[$project->id]) ? $leftTasks[$project->id]->tasks : '—'; - $project->teamMembers = isset($teamMembers[$project->id]) ? array_keys($teamMembers[$project->id]) : array(); - - /* Convert predefined HTML entities to characters. */ - $project->name = htmlspecialchars_decode($project->name, ENT_QUOTES); - - $stats[$key] = $project; - } return $stats; } From cec69298313a19f18456d24dba3d1f6be8a8ad95 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Fri, 5 May 2023 15:53:51 +0800 Subject: [PATCH 02/15] * code for sonar. --- module/program/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/program/model.php b/module/program/model.php index 0be3f59eee..40b7594511 100644 --- a/module/program/model.php +++ b/module/program/model.php @@ -1776,7 +1776,7 @@ class programModel extends model $actionsMap[] = $item; } - if($canSuspendProgram or ($canClose && $program->status != 'doing') or ($canActivateProgram and $program->status != 'closed')) + if($canSuspendProgram or ($canCloseProgram and $program->status != 'doing') or ($canActivateProgram and $program->status != 'closed')) { $other = new stdclass(); $other->name = 'other'; From 6adce696c69f15f81e507642e19cbd3f7c6343ae Mon Sep 17 00:00:00 2001 From: Wangyuting <851424971@qq.com> Date: Fri, 5 May 2023 18:05:43 +0800 Subject: [PATCH 03/15] * Optimize block contorl code. --- module/block/control.php | 1993 +---------------- module/block/css/projectstatisticblock.ui.css | 1 + .../block/ui/projectstatisticblock.html.php | 342 ++- .../block/view/projectstatisticblock.html.php | 1 - module/block/zen.php | 1683 ++++++++++++++ 5 files changed, 2114 insertions(+), 1906 deletions(-) create mode 100644 module/block/css/projectstatisticblock.ui.css diff --git a/module/block/control.php b/module/block/control.php index 76cddc6860..ede59744f9 100755 --- a/module/block/control.php +++ b/module/block/control.php @@ -160,1877 +160,6 @@ class block extends control return $this->send(array('result' => 'success')); } - /** - * 展示应用的仪表盘 - * Display dashboard for app. - * - * @param string $dashboard - * @param string $projectID - * @access public - * @return void - */ - public function dashboard(string $dashboard, string $projectID = '0') - { - $blocks = $this->block->getMyDashboard($dashboard); - $projectID = (int)$projectID; - - $isInitiated = $this->block->fetchBlockInitStatus($dashboard); - - /* Init block when vist index first. */ - if(empty($blocks) and !$isInitiated and !defined('TUTORIAL') and $this->block->initBlock($dashboard)) - { - return print(js::reload()); - } - - $blocks = $this->blockZen->processBlockForRender($blocks, $projectID); - - if($this->app->getViewType() == 'json') return print(json_encode($blocks)); - - list($shortBlocks, $longBlocks) = $this->blockZen->splitBlocksByLen($blocks); - - $this->view->title = zget($this->lang->block->dashboard, $dashboard, $this->lang->block->dashboard['default']); - $this->view->longBlocks = $longBlocks; - $this->view->shortBlocks = $shortBlocks; - $this->view->dashboard = $dashboard; - $this->render(); - } - - /** - * latest dynamic. - * - * @access public - * @return void - */ - public function printDynamicBlock() - { - /* Load pager. */ - $this->app->loadClass('pager', $static = true); - $pager = new pager(0, 30, 1); - - $this->view->actions = $this->loadModel('action')->getDynamic('all', 'today', 'date_desc', $pager); - $this->view->users = $this->loadModel('user')->getPairs('nodeleted|noletter|all'); - } - - /** - * Welcome block. - * - * @access public - * @return void - */ - public function printWelcomeBlock() - { - $this->view->tutorialed = $this->loadModel('tutorial')->getTutorialed(); - - $data = $this->block->getWelcomeBlockData(); - - $this->view->tasks = $data['tasks']; - $this->view->doneTasks = $data['doneTasks']; - $this->view->bugs = $data['bugs']; - $this->view->stories = $data['stories']; - $this->view->executions = $data['executions']; - - $this->view->delay['task'] = $data['delayTask']; - $this->view->delay['bug'] = $data['delayBug']; - - $time = date('H:i'); - $welcomeType = '19:00'; - foreach($this->lang->block->welcomeList as $type => $name) - { - if($time >= $type) $welcomeType = $type; - } - $this->view->welcomeType = $welcomeType; - } - - /** - * Print contribute block. - * - * @access public - * @return void - */ - public function contribute() - { - $this->view->data = $this->loadModel('user')->getPersonalData(); - $this->display(); - } - - - /** - * Print block. - * 输出区块 - * - * @param int $blockID - * @access public - * @return string - */ - public function printBlock($blockID) - { - $html = ''; - $blockID = (int)$blockID; - $block = $this->block->getByID($blockID); - - if(empty($block)) return $html; - - $code = $block->code; - if($code == 'statistic' or $code == 'list' or $code == 'overview') $code = $block->module . ucfirst($code); - - $function = 'print' . ucfirst($code) . 'Block'; - if(method_exists('block', $function)) $html = $this->$function($blockID); - - $this->display('block', strtolower($code) . 'block'); - } - - public function printBlockData($module = '', $blockID = 0) - { - $blockID = (int)$blockID; - - if(!$this->selfCall) - { - $lang = str_replace('_', '-', $this->get->lang); - $this->app->setClientLang($lang); - $this->app->loadLang('common'); - $this->app->loadLang('block'); - - if(!$this->block->checkAPI($this->get->hash)) return; - } - - $code = strtolower($this->get->blockid); - - $params = $this->get->param; - $params = json_decode(base64_decode($params)); - if(isset($params->num) and !isset($params->count)) $params->count = $params->num; - if(!$this->selfCall) - { - $this->app->user = $this->dao->select('*')->from(TABLE_USER)->where('ranzhi')->eq($params->account)->fetch(); - if(empty($this->app->user)) - { - $this->app->user = new stdclass(); - $this->app->user->account = 'guest'; - } - $this->app->user->admin = strpos($this->app->company->admins, ",{$this->app->user->account},") !== false; - $this->app->user->rights = $this->loadModel('user')->authorize($this->app->user->account); - $this->app->user->groups = $this->user->getGroups($this->app->user->account); - $this->app->user->view = $this->user->grantUserView($this->app->user->account, $this->app->user->rights['acls']); - - $sso = base64_decode($this->get->sso); - $this->view->sso = $sso; - $this->view->sign = strpos($sso, '?') === false ? '?' : '&'; - } - - if($blockID) $block = $this->block->getByID($blockID); - $this->view->longBlock = $this->block->isLongBlock($blockID ? $block : $params); - $this->view->selfCall = $this->selfCall; - $this->view->block = $blockID ? $block : ''; - - $this->viewType = (isset($params->viewType) and $params->viewType == 'json') ? 'json' : 'html'; - $this->params = $params; - $this->view->code = $this->get->blockid; - $this->view->title = $this->get->blockTitle; - - $func = 'print' . ucfirst($code) . 'Block'; - if(method_exists('block', $func)) - { - $this->$func($module); - } - else - { - $this->view->data = $this->block->$func($module, $params); - } - - $this->view->moreLink = ''; - if(isset($this->config->block->modules[$module]->moreLinkList->{$code})) - { - list($moduleName, $method, $vars) = explode('|', sprintf($this->config->block->modules[$module]->moreLinkList->{$code}, isset($params->type) ? $params->type : '')); - $this->view->moreLink = $this->createLink($moduleName, $method, $vars); - } - - if($this->viewType == 'json') - { - unset($this->view->app); - unset($this->view->config); - unset($this->view->lang); - unset($this->view->header); - unset($this->view->position); - unset($this->view->moduleTree); - - $output['status'] = is_object($this->view) ? 'success' : 'fail'; - $output['data'] = json_encode($this->view); - $output['md5'] = md5(json_encode($this->view)); - return print(json_encode($output)); - } - - $this->display('block', $code . 'block'); - } - - /** - * Print List block. - * - * @access public - * @return void - */ - public function printListBlock($module = 'product') - { - $func = 'print' . ucfirst($module) . 'Block'; - $this->view->module = $module; - $this->$func(); - - } - - /** - * Print todo block. - * - * @access public - * @return void - */ - public function printTodoBlock() - { - $limit = ($this->viewType == 'json' or !isset($this->params->count)) ? 0 : (int)$this->params->count; - $todos = $this->loadModel('todo')->getList('all', $this->app->user->account, 'wait, doing', $limit, null, 'date, begin'); - $uri = $this->createLink('my', 'index'); - - $this->session->set('todoList', $uri, 'my'); - $this->session->set('bugList', $uri, 'qa'); - $this->session->set('taskList', $uri, 'execution'); - $this->session->set('storyList', $uri, 'product'); - $this->session->set('testtaskList', $uri, 'qa'); - - $todos = $this->blockZen->unsetTodos($todos); - - $this->view->todos = $todos; - } - - /** - * Print task block. - * - * @access public - * @return void - */ - public function printTaskBlock() - { - $this->session->set('taskList', $this->createLink('my', 'index'), 'execution'); - if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - $account = $this->app->user->account; - $type = $this->params->type; - - $this->app->loadLang('execution'); - $this->view->tasks = $this->loadModel('task')->getUserTasks($account, $type, $this->viewType == 'json' ? 0 : (int)$this->params->count, null, $this->params->orderBy); - } - - /** - * Print bug block. - * - * @access public - * @return void - */ - public function printBugBlock() - { - $this->session->set('bugList', $this->createLink('my', 'index'), 'qa'); - if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - $projectID = $this->lang->navGroup->qa == 'project' ? $this->session->project : 0; - $projectID = $this->view->block->module == 'my' ? 0 : $projectID; - $this->view->bugs = $this->loadModel('bug')->getUserBugs($this->app->user->account, $this->params->type, $this->params->orderBy, $this->viewType == 'json' ? 0 : (int)$this->params->count, null, $projectID); - } - - /** - * Print case block. - * - * @access public - * @return void - */ - public function printCaseBlock() - { - $this->session->set('caseList', $this->createLink('my', 'index'), 'qa'); - $this->app->loadLang('testcase'); - $this->app->loadLang('testtask'); - - $projectID = $this->lang->navGroup->qa == 'project' ? $this->session->project : 0; - $projectID = $this->view->block->module == 'my' ? 0 : $projectID; - - $cases = array(); - if($this->params->type == 'assigntome') - { - $cases = $this->dao->select('t1.assignedTo AS assignedTo, t2.*')->from(TABLE_TESTRUN)->alias('t1') - ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id') - ->leftJoin(TABLE_TESTTASK)->alias('t3')->on('t1.task = t3.id') - ->Where('t1.assignedTo')->eq($this->app->user->account) - ->andWhere('t1.status')->ne('done') - ->andWhere('t3.status')->ne('done') - ->andWhere('t3.deleted')->eq(0) - ->andWhere('t2.deleted')->eq(0) - ->beginIF($projectID)->andWhere('t2.project')->eq($projectID)->fi() - ->orderBy($this->params->orderBy) - ->beginIF($this->viewType != 'json')->limit((int)$this->params->count)->fi() - ->fetchAll(); - } - elseif($this->params->type == 'openedbyme') - { - $cases = $this->dao->findByOpenedBy($this->app->user->account)->from(TABLE_CASE) - ->andWhere('deleted')->eq(0) - ->beginIF($projectID)->andWhere('project')->eq($projectID)->fi() - ->orderBy($this->params->orderBy) - ->beginIF($this->viewType != 'json')->limit((int)$this->params->count)->fi() - ->fetchAll(); - } - $this->view->cases = $cases; - } - - /** - * Print testtask block. - * - * @access public - * @return void - */ - public function printTesttaskBlock() - { - $this->app->loadLang('testtask'); - - $uri = $this->createLink('my', 'index'); - $this->session->set('productList', $uri, 'product'); - $this->session->set('testtaskList', $uri, 'qa'); - $this->session->set('buildList', $uri, 'execution'); - if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - $this->view->projects = $this->loadModel('project')->getPairsByProgram(); - $this->view->testtasks = $this->dao->select('t1.*,t2.name as productName,t2.shadow,t3.name as buildName,t4.name as projectName')->from(TABLE_TESTTASK)->alias('t1') - ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') - ->leftJoin(TABLE_BUILD)->alias('t3')->on('t1.build=t3.id') - ->leftJoin(TABLE_PROJECT)->alias('t4')->on('t1.execution=t4.id') - ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t5')->on('t1.execution=t5.project') - ->where('t1.deleted')->eq('0') - ->beginIF(!$this->app->user->admin)->andWhere('t1.product')->in($this->app->user->view->products)->fi() - ->beginIF(!$this->app->user->admin)->andWhere('t1.execution')->in($this->app->user->view->sprints)->fi() - ->andWhere('t1.product = t5.product') - ->beginIF($this->params->type != 'all')->andWhere('t1.status')->eq($this->params->type)->fi() - ->orderBy('t1.id desc') - ->beginIF($this->viewType != 'json')->limit((int)$this->params->count)->fi() - ->fetchAll(); - } - - /** - * Print story block. - * - * @access public - * @return void - */ - public function printStoryBlock() - { - $this->session->set('storyList', $this->createLink('my', 'index'), 'product'); - if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - $this->app->loadClass('pager', true); - $count = isset($this->params->count) ? (int)$this->params->count : 0; - $pager = pager::init(0, $count , 1); - $type = isset($this->params->type) ? $this->params->type : 'assignedTo'; - $orderBy = isset($this->params->type) ? $this->params->orderBy : 'id_asc'; - - $this->view->stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $orderBy, $this->viewType != 'json' ? $pager : '', 'story'); - } - - /** - * Print plan block. - * - * @access public - * @return void - */ - public function printPlanBlock() - { - $uri = $this->createLink('my', 'index'); - $this->session->set('productList', $uri, 'product'); - $this->session->set('productPlanList', $uri, 'product'); - - $this->app->loadLang('productplan'); - $this->view->plans = $this->dao->select('t1.*,t2.name as productName')->from(TABLE_PRODUCTPLAN)->alias('t1') - ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') - ->where('t1.deleted')->eq('0') - ->andWhere('t2.shadow')->eq(0) - ->beginIF(!$this->app->user->admin)->andWhere('t1.product')->in($this->app->user->view->products)->fi() - ->orderBy('t1.begin desc') - ->beginIF($this->viewType != 'json')->limit((int)$this->params->count)->fi() - ->fetchAll(); - } - - /** - * Print releases block. - * - * @access public - * @return void - */ - public function printReleaseBlock() - { - $uri = $this->createLink('my', 'index'); - $this->session->set('releaseList', $uri, 'product'); - $this->session->set('buildList', $uri, 'execution'); - - $this->app->loadLang('release'); - $this->view->releases = $this->dao->select('t1.*,t2.name as productName,t3.name as buildName')->from(TABLE_RELEASE)->alias('t1') - ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') - ->leftJoin(TABLE_BUILD)->alias('t3')->on('t1.build=t3.id') - ->where('t1.deleted')->eq('0') - ->andWhere('t2.shadow')->eq(0) - ->beginIF($this->view->block->module != 'my' and $this->session->project)->andWhere('t1.project')->eq((int)$this->session->project)->fi() - ->beginIF(!$this->app->user->admin)->andWhere('t1.product')->in($this->app->user->view->products)->fi() - ->orderBy('t1.id desc') - ->beginIF($this->viewType != 'json')->limit((int)$this->params->count)->fi() - ->fetchAll(); - } - - /** - * Print Build block. - * - * @access public - * @return void - */ - public function printBuildBlock() - { - $this->session->set('buildList', $this->createLink('my', 'index'), 'execution'); - $this->app->loadLang('build'); - - $builds = $this->dao->select('t1.*, t2.name AS productName, t2.shadow, t3.name AS projectName')->from(TABLE_BUILD)->alias('t1') - ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') - ->leftJoin(TABLE_PROJECT)->alias('t3')->on('t1.project=t3.id') - ->where('t1.deleted')->eq('0') - ->beginIF(!$this->app->user->admin)->andWhere('t1.execution')->in($this->app->user->view->sprints)->fi() - ->beginIF($this->view->block->module != 'my' and $this->session->project)->andWhere('t1.project')->eq((int)$this->session->project)->fi() - ->orderBy('t1.id desc') - ->beginIF($this->viewType != 'json')->limit((int)$this->params->count)->fi() - ->fetchAll(); - $this->view->builds = $builds; - } - - /** - * Print project block. - * - * @access public - * @return void - */ - public function printProjectBlock() - { - $this->app->loadLang('execution'); - $this->app->loadLang('task'); - $count = isset($this->params->count) ? $this->params->count : 15; - $type = isset($this->params->type) ? $this->params->type : 'all'; - $orderBy = isset($this->params->orderBy) ? $this->params->orderBy : 'id_desc'; - - $this->view->projects = $this->loadModel('project')->getOverviewList('byStatus', $type, $orderBy, $count); - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - } - - /** - * Print product block. - * - * @access public - * @return void - */ - public function printProductBlock() - { - $this->app->loadClass('pager', true); - if(!empty($this->params->type) and preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - $count = isset($this->params->count) ? (int)$this->params->count : 0; - $type = isset($this->params->type) ? $this->params->type : ''; - $pager = pager::init(0, $count , 1); - - $productStats = $this->loadModel('product')->getStats('order_desc', $this->viewType != 'json' ? $pager : '', $type); - $productIdList = array(); - foreach($productStats as $product) $productIdList[] = $product->id; - - $this->app->loadLang('project'); - $executions = $this->dao->select('t1.product,t2.id,t2.project,t2.name,t2.multiple')->from(TABLE_PROJECTPRODUCT)->alias('t1') - ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id') - ->where('t1.product')->in($productIdList) - ->andWhere('t2.type')->in('stage,sprint') - ->andWhere('t2.deleted')->eq(0) - ->orderBy('t1.project') - ->fetchAll('product'); - - $executionPairs = array(); - $noMultiples = array(); - foreach($executions as $execution) - { - if(empty($execution->multiple)) $noMultiples[$execution->product] = $execution->project; - $executionPairs[$execution->product] = $execution->name; - } - if($noMultiples) - { - $noMultipleProjects = $this->dao->select('id,name')->from(TABLE_PROJECT)->where('id')->in($noMultiples)->fetchPairs('id', 'name'); - foreach($noMultiples as $productID => $projectID) - { - if(isset($noMultipleProjects[$projectID])) $executionPairs[$productID] = $noMultipleProjects[$projectID] . "({$this->lang->project->disableExecution})"; - } - } - - $this->view->executions = $executionPairs; - $this->view->productStats = $productStats; - } - - /** - * Print statistic block. - * - * @param string $module - * @access public - * @return void - */ - public function printStatisticBlock($module = 'product') - { - $func = 'print' . ucfirst($module) . 'StatisticBlock'; - $this->view->module = $module; - $this->$func(); - } - - /** - * Print project statistic block. - * - * @access public - * @return void - */ - public function printProjectStatisticBlock() - { - if(!empty($this->params->type) and preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - /* Load models and langs. */ - $this->loadModel('project'); - $this->loadModel('weekly'); - $this->loadModel('execution'); - $this->app->loadLang('task'); - $this->app->loadLang('story'); - $this->app->loadLang('bug'); - - /* Set project status and count. */ - $status = isset($this->params->type) ? $this->params->type : 'all'; - $count = isset($this->params->count) ? (int)$this->params->count : 15; - - /* Get projects. */ - $excludedModel = $this->config->edition == 'max' ? '' : 'waterfall'; - $projects = $this->project->getOverviewList('byStatus', $status, 'order_asc', $count, $excludedModel); - if(empty($projects)) - { - $this->view->projects = $projects; - return false; - } - - $today = helper::today(); - $monday = date('Ymd', strtotime($this->loadModel('weekly')->getThisMonday($today))); - $tasks = $this->dao->select("project, - sum(consumed) as totalConsumed, - sum(if(status != 'cancel' and status != 'closed', `left`, 0)) as totalLeft") - ->from(TABLE_TASK) - ->where('project')->in(array_keys($projects)) - ->andWhere('deleted')->eq(0) - ->andWhere('parent')->lt(1) - ->groupBy('project') - ->fetchAll('project'); - - foreach($projects as $projectID => $project) - { - if(in_array($project->model, array('scrum', 'kanban', 'agileplus'))) - { - $this->app->loadClass('pager', true); - $pager = pager::init(0, 3, 1); - $project->progress = $project->allStories == 0 ? 0 : round($project->doneStories / $project->allStories, 3) * 100; - $project->executions = $this->execution->getStatData($projectID, 'all', 0, 0, false, '', 'id_desc', $pager); - } - elseif(in_array($project->model, array('waterfall', 'waterfallplus'))) - { - $begin = $project->begin; - $weeks = $this->weekly->getWeekPairs($begin); - $current = zget($weeks, $monday, ''); - $current = substr($current, 0, -11) . substr($current, -6); - - $PVEV = $this->weekly->getPVEV($projectID, $today); - $project->pv = $PVEV['PV']; - $project->ev = $PVEV['EV']; - $project->ac = $this->weekly->getAC($projectID, $today); - $project->sv = $this->weekly->getSV($project->ev, $project->pv); - $project->cv = $this->weekly->getCV($project->ev, $project->ac); - - $progress = isset($tasks[$projectID]) ? (($tasks[$projectID]->totalConsumed + $tasks[$projectID]->totalLeft)) ? round($tasks[$projectID]->totalConsumed / ($tasks[$projectID]->totalConsumed + $tasks[$projectID]->totalLeft), 3) * 100 : 0 : 0; - - $project->current = $current; - $project->progress = $progress; - } - } - - $this->view->projects = $projects; - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - } - - /** - * Print product statistic block. - * - * @access public - * @param string $storyType requirement|story - * @return void - */ - public function printProductStatisticBlock($storyType = 'story') - { - if(!empty($this->params->type) and preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - $status = isset($this->params->type) ? $this->params->type : ''; - $count = isset($this->params->count) ? $this->params->count : ''; - - $products = $this->loadModel('product')->getOrderedProducts($status, $count); - $productIdList = array_keys($products); - - if(empty($products)) - { - $this->view->products = $products; - return false; - } - - /* Get stories. */ - $stories = $this->dao->select('product, stage, COUNT(status) AS count')->from(TABLE_STORY) - ->where('deleted')->eq(0) - ->andWhere('product')->in($productIdList) - ->beginIF($storyType)->andWhere('type')->eq($storyType)->fi() - ->groupBy('product, stage') - ->fetchGroup('product', 'stage'); - - /* Padding the stories to sure all status have records. */ - foreach($stories as $product => $story) - { - foreach(array_keys($this->lang->story->stageList) as $stage) - { - $story[$stage] = isset($story[$stage]) ? $story[$stage]->count : 0; - } - $stories[$product] = $story; - } - - /* Get plans. */ - $plans = $this->dao->select('product, end')->from(TABLE_PRODUCTPLAN) - ->where('deleted')->eq(0) - ->andWhere('product')->in($productIdList) - ->fetchGroup('product'); - foreach($plans as $product => $productPlans) - { - $expired = 0; - $unexpired = 0; - - foreach($productPlans as $plan) - { - if($plan->end < helper::today()) $expired++; - if($plan->end >= helper::today()) $unexpired++; - } - - $plan = array(); - $plan['expired'] = $expired; - $plan['unexpired'] = $unexpired; - - $plans[$product] = $plan; - } - - /* Get releases. */ - $releases = $this->dao->select('product, status, COUNT(*) AS count')->from(TABLE_RELEASE) - ->where('deleted')->eq(0) - ->andWhere('product')->in($productIdList) - ->groupBy('product, status') - ->fetchGroup('product', 'status'); - foreach($releases as $product => $release) - { - $release['normal'] = isset($release['normal']) ? $release['normal']->count : 0; - $release['terminate'] = isset($release['terminate']) ? $release['terminate']->count : 0; - - $releases[$product] = $release; - } - - /* Get last releases. */ - $lastReleases = $this->dao->select('product, COUNT(*) AS count')->from(TABLE_RELEASE) - ->where('date')->eq(date('Y-m-d', strtotime('-1 day'))) - ->andWhere('product')->in($productIdList) - ->groupBy('product') - ->fetchPairs(); - - foreach($products as $productID => $product) - { - $product->stories = isset($stories[$productID]) ? $stories[$productID] : 0; - $product->plans = isset($plans[$productID]) ? $plans[$productID] : 0; - $product->releases = isset($releases[$productID]) ? $releases[$productID] : 0; - $product->lastRelease = isset($lastReleases[$productID]) ? $lastReleases[$productID] : 0; - } - - $this->app->loadLang('story'); - $this->app->loadLang('productplan'); - $this->app->loadLang('release'); - - $this->view->products = $products; - } - - /** - * Print execution statistic block. - * - * @access public - * @return void - */ - public function printExecutionStatisticBlock() - { - if(!empty($this->params->type) and preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - $this->app->loadLang('task'); - $this->app->loadLang('story'); - $this->app->loadLang('bug'); - - $status = isset($this->params->type) ? $this->params->type : 'undone'; - $count = isset($this->params->count) ? (int)$this->params->count : 0; - - /* Get projects. */ - $projectID = $this->view->block->module == 'my' ? 0 : (int)$this->session->project; - $executions = $this->loadModel('execution')->getOrderedExecutions($projectID, $status, $count, 'skipparent'); - if(empty($executions)) - { - $this->view->executions = $executions; - return false; - } - - $executionIdList = array_keys($executions); - - /* Get tasks. Fix bug #2918.*/ - $yesterday = date('Y-m-d', strtotime('-1 day')); - $taskGroups = $this->dao->select("id,parent,execution,status,finishedDate,estimate,consumed,`left`")->from(TABLE_TASK) - ->where('execution')->in($executionIdList) - ->andWhere('deleted')->eq(0) - ->fetchGroup('execution', 'id'); - - $tasks = array(); - foreach($taskGroups as $executionID => $taskGroup) - { - $undoneTasks = 0; - $yesterdayFinished = 0; - $totalEstimate = 0; - $totalConsumed = 0; - $totalLeft = 0; - - foreach($taskGroup as $taskID => $task) - { - if(strpos('wait|doing|pause|cancel', $task->status) !== false) $undoneTasks ++; - if(strpos($task->finishedDate, $yesterday) !== false) $yesterdayFinished ++; - - if($task->parent == '-1') continue; - - $totalConsumed += $task->consumed; - $totalEstimate += $task->estimate; - if($task->status != 'cancel' and $task->status != 'closed') $totalLeft += $task->left; - } - - $executions[$executionID]->totalTasks = count($taskGroup); - $executions[$executionID]->undoneTasks = $undoneTasks; - $executions[$executionID]->yesterdayFinished = $yesterdayFinished; - $executions[$executionID]->totalEstimate = round($totalEstimate, 1); - $executions[$executionID]->totalConsumed = round($totalConsumed, 1); - $executions[$executionID]->totalLeft = round($totalLeft, 1); - } - - /* Get stories. */ - $stories = $this->dao->select("t1.project, count(t2.status) as totalStories, count(t2.status != 'closed' or null) as unclosedStories, count(t2.stage = 'released' or null) as releasedStories")->from(TABLE_PROJECTSTORY)->alias('t1') - ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id') - ->where('t1.project')->in($executionIdList) - ->andWhere('t2.deleted')->eq(0) - ->groupBy('project') - ->fetchAll('project'); - - foreach($stories as $executionID => $story) - { - foreach($story as $key => $value) - { - if($key == 'project') continue; - $executions[$executionID]->$key = $value; - } - } - - /* Get bugs. */ - $bugs = $this->dao->select("execution, count(status) as totalBugs, count(status = 'active' or null) as activeBugs, count(resolvedDate like '{$yesterday}%' or null) as yesterdayResolved")->from(TABLE_BUG) - ->where('execution')->in($executionIdList) - ->andWhere('deleted')->eq(0) - ->groupBy('execution') - ->fetchAll('execution'); - - foreach($bugs as $executionID => $bug) - { - foreach($bug as $key => $value) - { - if($key == 'project') continue; - $executions[$executionID]->$key = $value; - } - } - - foreach($executions as $execution) - { - if(!isset($executions[$execution->id]->totalTasks)) - { - $executions[$execution->id]->totalTasks = 0; - $executions[$execution->id]->undoneTasks = 0; - $executions[$execution->id]->yesterdayFinished = 0; - $executions[$execution->id]->totalEstimate = 0; - $executions[$execution->id]->totalConsumed = 0; - $executions[$execution->id]->totalLeft = 0; - } - if(!isset($executions[$execution->id]->totalBugs)) - { - $executions[$execution->id]->totalBugs = 0; - $executions[$execution->id]->activeBugs = 0; - $executions[$execution->id]->yesterdayResolved = 0; - } - if(!isset($executions[$execution->id]->totalStories)) - { - $executions[$execution->id]->totalStories = 0; - $executions[$execution->id]->unclosedStories = 0; - $executions[$execution->id]->releasedStories = 0; - } - - $executions[$execution->id]->progress = ($execution->totalConsumed || $execution->totalLeft) ? round($execution->totalConsumed / ($execution->totalConsumed + $execution->totalLeft), 3) * 100 : 0; - $executions[$execution->id]->taskProgress = $execution->totalTasks ? round(($execution->totalTasks - $execution->undoneTasks) / $execution->totalTasks, 2) * 100 : 0; - $executions[$execution->id]->storyProgress = $execution->totalStories ? round(($execution->totalStories - $execution->unclosedStories) / $execution->totalStories, 2) * 100 : 0; - $executions[$execution->id]->bugProgress = $execution->totalBugs ? round(($execution->totalBugs - $execution->activeBugs) / $execution->totalBugs, 2) * 100 : 0; - } - - $this->view->executions = $executions; - } - - /** - * Print waterfall report block. - * - * @access public - * @return void - */ - public function printWaterfallReportBlock() - { - $this->app->loadLang('programplan'); - $project = $this->loadModel('project')->getByID($this->session->project); - $today = helper::today(); - $date = date('Ymd', strtotime('this week Monday')); - $begin = $project->begin; - $weeks = $this->loadModel('weekly')->getWeekPairs($begin); - $current = zget($weeks, $date, ''); - - $this->weekly->save($this->session->project, $date); - - $PVEV = $this->weekly->getPVEV($this->session->project, $today); - $this->view->pv = (float)$PVEV['PV']; - $this->view->ev = (float)$PVEV['EV']; - $this->view->ac = (float)$this->weekly->getAC($this->session->project, $today); - $this->view->sv = $this->weekly->getSV($this->view->ev, $this->view->pv); - $this->view->cv = $this->weekly->getCV($this->view->ev, $this->view->ac); - - $left = (float)$this->weekly->getLeft($this->session->project, $today); - $progress = (!empty($this->view->ac) or !empty($left)) ? floor($this->view->ac / ($this->view->ac + $left) * 1000) / 1000 * 100 : 0; - $this->view->progress = $progress > 100 ? 100 : $progress; - $this->view->current = $current; - } - - /** - * Print waterfall general report block. - * - * @access public - * @return void - */ - public function printWaterfallGeneralReportBlock() - { - $this->app->loadLang('programplan'); - $this->loadModel('project'); - $this->loadModel('weekly'); - - $data = $this->project->getWaterfallPVEVAC($this->session->project); - $this->view->pv = (float)$data['PV']; - $this->view->ev = (float)$data['EV']; - $this->view->ac = (float)$data['AC']; - $this->view->sv = $this->weekly->getSV($this->view->ev, $this->view->pv); - $this->view->cv = $this->weekly->getCV($this->view->ev, $this->view->ac); - - $left = (float)$data['left']; - $progress = (!empty($this->view->ac) or !empty($left)) ? floor($this->view->ac / ($this->view->ac + $left) * 1000) / 1000 * 100 : 0; - $this->view->progress = $progress > 100 ? 100 : $progress; - } - - /** - * Print waterfall gantt block. - * - * @access public - * @return void - */ - public function printWaterfallGanttBlock() - { - $products = $this->loadModel('product')->getProductPairsByProject($this->session->project); - $productID = $this->session->product ? $this->session->product : 0; - $productID = isset($products[$productID]) ? $productID : key($products); - - $this->view->plans = $this->loadModel('programplan')->getDataForGantt($this->session->project, $productID, 0, 'task', false); - $this->view->products = $products; - $this->view->productID = $productID; - } - - /** - * Print waterfall issue block. - * - * @access public - * @return void - */ - public function printWaterfallIssueBlock() - { - $uri = $this->app->tab == 'my' ? $this->createLink('my', 'index') : $this->server->http_referer; - $this->session->set('issueList', $uri, 'project'); - if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->issues = $this->loadModel('issue')->getBlockIssues($this->session->project, $this->params->type, $this->viewType == 'json' ? 0 : (int)$this->params->count, $this->params->orderBy); - } - - /** - * Print waterfall risk block. - * - * @access public - * @return void - */ - public function printWaterfallRiskBlock() - { - $uri = $this->app->tab == 'my' ? $this->createLink('my', 'index') : $this->server->http_referer; - $this->session->set('riskList', $uri, 'project'); - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->risks = $this->loadModel('risk')->getBlockRisks($this->session->project, $this->params->type, $this->viewType == 'json' ? 0 : (int)$this->params->count, $this->params->orderBy); - } - - /** - * Print waterfall estimate block. - * - * @access public - * @return void - */ - public function printWaterfallEstimateBlock() - { - $this->app->loadLang('durationestimation'); - $this->loadModel('project'); - - $projectID = $this->session->project; - $members = $this->loadModel('user')->getTeamMemberPairs($projectID, 'project'); - $budget = $this->loadModel('workestimation')->getBudget($projectID); - $workhour = $this->loadModel('project')->getWorkhour($projectID); - if(empty($budget)) $budget = new stdclass(); - - $this->view->people = $this->dao->select('sum(people) as people')->from(TABLE_DURATIONESTIMATION)->where('project')->eq($this->session->project)->fetch('people'); - $this->view->members = count($members) ? count($members) - 1 : 0; - $this->view->consumed = $this->dao->select('sum(cast(consumed as decimal(10,2))) as consumed')->from(TABLE_TASK)->where('project')->eq($projectID)->andWhere('deleted')->eq(0)->andWhere('parent')->lt(1)->fetch('consumed'); - $this->view->budget = $budget; - $this->view->totalLeft = (float)$workhour->totalLeft; - } - - /** - * Print waterfall progress block. - * - * @access public - * @return void - */ - public function printWaterfallProgressBlock() - { - $this->loadModel('milestone'); - $this->loadModel('weekly'); - $this->app->loadLang('execution'); - - $projectID = $this->session->project; - $project = $this->loadModel('project')->getByID($projectID); - - $projectWeekly = $this->dao->select('*')->from(TABLE_WEEKLYREPORT)->where('project')->eq($projectID)->orderBy('weekStart_asc')->fetchAll('weekStart'); - - $charts['PV'] = '['; - $charts['EV'] = '['; - $charts['AC'] = '['; - foreach($projectWeekly as $weekStart => $data) - { - $charts['labels'][] = $weekStart; - $charts['PV'] .= $data->pv . ','; - $charts['EV'] .= $data->ev . ','; - $charts['AC'] .= $data->ac . ','; - } - - $charts['PV'] .= ']'; - $charts['EV'] .= ']'; - $charts['AC'] .= ']'; - - $this->view->charts = $charts; - } - - /** - * Print srcum project block. - * - * @access public - * @return void - */ - public function printScrumOverviewBlock() - { - $projectID = $this->session->project; - $this->app->loadLang('execution'); - $this->app->loadLang('bug'); - $totalData = $this->loadModel('project')->getOverviewList('byId', $projectID, 'id_desc', 1); - - $this->view->totalData = $totalData; - $this->view->projectID = $projectID; - } - - /** - * Print srcum project list block. - * - * @access public - * @return void - */ - public function printScrumListBlock() - { - if(!empty($this->params->type) and preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - $count = isset($this->params->count) ? (int)$this->params->count : 15; - $type = isset($this->params->type) ? $this->params->type : 'undone'; - - $this->app->loadClass('pager', true); - $pager = pager::init(0, $count, 1); - $this->loadModel('execution'); - $this->view->executionStats = !defined('TUTORIAL') ? $this->execution->getStatData($this->session->project, $type, 0, 0, false, '', 'id_desc', $pager) : array($this->loadModel('tutorial')->getExecution()); - } - - /** - * Print srcum product block. - * - * @access public - * @return void - */ - public function printScrumProductBlock() - { - $stories = array(); - $bugs = array(); - $releases = array(); - $count = isset($this->params->count) ? (int)$this->params->count : 15; - - $products = $this->dao->select('id, name')->from(TABLE_PRODUCT)->where('program')->eq($this->session->program)->limit(15)->fetchPairs(); - $productIdList = array_keys($products); - if(!empty($productIdList)) - { - $fields = 'product, count(*) as total'; - $stories = $this->dao->select($fields)->from(TABLE_STORY)->where('product')->in($productIdList)->andWhere('deleted')->eq('0')->groupBy('product')->fetchPairs(); - $bugs = $this->dao->select($fields)->from(TABLE_BUG)->where('product')->in($productIdList)->andWhere('deleted')->eq('0')->groupBy('product')->fetchPairs(); - $releases = $this->dao->select($fields)->from(TABLE_RELEASE)->where('product')->in($productIdList)->andWhere('deleted')->eq('0')->groupBy('product')->fetchPairs(); - } - - $this->view->products = $products; - $this->view->stories = $stories; - $this->view->bugs = $bugs; - $this->view->releases = $releases; - } - - /** - * Print scrum issue block. - * - * @access public - * @return void - */ - public function printScrumIssueBlock() - { - $uri = $this->app->tab == 'my' ? $this->createLink('my', 'index') : $this->server->http_referer; - $this->session->set('issueList', $uri, 'project'); - if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->issues = $this->loadModel('issue')->getBlockIssues($this->session->project, $this->params->type, $this->viewType == 'json' ? 0 : (int)$this->params->count, $this->params->orderBy); - } - - /** - * Print scrum risk block. - * - * @access public - * @return void - */ - public function printScrumRiskBlock() - { - $uri = $this->app->tab == 'my' ? $this->createLink('my', 'index') : $this->server->http_referer; - $this->session->set('riskList', $uri, 'project'); - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->risks = $this->loadModel('risk')->getBlockRisks($this->session->project, $this->params->type, $this->viewType == 'json' ? 0 : (int)$this->params->count, $this->params->orderBy); - } - - /** - * Print sprint block. - * - * @access public - * @return void - */ - public function printSprintBlock() - { - $sprints = $this->dao->select('status, count(*) as sprints')->from(TABLE_EXECUTION) - ->where('deleted')->eq(0) - ->beginIF($this->config->vision == 'lite')->andWhere('type')->eq('kanban')->fi() - ->beginIF($this->config->vision == 'rnd')->andWhere('type')->in('sprint,kanban')->fi() - ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi() - ->andWhere('project')->eq($this->session->project) - ->groupBy('status') - ->fetchPairs(); - - $summary = new stdclass(); - $summary->total = array_sum($sprints); - $summary->doing = zget($sprints, 'doing', 0); - $summary->closed = zget($sprints, 'closed', 0); - - $progress = new stdclass(); - $progress->doing = $summary->total == 0 ? 0 : round($summary->doing / $summary->total, 3); - $progress->closed = $summary->total == 0 ? 0 : round($summary->closed / $summary->total, 3); - - $this->view->summary = $summary; - $this->view->progress = $progress; - } - - /** - * Print project dynamic block. - * - * @access public - * @return void - */ - public function printProjectDynamicBlock() - { - $projectID = $this->session->project; - - $executions = $this->loadModel('execution')->getPairs($projectID); - $products = $this->loadModel('product')->getProductPairsByProject($projectID); - $count = isset($this->params->count) ? (int)$this->params->count : 10; - - /* Load pager. */ - $this->app->loadClass('pager', $static = true); - $pager = new pager(0, 30, 1); - - $this->view->actions = $this->loadModel('action')->getDynamic('all', 'all', 'date_desc', $pager, 'all', $projectID); - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - } - - /** - * Print srcum road map block. - * - * @param int $productID - * @param int $roadMapID - * @access public - * @return void - */ - public function printScrumRoadMapBlock($productID = 0, $roadMapID = 0) - { - $uri = $this->app->tab == 'my' ? $this->createLink('my', 'index') : $this->server->http_referer; - $this->session->set('releaseList', $uri, 'product'); - $this->session->set('productPlanList', $uri, 'product'); - - $products = $this->loadModel('product')->getPairs('', $this->session->project); - if(!is_numeric($productID)) $productID = key($products); - - $this->view->roadmaps = $this->product->getRoadmap($productID, 0, 6); - $this->view->productID = $productID; - $this->view->roadMapID = $roadMapID; - $this->view->products = $products; - $this->view->sync = 1; - - if($_POST) - { - $this->view->sync = 0; - $this->display('block', 'scrumroadmapblock'); - } - } - - /** - * Print srcum test block. - * - * @access public - * @return void - */ - public function printScrumTestBlock() - { - $uri = $this->app->tab == 'my' ? $this->createLink('my', 'index') : $this->server->http_referer; - $this->session->set('testtaskList', $uri, 'qa'); - $this->session->set('productList', $uri, 'product'); - $this->session->set('projectList', $uri, 'project'); - $this->session->set('buildList', $uri, 'execution'); - $this->app->loadLang('testtask'); - - $count = zget($this->params, 'count', 10); - $status = isset($this->params->type) ? $this->params->type : 'wait'; - - $this->view->project = $this->loadModel('project')->getByID($this->session->project); - $this->view->testtasks = $this->dao->select('t1.*,t2.name as productName,t3.name as buildName,t4.name as projectName') - ->from(TABLE_TESTTASK)->alias('t1') - ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') - ->leftJoin(TABLE_BUILD)->alias('t3')->on('t1.build=t3.id') - ->leftJoin(TABLE_PROJECT)->alias('t4')->on('t1.project=t4.id') - ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t5')->on('t1.project=t5.project') - ->where('t1.deleted')->eq('0') - ->andWhere('t1.project')->eq($this->session->project)->fi() - ->andWhere('t1.product = t5.product') - ->beginIF($status != 'all')->andWhere('t1.status')->eq($status)->fi() - ->orderBy('t1.id desc') - ->limit($count) - ->fetchAll(); - } - - /** - * Print qa statistic block. - * - * @access public - * @return void - */ - public function printQaStatisticBlock() - { - if(!empty($this->params->type) and preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - $this->app->loadLang('bug'); - $status = isset($this->params->type) ? $this->params->type : ''; - $count = isset($this->params->count) ? (int)$this->params->count : 0; - - $projectID = $this->lang->navGroup->qa == 'project' ? $this->session->project : 0; - $products = $this->loadModel('product')->getOrderedProducts($status, $count, $projectID, 'all'); - $executions = $this->loadModel('execution')->getPairs($projectID, 'all', 'empty|withdelete'); - if(empty($products)) - { - $this->view->products = $products; - return false; - } - - $productIdList = array_keys($products); - $today = date(DT_DATE1); - $yesterday = date(DT_DATE1, strtotime('yesterday')); - $testtasks = $this->dao->select('*')->from(TABLE_TESTTASK)->where('product')->in($productIdList)->andWhere('project')->ne(0)->andWhere('deleted')->eq(0)->orderBy('id')->fetchAll('product'); - $bugs = $this->dao->select("product, count(id) as total, - count(IF(assignedTo = '{$this->app->user->account}', 1, null)) as assignedToMe, - count(IF(status != 'closed', 1, null)) as unclosed, - count(IF(status != 'closed' and status != 'resolved', 1, null)) as unresolved, - count(IF(confirmed = '0' and toStory = '0', 1, null)) as unconfirmed, - count(IF(resolvedDate >= '$yesterday' and resolvedDate < '$today', 1, null)) as yesterdayResolved, - count(IF(closedDate >= '$yesterday' and closedDate < '$today', 1, null)) as yesterdayClosed") - ->from(TABLE_BUG) - ->where('product')->in($productIdList) - ->andWhere('execution')->in(array_keys($executions)) - ->andWhere('deleted')->eq(0) - ->groupBy('product') - ->fetchAll('product'); - - $confirmedBugs = $this->dao->select('product')->from(TABLE_ACTION) - ->where('objectType')->eq('bug') - ->andWhere('action')->eq('bugconfirmed') - ->andWhere('date')->ge($yesterday) - ->andWhere('date')->lt($today) - ->fetchAll(); - $productConfirmedBugs = array(); - foreach($confirmedBugs as $bug) - { - if(!isset($productConfirmedBugs[$bug->product])) $productConfirmedBugs[$bug->product] = 0; - $productConfirmedBugs[$bug->product]++; - } - - foreach($products as $productID => $product) - { - $bug = isset($bugs[$productID]) ? $bugs[$productID] : ''; - $product->total = empty($bug) ? 0 : $bug->total; - $product->assignedToMe = empty($bug) ? 0 : $bug->assignedToMe; - $product->unclosed = empty($bug) ? 0 : $bug->unclosed; - $product->unresolved = empty($bug) ? 0 : $bug->unresolved; - $product->unconfirmed = empty($bug) ? 0 : $bug->unconfirmed; - $product->yesterdayResolved = empty($bug) ? 0 : $bug->yesterdayResolved; - $product->yesterdayClosed = empty($bug) ? 0 : $bug->yesterdayClosed; - $product->yesterdayConfirmed = empty($productConfirmedBugs[",$productID,"]) ? 0 : $productConfirmedBugs[",$productID,"]; - - $product->assignedRate = $product->total ? round($product->assignedToMe / $product->total * 100, 2) : 0; - $product->unresolvedRate = $product->total ? round($product->unresolved / $product->total * 100, 2) : 0; - $product->unconfirmedRate = $product->total ? round($product->unconfirmed / $product->total * 100, 2) : 0; - $product->unclosedRate = $product->total ? round($product->unclosed / $product->total * 100, 2) : 0; - $product->testtask = isset($testtasks[$productID]) ? $testtasks[$productID] : ''; - } - - $this->view->products = $products; - } - - /** - * Print overview block. - * - * @access public - * @return void - */ - public function printOverviewBlock($module = 'product') - { - $func = 'print' . ucfirst($module) . 'OverviewBlock'; - $this->view->module = $module; - $this->$func(); - } - - /** - * Print product overview block. - * - * @access public - * @return void - */ - public function printProductOverviewBlock() - { - $normal = 0; - $closed = 0; - - $products = $this->loadModel('product')->getList(); - foreach($products as $product) - { - if(!$this->product->checkPriv($product->id)) continue; - - if($product->status == 'normal') $normal++; - if($product->status == 'closed') $closed++; - } - - $total = $normal + $closed; - - $this->view->total = $total; - $this->view->normal = $normal; - $this->view->closed = $closed; - $this->view->normalPercent = $total ? round(($normal / $total), 2) * 100 : 0; - } - - /** - * Print execution overview block. - * - * @access public - * @return void - */ - public function printExecutionOverviewBlock() - { - $projectID = $this->view->block->module == 'my' ? 0 : (int)$this->session->project; - $executions = $this->loadModel('execution')->getList($projectID); - - $total = 0; - foreach($executions as $execution) - { - if(empty($execution->multiple)) continue; - if(!isset($overview[$execution->status])) $overview[$execution->status] = 0; - $overview[$execution->status]++; - $total++; - } - - $overviewPercent = array(); - $this->app->loadLang('project'); - foreach($this->lang->project->statusList as $statusKey => $statusName) - { - if(!isset($overview[$statusKey])) $overview[$statusKey] = 0; - $overviewPercent[$statusKey] = $total ? round($overview[$statusKey] / $total, 2) * 100 . '%' : '0%'; - } - - $this->view->total = $total; - $this->view->overview = $overview; - $this->view->overviewPercent = $overviewPercent; - } - - /** - * Print qa overview block. - * - * @access public - * @return void - */ - public function printQaOverviewBlock() - { - $casePairs = $this->dao->select('lastRunResult, COUNT(*) AS count')->from(TABLE_CASE) - ->where('1=1') - ->beginIF($this->view->block->module != 'my' and $this->session->project)->andWhere('project')->eq((int)$this->session->project)->fi() - ->groupBy('lastRunResult') - ->fetchPairs(); - - $total = array_sum($casePairs); - - $this->app->loadLang('testcase'); - foreach($this->lang->testcase->resultList as $result => $label) - { - if(!isset($casePairs[$result])) $casePairs[$result] = 0; - } - - $casePercents = array(); - foreach($casePairs as $result => $count) - { - $casePercents[$result] = $total ? round($count / $total * 100, 2) : 0; - } - - $this->view->total = $total; - $this->view->casePairs = $casePairs; - $this->view->casePercents = $casePercents; - } - - /** - * Print execution block. - * - * @access public - * @return void - */ - public function printExecutionBlock() - { - if(!empty($this->params->type) and preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) return; - - $count = isset($this->params->count) ? (int)$this->params->count : 0; - $status = isset($this->params->type) ? $this->params->type : 'all'; - - $this->loadModel('execution'); - $this->app->loadClass('pager', true); - $pager = pager::init(0, $count, 1); - - $projectPairs = $this->dao->select('id,name')->from(TABLE_PROJECT)->where('type')->eq('project')->fetchPairs('id', 'name'); - $projectID = $this->view->block->module == 'my' ? 0 : (int)$this->session->project; - - $this->view->executionStats = $this->execution->getStatData($projectID, $status, 0, 0, false, 'skipParent', 'id_asc', $pager); - } - - /** - * Print assign to me block. - * - * @access public - * @return void - */ - public function printAssignToMeBlock($longBlock = true) - { - $hasIssue = helper::hasFeature('issue'); - $hasRisk = helper::hasFeature('risk'); - $hasMeeting = helper::hasFeature('meeting'); - - $hasViewPriv = array(); - if(common::hasPriv('todo', 'view')) $hasViewPriv['todo'] = true; - if(common::hasPriv('task', 'view')) $hasViewPriv['task'] = true; - if(common::hasPriv('bug', 'view') and $this->config->vision != 'lite') $hasViewPriv['bug'] = true; - if(common::hasPriv('story', 'view') and $this->config->vision != 'lite') $hasViewPriv['story'] = true; - if($this->config->URAndSR and common::hasPriv('story', 'view') and $this->config->vision != 'lite') $hasViewPriv['requirement'] = true; - if(common::hasPriv('risk', 'view') and $this->config->edition == 'max' and $this->config->vision != 'lite' && $hasRisk) $hasViewPriv['risk'] = true; - if(common::hasPriv('issue', 'view') and $this->config->edition == 'max' and $this->config->vision != 'lite' && $hasIssue) $hasViewPriv['issue'] = true; - if(common::hasPriv('meeting', 'view') and $this->config->edition == 'max' and $this->config->vision != 'lite' && $hasMeeting) $hasViewPriv['meeting'] = true; - if(common::hasPriv('feedback', 'view') and in_array($this->config->edition, array('max', 'biz'))) $hasViewPriv['feedback'] = true; - if(common::hasPriv('ticket', 'view') and in_array($this->config->edition, array('max', 'biz'))) $hasViewPriv['ticket'] = true; - - $params = $this->get->param; - $params = json_decode(base64_decode($params)); - $count = array(); - $objectList = array('todo' => 'todos', 'task' => 'tasks', 'bug' => 'bugs', 'story' => 'stories', 'requirement' => 'requirements'); - $objectCountList = array('todo' => 'todoCount', 'task' => 'taskCount', 'bug' => 'bugCount', 'story' => 'storyCount', 'requirement' => 'requirementCount'); - if($this->config->edition == 'max') - { - if($hasRisk) - { - $objectList += array('risk' => 'risks'); - $objectCountList += array('risk' => 'riskCount'); - } - - if($hasIssue) - { - $objectList += array('issue' => 'issues'); - $objectCountList += array('issue' => 'issueCount'); - } - - $objectList += array('feedback' => 'feedbacks', 'ticket' => 'tickets'); - $objectCountList += array('feedback' => 'feedbackCount', 'ticket' => 'ticketCount'); - } - - if($this->config->edition == 'biz') - { - $objectList += array('feedback' => 'feedbacks', 'ticket' => 'tickets'); - $objectCountList += array('feedback' => 'feedbackCount', 'ticket' => 'ticketCount'); - } - - $tasks = $this->loadModel('task')->getUserSuspendedTasks($this->app->user->account); - foreach($objectCountList as $objectType => $objectCount) - { - if(!isset($hasViewPriv[$objectType])) continue; - - $table = $objectType == 'requirement' ? TABLE_STORY : $this->config->objectTables[$objectType]; - $orderBy = $objectType == 'todo' ? "`date` desc" : 'id_desc'; - $limitCount = isset($params->{$objectCount}) ? $params->{$objectCount} : 0; - $objects = $this->dao->select('t1.*')->from($table)->alias('t1') - ->beginIF($objectType == 'story' or $objectType == 'requirement')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')->fi() - ->beginIF($objectType == 'bug')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')->fi() - ->beginIF($objectType == 'task')->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution=t2.id')->fi() - ->beginIF($objectType == 'issue' or $objectType == 'risk')->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')->fi() - ->beginIF($objectType == 'ticket')->leftJoin(TABLE_USER)->alias('t2')->on('t1.openedBy = t2.account')->fi() - ->where('t1.deleted')->eq(0) - ->andWhere('t1.assignedTo')->eq($this->app->user->account)->fi() - ->beginIF($objectType == 'story')->andWhere('t1.type')->eq('story')->andWhere('t2.deleted')->eq('0')->fi() - ->beginIF($objectType == 'requirement')->andWhere('t1.type')->eq('requirement')->andWhere('t2.deleted')->eq('0')->fi() - ->beginIF($objectType == 'bug')->andWhere('t2.deleted')->eq('0')->fi() - ->beginIF($objectType == 'story' or $objectType == 'requirement')->andWhere('t2.deleted')->eq('0')->fi() - ->beginIF($objectType == 'todo')->andWhere('t1.cycle')->eq(0)->andWhere('t1.status')->eq('wait')->andWhere('t1.vision')->eq($this->config->vision)->fi() - ->beginIF($objectType != 'todo')->andWhere('t1.status')->ne('closed')->fi() - ->beginIF($objectType == 'feedback')->andWhere('t1.status')->in('wait, noreview')->fi() - ->beginIF($objectType == 'issue' or $objectType == 'risk')->andWhere('t2.deleted')->eq(0)->fi() - ->beginIF($objectType == 'ticket')->andWhere('t1.status')->in('wait,doing,done')->fi() - ->orderBy($orderBy) - ->beginIF($limitCount)->limit($limitCount)->fi() - ->fetchAll(); - - if($objectType == 'todo') - { - $this->app->loadClass('date'); - $this->app->loadLang('todo'); - foreach($objects as $key => $todo) - { - if($todo->status == 'done' and $todo->finishedBy == $this->app->user->account) - { - unset($objects[$key]); - continue; - } - if($todo->type == 'task' and isset($tasks[$todo->idvalue])) - { - unset($objects[$key]); - continue; - } - - $todo->begin = date::formatTime($todo->begin); - $todo->end = date::formatTime($todo->end); - } - } - - if($objectType == 'task') - { - $this->app->loadLang('task'); - $this->app->loadLang('execution'); - - $objects = $this->loadModel('task')->getUserTasks($this->app->user->account, 'assignedTo'); - - foreach($objects as $k => $task) - { - if(in_array($task->status, array('closed', 'cancel'))) unset($objects[$k]); - } - if($limitCount > 0) $objects = array_slice($objects, 0, $limitCount); - } - - if($objectType == 'bug') $this->app->loadLang('bug'); - if($objectType == 'risk') $this->app->loadLang('risk'); - if($objectType == 'issue') $this->app->loadLang('issue'); - - if($objectType == 'feedback' or $objectType == 'ticket') - { - $this->app->loadLang('feedback'); - $this->app->loadLang('ticket'); - $this->view->users = $this->loadModel('user')->getPairs('all,noletter'); - $this->view->products = $this->dao->select('id, name')->from(TABLE_PRODUCT)->where('deleted')->eq('0')->fetchPairs('id', 'name'); - } - - $count[$objectType] = count($objects); - $this->view->{$objectList[$objectType]} = $objects; - } - - if(isset($hasViewPriv['meeting'])) - { - $this->app->loadLang('meeting'); - $today = helper::today(); - $now = date('H:i:s', strtotime(helper::now())); - $meetingCount = isset($params->meetingCount) ? isset($params->meetingCount) : 0; - - $meetings = $this->dao->select('*')->from(TABLE_MEETING)->alias('t1') - ->leftjoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') - ->where('t1.deleted')->eq('0') - ->andWhere('t2.deleted')->eq('0') - ->andWhere('(t1.date')->gt($today) - ->orWhere('(t1.begin')->gt($now) - ->andWhere('t1.date')->eq($today) - ->markRight(2) - ->andwhere('(t1.host')->eq($this->app->user->account) - ->orWhere('t1.participant')->in($this->app->user->account) - ->markRight(1) - ->orderBy('t1.id_desc') - ->beginIF($meetingCount)->limit($meetingCount)->fi() - ->fetchAll(); - - $count['meeting'] = count($meetings); - $this->view->meetings = $meetings; - $this->view->depts = $this->loadModel('dept')->getOptionMenu(); - $this->view->users = $this->loadModel('user')->getPairs('all,noletter'); - } - - $limitCount = !empty($params->reviewCount) ? $params->reviewCount : 20; - $this->app->loadClass('pager', $static = true); - $pager = new pager(0, $limitCount, 1); - $reviews = $this->loadModel('my')->getReviewingList('all', 'time_desc', $pager); - if($reviews) - { - $hasViewPriv['review'] = true; - $count['review'] = count($reviews); - $this->view->reviews = $reviews; - if($this->config->edition == 'max') - { - $this->app->loadLang('approval'); - $this->view->flows = $this->dao->select('module,name')->from(TABLE_WORKFLOW)->where('buildin')->eq(0)->fetchPairs('module', 'name'); - } - } - - $this->view->selfCall = $this->selfCall; - $this->view->hasViewPriv = $hasViewPriv; - $this->view->count = $count; - $this->view->longBlock = $longBlock; - $this->display(); - } - - /** - * Print recent project block. - * - * @access public - * @return void - */ - public function printRecentProjectBlock() - { - /* load pager. */ - $this->app->loadClass('pager', $static = true); - $pager = new pager(0, 3, 1); - $this->view->projects = $this->loadModel('project')->getList('all', 'id_desc', true, $pager); - } - - /** - * Print project team block. - * - * @access public - * @return void - */ - public function printProjectTeamBlock() - { - $count = isset($this->params->count) ? $this->params->count : 15; - $status = isset($this->params->type) ? $this->params->type : 'all'; - $orderBy = isset($this->params->orderBy) ? $this->params->orderBy : 'id_desc'; - - /* Get projects. */ - $this->app->loadLang('task'); - $this->app->loadLang('program'); - $this->app->loadLang('execution'); - $this->view->projects = $this->loadModel('project')->getOverviewList('byStatus', $status, $orderBy, $count); - } - - /** - * Print document statistic block. - * - * @access public - * @return void - */ - public function printDocStatisticBlock() - { - $this->view->statistic = $this->loadModel('doc')->getStatisticInfo(); - } - - /** - * Print document dynamic block. - * - * @access public - * @return void - */ - public function printDocDynamicBlock() - { - /* Load pager. */ - $this->app->loadClass('pager', true); - $pager = new pager(0, 30, 1); - - $this->view->actions = $this->loadModel('doc')->getDynamic($pager); - $this->view->users = $this->loadModel('user')->getPairs('nodeleted|noletter|all'); - } - - /** - * Print my collection of documents block. - * - * @access public - * @return void - */ - public function printDocMyCollectionBlock() - { - /* Load pager. */ - $this->app->loadClass('pager', $static = true); - $pager = new pager(0, 6, 1); - - $docList = $this->loadModel('doc')->getDocsByBrowseType('collectedbyme', 0, 0, 'editedDate_desc', $pager); - $libList = array(); - foreach($docList as $doc) - { - $doc->editedDate = substr($doc->editedDate, 0, 10); - $doc->editInterval = helper::getDateInterval($doc->editedDate); - - $libList[] = $doc->lib; - } - - $this->view->docList = $docList; - } - - /** - * Print recent update block. - * - * @access public - * @return void - */ - public function printDocRecentUpdateBlock() - { - /* Load pager. */ - $this->app->loadClass('pager', $static = true); - $pager = new pager(0, 6, 1); - - $docList = $this->loadModel('doc')->getDocsByBrowseType('byediteddate', 0, 0, 'editedDate_desc', $pager); - $libList = array(); - foreach($docList as $doc) - { - $doc->editedDate = substr($doc->editedDate, 0, 10); - $doc->editInterval = helper::getDateInterval($doc->editedDate); - - $libList[] = $doc->lib; - } - - $this->view->docList = $docList; - } - - /** - * Print view list block. - * - * @access public - * @return void - */ - public function printDocViewListBlock() - { - /* Load pager. */ - $this->app->loadClass('pager', true); - $pager = new pager(0, 6, 1); - - $this->view->docList = $this->loadModel('doc')->getDocsByBrowseType('all', 0, 0,'views_desc', $pager); - } - - /** - * Print collect list block. - * - * @access public - * @return void - */ - public function printDocCollectListBlock() - { - /* Load pager. */ - $this->app->loadClass('pager', true); - $pager = new pager(0, 6, 1); - - $this->view->docList = $this->loadModel('doc')->getDocsByBrowseType('all', 0, 0, 'collects_desc', $pager); - } - - /** - * Print product's document block. - * - * @access public - * @return void - */ - public function printProductDocBlock() - { - $this->loadModel('doc'); - $this->session->set('docList', $this->createLink('doc', 'index'), 'doc'); - - /* Set project status and count. */ - $count = isset($this->params->count) ? (int)$this->params->count : 15; - $products = $this->loadModel('product')->getOrderedProducts('all'); - $involveds = $this->product->getOrderedProducts('involved'); - $productIdList = array_merge(array_keys($products), array_keys($involveds)); - - $stmt = $this->dao->select('id,product,lib,title,type,addedBy,addedDate,editedDate,status,acl,groups,users,deleted')->from(TABLE_DOC)->alias('t1') - ->where('deleted')->eq(0) - ->andWhere('product')->in($productIdList) - ->orderBy('product,status,editedDate_desc') - ->query(); - $docGroup = array(); - while($doc = $stmt->fetch()) - { - if(!isset($docGroup[$doc->product])) $docGroup[$doc->product] = array(); - if(count($docGroup[$doc->product]) >= $count) continue; - if($this->doc->checkPrivDoc($doc)) $docGroup[$doc->product][$doc->id] = $doc; - } - - $hasDataProducts = $hasDataInvolveds = array(); - foreach($products as $productID => $product) - { - if(isset($docGroup[$productID]) and count($docGroup[$productID]) > 0) - { - $hasDataProducts[$productID] = $product; - if(isset($involveds[$productID])) $hasDataInvolveds[$productID] = $product; - } - } - - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->products = $hasDataProducts; - $this->view->involveds = $hasDataInvolveds; - $this->view->docGroup = $docGroup; - } - - /** - * Print project's document block. - * - * @access public - * @return void - */ - public function printProjectDocBlock() - { - $this->loadModel('doc'); - $this->app->loadLang('project'); - $this->session->set('docList', $this->createLink('doc', 'index'), 'doc'); - - /* Set project status and count. */ - $count = isset($this->params->count) ? (int)$this->params->count : 15; - $projects = $this->dao->select('*')->from(TABLE_PROJECT) - ->where('deleted')->eq('0') - ->andWhere('vision')->eq($this->config->vision) - ->andWhere('type')->eq('project') - ->beginIF($this->config->vision == 'rnd')->andWhere('model')->ne('kanban')->fi() - ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi() - ->orderBy('order_asc,id_desc') - ->fetchAll('id'); - - $involveds = $this->dao->select('t1.*')->from(TABLE_PROJECT)->alias('t1') - ->leftJoin(TABLE_TEAM)->alias('t2')->on('t1.id=t2.root') - ->where('t1.deleted')->eq('0') - ->andWhere('t1.vision')->eq($this->config->vision) - ->andWhere('t1.type')->eq('project') - ->beginIF($this->config->vision == 'rnd')->andWhere('t1.model')->ne('kanban')->fi() - ->andWhere('t2.type')->eq('project') - ->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->projects)->fi() - ->andWhere('t1.openedBy', true)->eq($this->app->user->account) - ->orWhere('t1.PM')->eq($this->app->user->account) - ->orWhere('t2.account')->eq($this->app->user->account) - ->markRight(1) - ->orderBy('t1.order_asc,t1.id_desc') - ->fetchAll('id'); - - $projectIdList = array_keys($projects); - - $stmt = $this->dao->select('t1.id,t1.lib,t1.title,t1.type,t1.addedBy,t1.addedDate,t1.editedDate,t1.status,t1.acl,t1.groups,t1.users,t1.deleted,if(t1.project = 0, t2.project, t1.project) as project')->from(TABLE_DOC)->alias('t1') - ->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.execution=t2.id') - ->where('t1.deleted')->eq(0) - ->andWhere('t2.deleted', true)->eq(0) - ->orWhere('t2.deleted is null') - ->markRight(1) - ->andWhere('t1.project', true)->in($projectIdList) - ->orWhere('t2.project')->in($projectIdList) - ->markRight(1) - ->orderBy('project,t1.status,t1.editedDate_desc') - ->query(); - $docGroup = array(); - while($doc = $stmt->fetch()) - { - if(!isset($docGroup[$doc->project])) $docGroup[$doc->project] = array(); - if(count($docGroup[$doc->project]) >= $count) continue; - if($this->doc->checkPrivDoc($doc)) $docGroup[$doc->project][$doc->id] = $doc; - } - - $hasDataProjects = $hasDataInvolveds = array(); - foreach($projects as $projectID => $project) - { - if(isset($docGroup[$projectID]) and count($docGroup[$projectID]) > 0) - { - $hasDataProjects[$projectID] = $project; - if(isset($involveds[$projectID])) $hasDataInvolveds[$projectID] = $project; - } - } - - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->projects = $hasDataProjects; - $this->view->involveds = $hasDataInvolveds; - $this->view->docGroup = $docGroup; - } - - /** - * Print guide block - * - * @param int $blockID - * @access public - * @return void - */ - public function guide($blockID = 0) - { - $this->app->loadLang('custom'); - $this->app->loadLang('my'); - $this->loadModel('setting'); - - $this->view->blockID = $blockID; - $this->view->programs = $this->loadModel('program')->getTopPairs('', 'noclosed', true); - $this->view->programID = isset($this->config->global->defaultProgram) ? $this->config->global->defaultProgram : 0; - $this->view->URSRList = $this->loadModel('custom')->getURSRPairs(); - $this->view->URSR = $this->setting->getURSR(); - $this->view->programLink = isset($this->config->programLink) ? $this->config->programLink : 'program-browse'; - $this->view->productLink = isset($this->config->productLink) ? $this->config->productLink : 'product-all'; - $this->view->projectLink = isset($this->config->projectLink) ? $this->config->projectLink : 'project-browse'; - $this->view->executionLink = isset($this->config->executionLink) ? $this->config->executionLink : 'execution-task'; - - $this->display(); - } - /** * Close block forever. * @@ -2083,4 +212,126 @@ class block extends control $this->loadModel('setting')->setItem("{$this->app->user->account}.$module.block.initVersion", $this->config->block->version); } } + + /** + * 展示应用的仪表盘 + * Display dashboard for app. + * + * @param string $dashboard + * @param string $projectID + * @access public + * @return void + */ + public function dashboard(string $dashboard, string $projectID = '0') + { + $projectID = (int)$projectID; + $blocks = $this->block->getMyDashboard($dashboard); + $isInitiated = $this->block->fetchBlockInitStatus($dashboard); + + /* Init block when vist index first. */ + if(empty($blocks) and !$isInitiated and !defined('TUTORIAL') and $this->block->initBlock($dashboard)) + { + return print(js::reload()); + } + + $blocks = $this->blockZen->processBlockForRender($blocks, $projectID); + + if($this->app->getViewType() == 'json') return print(json_encode($blocks)); + + list($shortBlocks, $longBlocks) = $this->blockZen->splitBlocksByLen($blocks); + + $this->view->title = zget($this->lang->block->dashboard, $dashboard, $this->lang->block->dashboard['default']); + $this->view->longBlocks = $longBlocks; + $this->view->shortBlocks = $shortBlocks; + $this->view->dashboard = $dashboard; + $this->render(); + } + + + + + /** + * Print block. + * 输出区块 + * + * @param int $blockID + * @access public + * @return string + */ + public function printBlock($blockID) + { + if(!$this->selfCall) + { + $lang = str_replace('_', '-', $this->get->lang); + $this->app->setClientLang($lang); + $this->app->loadLang('common'); + $this->app->loadLang('block'); + + if(!$this->block->checkAPI($this->get->hash)) return; + } + + $html = ''; + $blockID = (int)$blockID; + $block = $this->block->getByID($blockID); + + if(empty($block)) return $html; + + $code = $block->code; + if($code == 'statistic' or $code == 'list' or $code == 'overview') $code = $block->module . ucfirst($code); + + $function = 'print' . ucfirst($code) . 'Block'; + if(method_exists('blockZen', $function)) $this->blockZen->$function($blockID); + + $params = $block->params; + if(isset($params->num) and !isset($params->count)) $params->count = $params->num; + if(!$this->selfCall) + { + $this->app->user = $this->dao->select('*')->from(TABLE_USER)->where('ranzhi')->eq($params->account)->fetch(); + if(empty($this->app->user)) + { + $this->app->user = new stdclass(); + $this->app->user->account = 'guest'; + } + $this->app->user->admin = strpos($this->app->company->admins, ",{$this->app->user->account},") !== false; + $this->app->user->rights = $this->loadModel('user')->authorize($this->app->user->account); + $this->app->user->groups = $this->user->getGroups($this->app->user->account); + $this->app->user->view = $this->user->grantUserView($this->app->user->account, $this->app->user->rights['acls']); + + $sso = base64_decode($this->get->sso); + $this->view->sso = $sso; + $this->view->sign = strpos($sso, '?') === false ? '?' : '&'; + } + + $this->view->title = $block->title; + $this->view->block = $block; + $this->view->longBlock = $this->block->isLongBlock($block); + $this->view->selfCall = $this->selfCall; + + $module = $block->module; + $moreLink = ''; + if(isset($this->config->block->modules[$module]->moreLinkList->{$code})) + { + list($moduleName, $method, $vars) = explode('|', sprintf($this->config->block->modules[$module]->moreLinkList->{$code}, isset($params->type) ? $params->type : '')); + $this->view->moreLink = $this->createLink($moduleName, $method, $vars); + } + $this->view->moreLink = $moreLink; + + $viewType = (isset($params->viewType) and $params->viewType == 'json') ? 'json' : 'html'; + if($viewType == 'json') + { + unset($this->view->app); + unset($this->view->config); + unset($this->view->lang); + unset($this->view->header); + unset($this->view->position); + unset($this->view->moduleTree); + + $output['status'] = is_object($this->view) ? 'success' : 'fail'; + $output['data'] = json_encode($this->view); + $output['md5'] = md5(json_encode($this->view)); + return print(json_encode($output)); + } + + $this->display('block', strtolower($code) . 'block'); + } } diff --git a/module/block/css/projectstatisticblock.ui.css b/module/block/css/projectstatisticblock.ui.css new file mode 100644 index 0000000000..7cc3a64d9c --- /dev/null +++ b/module/block/css/projectstatisticblock.ui.css @@ -0,0 +1 @@ +.projectstatistic-block .tab-content {width: 100%} diff --git a/module/block/ui/projectstatisticblock.html.php b/module/block/ui/projectstatisticblock.html.php index 4f15140101..200b0b274d 100644 --- a/module/block/ui/projectstatisticblock.html.php +++ b/module/block/ui/projectstatisticblock.html.php @@ -11,47 +11,321 @@ declare(strict_types=1); namespace zin; +$blockNavID = 'nav-' . uniqid(); +$selected = key($projects); +$navTabs = array(); +foreach($projects as $project) +{ + $navTabs[] = li + ( + set('class', 'nav-item' . ($project->id == $selected ? ' active' : '')), + a + ( + set('data-toggle', 'tab'), + set('href', "#tab3{$blockNavID}Content{$project->id}"), + $project->name + ) + ); +} + +$tabItems = array(); +foreach($projects as $project) +{ + $tabItems[] = div + ( + set('class', 'tab-pane' . ($project->id == $selected ? ' active' : '')), + set('id', "tab3{$blockNavID}Content{$project->id}"), + in_array($project->model, array('scrum', 'kanban', 'agileplus')) ? div + ( + div + ( + set('class', 'flex justify-around'), + div + ( + set('class', 'text-center'), + h4($lang->block->storyCount), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->block->allStories . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->allStories, + ) + ), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->block->finish . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->doneStories, + ) + ), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->project->surplus . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->leftStories, + ) + ) + ), + div + ( + set('class', 'text-center'), + h4($lang->block->investment), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->block->totalPeople . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->teamCount, + ) + ), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->block->estimate . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->estimate . $lang->execution->workHourUnit, + ) + ), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->block->consumedHours . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->consumed . $lang->execution->workHourUnit, + ) + ) + ), + div + ( + set('class', 'text-center'), + h4($lang->block->taskCount), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->block->wait . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->waitTasks, + ) + ), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->block->doing . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->doingTasks, + ) + ), + div + ( + div + ( + set('class', 'statistic-title'), + $lang->block->done . ":", + ), + div + ( + set('class', 'statistic-data'), + $project->rndDoneTasks, + ) + ) + ), + div + ( + set('class', 'text-center'), + h4($lang->block->bugCount), + div + ( + div + ( + set('class', 'col col-title'), + $lang->block->totalBug . ":", + ), + div + ( + set('class', 'col col-data'), + $project->allBugs, + ) + ), + div + ( + div + ( + set('class', 'col col-title'), + $lang->bug->statusList['resolved'] . ":", + ), + div + ( + set('class', 'col col-data'), + $project->doneBugs, + ) + ), + div + ( + div + ( + set('class', 'col col-title'), + $lang->bug->unResolved . ":", + ), + div + ( + set('class', 'col col-data'), + $project->leftBugs, + ) + ) + ) + ), + (!empty($project->executions) and $project->multiple) ? div + ( + set('class', 'flex'), + div + ( + set('class', 'flex-1 text-right'), + h4($lang->block->last) + ), + div + ( + set('class', 'flex-1 text-center'), + a + ( + set('href', $this->createLink('execution', 'task', "executionID={$project->executions[0]->id}")), + set('title', $project->executions[0]->name), + $project->executions[0]->name, + ) + ), + div + ( + set('class', 'flex-1'), + div + ( + set('class', 'progress'), + span + ( + set('class', 'mr-4'), + $project->executions[0]->hours->progress . '%' + ), + div + ( + set('class', 'progress-bar'), + set('role', 'progressbar'), + setStyle(['width' => $project->executions[0]->hours->progress . '%']), + ) + ) + ) + ) : null + ) : div + ( + set('class', 'weekly-row'), + div + ( + span + ( + set('class', 'weekly-title'), + $lang->project->weekly + ), + span + ( + set('class', 'weekly-stage'), + $project->current + ) + ), + div + ( + set('class', 'flex'), + div + ( + set('class', 'flex-1'), + div + ( + set('class', 'progress'), + span + ( + set('class', 'mr-4'), + $lang->project->progress . ' : ' . $project->progress . '%' + ), + div + ( + set('class', 'progress-bar'), + set('role', 'progressbar'), + setStyle(['width' => $project->progress . '%']), + ) + ) + ), + div + ( + set('class', 'flex-1 text-center'), + $lang->project->teamCount . ' : ' . $project->teamCount + ), + div + ( + set('class', 'flex-1 text-left'), + $lang->project->budget . ' : ' . ($project->budget != 0 ? $project->budget : $this->lang->project->future) + ), + div(set('class', 'flex-1')) + ) + ) + ); +} + panel ( - set('class', 'welcome-block'), - ul - ( - set('class', 'nav nav-tabs'), - li - ( - set('class', 'nav-item'), - a - ( - set('href', '#tabCount1'), - '标签1' - ) - ), - li - ( - set('class', 'nav-item'), - a - ( - set('href', '#tabCount2'), - '标签2' - ) - ) - ), + set('class', 'projectstatistic-block'), div ( - set('class', 'tab-content'), + set('class', 'flex'), + ul + ( + set('class', 'nav nav-tabs nav-stacked'), + $navTabs + ), div ( - set('class', 'tab-pane'), - set('id', 'tabCount1'), - "我是标签1" - ) - div - ( - set('class', 'tab-pane'), - set('id', 'tabCount2'), - "我是标签2" + set('class', 'tab-content'), + $tabItems ) ) -) +); render(); diff --git a/module/block/view/projectstatisticblock.html.php b/module/block/view/projectstatisticblock.html.php index b19671ee3c..acc2b7f7b5 100755 --- a/module/block/view/projectstatisticblock.html.php +++ b/module/block/view/projectstatisticblock.html.php @@ -88,7 +88,6 @@ html[lang="en"] .product-info .type-info {color: #A6AAB8; text-align: center; po .forty-percent {width: 40%;}