From 82fbee89e344a18fedc2ea325611c46454b1592a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Tue, 19 Oct 2021 15:09:10 +0800 Subject: [PATCH 01/21] * add project api test. --- api/v1/entries/projects.php | 3 +- test/api/project/get.php | 23 ++++++++++++++ test/api/projects/get.php | 18 +++++++++++ test/api/projects/post.php | 61 +++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 test/api/project/get.php create mode 100644 test/api/projects/get.php create mode 100644 test/api/projects/post.php diff --git a/api/v1/entries/projects.php b/api/v1/entries/projects.php index 77f9c45f28..7a09c12e44 100644 --- a/api/v1/entries/projects.php +++ b/api/v1/entries/projects.php @@ -57,6 +57,7 @@ class projectsEntry extends entry $fields = 'name,begin,end,products'; $this->batchSetPost($fields); + $this->setPost('code', $this->request('code', '')); $this->setPost('acl', $this->request('acl', 'private')); $this->setPost('parent', $this->request('program', 0)); $this->setPost('whitelist', $this->request('whitelist', array())); @@ -64,7 +65,7 @@ class projectsEntry extends entry $this->setPost('model', $this->request('model', 'scrum')); $control = $this->loadController('project', 'create'); - $this->requireFields('name,begin,end,products'); + $this->requireFields('name,code,begin,end,products'); $control->create($this->request('model', 'scrum')); 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』`'); // 创建失败,获取错误信息 From 16bd74d1bc52d3059144159353c64de091622227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Wed, 20 Oct 2021 13:25:32 +0800 Subject: [PATCH 02/21] * code for task #43333. --- api/v1/entries/user.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index 8642cff4ee..614091a903 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -165,6 +165,14 @@ class userEntry extends Entry case 'contribute': $info->contribute = $this->my->getContribute(); break; + case 'rights': + $info->rights = array(); + $inAdminGroup = $this->dao->select('t1.*')->from(TABLE_USERGROUP)->alias('t1') + ->leftJoin(TABLE_GROUP)->alias('t2')->on('t1.group=t2.id') + ->where('t1.account')->eq($info->profile->account) + ->andWhere('t2.role')->eq('admin') + ->fetch(); + $info->rights['admin'] = (!empty($inAdminGroup) or $this->app->user->admin); } } From fe1f206e3d080acc48b92d20761e5143cb842678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Wed, 20 Oct 2021 17:08:30 +0800 Subject: [PATCH 03/21] * adjust for task #43336. --- api/v1/entries/tabs.php | 45 +++++++++++ api/v1/entries/user.php | 70 ++++++++++++---- config/routes.php | 1 + module/my/model.php | 175 ++++++++++++++++++++++++++++------------ 4 files changed, 223 insertions(+), 68 deletions(-) create mode 100644 api/v1/entries/tabs.php diff --git a/api/v1/entries/tabs.php b/api/v1/entries/tabs.php new file mode 100644 index 0000000000..cdde9befac --- /dev/null +++ b/api/v1/entries/tabs.php @@ -0,0 +1,45 @@ + + * @package entries + * @version 1 + * @link http://www.zentao.net + */ +class tabsEntry extends baseEntry +{ + /** + * Get tabs. + * + * @param string $moduleName work| + * @access public + * @return void + */ + public function get($moduleName) + { + $menus = array(); + if($moduleName == 'work') + { + $this->app->loadLang('my'); + $tabs = array('calendar', 'task', 'bug', 'story', 'issue', 'risk', 'myMeeting'); + + foreach($tabs as $menuKey) + { + if(!common::hasPriv('my', $menuKey)) continue; + $label = $this->lang->my->$menuKey; + if($menuKey == 'calendar') $label = $this->lang->my->calendarAction; + + $menu = new stdclass(); + $menu->code = $menuKey; + $menu->name = $label; + + $menus[] = $menu; + } + } + + $this->send(200, array('menus' => $menus)); + } +} diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index 614091a903..f3fa1818ae 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -50,7 +50,8 @@ class userEntry extends Entry unset($profile->password); $info->profile = $this->format($profile, 'last:time,locked:time,birthday:date,join:date'); - $info->profile->role = array('code' => $info->profile->role, 'name' => $this->lang->user->roleList[$info->profile->role]); + $info->profile->role = array('code' => $info->profile->role, 'name' => $this->lang->user->roleList[$info->profile->role]); + $info->profile->admin = $this->lang->user->admin; if(!$fields) return $this->send(200, $info); @@ -87,6 +88,7 @@ class userEntry extends Entry break; case 'task': $info->task = array('total' => 0, 'tasks' => array()); + if(!common::hasPriv('my', 'task')) break; $control = $this->loadController('my', 'task'); $control->task($this->param('type', 'assignedTo'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 5), $this->param('page', 1)); @@ -95,12 +97,13 @@ class userEntry extends Entry if($data->status == 'success') { $info->task['total'] = $data->data->pager->recTotal; - $info->task['tasks'] = $data->data->tasks; + $info->task['tasks'] = array_values((array)$data->data->tasks); } break; case 'bug': $info->bug = array('total' => 0, 'bugs' => array()); + if(!common::hasPriv('my', 'bug')) break; $control = $this->loadController('my', 'bug'); $control->bug($this->param('type', 'assignedTo'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 5), $this->param('page', 1)); @@ -109,53 +112,88 @@ class userEntry extends Entry if($data->status == 'success') { $info->bug['total'] = $data->data->pager->recTotal; - $info->bug['bugs'] = $data->data->bugs; + $info->bug['bugs'] = array_values((array)$data->data->bugs); } break; case 'todo': $info->todo = array('total' => 0, 'todos' => array()); + if(!common::hasPriv('my', 'todo')) break; $control = $this->loadController('my', 'todo'); - $control->todo($this->param('date', 'all'), '', 'all', 'date_desc', 0, 0, $this->param('limit', 10), 1); + $control->todo($this->param('date', 'all'), '', 'all', 'date_desc', 0, 0, $this->param('limit', 5), 1); $data = $this->getData(); if($data->status == 'success') { $info->todo['total'] = $data->data->pager->recTotal; - $info->todo['todos'] = $data->data->todos; + $info->todo['todos'] = array_values((array)$data->data->todos); + } + + break; + case 'story': + $info->story = array('total' => 0, 'stories' => array()); + if(!common::hasPriv('my', 'story')) break; + + $control = $this->loadController('my', 'story'); + $control->story($this->param('type', 'assignedTo'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 5), $this->param('page', 1)); + $data = $this->getData(); + + if($data->status == 'success') + { + $info->story['total'] = $data->data->pager->recTotal; + $info->story['stories'] = array_values((array)$data->data->stories); } break; case 'issue': + $info->issue = array('total' => 0, 'issues' => array()); + if(!common::hasPriv('my', 'issue')) break; + if(!empty($this->config->maxVersion)) { - $info->issue = array('total' => 0, 'issues' => array()); - $control = $this->loadController('my', 'issue'); - $control->issue('createdBy', 'id_desc', 0, $this->param('limit', 10), 1); + $control->issue('createdBy', 'id_desc', 0, $this->param('limit', 5), 1); $data = $this->getData(); if($data->status == 'success') { $info->issue['total'] = $data->data->pager->recTotal; - $info->issue['issues'] = $data->data->issues; + $info->issue['issues'] = array_values((array)$data->data->issues); } } break; case 'risk': + $info->risk = array('total' => 0, 'risks' => array()); + if(!common::hasPriv('my', 'risk')) break; + if(!empty($this->config->maxVersion)) { - $info->risk = array('total' => 0, 'risks' => array()); - $control = $this->loadController('my', 'risk'); - $control->risk('createdBy', 'id_desc', 0, $this->param('limit', 10), 1); + $control->risk('createdBy', 'id_desc', 0, $this->param('limit', 5), 1); $data = $this->getData(); if($data->status == 'success') { $info->risk['total'] = $data->data->pager->recTotal; - $info->risk['risks'] = $data->data->risks; + $info->risk['risks'] = array_values((array)$data->data->risks); + } + } + break; + case 'meeting': + $info->meeting = array('total' => 0, 'meetings' => array()); + if(!common::hasPriv('my', 'myMeeting')) break; + + if(!empty($this->config->maxVersion)) + { + $control = $this->loadController('my', 'myMeeting'); + $control->myMeeting('futureMeeting', 'id_desc', 0, $this->param('limit', 5), 1); + $data = $this->getData(); + + if($data->status == 'success') + { + $info->meeting['total'] = $data->data->pager->recTotal; + $info->meeting['meetings'] = array_values((array)$data->data->meetings); } } break; @@ -166,13 +204,15 @@ class userEntry extends Entry $info->contribute = $this->my->getContribute(); break; case 'rights': - $info->rights = array(); $inAdminGroup = $this->dao->select('t1.*')->from(TABLE_USERGROUP)->alias('t1') ->leftJoin(TABLE_GROUP)->alias('t2')->on('t1.group=t2.id') ->where('t1.account')->eq($info->profile->account) ->andWhere('t2.role')->eq('admin') ->fetch(); - $info->rights['admin'] = (!empty($inAdminGroup) or $this->app->user->admin); + + $info->rights = array(); + $info->rights['admin'] = (!empty($inAdminGroup) or $this->app->user->admin); + $info->rights['rights'] = $this->app->user->rights['rights']; } } diff --git a/config/routes.php b/config/routes.php index c55fa34bb2..5ee9b53bb0 100644 --- a/config/routes.php +++ b/config/routes.php @@ -5,6 +5,7 @@ $routes = array(); $routes['/tokens'] = 'tokens'; +$routes['/tabs/:module'] = 'tabs'; $routes['/configurations'] = 'configs'; $routes['/configurations/:name'] = 'config'; diff --git a/module/my/model.php b/module/my/model.php index ce75ba9b1e..c79c0c1551 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -66,39 +66,61 @@ class myModel extends model public function getProducts() { $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'); + ->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'); $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(); + $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) + { + $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(); + ->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(); + ->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'); + ->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) { $execution = $this->loadModel('execution')->getById($execuData->id); @@ -112,7 +134,11 @@ 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; if($product->status != 'closed') $unclosedCount ++; if($product->status == 'closed') unset($products[$key]); } @@ -137,22 +163,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 +205,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'); + $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 = $allConsumed; + $overview->thisYearConsumed = $thisYearConsumed; + } + 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 +280,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,8 +297,14 @@ class myModel extends model $simplifyUsers[$user->account] = $simplifyUser; } + $i = 1; + $maxCount = 5; + $filterActions = array(); foreach($actions as $key => $action) { + if($i > $maxCount) break; + if($action->objectType == 'user') continue; + $simplifyUser = zget($simplifyUsers, $action->actor, ''); $actionActor = $simplifyUser; if(empty($simplifyUser)) @@ -249,9 +315,12 @@ class myModel extends model $actionActor->realname = $action->actor; $actionActor->avatar = ''; } - $actions[$key]->actor = $actionActor; + + $action->actor = $actionActor; + $filterActions[] = $action; + $i++; } - return $actions; + return $filterActions; } } From 784f729a813c510e4531670802c11e6c701f4b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Thu, 21 Oct 2021 15:10:40 +0800 Subject: [PATCH 04/21] * code for task #43338. --- api/v1/entries/tabs.php | 2 +- api/v1/entries/user.php | 16 +++++++++++++++- module/user/model.php | 9 +++++++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/api/v1/entries/tabs.php b/api/v1/entries/tabs.php index cdde9befac..0448b012e9 100644 --- a/api/v1/entries/tabs.php +++ b/api/v1/entries/tabs.php @@ -40,6 +40,6 @@ class tabsEntry extends baseEntry } } - $this->send(200, array('menus' => $menus)); + $this->send(200, array('tabs' => $menus)); } } diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index f3fa1818ae..37dc570bae 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -51,7 +51,7 @@ class userEntry extends Entry $info->profile = $this->format($profile, 'last:time,locked:time,birthday:date,join:date'); $info->profile->role = array('code' => $info->profile->role, 'name' => $this->lang->user->roleList[$info->profile->role]); - $info->profile->admin = $this->lang->user->admin; + $info->profile->admin = strpos($this->app->company->admins, ",{$profile->account},") !== false; if(!$fields) return $this->send(200, $info); @@ -83,6 +83,20 @@ class userEntry extends Entry $info->project['projects'] = $projects->projects; } break; + case 'execution': + $info->execution = array('total' => 0, 'executions' => array()); + if(!common::hasPriv('my', 'execution')) break; + + $control = $this->loadController('my', 'execution'); + $control->execution($this->param('type', 'undone'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 5), $this->param('page', 1)); + $data = $this->getData(); + + if($data->status == 'success') + { + $info->execution['total'] = $data->data->pager->recTotal; + $info->execution['executions'] = array_values((array)$data->data->executions); + } + break; case 'actions': $info->actions = $this->my->getActions(); break; diff --git a/module/user/model.php b/module/user/model.php index 3533390f11..9290a20473 100644 --- a/module/user/model.php +++ b/module/user/model.php @@ -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) From 5920491ee067f82f304ee1b83c534bd59d679c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Thu, 21 Oct 2021 16:22:43 +0800 Subject: [PATCH 05/21] * code for task #43339. --- api/v1/entries/todofinish.php | 32 ++++++++++++++++++++++++++++++++ api/v1/entries/todos.php | 4 ++-- config/routes.php | 5 +++-- module/todo/control.php | 2 ++ 4 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 api/v1/entries/todofinish.php diff --git a/api/v1/entries/todofinish.php b/api/v1/entries/todofinish.php new file mode 100644 index 0000000000..6d6f97339d --- /dev/null +++ b/api/v1/entries/todofinish.php @@ -0,0 +1,32 @@ + + * @package entries + * @version 1 + * @link http://www.zentao.net + */ +class todoFinishEntry extends Entry +{ + /** + * GET method. + * + * @param int $taskID + * @access public + * @return void + */ + public function get($todoID) + { + $control = $this->loadController('todo', 'finish'); + $control->finish($todoID); + + $data = $this->getData(); + if($data->status == 'fail') return $this->sendError(400, $data->message); + + $todo = $this->loadModel('todo')->getByID($todoID); + $this->send(200, $this->format($todo, 'assignedDate:time,finishedDate:time,closedDate:time')); + } +} diff --git a/api/v1/entries/todos.php b/api/v1/entries/todos.php index e03e26d90d..81e612c152 100644 --- a/api/v1/entries/todos.php +++ b/api/v1/entries/todos.php @@ -20,7 +20,7 @@ class todosEntry extends entry public function get() { $control = $this->loadController('my', 'todo'); - $control->todo($this->param('date', 'all'), $this->param('user', ''), $this->param('status', 'all'), $this->param('order', 'date_desc'), 0, $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1)); + $control->todo($this->param('type', 'all'), $this->param('userID', ''), $this->param('status', 'all'), $this->param('order', 'date_desc,status,begin'), $this->param('total', 0), $this->param('limit', 100), $this->param('page', 1)); $data = $this->getData(); if(!isset($data->status)) return $this->sendError(400, 'error'); @@ -63,7 +63,7 @@ class todosEntry extends entry if(isset($data->result) and !isset($data->id)) return $this->sendError(400, $data->message); $todo = $this->loadModel('todo')->getByID($data->id); - + $this->send(201, $this->format($todo, 'assignedDate:time,finishedDate:time,closedDate:time')); } } diff --git a/config/routes.php b/config/routes.php index 5ee9b53bb0..4adf44290a 100644 --- a/config/routes.php +++ b/config/routes.php @@ -63,8 +63,9 @@ $routes['/projects/:projectID/issues'] = 'issues'; $routes['/issues'] = 'issues'; $routes['/issues/:issueID'] = 'issue'; -$routes['/todos'] = 'todos'; -$routes['/todos/:id'] = 'todo'; +$routes['/todos'] = 'todos'; +$routes['/todos/:id'] = 'todo'; +$routes['/todos/:id/finish'] = 'todoFinish'; $routes['/projects/:projectID/builds'] = 'builds'; $routes['/builds'] = 'builds'; diff --git a/module/todo/control.php b/module/todo/control.php index 0746c82942..5b207f0671 100644 --- a/module/todo/control.php +++ b/module/todo/control.php @@ -476,8 +476,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')); } From 96bf22be3a826afdfb1bbb53223bbb21381fcd61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Thu, 21 Oct 2021 16:43:37 +0800 Subject: [PATCH 06/21] * finish task #43339. --- api/v1/entries/todoactivate.php | 32 ++++++++++++++++++++++++++++++++ config/routes.php | 7 ++++--- module/todo/control.php | 1 + 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 api/v1/entries/todoactivate.php diff --git a/api/v1/entries/todoactivate.php b/api/v1/entries/todoactivate.php new file mode 100644 index 0000000000..fe26a2ef2a --- /dev/null +++ b/api/v1/entries/todoactivate.php @@ -0,0 +1,32 @@ + + * @package entries + * @version 1 + * @link http://www.zentao.net + */ +class todoActivateEntry extends Entry +{ + /** + * GET method. + * + * @param int $taskID + * @access public + * @return void + */ + public function get($todoID) + { + $control = $this->loadController('todo', 'activate'); + $control->activate($todoID); + + $data = $this->getData(); + if($data->status == 'fail') return $this->sendError(400, $data->message); + + $todo = $this->loadModel('todo')->getByID($todoID); + $this->send(200, $this->format($todo, 'assignedDate:time,finishedDate:time,closedDate:time')); + } +} diff --git a/config/routes.php b/config/routes.php index 4adf44290a..53e4fec658 100644 --- a/config/routes.php +++ b/config/routes.php @@ -63,9 +63,10 @@ $routes['/projects/:projectID/issues'] = 'issues'; $routes['/issues'] = 'issues'; $routes['/issues/:issueID'] = 'issue'; -$routes['/todos'] = 'todos'; -$routes['/todos/:id'] = 'todo'; -$routes['/todos/:id/finish'] = 'todoFinish'; +$routes['/todos'] = 'todos'; +$routes['/todos/:id'] = 'todo'; +$routes['/todos/:id/finish'] = 'todoFinish'; +$routes['/todos/:id/activate'] = 'todoActivate'; $routes['/projects/:projectID/builds'] = 'builds'; $routes['/builds'] = 'builds'; diff --git a/module/todo/control.php b/module/todo/control.php index 5b207f0671..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')); } From 43ac49410668f893380db85fd835ea6d87aa5039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Mon, 25 Oct 2021 08:42:13 +0800 Subject: [PATCH 07/21] * fix bug for loophole. --- module/action/model.php | 2 +- module/product/model.php | 13 +++++++------ module/testcase/control.php | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/module/action/model.php b/module/action/model.php index 61cf3e0d9b..3584f68631 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -69,7 +69,7 @@ class actionModel extends model $this->dao->insert(TABLE_ACTION)->data($action)->autoCheck()->exec(); - $actionID = $this->dbh->lastInsertID(); + $actionID = $this->dao->lastInsertID(); if($this->post->uid) $this->file->updateObjectID($this->post->uid, $objectID, $objectType); diff --git a/module/product/model.php b/module/product/model.php index d8be0d310d..e5ca8375eb 100644 --- a/module/product/model.php +++ b/module/product/model.php @@ -145,14 +145,15 @@ class productModel extends model */ public function saveState($productID, $products) { - if($productID > 0) $this->session->set('product', (int)$productID); - if($productID == 0 and $this->cookie->lastProduct) $this->session->set('product', (int)$this->cookie->lastProduct); - if($productID == 0 and $this->session->product == '') $this->session->set('product', key($products)); + if($productID == 0 and $this->cookie->lastProduct) $productID = $this->cookie->lastProduct; + 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(); + $productID = key($products); + $this->session->set('product', (int)$productID, $this->app->tab); + if($productID && strpos(",{$this->app->user->view->products},", ",{$productID},") === false) $this->accessDenied(); } if($this->cookie->preProductID != $productID) { diff --git a/module/testcase/control.php b/module/testcase/control.php index aed4fff98d..b9ae238cda 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -1307,7 +1307,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; From 95fde8eaca079df47413545b4697e07cf62000a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Mon, 25 Oct 2021 09:49:14 +0800 Subject: [PATCH 08/21] * code task #43342. --- api/v1/entries/executions.php | 2 +- api/v1/entries/program.php | 2 +- api/v1/entries/user.php | 12 +++++++++++- module/my/model.php | 13 +++++++++---- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/api/v1/entries/executions.php b/api/v1/entries/executions.php index 41de1524eb..720df0e3a7 100644 --- a/api/v1/entries/executions.php +++ b/api/v1/entries/executions.php @@ -21,7 +21,7 @@ class executionsEntry extends entry public function get($projectID = 0) { $control = $this->loadController('execution', 'all'); - $control->all($this->param('status', 'all'), $this->param('project', $projectID), $this->param('order', 'id_desc'), 0, $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1)); + $control->all($this->param('status', 'all'), $this->param('project', $projectID), $this->param('order', 'id_desc'), 0, 0, $this->param('limit', 20), $this->param('page', 1)); $data = $this->getData(); if(isset($data->status) and $data->status == 'success') diff --git a/api/v1/entries/program.php b/api/v1/entries/program.php index f423487ef4..17a023d90b 100644 --- a/api/v1/entries/program.php +++ b/api/v1/entries/program.php @@ -9,6 +9,6 @@ * @version 1 * @link http://www.zentao.net */ -class ProgramEntry extends Entry +class programEntry extends Entry { } diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index 37dc570bae..030090ba3e 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -66,7 +66,17 @@ class userEntry extends Entry case 'product': $info->product = array('total' => 0, 'products' => array()); - $products = $this->my->getProducts(); + $products = $this->my->getProducts('ownbyme'); + if($products) + { + $info->product['total'] = $products->allCount; + $info->product['products'] = $products->products; + } + break; + case 'undoneproduct': + $info->product = array('total' => 0, 'products' => array()); + + $products = $this->my->getProducts('undone'); if($products) { $info->product['total'] = $products->allCount; diff --git a/module/my/model.php b/module/my/model.php index c79c0c1551..da415df6a8 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -60,14 +60,17 @@ 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($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'); @@ -121,10 +124,11 @@ class myModel extends model ->andWhere('t2.deleted')->eq(0) ->orderBy('t1.project') ->fetchAll('product'); - foreach($executions as $key => $execuData) + $this->loadModel('execution'); + foreach($executions as $productID => $execution) { - $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; + $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); @@ -139,6 +143,7 @@ class myModel extends model $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]); } From f21be3ec126a2aecbbe91d9708d2470a3239632b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Mon, 25 Oct 2021 13:53:34 +0800 Subject: [PATCH 09/21] * code for task #43341. --- api/v1/entries/user.php | 26 +++++++++++++++++++++++++- module/bug/lang/zh-cn.php | 15 +++++++++------ module/my/model.php | 2 +- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index 030090ba3e..c543aac41d 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -135,8 +135,32 @@ class userEntry extends Entry if($data->status == 'success') { + $bugs = array(); + foreach($data->data->bugs as $bug) + { + $status = array('code' => $bug->status, 'name' => $this->lang->bug->statusList[$bug->status]); + if($bug->status == 'active' and $bug->confirmed) $status = array('code' => 'confirmed', 'name' => $this->lang->bug->labelConfirmed); + if($bug->resolution == 'postponed') $status = array('code' => 'postponed', 'name' => $this->lang->bug->labelPostponed); + if(!empty($bug->delay)) $status = array('code' => 'delay', 'name' => $this->lang->bug->overdueBugs); + $bug->status = $status; + + $bugs[$bug->id] = $bug; + } + + $storyChangeds = $this->dao->select('t1.id')->from(TABLE_BUG)->alias('t1') + ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story=t2.id') + ->where('t1.id')->in(array_keys($bugs)) + ->andWhere('t1.story')->ne('0') + ->andWhere('t1.storyVersion != t2.version') + ->fetchAll(); + foreach($storyChangeds as $bugID) + { + $status = array('code' => 'storyChanged', 'name' => $this->lang->bug->storyChanged); + $bugs[$bugID]->status = $status; + } + $info->bug['total'] = $data->data->pager->recTotal; - $info->bug['bugs'] = array_values((array)$data->data->bugs); + $info->bug['bugs'] = array_values($bugs); } break; diff --git a/module/bug/lang/zh-cn.php b/module/bug/lang/zh-cn.php index 8c11dead7d..fe7157f155 100644 --- a/module/bug/lang/zh-cn.php +++ b/module/bug/lang/zh-cn.php @@ -150,12 +150,15 @@ $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->storyChanged = '需求变动'; /* 页面标签。*/ $lang->bug->lblAssignedTo = '当前指派'; diff --git a/module/my/model.php b/module/my/model.php index da415df6a8..0b2a63a211 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -66,7 +66,7 @@ class myModel extends model */ public function getProducts($type = 'undone') { - $products = $this->dao->select('t1.id as id,t1.*')->from(TABLE_PRODUCT)->alias('t1') + $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() From 7372f327d98afa76ddfe47ff2d9dde5671c1a8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Tue, 26 Oct 2021 09:41:15 +0800 Subject: [PATCH 10/21] * code for task #43565. --- api/v1/entries/langs.php | 50 ++++++++++++++++++++++++++++++++++++++++ api/v1/entries/user.php | 9 +++++++- config/routes.php | 2 ++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 api/v1/entries/langs.php diff --git a/api/v1/entries/langs.php b/api/v1/entries/langs.php new file mode 100644 index 0000000000..09e1e66947 --- /dev/null +++ b/api/v1/entries/langs.php @@ -0,0 +1,50 @@ + + * @package entries + * @version 1 + * @link http://www.zentao.net + */ +class langsEntry extends entry +{ + /** + * GET method. + * + * @access public + * @return void + */ + public function get() + { + $modules = $this->param('modules', ''); + $language = $this->param('lang', ''); + + if($language and !isset($this->config->langs[$language])) return $this->sendError(400, 'Error lang parameter'); + if(empty($modules)) return $this->sendError(400, 'Need modules'); + + if($language) $this->app->setClientLang($language); + + $modules = explode(',', $modules); + foreach($modules as $module) + { + if($module == 'all') + { + foreach(glob($this->app->getModuleRoot() . '*') as $modulePath) + { + if(!is_dir($modulePath)) continue; + + $moduleName = basename($modulePath); + $this->app->loadLang($moduleName); + } + break; + } + + $this->app->loadLang($module); + } + + return $this->send(200, array('lang' => $this->lang)); + } +} diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index c543aac41d..440e9d900f 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -189,8 +189,15 @@ class userEntry extends Entry if($data->status == 'success') { + $stories = array(); + foreach($data->data->stories as $story) + { + $story->status = array('code' => $story->status, 'name' => $this->lang->story->statusList[$story->status]); + $stories[$story->id] = $story; + } + $info->story['total'] = $data->data->pager->recTotal; - $info->story['stories'] = array_values((array)$data->data->stories); + $info->story['stories'] = array_values($stories); } break; diff --git a/config/routes.php b/config/routes.php index 53e4fec658..05abfdf681 100644 --- a/config/routes.php +++ b/config/routes.php @@ -5,6 +5,8 @@ $routes = array(); $routes['/tokens'] = 'tokens'; +$routes['/langs'] = 'langs'; + $routes['/tabs/:module'] = 'tabs'; $routes['/configurations'] = 'configs'; From 0172e0106e86a76c17797b77b093dba8078a0ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Tue, 26 Oct 2021 09:47:26 +0800 Subject: [PATCH 11/21] * code for task #43565. --- api/v1/entries/langs.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/v1/entries/langs.php b/api/v1/entries/langs.php index 09e1e66947..c1ae36bbd5 100644 --- a/api/v1/entries/langs.php +++ b/api/v1/entries/langs.php @@ -25,7 +25,8 @@ class langsEntry extends entry if($language and !isset($this->config->langs[$language])) return $this->sendError(400, 'Error lang parameter'); if(empty($modules)) return $this->sendError(400, 'Need modules'); - if($language) $this->app->setClientLang($language); + if(empty($language)) $language = 'zh-cn'; + $this->app->setClientLang($language); $modules = explode(',', $modules); foreach($modules as $module) From 7fb8c5ca8b5498548d520e613c621d5679f0a279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Tue, 26 Oct 2021 10:57:52 +0800 Subject: [PATCH 12/21] * adjust for task #43565. --- api/v1/entries/langs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/entries/langs.php b/api/v1/entries/langs.php index c1ae36bbd5..a29ee1b0f8 100644 --- a/api/v1/entries/langs.php +++ b/api/v1/entries/langs.php @@ -46,6 +46,6 @@ class langsEntry extends entry $this->app->loadLang($module); } - return $this->send(200, array('lang' => $this->lang)); + return $this->send(200, $this->lang); } } From 001ca0ef8f2b372ec89f59251b6a1c9ef87574f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Tue, 26 Oct 2021 15:39:46 +0800 Subject: [PATCH 13/21] * code for task #43347. --- api/v1/entries/execution.php | 11 ++++++++++- api/v1/entries/executions.php | 3 +++ api/v1/entries/projects.php | 2 ++ framework/api/entry.class.php | 24 ++++++++++++++++++++++++ module/program/model.php | 3 ++- 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/api/v1/entries/execution.php b/api/v1/entries/execution.php index 534b70d98f..0868abf625 100644 --- a/api/v1/entries/execution.php +++ b/api/v1/entries/execution.php @@ -36,7 +36,7 @@ class executionEntry extends Entry if(!$fields) $this->send(200, $execution); /* Set other fields. */ - $fields = explode(',', $fields); + $fields = explode(',', strtolower($fields)); foreach($fields as $field) { switch($field) @@ -50,6 +50,15 @@ class executionEntry extends Entry $execution->modules = $data->data->tree; } break; + case 'moduleoptionmenu': + $execution->moduleOptionMenu = $this->loadModel('tree')->getTaskOptionMenu($executionID, 0, 0, 'allModule'); + break; + case 'members': + $execution->members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');; + break; + case 'stories': + $execution->stories = $this->loadModel('story')->getExecutionStoryPairs($executionID, 0, 0, '', 'full', 'unclosed'); + break; } } diff --git a/api/v1/entries/executions.php b/api/v1/entries/executions.php index 720df0e3a7..9d378362aa 100644 --- a/api/v1/entries/executions.php +++ b/api/v1/entries/executions.php @@ -20,6 +20,8 @@ class executionsEntry extends entry */ public function get($projectID = 0) { + $returnFields = explode(',', $this->param('return', '')); + $control = $this->loadController('execution', 'all'); $control->all($this->param('status', 'all'), $this->param('project', $projectID), $this->param('order', 'id_desc'), 0, 0, $this->param('limit', 20), $this->param('page', 1)); $data = $this->getData(); @@ -30,6 +32,7 @@ class executionsEntry extends entry $result = array(); foreach($data->data->executionStats as $execution) { + if($returnFields) $execution = $this->filterFields($execution, $returnFields); $result[] = $this->format($execution, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,begin:date,end:date,realBegan:date,realEnd:date,deleted:bool'); } return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'executions' => $result)); diff --git a/api/v1/entries/projects.php b/api/v1/entries/projects.php index 7a09c12e44..470731ef41 100644 --- a/api/v1/entries/projects.php +++ b/api/v1/entries/projects.php @@ -21,6 +21,7 @@ class projectsEntry extends entry public function get($programID = 0) { if(!$programID) $programID = $this->param('program', 0); + $returnFields = explode(',', $this->param('return', '')); $control = $this->loadController('project', 'browse'); $control->browse($programID, $this->param('status', 'all'), 0, $this->param('order', 'order_asc'), 0, $this->param('limit', 20), $this->param('page', 1)); @@ -32,6 +33,7 @@ class projectsEntry extends entry $result = array(); foreach($data->data->projectStats as $project) { + if($returnFields) $project = $this->filterFields($project, $returnFields); $result[] = $this->format($project, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time'); } return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => (int)$pager->recPerPage, 'projects' => $result)); diff --git a/framework/api/entry.class.php b/framework/api/entry.class.php index 39f469e22a..41b6284a60 100644 --- a/framework/api/entry.class.php +++ b/framework/api/entry.class.php @@ -486,6 +486,8 @@ class baseEntry $type = $field[1]; $isArray = false; + if(!isset($object->$key)) continue; + $pos = strpos($type, ']'); if($pos !== FALSE) { @@ -519,6 +521,28 @@ class baseEntry } } + /** + * Filter fields. + * + * @param object $object + * @param array $filters + * @access public + * @return object + */ + public function filterFields($object, $allowable = '') + { + if(empty($allowable)) return $object; + + $filtered = new stdclass(); + foreach($allowable as $field) + { + if(!isset($object->$field)) continue; + $filtered->$field = $object->$field; + } + + return $filtered; + } + /** * 类型转换. * Typecasting. diff --git a/module/program/model.php b/module/program/model.php index 0ba7e0cfd4..5c1ec55921 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() From 089a1204f104ae3548134aec308bf403fd13c8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Tue, 26 Oct 2021 16:18:35 +0800 Subject: [PATCH 14/21] * fix for return fields. --- api/v1/entries/executions.php | 2 +- api/v1/entries/projects.php | 2 +- framework/api/entry.class.php | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/api/v1/entries/executions.php b/api/v1/entries/executions.php index 9d378362aa..08409c4bf0 100644 --- a/api/v1/entries/executions.php +++ b/api/v1/entries/executions.php @@ -20,7 +20,7 @@ class executionsEntry extends entry */ public function get($projectID = 0) { - $returnFields = explode(',', $this->param('return', '')); + $returnFields = $this->param('return', ''); $control = $this->loadController('execution', 'all'); $control->all($this->param('status', 'all'), $this->param('project', $projectID), $this->param('order', 'id_desc'), 0, 0, $this->param('limit', 20), $this->param('page', 1)); diff --git a/api/v1/entries/projects.php b/api/v1/entries/projects.php index 470731ef41..2ff535b664 100644 --- a/api/v1/entries/projects.php +++ b/api/v1/entries/projects.php @@ -21,7 +21,7 @@ class projectsEntry extends entry public function get($programID = 0) { if(!$programID) $programID = $this->param('program', 0); - $returnFields = explode(',', $this->param('return', '')); + $returnFields = $this->param('return', ''); $control = $this->loadController('project', 'browse'); $control->browse($programID, $this->param('status', 'all'), 0, $this->param('order', 'order_asc'), 0, $this->param('limit', 20), $this->param('page', 1)); diff --git a/framework/api/entry.class.php b/framework/api/entry.class.php index 41b6284a60..8eec15e274 100644 --- a/framework/api/entry.class.php +++ b/framework/api/entry.class.php @@ -532,10 +532,12 @@ class baseEntry public function filterFields($object, $allowable = '') { if(empty($allowable)) return $object; + if(is_string($allowable)) $allowable = explode(',', $allowable); $filtered = new stdclass(); foreach($allowable as $field) { + $field = trim($field); if(!isset($object->$field)) continue; $filtered->$field = $object->$field; } From 4b31274644e41789f3f04f9d62156c5290db7aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Tue, 26 Oct 2021 16:25:25 +0800 Subject: [PATCH 15/21] * adjust for get undone product. --- api/v1/entries/user.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index 440e9d900f..0f4158614e 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -56,7 +56,7 @@ class userEntry extends Entry if(!$fields) return $this->send(200, $info); /* Set other fields. */ - $fields = explode(',', $fields); + $fields = explode(',', strtolower($fields)); $this->loadModel('my'); foreach($fields as $field) @@ -74,13 +74,13 @@ class userEntry extends Entry } break; case 'undoneproduct': - $info->product = array('total' => 0, 'products' => array()); + $info->undoneProduct = array('total' => 0, 'products' => array()); $products = $this->my->getProducts('undone'); if($products) { - $info->product['total'] = $products->allCount; - $info->product['products'] = $products->products; + $info->undoneProduct['total'] = $products->allCount; + $info->undoneProduct['products'] = $products->products; } break; case 'project': From 076f32891492262d59b594f1c6882154f81e7247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Wed, 27 Oct 2021 19:58:50 +0800 Subject: [PATCH 16/21] * add lang for zentaoapp and adjust api. --- api/v1/entries/execution.php | 5 ++++- api/v1/entries/executions.php | 4 ++-- api/v1/entries/projects.php | 4 ++-- framework/api/entry.class.php | 1 + module/block/lang/zh-cn.php | 17 +++++++++++++++++ module/common/lang/zh-cn.php | 11 +++++++---- module/my/lang/zh-cn.php | 1 + module/my/model.php | 2 +- module/program/model.php | 2 +- module/project/model.php | 22 +++++++++++----------- module/todo/lang/zh-cn.php | 1 + module/user/lang/zh-cn.php | 30 ++++++++++++++++++++---------- 12 files changed, 68 insertions(+), 32 deletions(-) diff --git a/api/v1/entries/execution.php b/api/v1/entries/execution.php index 0868abf625..ad0881dfbb 100644 --- a/api/v1/entries/execution.php +++ b/api/v1/entries/execution.php @@ -57,7 +57,10 @@ class executionEntry extends Entry $execution->members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');; break; case 'stories': - $execution->stories = $this->loadModel('story')->getExecutionStoryPairs($executionID, 0, 0, '', 'full', 'unclosed'); + $stories = $this->loadModel('story')->getExecutionStories($executionID); + foreach($stories as $storyID => $story) $stories[$storyID] = $this->filterFields($story, 'id,title,module,pri,status,stage,estimate'); + + $execution->stories = array_values($stories); break; } } diff --git a/api/v1/entries/executions.php b/api/v1/entries/executions.php index 08409c4bf0..594f5722cd 100644 --- a/api/v1/entries/executions.php +++ b/api/v1/entries/executions.php @@ -20,7 +20,7 @@ class executionsEntry extends entry */ public function get($projectID = 0) { - $returnFields = $this->param('return', ''); + $appendFields = $this->param('fields', ''); $control = $this->loadController('execution', 'all'); $control->all($this->param('status', 'all'), $this->param('project', $projectID), $this->param('order', 'id_desc'), 0, 0, $this->param('limit', 20), $this->param('page', 1)); @@ -32,7 +32,7 @@ class executionsEntry extends entry $result = array(); foreach($data->data->executionStats as $execution) { - if($returnFields) $execution = $this->filterFields($execution, $returnFields); + $execution = $this->filterFields($execution, 'id,name,project,code,type,parent,begin,end,status,openedBy,openedDate,' . $appendFields); $result[] = $this->format($execution, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time,begin:date,end:date,realBegan:date,realEnd:date,deleted:bool'); } return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'executions' => $result)); diff --git a/api/v1/entries/projects.php b/api/v1/entries/projects.php index 2ff535b664..4ff7ba360f 100644 --- a/api/v1/entries/projects.php +++ b/api/v1/entries/projects.php @@ -21,7 +21,7 @@ class projectsEntry extends entry public function get($programID = 0) { if(!$programID) $programID = $this->param('program', 0); - $returnFields = $this->param('return', ''); + $appendFields = $this->param('fields', ''); $control = $this->loadController('project', 'browse'); $control->browse($programID, $this->param('status', 'all'), 0, $this->param('order', 'order_asc'), 0, $this->param('limit', 20), $this->param('page', 1)); @@ -33,7 +33,7 @@ class projectsEntry extends entry $result = array(); foreach($data->data->projectStats as $project) { - if($returnFields) $project = $this->filterFields($project, $returnFields); + $project = $this->filterFields($project, 'id,name,code,type,parent,begin,end,status,openedBy,openedDate,' . $appendFields); $result[] = $this->format($project, 'openedDate:time,lastEditedDate:time,closedDate:time,canceledDate:time'); } return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => (int)$pager->recPerPage, 'projects' => $result)); diff --git a/framework/api/entry.class.php b/framework/api/entry.class.php index 8eec15e274..7a0f698d7d 100644 --- a/framework/api/entry.class.php +++ b/framework/api/entry.class.php @@ -538,6 +538,7 @@ class baseEntry foreach($allowable as $field) { $field = trim($field); + if(empty($field)) continue; if(!isset($object->$field)) continue; $filtered->$field = $object->$field; } diff --git a/module/block/lang/zh-cn.php b/module/block/lang/zh-cn.php index dbd8e2ed9f..7ce129a602 100644 --- a/module/block/lang/zh-cn.php +++ b/module/block/lang/zh-cn.php @@ -585,3 +585,20 @@ $lang->block->flowchart['project'] = array('项目经理', '创建' . $lang->exe if($config->systemMode == 'new') $lang->block->flowchart['project'] = array('项目经理', '创建项目、' . $lang->executionCommon, '维护团队', "关联需求", '分解任务', '跟踪进度'); $lang->block->flowchart['dev'] = array('研发人员', '领取任务和Bug', '设计实现方案', '更新状态', '完成任务和Bug', '提交代码'); $lang->block->flowchart['tester'] = array('测试人员', '撰写用例', '执行用例', '提交Bug', '验证Bug', '关闭Bug'); + +$lang->block->zentaoapp = new stdclass(); +$lang->block->zentaoapp->thisYearInvestment = '今年投入'; +$lang->block->zentaoapp->sinceTotalInvestment = '从使用至今,总投入'; +$lang->block->zentaoapp->myStory = '我的需求'; +$lang->block->zentaoapp->allStorySum = '需求总数'; +$lang->block->zentaoapp->storyCompleteRate = '需求完成率'; +$lang->block->zentaoapp->latestExecution = '近期执行'; +$lang->block->zentaoapp->involvedExecution = '我参与的执行'; +$lang->block->zentaoapp->mangedProduct = '负责产品'; +$lang->block->zentaoapp->involvedProject = '参与项目'; +$lang->block->zentaoapp->customIndexCard = '定制首页卡片'; +$lang->block->zentaoapp->createStory = '提需求'; +$lang->block->zentaoapp->createEffort = '记日志'; +$lang->block->zentaoapp->createDoc = '建文档'; +$lang->block->zentaoapp->createTodo = '建待办'; +$lang->block->zentaoapp->workbench = '工作台'; diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index 3888dd0a4d..44de2a8baf 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 = '全选'; @@ -304,7 +306,8 @@ $lang->createObjects['program'] = '项目集'; $lang->createObjects['doc'] = '文档'; /* 语言 */ -$lang->lang = 'Language'; +$lang->lang = 'Language'; +$lang->setLang = '语音设置'; /* 风格列表。*/ $lang->theme = '主题'; 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 0b2a63a211..c21f4f159f 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -222,7 +222,7 @@ class myModel extends model $allConsumed = 0; $thisYearConsumed = 0; - $projects = $this->loadModel('project')->getOverviewList('byStatus', 'all', 'id_desc'); + $projects = $this->loadModel('project')->getOverviewList('byStatus', 'all', 'id_desc', 0); $projectsConsumed = $this->project->getProjectsConsumed(array_keys($projects), 'THIS_YEAR'); foreach($projects as $project) { diff --git a/module/program/model.php b/module/program/model.php index 5c1ec55921..af32a189a8 100644 --- a/module/program/model.php +++ b/module/program/model.php @@ -444,7 +444,7 @@ class programModel extends model ->where('deleted')->eq('0') ->beginIF($this->config->systemMode == 'new')->andWhere('type')->eq('project')->fi() ->beginIF($browseType != 'all' and $browseType != 'undone')->andWhere('status')->eq($browseType)->fi() - ->beginIF($browseType != 'undone')->andWhere('status')->in('wait,doing')->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() diff --git a/module/project/model.php b/module/project/model.php index 24dcbda385..44358df6ad 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -286,18 +286,18 @@ class projectModel extends model $hours = $this->dao->select('t2.parent as project, sum(t1.consumed) as consumed, sum(t1.estimate) 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('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('t1.deleted')->eq(0) ->andWhere('t1.status')->in('wait,doing,pause') - ->groupBy('t2.parent') + ->groupBy('t2.project') ->fetchPairs(); $this->loadModel('product'); @@ -412,13 +412,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) 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 = <<
-
不能创建临时目录,请确认目录%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 = '退出登录'; From fb17df4424b74f0456a3aa249d6f1956716ed8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Thu, 28 Oct 2021 13:12:33 +0800 Subject: [PATCH 17/21] * add upload file api. --- api/v1/entries/execution.php | 1 + api/v1/entries/files.php | 37 ++++++++++++++++++++++++++++++++++++ api/v1/entries/tasks.php | 4 ++-- config/routes.php | 2 ++ module/file/control.php | 2 +- module/my/model.php | 4 ++-- module/task/control.php | 16 ++++++++-------- 7 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 api/v1/entries/files.php diff --git a/api/v1/entries/execution.php b/api/v1/entries/execution.php index ad0881dfbb..4134a3c3d3 100644 --- a/api/v1/entries/execution.php +++ b/api/v1/entries/execution.php @@ -55,6 +55,7 @@ class executionEntry extends Entry break; case 'members': $execution->members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');; + unset($execution->members['']); break; case 'stories': $stories = $this->loadModel('story')->getExecutionStories($executionID); diff --git a/api/v1/entries/files.php b/api/v1/entries/files.php new file mode 100644 index 0000000000..17faf9f28f --- /dev/null +++ b/api/v1/entries/files.php @@ -0,0 +1,37 @@ + + * @package entries + * @version 1 + * @link http://www.zentao.net + */ +class filesEntry extends Entry +{ + /** + * POST method. + * + * @access public + * @return void + */ + public function post() + { + $uid = $this->param('uid', ''); + + $control = $this->loadController('file', 'ajaxUpload'); + $control->ajaxUpload($uid); + + $data = $this->getData(); + + if(!$data or !isset($data->status)) return $this->send400('error'); + if(isset($data->status) and $data->status == 'error') + { + return isset($data->code) and $data->code == 404 ? $this->send404() : $this->sendError(400, $data->message); + } + + $this->send(200, array('id' => $data->id)); + } +} diff --git a/api/v1/entries/tasks.php b/api/v1/entries/tasks.php index 0a6b31ac07..160c6fce35 100644 --- a/api/v1/entries/tasks.php +++ b/api/v1/entries/tasks.php @@ -9,7 +9,7 @@ * @version 1 * @link http://www.zentao.net */ -class tasksEntry extends entry +class tasksEntry extends entry { /** * GET method. @@ -71,7 +71,7 @@ class tasksEntry extends entry $this->requireFields('name,assignedTo,type,estStarted,deadline'); $control->create($executionID, $this->request('storyID', 0), $this->request('moduleID', 0), $this->request('copyTaskID', 0), $this->request('copyTodoID', 0)); - + $data = $this->getData(); if(!isset($data->id)) return $this->sendError(400, $data->message); diff --git a/config/routes.php b/config/routes.php index 05abfdf681..3bab8cee8c 100644 --- a/config/routes.php +++ b/config/routes.php @@ -9,6 +9,8 @@ $routes['/langs'] = 'langs'; $routes['/tabs/:module'] = 'tabs'; +$routes['/files'] = 'files'; + $routes['/configurations'] = 'configs'; $routes['/configurations/:name'] = 'config'; diff --git a/module/file/control.php b/module/file/control.php index ba4318d1d4..3ae7ddde32 100644 --- a/module/file/control.php +++ b/module/file/control.php @@ -79,7 +79,7 @@ class file extends control if(defined('RUN_MODE') && RUN_MODE == 'api') { $_SERVER['SCRIPT_NAME'] = 'index.php'; - die(json_encode(array('status' => 'success', 'data' => commonModel::getSysURL() . $this->config->webRoot . $url))); + die(json_encode(array('status' => 'success', 'id' => $fileID, 'data' => commonModel::getSysURL() . $this->config->webRoot . $url))); } else { diff --git a/module/my/model.php b/module/my/model.php index c21f4f159f..2e858a1e26 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -231,8 +231,8 @@ class myModel extends model } $overview->projectTotal = count($projects); - $overview->allConsumed = $allConsumed; - $overview->thisYearConsumed = $thisYearConsumed; + $overview->allConsumed = round($allConsumed, 1); + $overview->thisYearConsumed = round($thisYearConsumed, 1); } else { diff --git a/module/task/control.php b/module/task/control.php index 0c39718954..4348f64c7b 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')); @@ -408,7 +408,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)); } @@ -576,7 +576,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())); } @@ -585,7 +585,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')); } @@ -780,7 +780,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())); } @@ -808,7 +808,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')); } @@ -940,7 +940,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); From 2c3d42360c5eaee0574d2a54bda0ad816f14212b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Thu, 28 Oct 2021 16:26:16 +0800 Subject: [PATCH 18/21] * adjust for delete file session. --- api/v1/entries/file.php | 28 ++++++++++++++++++++++++++++ api/v1/entries/tasks.php | 2 +- config/routes.php | 3 ++- module/file/control.php | 7 ++++--- module/file/model.php | 2 +- module/task/model.php | 1 - 6 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 api/v1/entries/file.php diff --git a/api/v1/entries/file.php b/api/v1/entries/file.php new file mode 100644 index 0000000000..b69ed0539d --- /dev/null +++ b/api/v1/entries/file.php @@ -0,0 +1,28 @@ + + * @package entries + * @version 1 + * @link http://www.zentao.net + */ +class fileEntry extends Entry +{ + /** + * PUT method. + * + * @access public + * @return void + */ + public function put($fileID) + { + $uid = $this->param('uid', ''); + $action = $this->param('action', ''); + if($action == 'remove') unset($_SESSION['album']['used'][$uid][$fileID]); + + $this->send(200, array('id' => $fileID)); + } +} diff --git a/api/v1/entries/tasks.php b/api/v1/entries/tasks.php index 160c6fce35..3f04889092 100644 --- a/api/v1/entries/tasks.php +++ b/api/v1/entries/tasks.php @@ -61,7 +61,7 @@ class tasksEntry extends entry */ public function post($executionID) { - $fields = 'name,type,assignedTo,estimate,story,parent,execution,module,pri,desc,estStarted,deadline,mailto'; + $fields = 'name,type,assignedTo,estimate,story,execution,project,module,pri,desc,estStarted,deadline,mailto,team,teamEstimate,multiple,uid'; $this->batchSetPost($fields); $assignedTo = $this->request('assignedTo'); diff --git a/config/routes.php b/config/routes.php index 3bab8cee8c..0ab76b3989 100644 --- a/config/routes.php +++ b/config/routes.php @@ -9,7 +9,8 @@ $routes['/langs'] = 'langs'; $routes['/tabs/:module'] = 'tabs'; -$routes['/files'] = 'files'; +$routes['/files'] = 'files'; +$routes['/files/:id'] = 'file'; $routes['/configurations'] = 'configs'; $routes['/configurations/:name'] = 'config'; diff --git a/module/file/control.php b/module/file/control.php index 3ae7ddde32..b36252c3c3 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', 'id' => $fileID, '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)); } /** diff --git a/module/file/model.php b/module/file/model.php index 5cc061e6f7..112a8b4d6d 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/task/model.php b/module/task/model.php index 52acee0b86..0a0606e289 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; From a89fb80abcaac72e2d641714c8d072489d59f95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Fri, 29 Oct 2021 11:11:09 +0800 Subject: [PATCH 19/21] * fix bug for export testcase. --- module/testcase/control.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/testcase/control.php b/module/testcase/control.php index b9ae238cda..313833643a 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -1368,7 +1368,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']; From c295854d261a165cf0df54808d8a7f9ce1d60e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Fri, 29 Oct 2021 14:01:52 +0800 Subject: [PATCH 20/21] * adjust for users api. --- api/v1/entries/user.php | 2 +- api/v1/entries/users.php | 34 ++++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index 0f4158614e..500721942d 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -242,7 +242,7 @@ class userEntry extends Entry if(!empty($this->config->maxVersion)) { $control = $this->loadController('my', 'myMeeting'); - $control->myMeeting('futureMeeting', 'id_desc', 0, $this->param('limit', 5), 1); + $control->myMeeting('all', 'id_desc', 0, $this->param('limit', 5), 1); $data = $this->getData(); if($data->status == 'success') diff --git a/api/v1/entries/users.php b/api/v1/entries/users.php index 3401fc4781..c3ae22f01a 100644 --- a/api/v1/entries/users.php +++ b/api/v1/entries/users.php @@ -9,7 +9,7 @@ * @version 1 * @link http://www.zentao.net */ -class usersEntry extends entry +class usersEntry extends entry { /** * GET method. @@ -19,22 +19,32 @@ class usersEntry extends entry */ public function get() { - $control = $this->loadController('company', 'browse'); - $control->browse('inside', 0, $this->param('type', 'bydept'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1)); - $data = $this->getData(); + if(!common::hasPriv('company', 'browse')) return $this->sendError(400, 'error: no company-browse priv.'); - if(isset($data->status) and $data->status == 'success') + $appendFields = $this->param('fileds', ''); + $type = $this->param('type', 'bydept'); + $limit = (int)$this->param('limit', 20); + + $pager = null; + if($limit) { - $users = $data->data->users; - $pager = $data->data->pager; - $result = array(); - foreach($users as $user) $result[] = $this->format($user, 'locked:time'); - return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'users' => $result)); + $this->app->loadClass('pager', $static = true); + $pager = pager::init(0, $limit, $this->param('page', 1)); } - if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message); + $users = $this->loadModel('company')->getUsers($this->param('browse', 'inside'), $type, 0, 0, $this->param('order', 'id_desc'), $pager); + $result = array(); + foreach($users as $user) + { + $user = $this->filterFields($user, 'id,dept,account,realname,role,pinyin,email,' . $appendFields); + $result[] = $this->format($user, 'locked:time'); + } - return $this->sendError(400, 'error'); + $pageID = $pager ? $pager->pageID : 1; + $total = $pager ? $pager->recTotal : count($result); + $limit = $pager ? $pager->recPerPage : $total; + + return $this->send(200, array('page' => $pageID, 'total' => $total, 'limit' => $limit, 'users' => $result)); } /** From a459f5fd4797fa00c63f6e0a88656bae317dfc96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Mon, 1 Nov 2021 15:15:28 +0800 Subject: [PATCH 21/21] * fix bug for set actions. --- module/group/model.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/module/group/model.php b/module/group/model.php index db27dc620c..303f0d09b4 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; }