diff --git a/api/v1/entries/builds.php b/api/v1/entries/builds.php index 1157014715..603d71e8ba 100644 --- a/api/v1/entries/builds.php +++ b/api/v1/entries/builds.php @@ -53,7 +53,7 @@ class buildsEntry extends entry $project = $this->loadModel('project')->getByID($projectID); if(!$project) return $this->send404(); - $fields = 'execution,product,name,builder,date,scmPath,filePath,desc'; + $fields = 'execution,product,name,builder,date,scmPath,filePath,desc,branch'; $this->batchSetPost($fields); $control = $this->loadController('build', 'create'); diff --git a/config/filter.php b/config/filter.php index d54e28622e..89ee1296f0 100755 --- a/config/filter.php +++ b/config/filter.php @@ -97,6 +97,7 @@ $filter->mail->batchdelete = new stdclass(); $filter->misc->checkupdate = new stdclass(); $filter->file->download = new stdclass(); $filter->product->browse = new stdclass(); +$filter->product->all = new stdclass(); $filter->product->default = new stdclass(); $filter->product->index = new stdclass(); $filter->product->export = new stdclass(); @@ -108,6 +109,7 @@ $filter->program->default = new stdclass(); $filter->program->pgmproject = new stdclass(); $filter->program->prjbrowse = new stdclass(); $filter->program->project = new stdclass(); +$filter->program->product = new stdclass(); $filter->program->browse = new stdclass(); $filter->program->export = new stdclass(); $filter->program->pgmbrowse = new stdclass(); @@ -236,6 +238,7 @@ $filter->product->browse->cookie['productStoryOrder'] = 'reg::orderBy'; $filter->product->browse->cookie['storyModule'] = 'int'; $filter->product->browse->cookie['storyBranch'] = 'int'; $filter->product->browse->cookie['treeBranch'] = 'reg::word'; +$filter->product->all->cookie['showProductBatchEdit'] = 'int'; $filter->product->default->cookie['lastProduct'] = 'int'; $filter->product->default->cookie['preProductID'] = 'int'; $filter->product->index->cookie['preBranch'] = 'reg::word'; @@ -247,19 +250,22 @@ $filter->product->dynamic->cookie['preBranch'] = 'reg::word'; $filter->branch->default->cookie['preBranch'] = 'reg::word'; -$filter->program->default->cookie['lastPGM'] = 'int'; -$filter->program->default->cookie['lastPRJ'] = 'int'; -$filter->program->prjbrowse->cookie['programType'] = 'code'; -$filter->program->project->cookie['involved'] = 'code'; -$filter->program->browse->cookie['showClosed'] = 'code'; -$filter->program->export->cookie['checkedItem'] = 'reg::checked'; -$filter->program->ajaxgetdropmenu->cookie['showClosed'] = 'code'; +$filter->program->default->cookie['lastPGM'] = 'int'; +$filter->program->default->cookie['lastPRJ'] = 'int'; +$filter->program->prjbrowse->cookie['programType'] = 'code'; +$filter->program->project->cookie['involved'] = 'code'; +$filter->program->project->cookie['showProjectBatchEdit'] = 'int'; +$filter->program->product->cookie['showProductBatchEdit'] = 'int'; +$filter->program->browse->cookie['showClosed'] = 'code'; +$filter->program->export->cookie['checkedItem'] = 'reg::checked'; +$filter->program->ajaxgetdropmenu->cookie['showClosed'] = 'code'; $filter->project->default->cookie['lastProject'] = 'int'; $filter->project->default->cookie['lastPRJ'] = 'int'; $filter->project->default->cookie['projectMode'] = 'code'; $filter->project->default->cookie['kanbanview'] = 'code'; $filter->project->browse->cookie['involved'] = 'code'; +$filter->project->browse->cookie['showProjectBatchEdit'] = 'int'; $filter->project->export->cookie['involved'] = 'code'; $filter->project->browse->cookie['projectType'] = 'code'; $filter->project->story->cookie['storyModuleParam'] = 'int'; diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 6113f6b03c..8b2347e370 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -34,6 +34,13 @@ 35025 需求导入模板增加由谁评审字段 旗舰版: 35042 旗舰版周报3个工作项列表能显示总数 +修复的Bug +27875 工时记录页面日期控件展开后显示遮挡 +27778 创建项目和编辑项目的所属项目集字段显示不一致 +27690 创建项目页面提示信息窗口建议向右展开 +27632 复制bug时希望可以同步反馈者和通知邮箱两个字段的信息 +27561 提需求页面所属产品右侧边框线缺失 +27547 编辑项目集预算,点击保存,提示有未提交表单 2022-09-08 17.6.1 完成的需求 diff --git a/extension/lite/my/ext/view/task.html.php b/extension/lite/my/ext/view/task.html.php index 01f53c561d..51885c6128 100644 --- a/extension/lite/my/ext/view/task.html.php +++ b/extension/lite/my/ext/view/task.html.php @@ -35,8 +35,15 @@
- - + @@ -134,10 +141,16 @@ } else { + $canStart = ($task->status != 'pause' and common::hasPriv('task', 'start')); + $canRestart = ($task->status == 'pause' and common::hasPriv('task', 'restart')); if($task->status != 'pause') common::printIcon('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project); if($task->status == 'pause') common::printIcon('task', 'restart', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project); - common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project); common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project); + common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project); + if(($canStart or $canRestart or $canFinish or $canClose) and ($canRecordEstimate or $canEdit or $canBatchCreate)) + { + echo "
"; + } common::printIcon('task', 'recordEstimate', "taskID=$task->id", $task, 'list', 'time', '', 'iframe', true, '', '', $task->project); common::printIcon('task', 'edit', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, "data-width='95%'", '', $task->project); @@ -198,10 +211,17 @@ } else { + $canStart = ($child->status != 'pause' and common::hasPriv('task', 'start')); + $canRestart = ($child->status == 'pause' and common::hasPriv('task', 'restart')); if($child->status != 'pause') common::printIcon('task', 'start', "taskID=$child->id", $child, 'list', '', '', 'iframe', true, '', '', $child->project); if($child->status == 'pause') common::printIcon('task', 'restart', "taskID=$child->id", $child, 'list', '', '', 'iframe', true, '', '', $child->project); - common::printIcon('task', 'close', "taskID=$child->id", $child, 'list', '', '', 'iframe', true, '', '', $child->project); common::printIcon('task', 'finish', "taskID=$child->id", $child, 'list', '', '', 'iframe', true, '', '', $child->project); + common::printIcon('task', 'close', "taskID=$child->id", $child, 'list', '', '', 'iframe', true, '', '', $child->project); + + if(($canStart or $canRestart or $canFinish or $canClose) and ($canRecordEstimate or $canEdit or $canBatchCreate)) + { + echo "
"; + } common::printIcon('task', 'recordEstimate', "taskID=$child->id", $child, 'list', 'time', '', 'iframe', true, '', '', $child->project); common::printIcon('task', 'edit', "taskID=$child->id", $child, 'list', '', '', '', '', '', '', $child->project); diff --git a/extension/lite/product/ext/view/browse.html.php b/extension/lite/product/ext/view/browse.html.php index 5e563f9c1a..524ca6c491 100644 --- a/extension/lite/product/ext/view/browse.html.php +++ b/extension/lite/product/ext/view/browse.html.php @@ -18,6 +18,7 @@ body {margin-bottom: 25px;} #mainMenu .btn-toolbar .btn-group .dropdown-menu .btn-active-text:hover .text:after {border-bottom: unset;} .body-modal #mainMenu>.btn-toolbar {width: auto;} .assignedTo{border-radius: 4px !important;} +#productStoryForm table tbody tr td.c-actions .dividing-line {width: 1px; height: 16px; display: inline-block; vertical-align: middle; background: #F4F5F7; margin: 0 4px 0 0;} diff --git a/module/action/model.php b/module/action/model.php index 0239ec5dc0..d626791567 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -374,10 +374,10 @@ class actionModel extends model elseif($actionName == 'linked2execution' or $actionName == 'linked2kanban') { $execution = $this->dao->select('name,type')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch(); - $name = $execution->name; - $method = $execution->type == 'kanban' ? 'kanban' : 'view'; - if($name) + if(!empty($execution)) { + $name = $execution->name; + $method = $execution->type == 'kanban' ? 'kanban' : 'view'; $action->extra = (!common::hasPriv('execution', $method) or ($method == 'kanban' and isonlybody())) ? $name : html::a(helper::createLink('execution', $method, "executionID=$action->execution"), $name); } } @@ -1060,6 +1060,9 @@ class actionModel extends model $programCondition = empty($this->app->user->view->programs) ? '0' : $this->app->user->view->programs; + $efforts = $this->dao->select('id')->from(TABLE_EFFORT)->where($condition)->fetchPairs(); + $efforts = !empty($efforts) ? implode(',', $efforts) : 0; + /* Get actions. */ $actions = $this->dao->select('*')->from(TABLE_ACTION) ->where('objectType')->notIN($this->config->action->ignoreObjectType4Dynamic) @@ -1087,6 +1090,7 @@ class actionModel extends model /* Filter out client login/logout actions. */ ->andWhere('action')->notin('disconnectxuanxuan,reconnectxuanxuan,loginxuanxuan,logoutxuanxuan') ->andWhere("IF((objectType = 'program'), (objectID in ($programCondition)), '1=1')") + ->andWhere("IF((objectType = 'effort'), (objectID in ($efforts)), '1=1')") ->orderBy($orderBy) ->page($pager) ->fetchAll(); diff --git a/module/bug/view/ajaxgetdropmenu.html.php b/module/bug/view/ajaxgetdropmenu.html.php index 7315c653b6..c4c129476a 100644 --- a/module/bug/view/ajaxgetdropmenu.html.php +++ b/module/bug/view/ajaxgetdropmenu.html.php @@ -17,7 +17,7 @@ foreach($products as $product) } $productsPinYin = common::convert2Pinyin($productNames); $linkHtml = $this->product->setParamsForLink($module, $link, $projectID, 0); -$productsHtml = count($products) > 1 ? html::a($linkHtml, $this->lang->bug->allProduct, '', "class='text-important' title='{$this->lang->bug->allProduct}' data-key='" . zget($productsPinYin, $this->lang->bug->allProduct, '') . "' data-app='{$this->app->tab}'") : ''; +$productsHtml = count($products) > 1 ? html::a($linkHtml, $this->lang->bug->allProduct, '', "class='text-primary' title='{$this->lang->bug->allProduct}' data-key='" . zget($productsPinYin, $this->lang->bug->allProduct, '') . "' data-app='{$this->app->tab}'") : ''; foreach($products as $product) { @@ -26,7 +26,7 @@ foreach($products as $product) $productName .= $product->line ? zget($lines, $product->line, '') . '/' . $product->name : $product->name; $objectID = ($product->type != 'platform' && $module == 'branch' && $method == 'manage') ? $productID : $product->id; $linkHtml = $this->product->setParamsForLink($module, $link, $projectID, $product->id); - $productsHtml .= html::a($linkHtml, $productName, '', "class='text-important $selected' title='{$productName}' data-key='" . zget($productsPinYin, $product->name, '') . "' data-app='{$this->app->tab}'"); + $productsHtml .= html::a($linkHtml, $productName, '', "class='text-primary $selected' title='{$productName}' data-key='" . zget($productsPinYin, $product->name, '') . "' data-app='{$this->app->tab}'"); } ?>
diff --git a/module/build/view/view.html.php b/module/build/view/view.html.php index d1f39416fd..45aa9e67d7 100644 --- a/module/build/view/view.html.php +++ b/module/build/view/view.html.php @@ -20,6 +20,7 @@ #stories .action {display: none;} #bugs .action {display: none;} tbody tr td:first-child input {display: none;} +.page-title .dropdown-menu {top: 40px; left: 70px;}
@@ -65,9 +60,13 @@ app->getClientLang(), array('zh-cn','zh-tw')) ? round((float)$project->budget / 10000, 2) . $this->lang->project->tenThousand : round((float)$project->budget, 2);?> - @@ -163,8 +176,19 @@ } common::printIcon('story', 'recall', "$vars&from=list&storyType=story", $child, 'list', 'undo', 'hiddenwin', '', '', '', $lang->story->recall); - common::printIcon('story', 'close', "$vars&from=&storyType=story", $child, 'list', '', '', 'iframe', true); common::printIcon('story', 'edit', "$vars&from=default&storyType=story", $child, 'list'); + $storyType = 'storyType=story'; + $canChange = common::hasPriv('story', 'change', '', $storyType); + $canSubmitReview = (strpos('draft,changing', $child->status) !== false and common::hasPriv('story', 'submitReview', '', $storyType)); + $canReview = (strpos('draft,draft', $child->status) === false and common::hasPriv('story', 'review', '', $storyType)); + $canRecall = common::hasPriv('story', 'recall', '', $storyType); + $canEdit = common::hasPriv('story', 'edit', '', $storyType); + $canClose = common::hasPriv('story', 'close', '', $storyType); + if(($canChange or $canSubmitReview or $canReview or $canRecall or $canEdit) and $canClose) + { + echo "
"; + } + common::printIcon('story', 'close', "$vars&from=&storyType=story", $child, 'list', '', '', 'iframe', true); } } ?> diff --git a/module/my/view/story.html.php b/module/my/view/story.html.php index b309858436..933b5ce449 100644 --- a/module/my/view/story.html.php +++ b/module/my/view/story.html.php @@ -50,6 +50,11 @@ $canBatchReview = common::hasPriv('story', 'batchReview'); $canBatchAssignTo = common::hasPriv('story', 'batchAssignTo'); $canBatchAction = ($canBatchEdit or $canBatchClose or $canBatchReview or $canBatchAssignTo); + $canChange = common::hasPriv('story', 'change'); + $canRecall = common::hasPriv('story', 'recall'); + $canEdit = common::hasPriv('story', 'edit'); + $canCreateCase = common::hasPriv('testcase', 'create'); + $canClose = common::hasPriv('story', 'close'); ?> @@ -116,9 +121,21 @@ } echo common::buildIconButton('story', 'recall', $vars, $story, 'list', 'undo', 'hiddenwin', '', '', '', $lang->story->recall); - echo common::buildIconButton('story', 'close', $vars, $story, 'list', '', '', 'iframe', true); echo common::buildIconButton('story', 'edit', $vars, $story, 'list', '', '', 'iframe', true, "data-width='95%'"); - echo common::buildIconButton('story', 'createCase', "productID=$story->product&branch=$story->branch&module=0&from=¶m=0&$vars", $story, 'list', 'sitemap', '', 'iframe', true, "data-width='95%'"); + + $canSubmitReview = (strpos('draft,changing', $story->status) !== false and common::hasPriv('story', 'submitReview')); + $canReview = (strpos('draft,changing', $story->status) === false and common::hasPriv('story', 'review')); + + if(($canChange or $canSubmitReview or $canReview or $canRecall or $canEdit) and ($canCreateCase or $canClose)) + { + echo "
"; + } + + echo common::buildIconButton('testcase', 'create', "productID=$story->product&branch=$story->branch&module=0&from=¶m=0&$vars", $story, 'list', 'sitemap', '', 'iframe', true, "data-width='95%'"); + + if($canCreateCase and $canClose) echo "
"; + + echo common::buildIconButton('story', 'close', $vars, $story, 'list', '', '', 'iframe', true); } ?> @@ -165,9 +182,21 @@ common::printIcon('story', 'review', $vars, $child, 'list', 'search', '', 'iframe', true); } common::printIcon('story', 'recall', $vars, $child, 'list', 'undo', 'hiddenwin', '', '', '', $lang->story->recall); - common::printIcon('story', 'close', $vars, $child, 'list', '', '', 'iframe', true); common::printIcon('story', 'edit', $vars, $child, 'list', '', '', 'iframe', true, "data-width='95%'"); - common::printIcon('story', 'createCase', "productID=$child->product&branch=$child->branch&module=0&from=¶m=0&$vars", $child, 'list', 'sitemap', '', 'iframe', true, "data-width='95%'"); + + $canSubmitReview = (strpos('draft,changing', $child->status) !== false and common::hasPriv('story', 'submitReview')); + $canReview = (strpos('draft,changing', $child->status) === false and common::hasPriv('story', 'review')); + + if(($canChange or $canSubmitReview or $canReview or $canRecall or $canEdit) and ($canCreateCase or $canClose)) + { + echo "
"; + } + + common::printIcon('testcase', 'create', "productID=$child->product&branch=$child->branch&module=0&from=¶m=0&$vars", $child, 'list', 'sitemap', '', 'iframe', true, "data-width='95%'"); + + if($canCreateCase and $canClose) echo "
"; + + common::printIcon('story', 'close', $vars, $child, 'list', '', '', 'iframe', true); } ?> diff --git a/module/my/view/task.html.php b/module/my/view/task.html.php index 69b47b7bfc..289ab3f422 100644 --- a/module/my/view/task.html.php +++ b/module/my/view/task.html.php @@ -44,8 +44,15 @@ - - +
id);?> - model === 'waterfall') echo "{$lang->project->waterfall} "; - if($project->model === 'scrum') echo "{$lang->project->scrum} "; - if($project->model === 'kanban') echo "{$lang->project->kanban} "; - echo html::a($this->createLink('project', 'index', "projectID=$project->id", '', '', $project->id), $project->name, '', "data-group='project'"); - ?> + createLink('project', 'index', "projectID=$project->id", '', '', $project->id), $project->name, '', "data-group='project' title='{$project->name} ({$this->lang->project->{$project->model}})'");?> project->statusList, $project->status, '');?>end == LONG_TIME ? $this->lang->project->longTime : $project->end;?> budget != 0 ? zget($lang->project->currencySymbol, $project->budgetUnit) . ' ' . $projectBudget : $lang->project->future;?> - PM]) ? $PMList[$project->PM]->id : ''?> - PM)) echo html::a($this->createLink('user', 'profile', "userID=$userID", '', true), zget($users, $project->PM), '', "data-toggle='modal' data-type='iframe' data-width='600'");?> + + PM)):?> + PM);?> + $usersAvatar[$project->PM], 'account' => $project->PM, 'name' => $userName), "avatar-circle avatar-top avatar-{$project->PM}"); ?> + PM]) ? $PMList[$project->PM]->id : '';?> + createLink('user', 'profile', "userID=$userID", '', true), $userName, '', "title='{$userName}' data-toggle='modal' data-type='iframe' data-width='600'");?> + status == 'wait' || $project->status == 'suspended') common::printIcon('project', 'start', "projectID=$project->id", $project, 'list', 'play', '', 'iframe', true);?> diff --git a/module/my/view/requirement.html.php b/module/my/view/requirement.html.php index 37248276c6..d607ca8e1c 100644 --- a/module/my/view/requirement.html.php +++ b/module/my/view/requirement.html.php @@ -114,8 +114,21 @@ echo common::buildIconButton('story', 'review', "$vars&from=product&storyType=requirement", $story, 'list', 'search', '', 'iframe', true); } echo common::buildIconButton('story', 'recall', "$vars&from=list&storyType=requirement", $story, 'list', 'undo', 'hiddenwin', '', '', '', $lang->story->recall); - echo common::buildIconButton('story', 'close', "$vars&from=&storyType=requirement", $story, 'list', '', '', 'iframe', true); echo common::buildIconButton('story', 'edit', "$vars&from=default&storyType=requirement", $story, 'list', '', '', 'iframe', true, "data-width='95%'"); + + $storyType = 'storyType=requirement'; + $canChange = common::hasPriv('story', 'change', '', $storyType); + $canSubmitReview = (strpos('draft,changing', $story->status) !== false and common::hasPriv('story', 'submitReview', '', $storyType)); + $canReview = (strpos('draft,changing', $story->status) === false and common::hasPriv('story', 'review', '', $storyType)); + $canRecall = common::hasPriv('story', 'recall', '', $storyType); + $canEdit = common::hasPriv('story', 'edit', '', $storyType); + $canClose = common::hasPriv('story', 'close', '', $storyType); + if(($canChange or $canSubmitReview or $canReview or $canRecall or $canEdit) and $canClose) + { + echo "
"; + } + + echo common::buildIconButton('story', 'close', "$vars&from=&storyType=requirement", $story, 'list', '', '', 'iframe', true); } ?>
@@ -151,11 +158,18 @@ } else { - $attr = isset($kanbanList[$task->execution]) ? "disabled" : ''; + $attr = isset($kanbanList[$task->execution]) ? "disabled" : ''; + $canStart = ($task->status != 'pause' and common::hasPriv('task', 'start')); + $canRestart = ($task->status == 'pause' and common::hasPriv('task', 'restart')); if($task->status != 'pause') common::printIcon('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); if($task->status == 'pause') common::printIcon('task', 'restart', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); - common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); + common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); + + if(($canStart or $canRestart or $canFinish or $canClose) and ($canRecordEstimate or $canEdit or $canBatchCreate)) + { + echo "
"; + } common::printIcon('task', 'recordEstimate', "taskID=$task->id", $task, 'list', 'time', '', 'iframe', true); common::printIcon('task', 'edit', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, "data-width='95%'"); @@ -221,10 +235,17 @@ } else { + $canStart = ($child->status != 'pause' and common::hasPriv('task', 'start')); + $canRestart = ($child->status == 'pause' and common::hasPriv('task', 'restart')); if($child->status != 'pause') common::printIcon('task', 'start', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); if($child->status == 'pause') common::printIcon('task', 'restart', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); - common::printIcon('task', 'close', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); common::printIcon('task', 'finish', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); + common::printIcon('task', 'close', "taskID=$child->id", $child, 'list', '', '', 'iframe', true); + + if(($canStart or $canRestart or $canFinish or $canClose) and ($canRecordEstimate or $canEdit or $canBatchCreate)) + { + echo "
"; + } common::printIcon('task', 'recordEstimate', "taskID=$child->id", $child, 'list', 'time', '', 'iframe', true); common::printIcon('task', 'edit', "taskID=$child->id", $child, 'list', '', '', 'iframe', true, "data-width='95%'"); diff --git a/module/product/control.php b/module/product/control.php index ee3d3e1427..2bd4c6fb33 100644 --- a/module/product/control.php +++ b/module/product/control.php @@ -1286,9 +1286,13 @@ class product extends control $this->view->productStructure = $productStructure; $this->view->productLines = $productLines; $this->view->programLines = $programLines; + $this->view->users = $this->user->getPairs('noletter'); + $this->view->userIdPairs = $this->user->getPairs('noletter|showid'); + $this->view->usersAvatar = $this->user->getAvatarPairs(''); $this->view->orderBy = $orderBy; $this->view->browseType = $browseType; $this->view->pager = $pager; + $this->view->showBatchEdit = $this->cookie->showProductBatchEdit; $this->display(); } diff --git a/module/product/css/all.css b/module/product/css/all.css index 5a71bca831..21fbea6ed5 100644 --- a/module/product/css/all.css +++ b/module/product/css/all.css @@ -38,15 +38,14 @@ #sidebar>.cell {width: 100%;} th.c-name {width: 400px;} -th.c-requirement {width: 290px;} +th.c-PO {width: 100px;} th.c-story {width: 290px;} -th.c-bug {width: 200px;} +th.c-bug {width: 150px;} th.c-plan {width: 45px;} th.c-release {width: 50px;} th.c-actions {width: 50px;} [lang='en'] .en-wrap-text {white-space: normal; height: 24px; line-height: 1; font-size: 12px;} - #productListForm thead>tr>th, #productListForm tbody>tr>td {text-align: center; text-overflow: unset!important} #productListForm thead>tr>th {padding: 0; line-height: 24px;} @@ -86,8 +85,11 @@ th.c-actions {width: 50px;} [lang^='zh-'] th.c-story {width: 220px;} } +#productTableList .c-name > span.table-nest-icon {width: 0; visibility: hidden;} #productTableList > tr.row-line.is-nest-child > td.text-left.table-nest-title > span.table-nest-icon.icon:before{content: '\e6f2'; background-color: white; display: inline;} #productTableList > tr.row-line.no-nest > td.text-left.table-nest-title > span.table-nest-icon.icon:before{content: '\e6f2'; background-color: white; display: inline;} .statistic {margin-left: 10px; color: #838a9d; float: left; position: relative; line-height: 28px;} .table-footer.fixed-footer .statistic {color: #fff;} +#productTableList .c-manager {padding-left: 14px;} +#productTableList .c-manager a {top: 7px; left: 22px;} .checkbox-primary {margin-right: 10px;} diff --git a/module/product/js/all.js b/module/product/js/all.js index fd69289343..9aed531aa3 100644 --- a/module/product/js/all.js +++ b/module/product/js/all.js @@ -1,14 +1,12 @@ $("#" + browseType + "Tab").addClass('btn-active-text'); $(function() { - $('#productListForm .c-checkbox, #productListForm .check-all').hide(); - $('input[name^="showEdit"]').click(function() { - $('#productListForm .c-checkbox, #productListForm .check-all').hide(); - var showEdit = $(this).is(':checked') ? 1 : 0; - if(showEdit) $('#productListForm .c-checkbox, #productListForm .check-all').show(); + $.cookie('showProductBatchEdit', $(this).is(':checked') ? 1 : 0, {expires: config.cookieLife, path: config.webRoot}); + setCheckbox(); }); + setCheckbox(); /* Init table sort. */ $('#productTableList').addClass('sortable').sortable( @@ -45,6 +43,23 @@ $(function() } }); + /** + * Set batch edit checkbox. + * + * @access public + * @return void + */ + function setCheckbox() + { + $('#productListForm .c-checkbox, #productListForm .check-all').hide(); + $('.c-name').css('border-left', 'none'); + if($.cookie('showProductBatchEdit') == 1) + { + $('#productListForm .c-checkbox, #productListForm .check-all').show(); + $('.c-name').css('border-left', '1px solid #ddd'); + } + } + /* Update parent checkbox */ function updatePrarentCheckbox($parent) { diff --git a/module/product/lang/de.php b/module/product/lang/de.php index 9acbd9b49e..913c2c6eda 100644 --- a/module/product/lang/de.php +++ b/module/product/lang/de.php @@ -37,8 +37,8 @@ $lang->product->addWhitelist = 'Add Whitelist'; $lang->product->unbindWhitelist = 'Remove Whitelist'; $lang->product->track = 'View Stories Matrix'; $lang->product->checkedProducts = '%s products selected'; -$lang->product->pageProducts = 'Total products: %s.'; -$lang->product->pageLines = 'Total product lines: %s, Total products: %s.'; +$lang->product->pageSummary = 'Total products: %s.'; +$lang->product->lineSummary = 'Total product lines: %s, Total products: %s.'; $lang->product->indexAction = "All {$lang->productCommon}"; $lang->product->closeAction = "Close {$lang->productCommon}"; diff --git a/module/product/lang/en.php b/module/product/lang/en.php index 0af7d531d3..bf6a91a961 100644 --- a/module/product/lang/en.php +++ b/module/product/lang/en.php @@ -37,8 +37,8 @@ $lang->product->addWhitelist = 'Add Whitelist'; $lang->product->unbindWhitelist = 'Unbind Whitelist'; $lang->product->track = 'View Stories Matrix'; $lang->product->checkedProducts = '%s products selected'; -$lang->product->pageProducts = 'Total products: %s.'; -$lang->product->pageLines = 'Total product lines: %s, Total products: %s.'; +$lang->product->pageSummary = 'Total products: %s.'; +$lang->product->lineSummary = 'Total product lines: %s, Total products: %s.'; $lang->product->indexAction = "All {$lang->productCommon}"; $lang->product->closeAction = "Close {$lang->productCommon}"; diff --git a/module/product/lang/fr.php b/module/product/lang/fr.php index 6ba1e4179a..6154559fac 100644 --- a/module/product/lang/fr.php +++ b/module/product/lang/fr.php @@ -37,8 +37,8 @@ $lang->product->addWhitelist = 'Add Whitelist'; $lang->product->unbindWhitelist = 'Remove Whitelist'; $lang->product->track = 'Consulter Stories Matrice'; $lang->product->checkedProducts = '%s produits s électionnés'; -$lang->product->pageProducts = 'Total products: %s.'; -$lang->product->pageLines = 'Total product lines: %s, Total products: %s.'; +$lang->product->pageSummary = 'Total products: %s.'; +$lang->product->lineSummary = 'Total product lines: %s, Total products: %s.'; $lang->product->indexAction = "All {$lang->productCommon}"; $lang->product->closeAction = "Fermer {$lang->productCommon}"; diff --git a/module/product/lang/zh-cn.php b/module/product/lang/zh-cn.php index 336926a768..6d33a54d1f 100644 --- a/module/product/lang/zh-cn.php +++ b/module/product/lang/zh-cn.php @@ -37,8 +37,8 @@ $lang->product->addWhitelist = '添加白名单'; $lang->product->unbindWhitelist = '移除白名单'; $lang->product->track = "查看需求矩阵"; $lang->product->checkedProducts = '已选择%s项产品'; -$lang->product->pageProducts = '本页共%s个产品。'; -$lang->product->pageLines = '本页共%s个产品线,%s个产品。'; +$lang->product->pageSummary = '本页共%s个产品。'; +$lang->product->lineSummary = '本页共%s个产品线,%s个产品。'; $lang->product->indexAction = "所有{$lang->productCommon}仪表盘"; $lang->product->closeAction = "关闭{$lang->productCommon}"; diff --git a/module/product/model.php b/module/product/model.php index 6d7da2880a..1999ef3c02 100644 --- a/module/product/model.php +++ b/module/product/model.php @@ -1817,11 +1817,15 @@ class productModel extends model { $programKeys = array(0 => 0); foreach($products as $product) $programKeys[] = $product->program; - $programs = $this->dao->select('id,name')->from(TABLE_PROGRAM) + $programs = $this->dao->select('id,name,PM')->from(TABLE_PROGRAM) ->where('id')->in(array_unique($programKeys)) - ->fetchPairs(); + ->fetchAll('id'); - foreach($products as $product) $product->programName = isset($programs[$product->program]) ? $programs[$product->program] : ''; + foreach($products as $product) + { + $product->programName = isset($programs[$product->program]) ? $programs[$product->program]->name : ''; + $product->programPM = isset($programs[$product->program]) ? $programs[$product->program]->PM : ''; + } } $stats = array(); @@ -2045,6 +2049,7 @@ class productModel extends model /* Init vars. */ /* Program name. */ $productStructure[$product->program]['programName'] = $product->programName; + $productStructure[$product->program]['programPM'] = $product->programPM; $productStructure[$product->program] = $this->statisticData('program', $productStructure, $product); } } diff --git a/module/product/view/all.html.php b/module/product/view/all.html.php index 1d85479802..a562604777 100644 --- a/module/product/view/all.html.php +++ b/module/product/view/all.html.php @@ -20,7 +20,7 @@ {$recTotal}" : '';?> {$label}" . $recTotalLabel, '', "class='btn btn-link' id='{$key}Tab'");?> - $lang->product->edit));?> + $lang->product->edit), $showBatchEdit);?> product->searchStory;?>
@@ -52,11 +52,11 @@ product->name);?> - config->URAndSR):?> -
- + - + actions;?> - config->URAndSR):?> - - - - - - - @@ -114,20 +106,25 @@ - config->URAndSR):?> - - - - - - + - @@ -161,20 +158,13 @@ - config->URAndSR):?> - - - - - - + - @@ -187,8 +177,7 @@ $product):?> stories['active'] + $product->stories['closed'] + $product->stories['draft'] + $product->stories['changing'] + $product->stories['reviewing']; - $totalRequirements = $product->requirements['active'] + $product->requirements['closed'] + $product->requirements['draft'] + $product->requirements['changing'] + $product->requirements['reviewing']; + $totalStories = $product->stories['active'] + $product->stories['closed'] + $product->stories['draft'] + $product->stories['changing'] + $product->stories['reviewing']; $trClass = ''; if($product->line) @@ -218,24 +207,27 @@ + - config->URAndSR):?> - - - - - - - @@ -253,8 +245,8 @@
product->pageProducts, count($productStats)) : sprintf($lang->product->pageLines, count($lineNames), count($productStats)); - echo "
$content
"; + $summary = empty($productLines) ? sprintf($lang->product->pageSummary, count($productStats)) : sprintf($lang->product->lineSummary, count($lineNames), count($productStats)); + echo "
$summary
"; ?>
children)):?> diff --git a/module/product/view/project.html.php b/module/product/view/project.html.php index 2037c8943c..edc33bec4e 100644 --- a/module/product/view/project.html.php +++ b/module/product/view/project.html.php @@ -49,11 +49,13 @@ + + + + - - @@ -79,20 +81,22 @@ } ?> + + processStatus('project', $project);?> + + - - - processStatus('project', $project);?> - app->getClientLang(), array('zh-cn','zh-tw')) ? round((float)$project->budget / 10000, 2) . $this->lang->project->tenThousand : round((float)$project->budget, 2);?> budget != 0 ? zget($this->lang->project->currencySymbol, $project->budgetUnit) . ' ' . $projectBudget : $this->lang->project->future;?> budget != 0 ? 'text-right' : 'text-center';?> + + -
story->requirement;?> + product->manager);?> + story->story;?>bug->common;?>bug->common;?> product->plan;?> product->release;?>
story->draft;?>story->activate;?>story->change;?>story->statusList['reviewing'];?>
story->completeRate;?>
story->draft;?> story->activate;?> story->change;?> story->statusList['reviewing'];?>
story->completeRate;?>
bug->activate;?>close;?> bug->fixedRate;?>
% + $usersAvatar[$programPM], 'account' => $programPM, 'name' => $userName), 'avatar-circle avatar-top avatar-' . zget($userIdPairs, $programPM)); + + $userID = isset($userIdPairs[$programPM]) ? $userIdPairs[$programPM] : ''; + echo html::a($this->createLink('user', 'profile', "userID=$userID", '', true), $userName, '', "title='{$userName}' data-toggle='modal' data-type='iframe' data-width='600'"); + } + ?> + % % % % % createLink('product', 'browse', 'productID=' . $product->id), $product->name); - echo "" . $productLink; + echo html::a($this->createLink('product', 'browse', 'productID=' . $product->id), $product->name); + ?> + + PO)) + { + $userName = zget($users, $product->PO); + echo html::smallAvatar(array('avatar' => $usersAvatar[$product->PO], 'account' => $product->PO, 'name' => $userName), 'avatar-circle avatar-' . zget($userIdPairs, $product->PO)); + + $userID = isset($userIdPairs[$product->PO]) ? $userIdPairs[$product->PO] : ''; + echo html::a($this->createLink('user', 'profile', "userID=$userID", '', true), $userName, '', "title='{$userName}' data-toggle='modal' data-type='iframe' data-width='600'"); + } ?> requirements['draft'];?>requirements['active'];?>requirements['changing'];?>requirements['reviewing'];?>requirements['closed'] / $totalRequirements, 3) * 100;?>% stories['draft'];?> stories['active'];?> stories['changing'];?> stories['reviewing'];?> stories['closed'] / $totalStories, 3) * 100;?>% unResolved;?>closedBugs;?> unResolved + $product->fixedBugs) == 0 ? 0 : round($product->fixedBugs / ($product->unResolved + $product->fixedBugs), 3) * 100;?>% plans;?> releases;?>
execution->name;?> project->status;?> project->PM;?>project->budget;?> project->begin;?> project->end;?>project->status;?>project->budget;?> project->estimate;?> project->consume;?> project->progress;?> + + PM]) ? $PMList[$project->PM]->id : ''?> PM)) echo html::a($this->createLink('user', 'profile', "userID=$userID", '', true), zget($users, $project->PM), '', "data-toggle='modal' data-type='iframe' data-width='800'");?> begin;?>end;?> - - begin;?>end;?> hours->totalEstimate . $lang->execution->workHourUnit;?> hours->totalConsumed . $lang->execution->workHourUnit;?> diff --git a/module/productplan/css/browse.css b/module/productplan/css/browse.css index 1f9f589b44..afb0db66ad 100644 --- a/module/productplan/css/browse.css +++ b/module/productplan/css/browse.css @@ -35,3 +35,7 @@ td.c-branch {overflow: hidden; text-align: left !important; text-overflow: ellip .panel-actions {position: relative; padding: 0 0; padding-top: 1px;} .icon-list {padding-left: 7px;} .icon-kanban {padding-left: 7px; font-size: 16px;} +#productplanList .c-actions {width: 265px;} +#productplanList .c-actions .btn+.btn {margin-left: -1px;} +#productplanList .c-actions .btn {display: block; float: left;} +#productplanList td.c-actions .dividing-line {width: 1px; height: 16px; display: inline-block; vertical-align: middle; background: #F4F5F7; margin: 6px 4px 0 0; float: left;} diff --git a/module/productplan/model.php b/module/productplan/model.php index 385ee5f43a..ffadeb6ce9 100644 --- a/module/productplan/model.php +++ b/module/productplan/model.php @@ -1130,6 +1130,16 @@ class productplanModel extends model { $params = "planID=$plan->id"; + $canStart = common::hasPriv('productplan', 'start'); + $canFinish = common::hasPriv('productplan', 'finish'); + $canClose = common::hasPriv('productplan', 'close'); + $canCreateExec = common::hasPriv('execution', 'create'); + $canLinkStory = common::hasPriv('productplan', 'linkStory'); + $canLinkBug = common::hasPriv('productplan', 'linkBug'); + $canEdit = common::hasPriv('productplan', 'edit'); + $canCreateChild = common::hasPriv('productplan', 'create'); + $canDelete = common::hasPriv('productplan', 'delete'); + $menu = ''; $menu .= $this->buildMenu('productplan', 'start', $params, $plan, $type, 'play', 'hiddenwin', '', false, '', $this->lang->productplan->startAB); $menu .= $this->buildMenu('productplan', 'finish', $params, $plan, $type, 'checked', 'hiddenwin', '', false, '', $this->lang->productplan->finishAB); @@ -1174,6 +1184,11 @@ class productplanModel extends model $menu .= ""; } + if(($canStart or $canFinsh or $canClose or $canCreateExec) and ($canLinkStory or $canLinkBug or $canEdit or $canCreateChild or $canDelete)) + { + $menu .= "
"; + } + if(common::hasPriv('productplan', 'linkStory', $plan) and $plan->parent >= 0) { $menu .= $this->buildMenu('productplan', 'view', "{$params}&type=story&orderBy=id_desc&link=true", $plan, $type, 'link', '', '', '', '', $this->lang->productplan->linkStory); @@ -1197,6 +1212,11 @@ class productplanModel extends model $menu .= $this->buildMenu('productplan', 'create', "product={$plan->product}&branch={$plan->branch}&parent={$plan->id}", $plan, $type, 'split', '', '', '', '', $this->lang->productplan->children); + if(($canLinkStory or $canLinkBug or $canEdit or $canCreateChild) and $canDelete) + { + $menu .= "
"; + } + if($type == 'browse') $menu .= $this->buildMenu('productplan', 'delete', "{$params}&confirm=no", $plan, $type, 'trash', 'hiddenwin', '', '', $this->lang->productplan->delete); if($type == 'view') diff --git a/module/productplan/view/browsebykanban.html.php b/module/productplan/view/browsebykanban.html.php index 10ff417e03..d3ffd92dbf 100644 --- a/module/productplan/view/browsebykanban.html.php +++ b/module/productplan/view/browsebykanban.html.php @@ -52,7 +52,7 @@ diff --git a/module/productplan/view/browsebylist.html.php b/module/productplan/view/browsebylist.html.php index a046895e57..e488ec94f8 100644 --- a/module/productplan/view/browsebylist.html.php +++ b/module/productplan/view/browsebylist.html.php @@ -9,11 +9,6 @@ * @link https://www.zentao.net */ ?> -
project->statusList, $program->status, '');?> + PM)):?> PM);?> - $usersAvatar[$program->PM], 'account' => $program->PM, 'name' => $userName), 'avatar-circle avatar-' . zget($userIdPairs, $program->PM)); ?> + $usersAvatar[$program->PM], 'account' => $program->PM, 'name' => $userName), (($program->type == 'program' and $program->grade == 1 )? 'avatar-circle avatar-top avatar-' : 'avatar-circle avatar-') . zget($userIdPairs, $program->PM)); ?> PM]) ? $PMList[$program->PM]->id : '';?> createLink('user', 'profile', "userID=$userID", '', true), $userName, '', "title='{$userName}' data-toggle='modal' data-type='iframe' data-width='600'");?> @@ -118,9 +119,8 @@ #programTableList .icon-scrum:before {content: '\e9a2';} #programTableList .icon-waterfall:before {content: '\e9a4';} #programTableList .icon-kanban:before {content: '\e983';} -/* #programTableList .has-nest-child > .c-name > a {color: #0b0f18!important;} -#programTableList .is-nest-child > .c-name > a {color: #2463c7!important;} */ -#programTableList .c-name .label-danger {position: absolute; right: 10px; padding: 2px 4px;} +#programTableList > tr[data-type="program"] > .c-name > a {color: #0b0f18 !important;} +#programTableList > tr[data-type="program"] > .c-name:hover > a {color: #313c52 !important;} diff --git a/module/testcase/view/view.html.php b/module/testcase/view/view.html.php index 935c0ecd7d..c2b0b04f5e 100644 --- a/module/testcase/view/view.html.php +++ b/module/testcase/view/view.html.php @@ -18,7 +18,7 @@