2023-05-16 10:47:08 +08:00

435 lines
17 KiB
PHP

<?php
/**
* The control file of sonarqube module of ZenTaoPMS.
*
* @copyright Copyright 2009-2022 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yanyi Cao <caoyanyi@easycorp.ltd>
* @package sonarqube
* @version $Id: ${FILE_NAME} 5144 2022/1/11 8:55 上午 caoyanyi@easycorp.ltd $
* @link http://www.zentao.net
*/
class sonarqube extends control
{
/**
* The mr constructor.
* @param string $moduleName
* @param string $methodName
*/
public function __construct($moduleName = '', $methodName = '')
{
parent::__construct($moduleName, $methodName);
/* This is essential when changing tab(menu) from gitlab to repo. */
/* Optional: common::setMenuVars('devops', $this->session->repoID); */
$this->loadModel('ci')->setMenu();
}
/**
* Browse sonarqube.
*
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browse($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$sonarqubeList = $this->loadModel('pipeline')->getList('sonarqube', $orderBy, $pager);
$this->view->title = $this->lang->sonarqube->common . $this->lang->colon . $this->lang->sonarqube->browse;
$this->view->sonarqubeList = $sonarqubeList;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* Show sonarqube report.
*
* @param int $jobID
* @access public
* @return void
*/
public function reportView($jobID)
{
$job = $this->loadModel('job')->getByID($jobID);
$qualitygate = $this->sonarqube->apiGetQualitygate($job->sonarqubeServer, $job->projectKey);
$report = $this->sonarqube->apiGetReport($job->sonarqubeServer, $job->projectKey);
$measures = array();
if(isset($report->component->measures))
{
foreach($report->component->measures as $measure)
{
if(in_array($measure->metric, array('security_hotspots_reviewed', 'coverage', 'duplicated_lines_density')))
{
$measures[$measure->metric] = $measure->value . '%';
}
else
{
$measures[$measure->metric] = $measure->value;
if($measure->value > 1000) $measures[$measure->metric] = round($measure->value / 1000, 1) . 'K';
}
}
}
$projectName = $job->projectKey;
$projects = $this->sonarqube->apiGetProjects($job->sonarqubeServer, '', $job->projectKey);
if(isset($projects[0]->name)) $projectName = $projects[0]->name;
$this->view->measures = $measures;
$this->view->qualitygate = $qualitygate;
$this->view->projectName = $projectName;
$this->view->sonarqubeID = $job->sonarqubeServer;
$this->display();
}
/**
* Ajax get project select.
*
* @param int $sonarqubeID
* @param string $projectKey
* @access public
* @return void
*/
public function ajaxGetProjectList($sonarqubeID, $projectKey = '')
{
$jobPairs = $this->loadModel('job')->getJobBySonarqubeProject($sonarqubeID, array(), true, true);
$existsProject = array_diff(array_keys($jobPairs), array($projectKey));
$projectList = $this->sonarqube->apiGetProjects($sonarqubeID);
$projectPairs = array('' => '');
foreach($projectList as $project)
{
if(!empty($project) and !in_array($project->key, $existsProject)) $projectPairs[$project->key] = $project->name;
}
echo html::select('projectKey', $projectPairs, str_replace('*', '-', $projectKey), "class='form-control chosen'");
}
/**
* create a sonarqube.
*
* @access public
* @return void
*/
public function create()
{
if($_POST)
{
$this->checkToken();
$sonarqubeID = $this->loadModel('pipeline')->create('sonarqube');
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$actionID = $this->loadModel('action')->create('sonarqube', $sonarqubeID, 'created');
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
}
$this->view->title = $this->lang->sonarqube->common . $this->lang->colon . $this->lang->sonarqube->createServer;
$this->display();
}
/**
* Check post info.
*
* @param int $sonarqubeID
* @access protected
* @return void
*/
protected function checkToken($sonarqubeID = 0)
{
$sonarqube = fixer::input('post')->trim('url,token,account,password')->get();
$this->dao->update('sonarqube')->data($sonarqube)
->batchCheck(empty($sonarqubeID) ? $this->config->sonarqube->create->requiredFields : $this->config->sonarqube->edit->requiredFields, 'notempty')
->batchCheck("url", 'URL');
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if(strpos($sonarqube->url, 'http') !== 0) return $this->send(array('result' => 'fail', 'message' => array('url' => array($this->lang->sonarqube->hostError))));
/* Check name and url unique. */
$existSonarQube = $this->dao->select('*')->from(TABLE_PIPELINE)
->where("type='sonarqube' and (name='{$sonarqube->name}' or url='{$sonarqube->url}')")
->andWhere('deleted')->eq('0')
->beginIF(!empty($sonarqubeID))->andWhere('id')->ne($sonarqubeID)->fi()
->fetch();
if(isset($existSonarQube->name) and $existSonarQube->name == $sonarqube->name) return $this->send(array('result' => 'fail', 'message' => $this->lang->sonarqube->nameRepeatError));
if(isset($existSonarQube->url) and $existSonarQube->url == $sonarqube->url) return $this->send(array('result' => 'fail', 'message' => $this->lang->sonarqube->urlRepeatError));
$token = base64_encode("{$sonarqube->account}:{$sonarqube->password}");
$result = $this->sonarqube->apiValidate($sonarqube->url, $token);
if(!empty($result)) return $this->send(array('result' => 'fail', 'message' => $result));
$this->post->set('token', $token);
}
/**
* Edit a sonarqube.
*
* @param int $sonarqubeID
* @access public
* @return void
*/
public function edit($sonarqubeID)
{
$oldSonarQube = $this->loadModel('pipeline')->getByID($sonarqubeID);
if($_POST)
{
$this->checkToken($sonarqubeID);
$this->pipeline->update($sonarqubeID);
$sonarqube = $this->pipeline->getByID($sonarqubeID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('action');
$actionID = $this->action->create('sonarqube', $sonarqubeID, 'edited');
$changes = common::createChanges($oldSonarQube, $sonarqube);
$this->action->logHistory($actionID, $changes);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
}
$this->view->title = $this->lang->sonarqube->common . $this->lang->colon . $this->lang->sonarqube->editServer;
$this->view->sonarqube = $oldSonarQube;
$this->display();
}
/**
* Delete a sonarqube.
*
* @param int $sonarqubeID
* @access public
* @return void
*/
public function delete($sonarqubeID, $confirm = 'no')
{
if($confirm != 'yes') return print(js::confirm($this->lang->sonarqube->confirmDelete, inlink('delete', "sonarqubeID=$sonarqubeID&confirm=yes")));
$oldSonarQube = $this->loadModel('pipeline')->getByID($sonarqubeID);
$this->loadModel('action');
$actionID = $this->pipeline->delete($sonarqubeID, 'sonarqube');
if($actionID) return print(js::error($this->lang->sonarqube->delError));
$sonarQube = $this->pipeline->getByID($sonarqubeID);
$changes = common::createChanges($oldSonarQube, $sonarQube);
$this->action->logHistory($actionID, $changes);
echo js::reload('parent');
}
/**
* Exec job.
*
* @param int $jobID
* @access public
* @return void
*/
public function execJob($jobID)
{
echo $this->fetch('job', 'exec', "jobID=$jobID");
}
/**
* Browse sonarqube project.
*
* @param int $sonarqubeID
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browseProject($sonarqubeID, $orderBy = 'name_desc', $recTotal = 0, $recPerPage = 15, $pageID = 1)
{
$this->app->loadClass('pager', $static = true);
$keyword = fixer::input('post')->setDefault('keyword', '')->get('keyword');
$sonarqubeProjectList = $this->sonarqube->apiGetProjects($sonarqubeID, $keyword);
$projectKeyList = array();
foreach($sonarqubeProjectList as $key => $sonarqubeProject)
{
if(!isset($sonarqubeProject->lastAnalysisDate)) $sonarqubeProject->lastAnalysisDate = '';
$projectKeyList[] = $sonarqubeProject->key;
}
/* Data sort. */
list($order, $sort) = explode('_', $orderBy);
$orderList = array();
foreach($sonarqubeProjectList as $sonarqubeProject) $orderList[] = $sonarqubeProject->$order;
array_multisort($orderList, $sort == 'desc' ? SORT_DESC : SORT_ASC, $sonarqubeProjectList);
/* Pager. */
$this->app->loadClass('pager', $static = true);
$recTotal = count($sonarqubeProjectList);
$pager = new pager($recTotal, $recPerPage, $pageID);
$sonarqubeProjectList = array_chunk($sonarqubeProjectList, $pager->recPerPage);
/* Get success jobs of sonarqube.*/
$projectJobPairs = $this->loadModel('job')->getJobBySonarqubeProject($sonarqubeID, $projectKeyList);
$successJobs = $this->loadModel('compile')->getSuccessJobs($projectJobPairs);
$this->view->sonarqube = $this->loadModel('pipeline')->getByID($sonarqubeID);
$this->view->keyword = urldecode(urldecode($keyword));
$this->view->pager = $pager;
$this->view->title = $this->lang->sonarqube->common . $this->lang->colon . $this->lang->sonarqube->browseProject;
$this->view->sonarqubeID = $sonarqubeID;
$this->view->sonarqubeProjectList = (empty($sonarqubeProjectList) or empty($sonarqubeProjectList[$pageID - 1])) ? array() : $sonarqubeProjectList[$pageID - 1];
$this->view->projectJobPairs = $projectJobPairs;
$this->view->orderBy = $orderBy;
$this->view->successJobs = $successJobs;
$this->display();
}
/**
* Creat a sonarqube project.
*
* @param int $sonarqubeID
* @access public
* @return void
*/
public function createProject($sonarqubeID)
{
if($_POST)
{
$this->sonarqube->createProject($sonarqubeID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browseProject', "sonarqubeID=$sonarqubeID")));
}
$this->view->title = $this->lang->sonarqube->common . $this->lang->colon . $this->lang->sonarqube->createProject;
$this->view->sonarqubeID = $sonarqubeID;
$this->display();
}
/**
* Delete project.
*
* @param int $sonarqubeID
* @param string $projectKey
* @param string $confirm
* @access public
* @return void
*/
public function deleteProject($sonarqubeID, $projectKey, $confirm = 'no')
{
if($confirm != 'yes') return print(js::confirm($this->lang->sonarqube->confirmDeleteProject, inlink('deleteProject', "sonarqubeID=$sonarqubeID&projectKey=$projectKey&confirm=yes")));
/* Fix error when request type is PATH_INFO and the tag name contains '-'.*/
$projectKey = str_replace('*', '-', $projectKey);
$reponse = $this->sonarqube->apiDeleteProject($sonarqubeID, $projectKey);
if(isset($reponse->errors)) return print(js::alert($reponse->errors[0]->msg));
$this->loadModel('action')->create('sonarqubeproject', 0, 'deleted', '', $projectKey);
return print(js::reload('parent'));
}
/**
* Browse sonarqube issue.
*
* @param int $sonarqubeID
* @param string $projectKey
* @param bool $search
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browseIssue($sonarqubeID, $projectKey = '', $search = false, $orderBy = 'severity_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
if(isset($_POST['keyword']))
{
$keyword = htmlspecialchars(trim($_POST['keyword']));
$search = true;
$pageID = 1;
$this->session->set('sonarqubeIssueKeyword', $keyword);
}
else
{
$keyword = '';
if($search == true) $keyword = $this->session->sonarqubeIssueKeyword;
}
ini_set('memory_limit', '1024M');
$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);
foreach($sonarqubeIssueList as $key => $sonarqubeIssue)
{
if(!isset($sonarqubeIssue->line)) $sonarqubeIssue->line = '';
if(!isset($sonarqubeIssue->effort)) $sonarqubeIssue->effort = '';
$sonarqubeIssue->message = htmlspecialchars($sonarqubeIssue->message);
$sonarqubeIssue->creationDate = date('Y-m-d H:i:s', strtotime($sonarqubeIssue->creationDate));
list($project, $file) = explode(':', $sonarqubeIssue->component);
$sonarqubeIssue->file = $file;
}
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));
}
/* 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);
/* Get product. */
$products = $this->sonarqube->getLinkedProducts($sonarqubeID, $projectKey);
$productID = current(explode(',', $products));
/* 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->search = $search;
$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->sonarqube = $this->loadModel('pipeline')->getByID($sonarqubeID);
$this->view->sonarqubeIssueList = (empty($sonarqubeIssueList) or empty($sonarqubeIssueList[$pageID - 1])) ? array() : $sonarqubeIssueList[$pageID - 1];
$this->view->orderBy = $orderBy;
$this->view->productID = $productID;
$this->view->bugs = $this->loadModel('bug')->getBySonarqubeID($sonarqubeID);
$this->display();
}
}