* Finish task#48603,48609,48610

This commit is contained in:
zenggang
2022-02-10 02:20:02 +00:00
parent f7f52f101e
commit 5927cd62ad
9 changed files with 283 additions and 5 deletions
+2
View File
@@ -14,3 +14,5 @@ $config->sonarqube->projectStatusClass['ERROR'] = 'danger';
$config->sonarqube->createproject = new stdclass();
$config->sonarqube->createproject->requiredFields = 'projectName,projectKey';
$config->sonarqube->cacheTime = 10;
+81
View File
@@ -330,4 +330,85 @@ class sonarqube extends control
$this->loadModel('action')->create('sonarqubeproject', 0, 'deleted', '', $projectKey);
return print(js::reload('parent'));
}
/**
* Browse sonarqube issue.
*
* @param int $sonarqubeID
* @param string $projectKey
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browseIssue($sonarqubeID, $projectKey = '', $orderBy = 'severity_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
set_time_limit(600);
$this->app->loadClass('pager', $static = true);
$keyword = fixer::input('post')->setDefault('keyword', '')->get('keyword');
$cacheFile = $this->sonarqube->getCacheFile($sonarqubeID, $projectKey);
if(!$cacheFile or !file_exists($cacheFile) or (time() - filemtime($cacheFile)) / 60 > $this->config->sonarqube->cacheTime)
{
$sonarqubeIssueList = $this->sonarqube->apiGetIssues($sonarqubeID, $projectKey);
if($cacheFile)
{
if(!file_exists($cacheFile . '.lock'))
{
touch($cacheFile . '.lock');
file_put_contents($cacheFile, serialize($sonarqubeIssueList));
unlink($cacheFile . '.lock');
}
}
}
else
{
$sonarqubeIssueList = unserialize(file_get_contents($cacheFile));
}
foreach($sonarqubeIssueList as $key => $sonarqubeIssue)
{
if(!isset($sonarqubeIssue->line)) $sonarqubeIssue->line = '';
if(!isset($sonarqubeIssue->effort)) $sonarqubeIssue->effort = '';
$sonarqubeIssue->message = htmlspecialchars($sonarqubeIssue->message);
list($project, $file) = explode(':', $sonarqubeIssue->component);
$sonarqubeIssue->file = $file;
}
/* Data search. */
if($keyword)
{
foreach($sonarqubeIssueList as $key => $sonarqubeIssue)
{
if(strpos($sonarqubeIssue->message, $keyword) === false and strpos($sonarqubeIssue->file, $keyword) === false) unset($sonarqubeIssueList[$key]);
}
$sonarqubeIssueList = array_values($sonarqubeIssueList);
}
/* Data sort. */
list($order, $sort) = explode('_', $orderBy);
$orderList = array();
foreach($sonarqubeIssueList as $sonarqubeIssue) $orderList[] = $sonarqubeIssue->$order;
array_multisort($orderList, $sort == 'desc' ? SORT_DESC : SORT_ASC, $sonarqubeIssueList);
/* Pager. */
$this->app->loadClass('pager', $static = true);
$recTotal = count($sonarqubeIssueList);
$pager = new pager($recTotal, $recPerPage, $pageID);
$sonarqubeIssueList = array_chunk($sonarqubeIssueList, $pager->recPerPage);
$this->view->projectKey = $projectKey;
$this->view->keyword = $keyword;
$this->view->pager = $pager;
$this->view->title = $this->lang->sonarqube->common . $this->lang->colon . $this->lang->sonarqube->browseIssue;
$this->view->sonarqubeID = $sonarqubeID;
$this->view->sonarqubeIssueList = (empty($sonarqubeIssueList) or empty($sonarqubeIssueList[$pageID - 1])) ? array() : $sonarqubeIssueList[$pageID - 1];
$this->view->orderBy = $orderBy;
$this->display();
}
}
+5
View File
@@ -0,0 +1,5 @@
.text-c-counts > span {margin-left: 5px;}
#mainMenu .pull-left {margin-right: 15px;}
.c-message {width: 350px;}
.c-file {width: 300px;}
#keyword {width: 240px;}
+35
View File
@@ -0,0 +1,35 @@
$(document).ready(function()
{
$('#issueSearch').click(function()
{
triggerSearch();
});
$('#keyword').keypress(function(event)
{
if(event.which == 13) triggerSearch();
});
$('.pager a').each(function()
{
$(this).attr('data-url', $(this).attr('href'));
$(this).attr('href', '###');
});
$('.pager a').click(function()
{
$("#sonarqubeIssueForm").attr('action', $(this).data('url'))
triggerSearch();
});
});
/**
* Trigger filtering function.
*
* @access public
* @return void
*/
function triggerSearch()
{
$("#sonarqubeIssueForm").submit();
}
+16
View File
@@ -15,6 +15,7 @@ $lang->sonarqube->execJob = "Exec SonarQube Job";
$lang->sonarqube->desc = 'Description';
$lang->sonarqube->reportView = "SonarQube Report";
$lang->sonarqube->browseIssue = "SonarQube Issue List";
$lang->sonarqube->createBug = "Turn bug";
$lang->sonarqube->id = 'ID';
$lang->sonarqube->name = "Server Name";
@@ -35,6 +36,7 @@ $lang->sonarqube->placeholder->url = "Please fill in the access address
$lang->sonarqube->placeholder->account = "Please fill in the SonarQube user information with Administrator privileges";
$lang->sonarqube->placeholder->projectName = 'Up to 255 characters.';
$lang->sonarqube->placeholder->projectKey = "It may contain up to 400 characters. Allowed characters are alphanumeric, '-' (dash), '_' (underscore), '.' (period) and ':' (colon), with at least one non-digit.";
$lang->sonarqube->placeholder->searchIssue = "Please enter the problem name or file";
$lang->sonarqube->nameRepeatError = "Server name already exists!";
$lang->sonarqube->urlRepeatError = 'Server address already exists!';
@@ -62,3 +64,17 @@ $lang->sonarqube->qualitygateList = array();
$lang->sonarqube->qualitygateList['OK'] = 'Passed';
$lang->sonarqube->qualitygateList['WARN'] = 'Warning';
$lang->sonarqube->qualitygateList['ERROR'] = 'Failed';
$lang->sonarqube->apiErrorMap[1] = "/Malformed key for Project: '([\s\S]+)'. Allowed characters are alphanumeric, '-', '_', '\.' and ':', with at least one non-digit\./";
$lang->sonarqube->errorLang[1] = "Malformed key for Project Key,Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.";
$lang->sonarqube->issue = new stdclass();
$lang->sonarqube->issue->message = 'Issue Name';
$lang->sonarqube->issue->severity = 'Severity';
$lang->sonarqube->issue->type = 'Type';
$lang->sonarqube->issue->status = 'Status';
$lang->sonarqube->issue->file = 'File';
$lang->sonarqube->issue->line = 'Line';
$lang->sonarqube->issue->effort = 'Estimated repair time';
$lang->sonarqube->issue->creationDate = 'Creation date';
+15 -1
View File
@@ -15,6 +15,7 @@ $lang->sonarqube->execJob = "执行SonarQube任务";
$lang->sonarqube->desc = '描述';
$lang->sonarqube->reportView = "SonarQube报告";
$lang->sonarqube->browseIssue = "SonarQube问题列表";
$lang->sonarqube->createBug = "转bug";
$lang->sonarqube->id = 'ID';
$lang->sonarqube->name = "服务器名称";
@@ -35,6 +36,7 @@ $lang->sonarqube->placeholder->url = "请填写SonarQube Server首页的
$lang->sonarqube->placeholder->account = "请填写具有Administrator权限的SonarQube用户信息";
$lang->sonarqube->placeholder->projectName = '最多255个字符';
$lang->sonarqube->placeholder->projectKey = "最多400个字符。 允许的字符为字母数字,'-','_','. '和':',至少有一个非数字";
$lang->sonarqube->placeholder->searchIssue = "请输入问题名称或文件";
$lang->sonarqube->nameRepeatError = "服务器名称已存在!";
$lang->sonarqube->urlRepeatError = "服务器地址已存在!";
@@ -64,5 +66,17 @@ $lang->sonarqube->qualitygateList['WARN'] = 'Warning';
$lang->sonarqube->qualitygateList['ERROR'] = 'Failed';
$lang->sonarqube->apiErrorMap[1] = "/Malformed key for Project: '([\s\S]+)'. Allowed characters are alphanumeric, '-', '_', '\.' and ':', with at least one non-digit\./";
$lang->sonarqube->apiErrorMap[2] = "/Could not create Project, key already exists: ([\s\S]+)/";
$lang->sonarqube->errorLang[1] = "项目'%s'的格式不正确。允许的字符为字母数字、'-'、''、'.'和“:”,至少有一个非数字。";
$lang->sonarqube->errorLang[1] = "项目标识的格式不正确。允许的字符为字母数字、'-'、''、'.'和“:”,至少有一个非数字。";
$lang->sonarqube->errorLang[2] = "无法创建项目,项目标识已存在:%s";
$lang->sonarqube->issue = new stdclass();
$lang->sonarqube->issue->message = '问题名称';
$lang->sonarqube->issue->severity = '严重程度';
$lang->sonarqube->issue->type = '类型';
$lang->sonarqube->issue->status = '状态';
$lang->sonarqube->issue->file = '所属文件';
$lang->sonarqube->issue->line = '行数';
$lang->sonarqube->issue->effort = '预计修复时长';
$lang->sonarqube->issue->creationDate = '创建日期';
+50 -3
View File
@@ -89,6 +89,35 @@ class sonarqubeModel extends model
return json_decode(commonModel::http($url, null, array(), $header));
}
/**
* Get issues of one sonarqube.
*
* @param int $sonarqubeID
* @param string $projectKey
* @access public
* @return array
*/
public function apiGetIssues($sonarqubeID, $projectKey = '')
{
list($apiRoot, $header) = $this->getApiBase($sonarqubeID);
if(!$apiRoot) return array();
$url = sprintf($apiRoot, "issues/search");
$url .= "?ps=500";
if($projectKey) $url .= "&componentKeys={$projectKey}";
$allResults = array();
for($page = 1; true; $page++)
{
$result = json_decode(commonModel::http($url . "&p={$page}", null, array(), $header));
if(!isset($result->issues)) break;
if(!empty($result->issues)) $allResults = array_merge($allResults, $result->issues);
if(count($result->issues) < 500) break;
}
return $allResults;
}
/**
* Get projects of one sonarqube.
*
@@ -110,8 +139,7 @@ class sonarqubeModel extends model
$allResults = array();
for($page = 1; true; $page++)
{
$url .= "&p={$page}";
$result = json_decode(commonModel::http($url, null, array(), $header));
$result = json_decode(commonModel::http($url . "&p={$page}", null, array(), $header));
if(!isset($result->components)) break;
if(!empty($result->components)) $allResults = array_merge($allResults, $result->components);
if(count($result->components) < 500) break;
@@ -168,7 +196,10 @@ class sonarqubeModel extends model
{
$project = fixer::input('post')->get();
$this->dao->insert('sonarqube')->data($project)->batchCheck($this->config->sonarqube->createproject->requiredFields, 'notempty');
$this->dao->insert('sonarqube')->data($project)
->batchCheck($this->config->sonarqube->createproject->requiredFields, 'notempty')
->check('projectName', 'length', 255, 1)
->check('projectKey', 'length', 400, 1);
if(dao::isError()) return false;
$response = $this->apiCreateProject($sonarqubeID, $project);
@@ -235,4 +266,20 @@ class sonarqubeModel extends model
}
return isset($errorMessage) ? $errorMessage : $message;
}
/**
* Get cache file.
*
* @param int $sonarqubeID
* @param string $projectKey
* @access public
* @return string
*/
public function getCacheFile($sonarqubeID, $projectKey)
{
$cachePath = $this->app->getCacheRoot() . '/' . 'sonarqube';
if(!is_dir($cachePath)) mkdir($cachePath, 0777, true);
if(!is_writable($cachePath)) return false;
return $cachePath . '/' . $sonarqubeID . '-' . md5($projectKey);
}
}
@@ -0,0 +1,78 @@
<?php
/**
* The browse view file of sonarqube module of ZenTaoPMS.
*
* @copyright Copyright 2009-2022 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Gang Zeng <zenggang@cnezsoft.com>
* @package sonarqube
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id="mainMenu" class="clearfix">
<div class='pull-left'>
<?php echo html::a($this->createLink('sonarqube', 'browseProject', "sonarqubeID={$sonarqubeID}"), "<i class='icon icon-back icon-sm'></i> " . $lang->goback, '', "class='btn btn-secondary'");?>
</div>
<div id="sidebarHeader">
<div class="title"><?php echo $projectKey; ?></div>
</div>
<div class="btn-toolbar pull-left">
<div>
<form id='sonarqubeIssueForm' method='post'>
<?php echo html::input('keyword', $keyword, "class='form-control' placeholder='{$lang->sonarqube->placeholder->searchIssue}' style='display: inline-block;margin:0 10px'");?>
<a id="issueSearch" class="btn btn-primary"><?php echo $lang->sonarqube->search?></a>
</form>
</div>
</div>
</div>
<?php if(empty($sonarqubeIssueList)):?>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->noData;?></span>
</p>
</div>
<?php else:?>
<div id='mainContent' class='main-row'>
<form class='main-table' id='ajaxForm' method='post'>
<table id='sonarqubeIssueList' class='table has-sort-head table-fixed'>
<?php $vars = "sonarqubeID={$sonarqubeID}&projectKey={$projectKey}&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}";?>
<thead>
<tr>
<th class='c-message text-left'><?php common::printOrderLink('message', $orderBy, $vars, $lang->sonarqube->issue->message);?></th>
<th><?php common::printOrderLink('severity', $orderBy, $vars, $lang->sonarqube->issue->severity);?></th>
<th><?php common::printOrderLink('type', $orderBy, $vars, $lang->sonarqube->issue->type);?></th>
<th><?php common::printOrderLink('status', $orderBy, $vars, $lang->sonarqube->issue->status);?></th>
<th class='c-file text-left'><?php common::printOrderLink('file', $orderBy, $vars, $lang->sonarqube->issue->file);?></th>
<th><?php common::printOrderLink('line', $orderBy, $vars, $lang->sonarqube->issue->line);?></th>
<th><?php common::printOrderLink('effort', $orderBy, $vars, $lang->sonarqube->issue->effort);?></th>
<th class='c-actions-2'><?php echo $lang->actions;?></th>
</tr>
</thead>
<tbody>
<?php foreach ($sonarqubeIssueList as $id => $sonarqubeIssue): ?>
<tr class='text'>
<td class='text' title="<?php echo $sonarqubeIssue->message;?>"><?php echo $sonarqubeIssue->message;?></td>
<td class='text-c-name' title='<?php echo $sonarqubeIssue->severity;?>'><?php echo $sonarqubeIssue->severity;?></td>
<td class='text-c-name' title='<?php echo $sonarqubeIssue->type;?>'><?php echo $sonarqubeIssue->type;?></td>
<td class='text-c-name' title='<?php echo $sonarqubeIssue->status;?>'><?php echo $sonarqubeIssue->status;?></td>
<td class='text' title='<?php echo $sonarqubeIssue->file;?>'><?php echo $sonarqubeIssue->file;?></td>
<td class='text-c-name' title='<?php echo $sonarqubeIssue->line;?>'><?php echo $sonarqubeIssue->line;?></td>
<td class='text-c-name' title='<?php echo $sonarqubeIssue->effort;?>'><?php echo $sonarqubeIssue->effort;?></td>
<td class='c-actions text-left'>
<?php
common::printLink('bug', 'create', '', "<i class='icon-testcase-createBug icon-bug'></i> ", '', "title='{$lang->sonarqube->createBug}' class='btn btn-primary'");
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<?php if($sonarqubeIssueList):?>
<div class='table-footer'><?php $pager->show('right', 'pagerjs');?></div>
<?php endif;?>
</form>
</div>
<?php endif;?>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -27,7 +27,7 @@
</div>
</div>
<div class="btn-toolbar pull-right">
<?php if(common::hasPriv('sonarqube', 'createProject')) common::printLink('sonarqube', 'createProject', "sonarqubeID=$sonarqubeID", "<i class='icon icon-plus'></i> " . $lang->sonarqube->createProject, '', "class='btn btn-primary'");?>
<?php common::printLink('sonarqube', 'createProject', "sonarqubeID=$sonarqubeID", "<i class='icon icon-plus'></i> " . $lang->sonarqube->createProject, '', "class='btn btn-primary'");?>
</div>
</div>
<?php if(empty($sonarqubeProjectList)):?>