This commit is contained in:
zhujinyong
2021-09-01 09:08:15 +08:00
58 changed files with 413 additions and 265 deletions
+52 -1
View File
@@ -53,6 +53,7 @@ class productIssueEntry extends entry
}
break;
case 'bug':
$this->app->loadLang('bug');
$bugStatus = array('' => '', 'active' => 'opened', 'resolved' => 'opened', 'closed' => 'closed');
@@ -132,7 +133,9 @@ class productIssueEntry extends entry
$this->send404();
}
$actions = $this->loadModel('action')->getList($type, $issueID);
$actions = $this->loadModel('action')->getList($type, $id);
$issue->comments = array_values($this->processActions($type, $actions));
/**
* Get all users in issues so that we can bulk get user detail later.
@@ -159,4 +162,52 @@ class productIssueEntry extends entry
$this->send(200, array('issue' => $this->format($issue, 'openedDate:time,lastEditedDate:time')));
}
/**
* Process actions of one issue.
*
* @param string $type
* @param array $actions
* @access public
* @return array
*/
public function processActions($type, $actions)
{
$accountsList = array();
foreach($actions as $action)
{
$accountsList[] = $action->actor;
ob_start();
$this->action->printAction($action);
$action->title = ob_get_contents();
ob_clean();
$action->body_html = '';
if(!empty($action->history))
{
ob_start();
$this->action->printChanges($action->objectType, $action->history);
$action->body_html = ob_get_contents();
ob_clean();
}
if(!empty($action->comment))
{
$comment = strip_tags($action->comment) == $action->comment ? nl2br($action->comment) : $action->comment;
$action->body_html = "<div class='comment-content'>{$comment}</div>";
}
}
/* Format user detail and date. */
$accountsList = array_unique($accountsList);
$userDetails = $this->loadModel('user')->getUserDetailsForAPI($accountsList);
foreach($actions as $action)
{
$action->actor = $userDetails[$action->actor];
$action->date = gmdate("Y-m-d\TH:i:s\Z", strtotime($action->date));
}
return $actions;
}
}
+9 -6
View File
@@ -107,32 +107,35 @@ class productIssuesEntry extends entry
$bugFilter = array_unique($bugFilter);
$executions = $this->dao->select('project')->from(TABLE_PROJECTPRODUCT)->where('product')->eq($productID)->fetchPairs();
if(!empty($taskFilter))
/* Get tasks. */
if(empty($labelTypes) or in_array('task', $labelTypes))
{
$query = $this->dao->select($taskFields)->from(TABLE_TASK)->where('execution')->in(array_values($executions))
->beginIF($search)->andWhere('name')->like("%$search%")->fi()
->beginIF($status)->andWhere('status')->in($taskStatus[$status])->fi()
->andWhere('deleted')->eq(0);
foreach($taskFilter as $filter) if($filter != 'all') $query->andWhere('type')->eq($filter);
$tasks = $query->fetchAll();
foreach($tasks as $task) $issues[] = array('id' => $task->id, 'type' => 'task', 'order' => $task->$order, 'status' => $this->getKey($task->status, $taskStatus));
}
if(!empty($storyFilter))
/* Get stories. */
if(empty($labelTypes) or in_array('story', $labelTypes))
{
$query = $this->dao->select($storyFields)->from(TABLE_STORY)
->where('product')->eq($productID)
->beginIF($search)->andWhere('title')->like("%$search%")->fi()
->beginIF($status)->andWhere('status')->in($storyStatus[$status])->fi()
->andWhere('deleted')->eq(0);
foreach($storyFilter as $filter) if($filter != 'all') $query->andWhere('category')->eq($filter);
$stories = $query->fetchAll();
foreach($stories as $story) $issues[] = array('id' => $story->id, 'type' => 'story', 'order' => $story->$order, 'status' => $this->getKey($story->status, $storyStatus));
}
if(!empty($bugFilter))
/* Get bugs. */
/* Get stories. */
if(empty($labelTypes) or in_array('bug', $labelTypes))
{
$query = $this->dao->select($bugFields)->from(TABLE_BUG)
->where('product')->eq($productID)
@@ -305,7 +308,7 @@ class productIssuesEntry extends entry
{
foreach($issue->assignedTo as $index => $user)
{
$issue->assignedTo[$index] = $userDetails[$user];
$issue->assignedTo[$index] = $userDetails[$user];
}
$issue->openedBy = $userDetails[$issue->openedBy];
}
+1
View File
@@ -139,6 +139,7 @@ $config->openMethods[] = 'index.changelog';
$config->openMethods[] = 'my.preference';
$config->openMethods[] = 'my.changepassword';
$config->openMethods[] = 'my.profile';
$config->openMethods[] = 'my.settutorialconfig';
/* Define the tables. */
define('TABLE_COMPANY', '`' . $config->db->prefix . 'company`');
+1 -1
View File
@@ -188,7 +188,7 @@ class baseEntry
header("Content-type: application/json");
header("HTTP/1.1 {$this->statusCode[$code]}");
if($data) echo json_encode($data);
if($data) echo json_encode($data, JSON_HEX_TAG);
exit;
}
+1 -5
View File
@@ -490,11 +490,7 @@ class baseHTML
if(helper::inOnlyBodyMode()) return false;
global $lang, $app, $config;
if(empty($label))
{
global $lang, $config;
$label = $lang->goback;
}
if(empty($label)) $label = $lang->goback;
$tab = $_COOKIE['tab'];
$referer = strtolower($_SERVER['HTTP_REFERER']);
+1 -1
View File
@@ -327,7 +327,7 @@ js::set('moduleID', $moduleID);
<td colspan="3" class="text-center form-actions">
<?php $browseLink = $this->session->bugList ? $this->session->bugList : $this->inlink('browse', "productID=$productID");?>
<?php echo html::submitButton();?>
<?php if($caseID) echo html::linkButton($lang->goback, $browseLink, 'self', '', 'btn btn-wide');?>
<?php if($caseID) echo html::a($browseLink, $lang->goback, '', 'class="btn btn-wide"');?>
<?php if(!$caseID) echo html::backButton();?>
<?php echo html::hidden('case', (int)$caseID) . html::hidden('caseVersion', (int)$version);?>
<?php echo html::hidden('result', (int)$runID) . html::hidden('testtask', empty($testtask) ? 0 : $testtask->id);?>
+1 -1
View File
@@ -296,7 +296,7 @@ $lang->createObjects['story'] = 'Story';
$lang->createObjects['task'] = 'Task';
$lang->createObjects['testcase'] = 'Case';
$lang->createObjects['execution'] = $lang->execution->common;
$lang->createObjects['project'] = $lang->projectCommon;
$lang->createObjects['project'] = 'Project';
$lang->createObjects['product'] = 'Product';
$lang->createObjects['program'] = 'Program';
$lang->createObjects['doc'] = 'Doc';
+1 -1
View File
@@ -94,7 +94,7 @@ $lang->my->menu->work['subMenu']->task = array('link' => "{$lang->task->comm
if($config->URAndSR) $lang->my->menu->work['subMenu']->requirement = "$lang->URCommon|my|work|mode=requirement";
$lang->my->menu->work['subMenu']->story = "$lang->SRCommon|my|work|mode=story";
$lang->my->menu->work['subMenu']->bug = "{$lang->bug->common}|my|work|mode=bug";
$lang->my->menu->work['subMenu']->testcase = "{$lang->testcase->common}|my|work|mode=testcase&type=assigntome";
$lang->my->menu->work['subMenu']->testcase = array('link' => "{$lang->testcase->common}|my|work|mode=testcase&type=assigntome", 'subModule' => 'testtask');
$lang->my->menu->work['subMenu']->testtask = "{$lang->testtask->common}|my|work|mode=testtask&type=wait";
$lang->my->menu->work['menuOrder'][5] = 'task';
+1 -1
View File
@@ -296,7 +296,7 @@ $lang->createObjects['story'] = '需求';
$lang->createObjects['task'] = '任务';
$lang->createObjects['testcase'] = '用例';
$lang->createObjects['execution'] = $lang->execution->common;
$lang->createObjects['project'] = $lang->projectCommon;
$lang->createObjects['project'] = '项目';
$lang->createObjects['product'] = '产品';
$lang->createObjects['program'] = '项目集';
$lang->createObjects['doc'] = '文档';
+6 -4
View File
@@ -367,7 +367,8 @@ class commonModel extends model
foreach($lang->createIcons as $objectType => $objectIcon)
{
if(!isset($config->proVersion) and $objectType == 'effort') continue;
if($config->systemMode == 'classic' and ($objectType == 'project' or $objectType == 'porgram')) continue;
if($config->systemMode == 'classic' and strpos('project|program', $objectType) !== false) continue;
if(!empty($_COOKIE['feedbackView']) and strpos('todo|effort', $objectType) === false) continue;
/* Change icon when object type is execution and mode is classic. */
if($config->systemMode == 'classic' and $objectType == 'execution') $objectIcon = 'project';
@@ -392,12 +393,13 @@ class commonModel extends model
$showCreateList = true;
$isOnlyBody = strpos('effort|doc', $objectType) !== false;
$iconWord = $objectType == 'doc' ? 'W' : ''; // No ducument icon, print word instead of icon.
$attr = $objectType == 'effort' ? "data-width='95%' class='iframe' data-toggle='modal'" : "class='iframe'";
$params = '';
if($objectType == 'bug' or $objectType == 'testcase') $params = "productID=$productID";
if(strpos('bug|testcase|story', $objectType) !== false) $params = "productID=$productID";
if($objectType == 'doc') $params = "objectType=&objectID=0&libID=0";
$html .= '<li>' . html::a(helper::createLink($objectType, $createMethod, $params, '', $isOnlyBody), "<i class='icon icon-$objectIcon'>$iconWord</i> " . $lang->createObjects[$objectType], '', $isOnlyBody ? "class='iframe'" : '') . '</li>';
$html .= '<li>' . html::a(helper::createLink($objectType, $createMethod, $params, '', $isOnlyBody), "<i class='icon icon-$objectIcon'>$iconWord</i> " . $lang->createObjects[$objectType], '', $isOnlyBody ? $attr : '') . '</li>';
}
}
@@ -1350,7 +1352,7 @@ EOD;
$executionPairs = array();
$object = $app->dbh->query('SELECT project,type FROM ' . TABLE_EXECUTION . " WHERE `id` = '$executionID'")->fetch();
$orderBy = $object->type == 'stage' ? 'ORDER BY `id` ASC' : 'ORDER BY `id` DESC';
$executionList = $app->dbh->query("SELECT id,name FROM " . TABLE_EXECUTION . " WHERE `project` = '{$object->project}' $orderBy")->fetchAll();
$executionList = $app->dbh->query("SELECT id,name FROM " . TABLE_EXECUTION . " WHERE `project` = '{$object->project}' AND `deleted` = '0' $orderBy")->fetchAll();
foreach($executionList as $execution)
{
if($execution->id == $executionID) continue;
+1 -1
View File
@@ -19,7 +19,7 @@ include 'chosen.html.php';
<div id='userMenu'>
<ul id="userNav" class="nav nav-default">
<li class='dropdown dropdown-hover has-avatar'><?php common::printUserBar();?></li>
<li class='dropdown dropdown-hover'><?php common::printCreateList();?></li>
<li class='dropdown dropdown-hover' id='globalCreate'><?php common::printCreateList();?></li>
</ul>
</div>
</div>
-9
View File
@@ -1,9 +0,0 @@
$(function()
{
$('[name=mode]').change(function()
{
var mode = $(this).val();
if(mode == 'new') $('#modeTips').html(newTips);
if(mode == 'classic') $('#modeTips').html(classicTips);
})
})
+1 -3
View File
@@ -11,8 +11,6 @@
*/
?>
<?php include 'header.html.php';?>
<?php js::set('newTips', $lang->upgrade->selectedModeTips['new'])?>
<?php js::set('classicTips', $lang->custom->changeClassicTip)?>
<div id='mainContent' class='main-content'>
<form class="load-indicator main-form" method='post'>
<div class='main-header'>
@@ -29,7 +27,7 @@
<label class="radio-inline"><input type="radio" name="mode" value="classic" <?php echo $mode == 'classic'? "checked='checked'" : ''; echo $isDisabled;?> id="modeclassic"><?php echo $lang->upgrade->to15Mode['classic'];?></label>
<label class="radio-inline"><input type="radio" name="mode" value="new" <?php echo $mode == 'new'? "checked='checked'" : ''; echo $isDisabled;?> id="modenew"><?php echo $lang->upgrade->to15Mode['new'];?></label>
</p>
<p class='text-info' id='modeTips'><?php echo $mode == 'classic' ? $lang->custom->changeClassicTip : $lang->upgrade->selectedModeTips['new'];?></p>
<p class='text-info' id='modeTips'><?php echo $lang->upgrade->selectedModeTips['new'];?></p>
</td>
</tr>
<tr>
+1 -1
View File
@@ -9,7 +9,7 @@ $config->doc->edit = new stdclass();
$config->doc->createlib->requiredFields = 'name';
$config->doc->editlib->requiredFields = 'name';
$config->doc->create->requiredFields = 'title';
$config->doc->create->requiredFields = 'lib,title';
$config->doc->edit->requiredFields = 'title';
$config->doc->customObjectLibs = 'files,customFiles';
+7 -3
View File
@@ -320,14 +320,18 @@ class doc extends control
if($this->config->systemMode == 'classic') unset($this->lang->doc->menu->execution['subMenu']);
}
$lib = $this->doc->getLibByID($libID);
$type = !empty($lib) ? $lib->type : 'product';
/* Get libs and the default lib id. */
$unclosed = strpos($this->config->doc->custom->showLibs, 'unclosed') !== false ? 'unclosedProject' : '';
$libs = $this->doc->getLibs($objectType, $extra = "withObject,$unclosed", $libID, $objectID);
if(!$libID and !empty($libs)) $libID = key($libs);
$lib = $this->doc->getLibByID($libID);
$type = !empty($lib) ? $lib->type : 'product';
if(!isonlybody()) $this->view->title = $lib->name . $this->lang->colon . $this->lang->doc->create;
$this->view->libID = $libID;
$this->view->libs = $this->doc->getLibs($objectType, $extra = "withObject,$unclosed", $libID, $objectID);
$this->view->libs = $libs;
$this->view->libName = $this->dao->findByID($libID)->from(TABLE_DOCLIB)->fetch('name');
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($libID, 'doc', $startModuleID = 0);
$this->view->moduleID = $moduleID ? (int)$moduleID : (int)$this->cookie->lastDocModule;
+1
View File
@@ -7,6 +7,7 @@ $(function()
$('#content').html(draft);
editor = KindEditor.instances[0];
editor.html(draft);
$('.kindeditor-ph').remove();
}
}, 100)
+8 -2
View File
@@ -61,7 +61,7 @@ class docModel extends model
{
if($lib->product != 0 and !isset($products[$lib->product])) continue;
if($lib->execution != 0 and !isset($executions[$lib->execution])) continue;
if($lib->project != 0 and !isset($project[$lib->project])) continue;
if($lib->project != 0 and !isset($projects[$lib->project]) and $lib->type == 'project') continue;
if($this->checkPrivLib($lib, $extra))
{
@@ -499,6 +499,12 @@ class docModel extends model
->remove('files,labels,uid,contactListMenu')
->get();
if(empty($doc->lib))
{
dao::$errors['lib'] = sprintf($this->lang->error->notempty, $this->lang->doc->lib);
return false;
}
/* Fix bug #2929. strip_tags($this->post->contentMarkdown, $this->config->allowedTags)*/
$doc = $this->loadModel('file')->processImgURL($doc, $this->config->doc->editor->create['id'], $this->post->uid);
$doc->contentMarkdown = $this->post->contentMarkdown;
@@ -2304,7 +2310,7 @@ EOT;
{
$libs = $this->getLibsByObject('book', 0, '', $appendLib);
$this->app->rawMethod = 'book';
if($libID == 0 and !empty($libs)) $libID = reset($libs)->id;
if(!empty($libs) and ($libID == 0 or !isset($libs[$libID]))) $libID = reset($libs)->id;
$this->lang->modulePageNav = $this->select($type, $objects, $objectID, $libs, $libID);
$object = new stdclass();
+1 -1
View File
@@ -52,7 +52,7 @@ if(empty($type)) $type = 'product';
echo '</ul></div>';
}
if($type == 'book' and ($canEditLib or $canManageBook))
if($type == 'book' and ($canEditLib or $canManageBook) and !empty($libs))
{
echo "<div class='menu-actions'>";
echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");
+1
View File
@@ -2560,6 +2560,7 @@ class execution extends control
$this->session->set('bugList', $uri, 'qa');
$this->session->set('caseList', $uri, 'qa');
$this->session->set('testtaskList', $uri, 'qa');
$this->session->set('reportList', $uri, 'qa');
/* use first execution if executionID does not exist. */
if(!isset($this->executions[$executionID])) $executionID = key($this->executions);
+1
View File
@@ -14,3 +14,4 @@
.checkbox input[type=checkbox], .checkbox-inline input[type=checkbox], .radio input[type=radio], .radio-inline input[type=radio] {margin-top: 1px;}
#lifeTimeTips {display: none;}
#teams_chosen ul .label {margin-top: 3px;}
+5 -1
View File
@@ -46,7 +46,11 @@ $(function()
$('#teams_chosen').click(function()
{
if(systemMode == 'new') $('#teams_chosen ul li:first').after(' <label class="label">' + projectCommon + '</label>');
if(systemMode == 'new')
{
$('#teams_chosen ul .label').remove();
$('#teams_chosen ul li:first').after(' <label class="label">' + projectCommon + '</label>');
}
})
$('#teams').change(function()
+3 -8
View File
@@ -507,7 +507,7 @@ class executionModel extends model
->batchcheck($this->config->execution->edit->requiredFields, 'notempty')
->checkIF($execution->begin != '', 'begin', 'date')
->checkIF($execution->end != '', 'end', 'date')
->checkIF($execution->end != '', 'end', 'gt', $execution->begin)
->checkIF($execution->end != '', 'end', 'ge', $execution->begin)
->check('code', 'unique', "id != $executionID and code != '' and deleted = '0'")
->where('id')->eq($executionID)
->limit(1)
@@ -1005,7 +1005,6 @@ class executionModel extends model
->beginIF($projectID and $this->config->systemMode == 'new')->andWhere('project')->eq($projectID)->fi()
->beginIF($type != 'all' and $this->config->systemMode == 'new')->andWhere('type')->eq($type)->fi()
->beginIF(strpos($mode, 'withdelete') === false)->andWhere('deleted')->eq(0)->fi()
->beginIF(!$this->app->user->admin and $this->config->systemMode == 'new')->andWhere('project')->in($this->app->user->view->projects)->fi()
->beginIF(!$this->app->user->admin and strpos($mode, 'all') === false)->andWhere('id')->in($this->app->user->view->sprints)->fi()
->orderBy($orderBy)
->fetchAll();
@@ -1069,7 +1068,6 @@ class executionModel extends model
->beginIF($status == 'undone')->andWhere('t2.status')->notIN('done,closed')->fi()
->beginIF($branch)->andWhere('t1.branch')->eq($branch)->fi()
->beginIF($status != 'all' and $status != 'undone')->andWhere('status')->in($status)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t2.project')->in($this->app->user->view->projects)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->sprints)->fi()
->orderBy('order_desc')
->beginIF($limit)->limit($limit)->fi()
@@ -1084,7 +1082,6 @@ class executionModel extends model
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
->beginIF($status == 'undone')->andWhere('status')->notIN('done,closed')->fi()
->beginIF($status != 'all' and $status != 'undone')->andWhere('status')->in($status)->fi()
->beginIF(!$this->app->user->admin)->andWhere('project')->in($this->app->user->view->projects)->fi()
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi()
->orderBy('order_desc')
->beginIF($limit)->limit($limit)->fi()
@@ -1113,7 +1110,6 @@ class executionModel extends model
->where('t1.product')->eq($productID)
->andWhere('t2.deleted')->eq(0)
->beginIF($branch)->andWhere('t1.branch')->eq($branch)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t2.project')->in($this->app->user->view->projects)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->sprints)->fi()
->andWhere('t2.openedBy', true)->eq($this->app->user->account)
->orWhere('t3.account')->eq($this->app->user->account)
@@ -1129,7 +1125,6 @@ class executionModel extends model
return $this->dao->select('t1.*, IF(INSTR(" done,closed", t1.status) < 2, 0, 1) AS isDone')->from(TABLE_EXECUTION)->alias('t1')
->leftJoin(TABLE_TEAM)->alias('t2')->on('t2.root=t1.id')
->where('t1.deleted')->eq(0)
->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in($this->app->user->view->projects)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->sprints)->fi()
->andWhere('t1.openedBy', true)->eq($this->app->user->account)
->orWhere('t2.account')->eq($this->app->user->account)
@@ -1430,7 +1425,6 @@ class executionModel extends model
$list = $this->dao->select('t1.id,t1.name,t1.status,t2.product')->from(TABLE_EXECUTION)->alias('t1')
->leftJoin(TABLE_PROJECTPRODUCT)->alias('t2')->on('t1.id = t2.project')
->where('t1.deleted')->eq(0)
->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in($this->app->user->view->projects)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->sprints)->fi()
->fetchGroup('product');
@@ -2461,6 +2455,7 @@ class executionModel extends model
->andWhere('(project')->eq($projectID)
->orWhere('id')->eq($projectID)
->markRight(1)
->orderBy('project_asc')
->fetchAll('id');
if(empty($teams)) return array();
@@ -3396,7 +3391,7 @@ class executionModel extends model
$days = count($dateList) - 1;
$rate = $days ? $firstTime / $days : '';
$baselineJSON = '[';
foreach($dateList as $i => $date) $baselineJSON .= round(($days - $i) * $rate, 1) . ',';
foreach($dateList as $i => $date) $baselineJSON .= round(($days - $i) * (int)$rate, 1) . ',';
$baselineJSON = rtrim($baselineJSON, ',') . ']';
$chartData['labels'] = $this->report->convertFormat($dateList, DT_DATE5);
+1 -1
View File
@@ -38,7 +38,7 @@
<?php js::set('isStage', false);?>
<?php js::set('copyExecutionID', $copyExecutionID);?>
<?php js::set('systemMode', $config->systemMode);?>
<?php js::set('projectCommon', $lang->projectCommon);?>
<?php js::set('projectCommon', $lang->project->common);?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
+2 -2
View File
@@ -406,9 +406,9 @@ class gitlabModel extends model
$allResults = array();
for($page = 1; true; $page ++)
{
$results = json_decode(commonModel::http($host . "?private_token={$gitlab->token}&simple=true&membership=true&page={$page}&per_page=100"));
$results = json_decode(commonModel::http($host . "?private_token={$gitlab->token}&simple=true&page={$page}&per_page=100"));
if(empty($results) or $page > 10) break;
$allResults = $allResults + $results;
$allResults = array_merge($allResults, $results);
}
return $allResults;
+3 -1
View File
@@ -443,7 +443,9 @@ class my extends control
$this->loadModel('testtask');
/* Save session. */
$this->session->set('caseList', $this->app->getURI(true), 'qa');
$uri = $this->app->getURI(true);
$this->session->set('caseList', $uri, 'qa');
$this->session->set('bugList', $uri . "#app={$this->app->tab}", 'qa');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
+1 -1
View File
@@ -82,7 +82,7 @@
<td><span class='label-pri <?php echo 'label-pri-' . $case->pri?>' title='<?php echo zget($lang->testcase->priList, $case->pri, $case->pri);?>'><?php echo zget($lang->testcase->priList, $case->pri, $case->pri)?></span></td>
<?php $params = "testcaseID=$caseID&version=$case->version";?>
<?php if($type == 'assigntome') $params .= "&from=testtask&taskID=$case->task";?>
<td class='text-left'><?php echo html::a($this->createLink('testcase', 'view', $params), $case->title, null, "style='color: $case->color'");?></td>
<td class='c-name'><?php echo html::a($this->createLink('testcase', 'view', $params), $case->title, null, "style='color: $case->color' title='{$case->title}'");?></td>
<?php if($type == 'assigntome'):?>
<td class='c-name' title='<?php echo $case->taskName;?>'><?php echo $case->taskName;?></td>
<?php endif;?>
+12 -7
View File
@@ -1,12 +1,15 @@
#kanban {padding: 10px;}
.main-table tbody>tr>td {padding: 10px;}
.table caption {background: #fff; border-top-left-radius: 2px; border-top-right-radius: 2px; margin-bottom: 0; font-weight: 500;}
.table .program {writing-mode: vertical-lr; padding-left: 4px !important; color: #fff;}
.table caption {background: #fff; border-top-left-radius: 2px; border-top-right-radius: 2px; margin-bottom: 0; font-weight: 500; padding-top: 0;}
.table th, .table td {border-top: 2px solid #fff; border-bottom: 2px solid #fff; border-left: 3px solid #fff; border-bottom: 3px solid #fff;}
.table tbody tr:last-child td {border-bottom: unset;}
td.project, td.execution {padding: 10px 0 !important;}
td.plan, td.release {vertical-align: top !important;}
td.plan, td.release, td.project, td.execution {vertical-align: top !important;}
.main-table tbody>tr>td {padding: 0 10px 10px;}
.table .program {writing-mode: vertical-lr; padding-left: 4px !important; color: #fff;}
td.product {padding-top: 10px !important;}
td.project, td.execution {padding: 0 0 10px 0 !important;}
td > div:not(.board) > .board-item:first-child {margin-top: 10px;}
td > div.board:first-child {margin-top: 5px;}
.board-item {border: 1px solid #ebebeb; padding: 3px 10px; cursor: default; border-radius: 2px; background-color: #fff; text-align: left;}
.board-item:hover {border-color: #ccc;}
@@ -14,8 +17,10 @@ td.plan, td.release {vertical-align: top !important;}
.board + .board {margin-top: 5px;}
.board-item + .board-item {margin-top: 10px;}
.project .board, .execution .board {padding-top: 5px; border-top: 2px solid #fff;}
.project .board:first-child, .execution .board:first-child {padding-top: 0; border-top: unset;}
.project .board:first-child, .execution .board:first-child {padding-top: 5px; border-top: unset;}
.release .board-item {display: flex; flex-flow: row nowrap; justify-content: flex-start; align-items: center;}
.emptyBoard {padding: 3px 10px; cursor: default;}
.project .table-row > .table-col:last-child, .execution .table-row > .table-col:last-child {width: 24px; padding-top: 2px;}
.scroll {overflow-x: hidden;; overflow-y: auto;}
+9 -9
View File
@@ -346,15 +346,15 @@ class productModel extends model
*/
public function getProducts($projectID = 0, $status = 'all', $orderBy = '')
{
return $this->dao->select('t1.branch, t1.plan, t2.*')
->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PRODUCT)->alias('t2')
->on('t1.product = t2.id')
->where('t2.deleted')->eq(0)
->beginIF(!empty($projectID))->andWhere('t1.project')->eq($projectID)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->products)->fi()
return $this->dao->select('t1.*, t2.branch, t2.plan')
->from(TABLE_PRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECTPRODUCT)->alias('t2')
->on('t1.id = t2.product')
->where('t1.deleted')->eq(0)
->beginIF(!empty($projectID))->andWhere('t2.project')->eq($projectID)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->products)->fi()
->beginIF(strpos($status, 'noclosed') !== false)->andWhere('status')->ne('closed')->fi()
->orderBy($orderBy . 't2.order asc')
->orderBy($orderBy . 't1.order asc')
->fetchAll('id');
}
@@ -1101,7 +1101,7 @@ class productModel extends model
foreach($executions as $id => $execution) $projectIdList[$execution->project] = $execution->project;
$executionPairs = array(0 => '');
$projectPairs = $this->loadModel('project')->getPairsByIdList($projectIdList);
$projectPairs = $this->loadModel('project')->getPairsByIdList($projectIdList, 'all');
foreach($executions as $id => $execution)
{
if($execution->grade == 2 && isset($executions[$execution->parent]))
+40 -22
View File
@@ -39,29 +39,38 @@
<?php foreach($programs as $programID => $programProduct):?>
<?php $i = 0;?>
<?php foreach($programProduct as $productID):?>
<?php
$scroll = '';
$doingCounts = isset($projectProduct[$productID]) ? count($projectProduct[$productID]) : 0;
$planCounts = isset($planList[$productID]) ? count($planList[$productID]) : 0;
$releaseCount = isset($releaseList[$productID]) ? count($releaseList[$productID]) : 0;
if(($doingCounts < $planCounts or $doingCounts < $releaseCount) and ($planCounts > 5 or $releaseCount > 5)) $scroll = 'scroll';
?>
<tr>
<?php if($i == 0):?>
<td rowspan="<?php echo count($programs[$programID])?>" class="program text-ellipsis" style="background: <?php echo $this->lang->product->kanbanColorList[$colorIndex];?>"><?php echo zget($programList, $programID);?></td>
<?php endif;?>
<td title="<?php echo $productList[$productID]->name;?>">
<td class="product">
<?php if(common::hasPriv('product', 'browse')):?>
<?php echo html::a($this->createLink('product', 'browse', "productID=$productID"), $productList[$productID]->name, '', "title={$productList[$productID]->name}");?>
<?php echo "<span>" . html::a($this->createLink('product', 'browse', "productID=$productID"), $productList[$productID]->name, '', "title={$productList[$productID]->name}") . '</span>';?>
<?php else:?>
<?php echo "<span title={$productList[$productID]->name}>{$productList[$productID]->name}</span>";?>
<?php endif;?>
</td>
<td class="plan">
<?php if(isset($planList[$productID])):?>
<?php foreach($planList[$productID] as $planID => $plan):?>
<div class="board-item text-ellipsis">
<?php if(common::hasPriv('productplan', 'view')):?>
<?php echo html::a($this->createLink('productplan', 'view', "planID=$planID"), $plan->title, '', "title={$plan->title}");?>
<?php else:?>
<?php echo "<span title={$plan->title}>{$plan->title}</span>"?>
<div class="<?php echo $scroll;?>">
<?php if(isset($planList[$productID])):?>
<?php foreach($planList[$productID] as $planID => $plan):?>
<div class="board-item text-ellipsis">
<?php if(common::hasPriv('productplan', 'view')):?>
<?php echo html::a($this->createLink('productplan', 'view', "planID=$planID"), $plan->title, '', "title={$plan->title}");?>
<?php else:?>
<?php echo "<span title={$plan->title}>{$plan->title}</span>"?>
<?php endif;?>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
<?php endforeach;?>
<?php endif;?>
</td>
<td class="project">
<?php if(isset($projectProduct[$productID])):?>
@@ -133,18 +142,20 @@
<?php endif;?>
</td>
<td class="release">
<?php if(isset($releaseList[$productID])):?>
<?php foreach($releaseList[$productID] as $releaseID => $release):?>
<div class="board-item">
<?php if(common::hasPriv('release', 'view')):?>
<?php echo html::a($this->createLink('release', 'view', "releaseID=$releaseID"), $release->name, '', "title={$release->name} class='text-ellipsis'");?>
<?php else:?>
<?php echo "<span title={$release->name}>{$release->name}</span>"?>
<div class="<?php echo $scroll;?>">
<?php if(isset($releaseList[$productID])):?>
<?php foreach($releaseList[$productID] as $releaseID => $release):?>
<div class="board-item">
<?php if(common::hasPriv('release', 'view')):?>
<?php echo html::a($this->createLink('release', 'view', "releaseID=$releaseID"), $release->name, '', "title={$release->name} class='text-ellipsis'");?>
<?php else:?>
<?php echo "<span title={$release->name}>{$release->name}</span>"?>
<?php endif;?>
<?php if($release->marker) echo "&nbsp;<span><i class='icon icon-flag red'></i></span>";?>
</div>
<?php endforeach;?>
<?php endif;?>
<?php if($release->marker) echo "&nbsp;<span><i class='icon icon-flag red'></i></span>";?>
</div>
<?php endforeach;?>
<?php endif;?>
</td>
<?php $i ++;?>
</tr>
@@ -168,7 +179,14 @@ $(function()
}
})
$('.emptyBoard').parent().height($('.project .board').height());
$('.board').height($('.project .board').height());
$('.table div.scroll').each(function()
{
var count = $(this).parent().siblings('td.project').children('.board').length >= 5 ? $(this).parent().siblings('td.project').children('.board').length : 5;
var preHeight = $(this).parent().siblings('td.project').children('.board').length >= 5 ? $('.board').outerHeight(true) : $(this).find('.board-item').outerHeight(true);
$(this).css('max-height', preHeight * count);
})
})
</script>
<?php include '../../common/view/footer.html.php';?>
+2 -2
View File
@@ -55,11 +55,11 @@
<?php
if($config->systemMode == 'new')
{
echo html::a($this->createLink('project', 'index', 'project=' . $project->id), $project->name, '_parent');
echo html::a($this->createLink('project', 'index', 'project=' . $project->id), $project->name);
}
else
{
echo html::a($this->createLink('execution', 'task', 'project=' . $project->id), $project->name, '_parent');
echo html::a($this->createLink('execution', 'task', 'project=' . $project->id), $project->name);
}
?>
</td>
+5 -8
View File
@@ -96,9 +96,6 @@ class program extends control
$this->program->setMenu($programID);
$program = $this->program->getByID($programID);
if(empty($program) || $program->type != 'program') die(js::error($this->lang->notFound) . js::locate('back'));
/* Load pager and get tasks. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
@@ -106,6 +103,7 @@ class program extends control
/* Get the top programID. */
if($programID)
{
$program = $this->program->getByID($programID);
$path = explode(',', $program->path);
$path = array_filter($path);
$programID = current($path);
@@ -113,7 +111,7 @@ class program extends control
$this->view->title = $this->lang->program->product;
$this->view->position[] = $this->lang->program->product;
$this->view->program = $program;
$this->view->programID = $programID;
$this->view->browseType = $browseType;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
@@ -386,12 +384,12 @@ class program extends control
{
$programID = $this->program->saveState($programID, $this->program->getPairs());
setCookie("lastProgram", $programID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
if(!$programID) $this->locate($this->createLink('program', 'browse'));
$this->program->setMenu($programID);
$this->app->session->set('programProject', $this->app->getURI(true), 'program');
$this->app->session->set('projectList', $this->app->getURI(true), 'project');
$uri = $this->app->getURI(true);
$this->app->session->set('programProject', $uri, 'program');
$this->app->session->set('projectList', $uri, 'program');
$this->loadModel('datatable');
@@ -410,7 +408,6 @@ class program extends control
$this->view->projectStats = $projectStats;
$this->view->pager = $pager;
$this->view->programID = $programID;
$this->view->program = $this->program->getByID($programID);
$this->view->users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted');
$this->view->browseType = $browseType;
$this->view->orderBy = $orderBy;
+8 -7
View File
@@ -1,11 +1,13 @@
.main-table .table {cursor: default;}
.main-table .table td {background: #f5f5f5;}
.main-table tbody>tr>td:first-child {padding: 0px 8px}
.lane-name {overflow: hidden; text-overflow: ellipsis; writing-mode: vertical-lr;}
.board-item {border: 1px solid #EBEBEB; padding: 5px 10px; cursor: default; border-radius: 2px; background-color: #fff;}
.board-item:hover {border-color: #ccc;}
.board-item + .board-item {margin-top: 10px;}
.board-item {margin-top: 10px;}
.board-item:last-child {margin-bottom: 10px;}
#kanban {overflow-y: auto;}
#kanban tbody > tr > td {line-height: 24px;}
@@ -15,12 +17,11 @@
.doing-td, .wait-project, .normal-plan, .normal-release {vertical-align: top !important;}
.doing-td > .board-doing > .table-row+.table-row {margin-top: 10px;}
.doing-td .board:first-child{padding-top: 0; border-top: unset;}
.board-doing-project {padding-right: 6px}
.board-doing-execution{padding-left: 6px}
.board-doing-project .table-row .table-col:last-child, .c-progress {width: 30px;}
.board-doing-execution .table-row .table-col:last-child, .c-progress {width: 30px;}
.table-row .table-col:first-child {overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.project-line {width: 100%; height: 2px; background: #fff}
.doing-td .table-row .table-col:last-child, .c-progress {width: 30px;}
.doing-td .table-row .table-col:first-child {overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.table-grouped tbody > tr > td:first-child, .table-grouped thead > tr > th:first-child {padding-left: 3px;}
+7
View File
@@ -258,6 +258,8 @@ class programModel extends model
$project->hours = zget($hours, $project->id, array());
$product->projects[$status][] = $project;
}
$product->rowspan = isset($product->projects['doing']) ? count($product->projects['doing']) > 0 ? count($product->projects['doing']) : 1 : 1;
}
}
@@ -265,6 +267,11 @@ class programModel extends model
foreach($programs as $programID => $program)
{
$program->products = zget($productGroup, $programID, array());
$program->rowspan = count($program->products) > 0 ? count($program->products) : 1;
foreach($program->products as $product)
{
if(isset($product->projects['doing']) and count($product->projects['doing']) > 0) $program->rowspan += count($product->projects['doing']) - 1;
}
if(in_array($programID, $involvedPrograms))
{
+53 -44
View File
@@ -12,6 +12,7 @@
<?php include '../../common/view/header.html.php';?>
<div id="kanban" class="main-table fade auto-fade-in" data-ride="table" data-checkable="false" data-group="true">
<?php foreach($kanbanGroup as $type => $programGroup):?>
<?php if(empty($programGroup)) continue;?>
<?php $colorIndex = 0;?>
<div class="cell">
<div class='detail'>
@@ -35,13 +36,13 @@
<tbody>
<?php foreach($programGroup as $programID => $program):?>
<tr>
<td class='lane-name' style='background: <?php echo $lang->program->kanban->laneColorList[$colorIndex];?>; color: #fff; border-right: none;' rowspan='<?php echo count($program->products);?>' title=<?php echo $program->name;?>><?php echo $program->name;?></td>
<td class='lane-name' style='background: <?php echo $lang->program->kanban->laneColorList[$colorIndex];?>; color: #fff; border-right: none;' rowspan='<?php echo $program->rowspan;?>' title=<?php echo $program->name;?>><?php echo $program->name;?></td>
<?php $i = 0;?>
<?php if(!empty($program->products)):?>
<?php foreach($program->products as $productID => $product):?>
<?php if($i != 0) echo '<tr>';?>
<td title=<?php echo $product->name;?>><?php echo $product->name;?></td>
<td class='normal-plan'>
<td title=<?php echo $product->name;?> rowspan='<?php echo $product->rowspan;?>'><?php echo $product->name;?></td>
<td class='normal-plan' rowspan='<?php echo $product->rowspan;?>'>
<?php foreach($product->plans as $planID => $plan):?>
<div class='board-item'>
<div class='table-row'>
@@ -52,7 +53,7 @@
</div>
<?php endforeach;?>
</td>
<td class='wait-project'>
<td class='wait-project' rowspan='<?php echo $product->rowspan?>'>
<?php if(isset($product->projects['wait'])):?>
<?php foreach($product->projects['wait'] as $projectID => $project):?>
<div class='board-item' style='border-left: 3px solid #ccc'>
@@ -65,53 +66,52 @@
<?php endforeach;?>
<?php endif;?>
</td>
<td colspan='2' class='doing-td'>
<div class='board-doing'>
<?php if(isset($product->projects['doing'])):?>
<?php foreach($product->projects['doing'] as $projectID => $project):?>
<div class='table-row'>
<div class='table-col board-doing-project'>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php echo html::a($this->createLink('project', 'index', "projectID=$project->id"), $project->name);?>
</div>
<div class='table-col'>
<div class="c-progress">
<?php $projectProgress = isset($project->hours->progress) ? $project->hours->progress : 0;?>
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($projectProgress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($projectProgress);?></div>
</div>
</div>
<?php if(isset($product->projects['doing'])):?>
<?php $index = 0;?>
<?php foreach($product->projects['doing'] as $projectID => $project):?>
<?php if($index != 0) echo "<tr>";?>
<td class='doing-td'>
<div class='board'>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php echo html::a($this->createLink('project', 'index', "projectID=$project->id"), $project->name);?>
</div>
<div class='table-col'>
<div class="c-progress">
<?php $projectProgress = isset($project->hours->progress) ? $project->hours->progress : 0;?>
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($projectProgress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($projectProgress);?></div>
</div>
</div>
</div>
</div>
<div class='table-col board-doing-execution'>
<?php if(!empty($project->execution)):?>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->execution->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php echo html::a($this->createLink('execution', 'task', "executionID={$project->execution->id}"), $project->execution->name);?>
</div>
<div class='table-col'>
<div class="c-progress">
<?php $executionProgress = isset($project->execution->hours->progress) ? $project->execution->hours->progress : 0;?>
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($executionProgress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($executionProgress);?></div>
</div>
</div>
</div>
</div>
</div>
<?php endif;?>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
</td>
<td class='normal-release'>
<td class='doing-td'>
<?php if(!empty($project->execution)):?>
<div class='board'>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->execution->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php echo html::a($this->createLink('execution', 'task', "executionID={$project->execution->id}"), $project->execution->name);?>
</div>
<div class='table-col'>
<div class="c-progress">
<?php $executionProgress = isset($project->execution->hours->progress) ? $project->execution->hours->progress : 0;?>
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($executionProgress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($executionProgress);?></div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endif;?>
</td>
<?php if($index == 0):?>
<td class='normal-release' rowspan=<?php echo $product->rowspan;?>>
<?php foreach($product->releases as $releaseID => $release):?>
<div class='board-item'>
<div class='table-row'>
@@ -123,6 +123,15 @@
</div>
<?php endforeach;?>
</td>
<?php endif;?>
<?php if($index != 0) echo "</tr>";?>
<?php $index ++;?>
<?php endforeach;?>
<?php else:?>
<td></td>
<td></td>
<td></td>
<?php endif;?>
<?php if($i != 0) echo '</tr>';?>
<?php $i ++;?>
<?php endforeach;?>
+6 -6
View File
@@ -17,11 +17,11 @@
<?php foreach($lang->product->featureBar['all'] as $key => $label):?>
<?php $active = $key == $browseType ? 'btn-active-text' : '';?>
<?php if($key == $browseType) $label .= " <span class='label label-light label-badge'>{$pager->recTotal}</span>";?>
<?php echo html::a(inlink("product", "programID=$program->id&browseType=$key&orderBy=$orderBy"), "<span class='text'>{$label}</span>", '', "class='btn btn-link $active'");?>
<?php echo html::a(inlink("product", "programID=$programID&browseType=$key&orderBy=$orderBy"), "<span class='text'>{$label}</span>", '', "class='btn btn-link $active'");?>
<?php endforeach;?>
</div>
<div class="btn-toolbar pull-right">
<?php common::printLink('product', 'create', "programID=$program->id", '<i class="icon icon-plus"></i> ' . $lang->product->create, '', 'class="btn btn-primary"');?>
<?php common::printLink('product', 'create', "programID=$programID", '<i class="icon icon-plus"></i> ' . $lang->product->create, '', 'class="btn btn-primary"');?>
</div>
<?php endif;?>
</div>
@@ -32,11 +32,11 @@
</div>
<?php else:?>
<div class="main-col">
<form class="main-table table-product" data-ride="table" id="productListForm" method="post" action='<?php echo $this->createLink('product', 'batchEdit', "programID=$program->id");?>'>
<form class="main-table table-product" data-ride="table" id="productListForm" method="post" action='<?php echo $this->createLink('product', 'batchEdit', "programID=$programID");?>'>
<?php $canOrder = common::hasPriv('product', 'updateOrder');?>
<?php $canBatchEdit = common::hasPriv('product', 'batchEdit');?>
<table id="productList" class="table has-sort-head table-bordered table-fixed">
<?php $vars = "programID=$program->id&browseType=$browseType&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}";?>
<?php $vars = "programID=$programID&browseType=$browseType&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}";?>
<thead>
<tr class="text-center">
<th class='c-id text-left' rowspan='2'>
@@ -104,7 +104,7 @@
<td><?php echo $product->plans;?></td>
<td><?php echo $product->releases;?></td>
<td class='c-actions'>
<?php common::printIcon('product', 'edit', "product=$product->id&action=edit&extra=&programID=$program->id", $product, 'list', 'edit');?>
<?php common::printIcon('product', 'edit', "product=$product->id&action=edit&extra=&programID=$programID", $product, 'list', 'edit');?>
<?php if($canOrder):?>
<span class='c-actions sort-handler'><i class="icon icon-move"></i></span>
<?php endif;?>
@@ -129,7 +129,7 @@
<?php endif;?>
</div>
<?php js::set('orderBy', $orderBy)?>
<?php js::set('programID', $program->id)?>
<?php js::set('programID', $programID)?>
<script>
$(function()
{
+1
View File
@@ -743,6 +743,7 @@ class project extends control
$this->session->set('bugList', $uri, 'qa');
$this->session->set('caseList', $uri, 'qa');
$this->session->set('testtaskList', $uri, 'qa');
$this->session->set('reportList', $uri, 'qa');
if(isset($this->config->maxVersion))
{
+6 -7
View File
@@ -5,14 +5,13 @@
.board-item:hover {border-color: #ccc;}
.board-project .board-item + .board-item {margin-top: 10px;}
.board-project {padding: 10px;}
.board-project {padding: 0 10px 10px 10px;}
.board-project .board-item:first-child {margin-top: 10px;}
.board-doing {padding: 0px !important; vertical-align: top !important;}
.board-doing div:not(:last-child) .board-doing-project,
.board-doing div:not(:last-child) .board-doing-execution {padding: 10px; border-bottom: 2px solid #fff;}
.board-doing div:last-child .board-doing-project,
.board-doing div:last-child .board-doing-execution {padding: 10px;}
.board-doing, .board-wait, .board-closed {padding: 0px !important; vertical-align: top !important;}
.board-doing .board-doing-project,.board-doing .board-doing-execution {padding: 10px;}
.board-doing .board-doing-project:not(:last-child),
.board-doing .board-doing-execution:not(:last-child) {border-bottom: 2px solid #fff;}
.board-doing-project .table-row .table-col:last-child, .c-progress {width: 30px;}
.board-doing-execution .table-row .table-col:last-child, .c-progress {width: 30px;}
+27
View File
@@ -0,0 +1,27 @@
$(function()
{
$("div[class^='board-doing-']").height($('.board-doing-project').height());
$('.board-program').each(function()
{
var boardWaitCount = $(this).find('.board-wait .board-item').length;
var boardClosedCount = $(this).find('.board-closed .board-item').length;
var boardDoingCount = $(this).find('.board-doing .board-item').length;
if((boardWaitCount > 5 || boardClosedCount > 5) && (boardWaitCount > boardDoingCount || boardClosedCount > boardDoingCount))
{
var boardHeight = 0;
if(boardDoingCount > 5)
{
boardHeight = $('.board-doing-project').outerHeight(true) * boardDoingCount;
}
else
{
var boardHeight = $(this).find('.board-project .board-item').outerHeight(true) * 5;
}
$(this).find('.board-project').css("height", boardHeight);
$(this).find('.board-project').css("overflow", 'auto');
}
});
})
+1 -1
View File
@@ -51,7 +51,7 @@ function saveMemebers()
function setRole(account, roleID)
{
role = roles[account]; // get role according the account.
roleOBJ = $('#role' + roleID); // get role object.
roleOBJ = $(document.getElementById('roles[' + roleID + ']')); // get role object.
roleOBJ.val(role) // set the role.
}
+4 -2
View File
@@ -464,16 +464,17 @@ class projectModel extends model
* Get project pairs by id list.
*
* @param array $projectIdList
* @param string $mode
* @access public
* @return array
*/
public function getPairsByIdList($projectIdList = array())
public function getPairsByIdList($projectIdList = array(), $mode = '')
{
return $this->dao->select('id, name')->from(TABLE_PROJECT)
->where('type')->eq('project')
->andWhere('deleted')->eq(0)
->beginIF($projectIdList)->andWhere('id')->in($projectIdList)->fi()
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi()
->beginIF(!$this->app->user->admin and $mode != 'all')->andWhere('id')->in($this->app->user->view->projects)->fi()
->fetchPairs('id', 'name');
}
@@ -1812,6 +1813,7 @@ class projectModel extends model
{
$otherProjects[$topProgram][$project->status][$project->id] = $project;
}
}
return array('kanbanGroup' => array('my' => $myProjects, 'other' => $otherProjects), 'latestExecutions' => $latestExecutions);
+1 -1
View File
@@ -149,7 +149,7 @@
<?php echo html::textarea('desc', $project->desc, "rows='6' class='form-control kindeditor' hidefocus='true'" . (strpos($requiredFields, 'desc') !== false ? ' required' : ''));?>
</td>
</tr>
<?php $this->printExtendFields('', 'table', 'columns=3');?>
<?php $this->printExtendFields($project, 'table', 'columns=3');?>
<tr>
<th><?php echo $lang->project->acl;?></th>
<td colspan='3' class='aclBox'><?php echo nl2br(html::radio('acl', $aclList, $project->acl, "onclick='setWhite(this.value);'", 'block'));?></td>
+51 -50
View File
@@ -10,13 +10,11 @@
?>
<?php include '../../common/view/header.html.php';?>
<?php $colorIndex = 0;?>
<?php if(empty($kanbanGroup)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->project->empty;?></span></p>
</div>
<?php else:?>
<?php foreach($kanbanGroup as $type => $projectGroup):?>
<?php if(empty($projectGroup)) continue;?>
<div id="kanban" class="main-table fade auto-fade-in" data-ride="table" data-checkable="false" data-group="true">
@@ -39,73 +37,77 @@
</thead>
<tbody>
<?php foreach($projectGroup as $programID => $statusList):?>
<tr>
<tr class='board-program'>
<td class='text-center' style='background: <?php echo $lang->project->laneColorList[$colorIndex];?>; color: #fff; padding-left: 2px; writing-mode: vertical-lr;'><?php echo zget($programPairs, $programID);?></td>
<?php foreach(array('wait','doing','closed') as $status):?>
<?php if($status == 'doing'):?>
<td colspan='2' class='board-doing'>
<td class='board-doing'>
<?php if(isset($statusList[$status])):?>
<?php foreach($statusList[$status] as $project):?>
<div class='table-row'>
<div class='table-col board-doing-project'>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php
if(common::hasPriv('project', 'index'))
{
echo html::a($this->createLink('project', 'index', "projectID=$project->id"), $project->name, '', "title='{$project->name}'");
}
else
{
echo "<span title='{$project->name}'>{$project->name}</span>";
}
?>
</div>
<div class='table-col'>
<div class="c-progress">
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($project->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($project->hours->progress);?></div>
</div>
<div class='board-doing-project'>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php
if(common::hasPriv('project', 'index'))
{
echo html::a($this->createLink('project', 'index', "projectID=$project->id"), $project->name, '', "title='{$project->name}'");
}
else
{
echo "<span title='{$project->name}'>{$project->name}</span>";
}
?>
</div>
<div class='table-col'>
<div class="c-progress">
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($project->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($project->hours->progress);?></div>
</div>
</div>
</div>
</div>
</div>
<div class='table-col board-doing-execution'>
<?php if(isset($latestExecutions[$project->id])):?>
<?php $execution = $latestExecutions[$project->id];?>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($execution->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php
if(common::hasPriv('execution', 'task'))
{
echo html::a($this->createLink('execution', 'task', "executionID=$execution->id"), $execution->name, '', "title='{$execution->name}'");
}
else
{
echo "<span title='{$execution->name}'>{$execution->name}</span>";
}
?>
</div>
<div class='table-col'>
<div class="c-progress">
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($execution->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($execution->hours->progress);?></div>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</td>
<td class='board-doing'>
<?php if(isset($statusList[$status])):?>
<?php foreach($statusList[$status] as $project):?>
<div class='board-doing-execution'>
<?php if(isset($latestExecutions[$project->id])):?>
<?php $execution = $latestExecutions[$project->id];?>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($execution->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php
if(common::hasPriv('execution', 'task'))
{
echo html::a($this->createLink('execution', 'task', "executionID=$execution->id"), $execution->name, '', "title='{$execution->name}'");
}
else
{
echo "<span title='{$execution->name}'>{$execution->name}</span>";
}
?>
</div>
<div class='table-col'>
<div class="c-progress">
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($execution->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($execution->hours->progress);?></div>
</div>
</div>
</div>
</div>
<?php endif;?>
</div>
<?php endif;?>
</div>
<?php endforeach;?>
<?php endif;?>
</td>
<?php else:?>
<td>
<td class='board-<?php echo $status;?>'>
<div class='board-project'>
<?php if(isset($statusList[$status])):?>
<?php foreach($statusList[$status] as $project):?>
@@ -138,6 +140,5 @@
</div>
</div>
<?php endforeach;?>
<?php endif;?>
<?php include '../../common/view/footer.html.php';?>
-23
View File
@@ -1752,29 +1752,6 @@ class repoModel extends model
return $buildedURL;
}
/**
* Get gitlab projects.
*
* @param string $host
* @param string $token
* @access public
* @return array
*/
public function getGitlabProjects($host, $token)
{
$host = rtrim($host, '/');
$host .= '/api/v4/projects';
$allResults = array();
for($page = 1; true; $page ++)
{
$results = json_decode(common::http($host . "?private_token=$token&simple=true&membership=true&page={$page}&per_page=100"));
if(empty($results) or $page > 10) break;
$allResults = $allResults + $results;
}
return $allResults;
}
/**
* Process gitlab repo.
*
+5 -1
View File
@@ -48,6 +48,8 @@ class story extends control
*/
public function create($productID = 0, $branch = 0, $moduleID = 0, $storyID = 0, $objectID = 0, $bugID = 0, $planID = 0, $todoID = 0, $extra = '', $type = 'story')
{
if($productID == 0) $this->locate($this->createLink('product', 'create'));
$this->story->replaceURLang($type);
if($this->app->tab == 'product')
{
@@ -898,7 +900,9 @@ class story extends control
*/
public function view($storyID, $version = 0, $param = 0)
{
$this->session->set('productList', $this->app->getURI(true) . "#app={$this->app->tab}", 'product');
$uri = $this->app->getURI(true);
$this->session->set('productList', $uri . "#app={$this->app->tab}", 'product');
$this->session->set('productPlanList', $uri, 'product');
$storyID = (int)$storyID;
$story = $this->story->getById($storyID, $version, true);
+2
View File
@@ -39,6 +39,8 @@ class task extends control
*/
public function create($executionID = 0, $storyID = 0, $moduleID = 0, $taskID = 0, $todoID = 0)
{
if(empty($this->app->user->view->sprints) and !$executionID) $this->locate($this->createLink('execution', 'create'));
$executions = $this->execution->getPairs();
$executionID = $this->execution->saveState($executionID, $executions);
$this->execution->setMenu($executionID);
+14 -2
View File
@@ -62,7 +62,7 @@ function loadExecutionStories(executionID)
$('#story').next('.picker').remove();
$('#story').replaceWith(data);
$('#story').addClass('filled').chosen();
/* If there is no story option, select will be hidden and text will be displayed; otherwise, the opposite is true */
if($('#story option').length > 1)
{
@@ -70,7 +70,7 @@ function loadExecutionStories(executionID)
$('#storyBox').addClass('hidden');
}
else
{
{
$('#storyBox').removeClass('hidden');
$('#story').parent().addClass('hidden');
}
@@ -281,6 +281,18 @@ function setStories(moduleID, executionID)
$('#story_chosen').remove();
$('#story').next('.picker').remove();
$("#story").addClass('filled').chosen();
/* If there is no story option, select will be hidden and text will be displayed; otherwise, the opposite is true */
if($('#story option').length > 1)
{
$('#story').parent().removeClass('hidden');
$('#storyBox').addClass('hidden');
}
else
{
$('#storyBox').removeClass('hidden');
$('#story').parent().addClass('hidden');
}
});
}
+1 -1
View File
@@ -82,7 +82,7 @@ js::set('dittoNotice', $dittoNotice);
{
$prjInfo = $this->execution->getById($tasks[$taskID]->execution);
$modules = $this->tree->getTaskOptionMenu($tasks[$taskID]->execution, 0, 0, 'allModule');
foreach($modules as $moduleID => $moduleName) $modules[$moduleID] = '/' . $prjInfo->name. $moduleName;
foreach($modules as $moduleID => $moduleName) $modules[$moduleID] = $prjInfo->name. $moduleName;
$modules = array('ditto' => $this->lang->task->ditto) + $modules;
}
?>
+1 -1
View File
@@ -247,7 +247,7 @@
</div>
</div>
</div>
<div class="modal fade modal-team" id="modalTeam">
<div class="modal fade modal-team" id="modalTeam" data-scroll-inside='false'>
<div class="modal-dialog">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
+1 -1
View File
@@ -1478,7 +1478,7 @@ class testcaseModel extends model
$id = $col->id;
if($col->show)
{
$class = 'c-' . $id;
$class = $id == 'title' ? 'c-name' : 'c-' . $id;
$title = '';
if($id == 'title')
{
+1 -1
View File
@@ -67,7 +67,7 @@
<?php foreach($caseIDList as $caseID):?>
<?php
if(!isset($cases[$caseID])) continue;
if(!$productID and !$cases[$caseID]->lib)
if((!$productID and !$cases[$caseID]->lib) or $app->tab != 'qa')
{
$product = $this->product->getByID($cases[$caseID]->product);
$branches = $product->type == 'normal' ? array('' => '') : $this->loadModel('branch')->getPairs($product->id);
+2 -2
View File
@@ -48,8 +48,8 @@
<?php foreach($reports as $report):?>
<tr>
<?php $viewLink = helper::createLink('testreport', 'view', "reportID=$report->id");?>
<td><?php echo html::a($viewLink, sprintf('%03d', $report->id), '', "data-app='{$this->app->tab}'");?></td>
<td class='text-left' title='<?php $report->title?>'><?php echo html::a($viewLink, $report->title, '', "data-app='{$this->app->tab}'")?></td>
<td><?php echo html::a($viewLink, sprintf('%03d', $report->id), '', "data-app='{$app->tab}'");?></td>
<td class='c-name'><?php echo html::a($viewLink, $report->title, '', "data-app='{$app->tab}' title='{$report->title}'")?></td>
<td><?php echo zget($users, $report->createdBy);?></td>
<td><?php echo substr($report->createdDate, 2);?></td>
<?php $executionName = $report->execution ? '#' . $report->execution . zget($executions, $report->execution, '') : '';?>
+8 -8
View File
@@ -491,17 +491,17 @@ $(function()
};
}
/* Quit tutorial mode */
function quitTutorial()
{
var url = createLink('tutorial', 'quit');
if(typeof navigator.sendBeacon === 'function') navigator.sendBeacon(url);
else $.ajax({url: url, dataType: 'json', async: false});
}
/** Init current tutorial page */
function initTutorial()
{
/* Quit tutorial mode on unload page */
window.onbeforeunload = function()
{
var url = createLink('tutorial', 'quit');
if(typeof navigator.sendBeacon === 'function') navigator.sendBeacon(url);
else $.ajax({url: url, dataType: 'json', async: false});
}
if(finishCount >= totalCount) showModal(true);
$(document).on('click', '.btn-task', function()
+1 -1
View File
@@ -66,7 +66,7 @@ if($config->global->flow == 'full' or $config->global->flow == 'onlyTask')
$lang->tutorial->tasks['createProject']['desc'] = "<p>Create a {$lang->projectCommon}: </p><ul><li data-target='nav'>Open <span class='task-nav'> {$lang->projectCommon} <i class='icon icon-angle-right'></i> New</span> Page;</li><li data-target='form'>Fill the form with {$lang->projectCommon} information;</li><li data-target='submit'>Save</li></ul>";
$lang->tutorial->tasks['manageTeam'] = array('title' => "Manage {$lang->projectCommon} Team");
$lang->tutorial->tasks['manageTeam']['nav'] = array('app' => 'project', 'module' => 'project', 'method' => 'managemembers', 'menuModule' => 'settings', 'menu' => '#navbar>.nav>li[data-id="all"],#cards>.col>.panel:first .project-name,#navbar>.nav>li[data-id="settings"],#subNavbar>.nav>li[data-id="members"],.manage-team-btn', 'target' => '.manage-team-btn', 'form' => '#teamForm', 'requiredFields' => 'account1', 'submit' => '#submit', 'targetPageName' => 'Manage team members');
$lang->tutorial->tasks['manageTeam']['nav'] = array('app' => 'project', 'module' => 'project', 'method' => 'managemembers', 'menuModule' => 'settings', 'menu' => '#navbar>.nav>li[data-id="browse"],#cards>.col>.panel:first .project-name,#projectTableList>tr:first>.c-name>a,#navbar>.nav>li[data-id="settings"],#subNavbar>.nav>li[data-id="members"],.manage-team-btn', 'target' => '.manage-team-btn', 'form' => '#teamForm', 'requiredFields' => 'account1', 'submit' => '#submit', 'targetPageName' => 'Manage team members');
$lang->tutorial->tasks['manageTeam']['desc'] = "<p>Manage {$lang->projectCommon} team members: </p><ul><li data-target='nav'>Open <span class='task-nav'> {$lang->projectCommon} <i class='icon icon-angle-right'></i> Team <i class='icon icon-angle-right'></i> Manage Team Members</span> Page;</li><li data-target='form'>Choose users for the team.</li><li data-target='submit'>Save</li></ul>";
$lang->tutorial->tasks['createExecution'] = array('title' => 'Create a ' . $lang->executionCommon);
+1 -1
View File
@@ -65,7 +65,7 @@ if($config->global->flow == 'full' or $config->global->flow == 'onlyTask')
$lang->tutorial->tasks['createProject']['desc'] = "<p>在系统创建一个新的{$lang->projectCommon}:</p><ul><li data-target='nav'>打开 <span class='task-nav'> {$lang->projectCommon} <i class='icon icon-angle-right'></i> {$lang->projectCommon}列表 <i class='icon icon-angle-right'></i> 创建{$lang->projectCommon}</span> 页面;</li><li data-target='form'>在表单中填写要创建的{$lang->projectCommon}信息;</li><li data-target='submit'>保存{$lang->projectCommon}信息。</li></ul>";
$lang->tutorial->tasks['manageTeam'] = array('title' => "管理{$lang->projectCommon}团队");
$lang->tutorial->tasks['manageTeam']['nav'] = array('app' => 'project', 'module' => 'project', 'method' => 'managemembers', 'menuModule' => 'settings', 'menu' => '#navbar>.nav>li[data-id="all"],#cards>.col>.panel:first .project-name,#navbar>.nav>li[data-id="settings"],#subNavbar>.nav>li[data-id="members"],.manage-team-btn', 'target' => '.manage-team-btn', 'form' => '#teamForm', 'requiredFields' => 'account1', 'submit' => '#submit', 'targetPageName' => '团队管理');
$lang->tutorial->tasks['manageTeam']['nav'] = array('app' => 'project', 'module' => 'project', 'method' => 'managemembers', 'menuModule' => 'settings', 'menu' => '#navbar>.nav>li[data-id="browse"],#cards>.col>.panel:first .project-name,#projectTableList>tr:first>.c-name>a,#navbar>.nav>li[data-id="settings"],#subNavbar>.nav>li[data-id="members"],.manage-team-btn', 'target' => '.manage-team-btn', 'form' => '#teamForm', 'requiredFields' => 'account1', 'submit' => '#submit', 'targetPageName' => '团队管理');
$lang->tutorial->tasks['manageTeam']['desc'] = "<p>管理{$lang->projectCommon}团队成员:</p><ul><li data-target='nav'>打开 <span class='task-nav'> {$lang->projectCommon} <i class='icon icon-angle-right'></i> 设置 <i class='icon icon-angle-right'></i> 团队 <i class='icon icon-angle-right'></i> 团队管理</span> 页面;</li><li data-target='form'>选择要加入团队的成员;</li><li data-target='submit'>保存团队成员信息。</li></ul>";
$lang->tutorial->tasks['createExecution'] = array('title' => '创建' . $lang->executionCommon);
+17
View File
@@ -703,6 +703,17 @@ class upgradeModel extends model
$this->processTesttaskDate();
$this->processDocTempContent();
$this->appendExec('15_3');
case '15_4':
$this->saveLogs('Execute 15_4');
if(empty($this->config->isINT))
{
if(!$executeXuanxuan)
{
$xuanxuanSql = $this->app->getAppRoot() . 'db' . DS . 'upgradexuanxuan4.2.sql';
$this->execSQL($xuanxuanSql);
}
}
$this->appendExec('15_4');
}
$this->deletePatch();
@@ -896,6 +907,12 @@ class upgradeModel extends model
case '15_0_3': $confirmContent .= file_get_contents($this->getUpgradeFile('15.0.3'));
case '15_2': $confirmContent .= file_get_contents($this->getUpgradeFile('15.2'));
case '15_3': $confirmContent .= file_get_contents($this->getUpgradeFile('15.3'));
case '15_4':
if(empty($this->config->isINT))
{
$xuanxuanSql = $this->app->getAppRoot() . 'db' . DS . 'upgradexuanxuan4.2.sql';
$confirmContent .= file_get_contents($xuanxuanSql);
}
}
return str_replace('zt_', $this->config->db->prefix, $confirmContent);
}
+2 -2
View File
@@ -35,11 +35,11 @@ if(empty($config->notMd5Pwd))js::import($jsRoot . 'md5.js');
<tbody>
<tr>
<th><?php echo $lang->user->account;?></th>
<td><input class='form-control' type='text' name='account' id='account' autofocus /></td>
<td><input class='form-control' type='text' name='account' id='account' autocomplete='off' autofocus /></td>
</tr>
<tr>
<th><?php echo $lang->user->password;?></th>
<td><input class='form-control' type='password' name='password' /></td>
<td><input class='form-control' type='password' name='password' autocomplete='off' /></td>
</tr>
<?php if(!empty($this->config->safe->loginCaptcha)):?>
<tr>
+12
View File
@@ -874,4 +874,16 @@ $(document).ready(function()
{
$('a').attr('target', '');
});
/* Hide the global create drop-down when hovering over the avatar. */
$('.has-avatar').hover(function()
{
$(this).next().removeClass('open');
});
/* Hide the avatar drop-down when hovering over the global create button. */
$('#globalCreate').hover(function()
{
$(this).prev().removeClass('open');
});
});
+1
View File
@@ -44,6 +44,7 @@ a.showMoreImage:hover {opacity: 1;}
.table-footer .checked+div .btn, .table-actions .btn:hover {background: #16a8f8; border-color: #16a8f8}
#userMenu>ul>li {margin-right: 10px;}
#userNav .dropdown-menu>li>a>.icon-w {top: 2px;}
.icon-plus-solid-circle:before {font-size: 30px; color: #3b92f7;}
.create-list {min-width: 120px !important;}
.create-list>li>a {padding-left: 35px !important;}