Merge branch 'sunhuawei_fixbug_19247'

This commit is contained in:
liyuchun
2022-02-15 13:42:08 +08:00
90 changed files with 2722 additions and 279 deletions
+1 -1
View File
@@ -1 +1 @@
16.3
16.4
+1 -1
View File
@@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}}
if(!function_exists('getWebRoot')){function getWebRoot(){}}
/* 基本设置。Basic settings. */
$config->version = '16.3'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->version = '16.4'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP.
$config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time.
$config->timezone = 'Asia/Shanghai'; // 时区设置。 The time zone setting, for more see http://www.php.net/manual/en/timezones.php.
+64
View File
@@ -23,6 +23,14 @@ $config->productCommonList['fr'][0] = 'Product';
$config->productCommonList['fr'][1] = 'Project';
$config->productCommonList['vi'][0] = 'Sản phẩm';
$config->productCommonList['vi'][1] = 'Project';
$config->productCommonList['ru'][0] = 'продукт';
$config->productCommonList['ru'][1] = 'Проект';
$config->productCommonList['ja'][0] = '製品';
$config->productCommonList['ja'][1] = 'プロジェクト';
$config->productCommonList['es'][0] = 'Productos';
$config->productCommonList['es'][1] = 'Proyecto';
$config->productCommonList['pt'][0] = 'produto';
$config->productCommonList['pt'][1] = 'projecto';
/* Project common list. */
$config->projectCommonList['zh-cn'][0] = '项目';
@@ -49,6 +57,22 @@ $config->projectCommonList['vi'][0] = 'Project';
$config->projectCommonList['vi'][1] = 'Iteration';
$config->projectCommonList['vi'][2] = 'Sprint';
$config->projectCommonList['ru'][0] = 'Проект';
$config->projectCommonList['ru'][1] = 'итерация';
$config->projectCommonList['ru'][2] = 'пробивание';
$config->projectCommonList['ja'][0] = 'プロジェクト';
$config->projectCommonList['ja'][1] = '反復';
$config->projectCommonList['ja'][2] = 'スパート';
$config->projectCommonList['es'][0] = 'Proyecto';
$config->projectCommonList['es'][1] = 'Iteración';
$config->projectCommonList['es'][2] = 'Sprint';
$config->projectCommonList['pt'][0] = 'Projecto';
$config->projectCommonList['pt'][1] = 'Iteração';
$config->projectCommonList['pt'][2] = 'Sprint';
$config->executionCommonList['zh-cn'][0] = '迭代';
$config->executionCommonList['zh-cn'][1] = '冲刺';
$config->executionCommonList['zh-cn'][2] = '阶段';
@@ -73,6 +97,22 @@ $config->executionCommonList['vi'][0] = 'Lặp lại';
$config->executionCommonList['vi'][1] = 'Sprint';
$config->executionCommonList['vi'][2] = 'Giai đoạn';
$config->executionCommonList['ru'][0] = 'итерация';
$config->executionCommonList['ru'][1] = 'пробивание';
$config->executionCommonList['ru'][2] = ' этап';
$config->executionCommonList['ja'][0] = '反復';
$config->executionCommonList['ja'][1] = 'スパート';
$config->executionCommonList['ja'][2] = 'ステージ';
$config->executionCommonList['es'][0] = 'Iteración';
$config->executionCommonList['es'][1] = 'Sprint';
$config->executionCommonList['es'][2] = 'Fase';
$config->executionCommonList['pt'][0] = 'Iteração';
$config->executionCommonList['pt'][1] = 'Sprint';
$config->executionCommonList['pt'][2] = 'Fase';
/* Story common list. */
$config->hourPointCommonList['zh-cn'][0] = '工时';
$config->hourPointCommonList['zh-cn'][1] = '故事点';
@@ -98,6 +138,22 @@ $config->hourPointCommonList['vi'][0] = 'giờ';
$config->hourPointCommonList['vi'][1] = 'điểm';
$config->hourPointCommonList['vi'][2] = 'function point';
$config->hourPointCommonList['ru'][0] = 'продолжительность рабочего времени';
$config->hourPointCommonList['ru'][1] = 'точка повести';
$config->hourPointCommonList['ru'][2] = 'функциональная точка';
$config->hourPointCommonList['ja'][0] = '工数';
$config->hourPointCommonList['ja'][1] = 'ストーリーポイント';
$config->hourPointCommonList['ja'][2] = 'きのうてん';
$config->hourPointCommonList['es'][0] = 'Horas de trabajo';
$config->hourPointCommonList['es'][1] = 'Punto de historia';
$config->hourPointCommonList['es'][2] = 'Punto de función';
$config->hourPointCommonList['pt'][0] = 'horas de trabalho';
$config->hourPointCommonList['pt'][1] = 'Ponto da história';
$config->hourPointCommonList['pt'][2] = 'Ponto de função';
$config->manualUrl['home'] = 'https://www.zentao.net/book/zentaopmshelp.html?fullScreen=zentao';
$config->manualUrl['int'] = 'https://www.zentao.pm/book/zentaomanual/zentao-installation-11.html?fullScreen=zentao';
@@ -114,6 +170,14 @@ $config->charsets['fr']['utf-8'] = 'UTF-8';
$config->charsets['fr']['GBK'] = 'GBK';
$config->charsets['vi']['utf-8'] = 'UTF-8';
$config->charsets['vi']['GBK'] = 'GBK';
$config->charsets['ru']['utf-8'] = 'UTF-8';
$config->charsets['ru']['GBK'] = 'GBK';
$config->charsets['ja']['utf-8'] = 'UTF-8';
$config->charsets['ja']['GBK'] = 'GBK';
$config->charsets['es']['utf-8'] = 'UTF-8';
$config->charsets['es']['GBK'] = 'GBK';
$config->charsets['pt']['utf-8'] = 'UTF-8';
$config->charsets['pt']['GBK'] = 'GBK';
$config->openMethods = array();
$config->openMethods[] = 'gitlab.webhook';
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -29,7 +29,7 @@ CREATE TABLE `zt_kanban` (
`desc` text NOT NULL,
`acl` char(30) NOT NULL DEFAULT 'open',
`whitelist` text NOT NULL,
`archived` enum('0', '1') NOT NULL DEFAULT '1',
`archived` enum('0', '1') NOT NULL DEFAULT '0',
`status` enum('active','closed') NOT NULL default 'active',
`order` smallint(6) NOT NULL DEFAULT '0',
`createdBy` char(30) NOT NULL,
+31
View File
@@ -1,3 +1,34 @@
2022-02-15 16.4
完成的需求
30526 实现JIRA数据导入到禅道功能
30675 优化禅道中SonarQube的扫描结果详情页面
30583 SonarQube免登录探针需求
30576 在禅道中当对应的GitLab项目的代码库为空时,隐藏GitLab项目下下关于分支与标签操作的按钮。
30559 在禅道中设置GitLab项目或群组权限为公开时给予告警提示
30558 实现GitLab群组URL可点击的功能
30556 GitLab服务器列表中地址可点击
30554 修改GitLab服务器列表“GitLab名称”为“GitLab服务器”
30553 在禅道中实现创建GitLab服务器时对当前GitLab版本进行兼容性检查功能
30552 在禅道中针对无权限用户将合并请求编辑按钮置灰
30461 实现在禅道中将SonarQube问题转成禅道Bug的功能
30455 实现SonarQube问题详情页面
30453 实现SonarQube问题列表页面的搜索功能
30451 实现SonarQube问题列表页面的返回功能
30447 在SonarQube扫描报告概要页面添加问题详情列表的入口
30443 实现在添加/编辑SonarQube服务器时验证填写用户是否为SonarQube管理员的功能
30183 实现从禅道SonarQube项目列表查看SonarQube代码检查结果的入口
29859 实现禅道中创建SonarQube项目功能
29241 实现构建通知发送到喧喧功能
29228 实现SonarQube项目问题列表页面
修复的BUG
18816 DevOps-代码中,GitLab类型的版本库代码内容未显示完全
18382 代码页面版本列版本号显示不一致
18380 历史记录中重复显示多条记录
18104 VS Code COMMIT_EDITMSG文件中应只显示未选中的需求/Bug/任务
18076 VSCode中查看项目/执行下的需求时应跳转到项目/执行下的需求详情页
17869 VScode中查看需求、任务和Bug提示“请求后端出错”
17851 vscode查看执行下的需求,提示先选择项目
2022-01-26 16.3
完成的需求
29706 研发看板相关操作支持平滑刷新方式呈现
+1 -1
View File
@@ -321,7 +321,7 @@ class block extends control
$pager = new pager(0, 30, 1);
$this->view->actions = $this->loadModel('action')->getDynamic('all', 'today', 'date_desc', $pager);
$this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted|noletter|all');
$this->view->users = $this->loadModel('user')->getPairs('nodeleted|noletter|all');
$this->display();
}
+1 -1
View File
@@ -14,7 +14,7 @@
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.status-bars {display: table;width: 60%;height: 140px;padding: 5px;padding-top: 50px;margin: 0;overflow: hidden;}
.status-bars {display: table; width: 80%; height: 140px; padding: 5px; padding-top: 50px; margin: 0; overflow: hidden;}
</style>
<div class="panel-body table-row">
<div class="col-4 text-middle text-center">
+7 -4
View File
@@ -189,10 +189,13 @@ $config->bug->datatable->fieldList['type']['fixed'] = 'no';
$config->bug->datatable->fieldList['type']['width'] = '90';
$config->bug->datatable->fieldList['type']['required'] = 'no';
$config->bug->datatable->fieldList['project']['title'] = 'project';
$config->bug->datatable->fieldList['project']['fixed'] = 'no';
$config->bug->datatable->fieldList['project']['width'] = '120';
$config->bug->datatable->fieldList['project']['required'] = 'no';
if($config->systemMode == 'new')
{
$config->bug->datatable->fieldList['project']['title'] = 'project';
$config->bug->datatable->fieldList['project']['fixed'] = 'no';
$config->bug->datatable->fieldList['project']['width'] = '120';
$config->bug->datatable->fieldList['project']['required'] = 'no';
}
$config->bug->datatable->fieldList['execution']['title'] = 'execution';
$config->bug->datatable->fieldList['execution']['fixed'] = 'no';
+1 -1
View File
@@ -2228,6 +2228,6 @@ class bug extends control
{
$project = $this->loadModel('project')->getByID($projectID);
if($project->model == 'kanban') return print($this->lang->bug->kanban);
if($project->model != 'kanban') return print($this->lang->bug->execution);
return print($this->lang->bug->execution);
}
}
+1
View File
@@ -1 +1,2 @@
.main-table {height: 600px !important}
#mainContent {min-height: 400px;}
+1
View File
@@ -19,6 +19,7 @@ $lang->bug->module = 'Module';
$lang->bug->moduleAB = 'Module';
$lang->bug->project = 'Project';
$lang->bug->execution = $lang->execution->common;
$lang->bug->kanban = 'Kanban';
$lang->bug->story = 'Story';
$lang->bug->storyVersion = 'Story Version';
$lang->bug->color = 'Color';
+1 -1
View File
@@ -1366,7 +1366,7 @@ class bugModel extends model
{
$bug = $this->getById($bugID);
$bugIDList = $bug->id . ',' . $bug->linkBug;
$bugs2Link = $this->getBySearch($bug->product, 'all', $queryID, 'id', $bugIDList ,$pager);
$bugs2Link = $this->getBySearch($bug->product, 'all', $queryID, 'id', $bugIDList, $pager);
return $bugs2Link;
}
else
+1 -1
View File
@@ -53,7 +53,7 @@
<?php printf('%03d', $bug2Link->id);?>
</td>
<td><span class='label-pri <?php echo 'label-pri-' . $bug2Link->pri?>' title='<?php echo zget($lang->bug->priList, $bug2Link->pri, $bug2Link->pri);?>'><?php echo zget($lang->bug->priList, $bug2Link->pri, $bug2Link->pri);?></span></td>
<td><?php echo html::a($this->createLink('product', 'browse', "productID=$bug2Link->product&branch=$bug2Link->branch"), $products[$bug2Link->product], '_blank');?></td>
<td class='nobr' title="<?php echo $products[$bug2Link->product]?>"><?php echo html::a($this->createLink('product', 'browse', "productID=$bug2Link->product&branch=$bug2Link->branch"), $products[$bug2Link->product], '_blank');?></td>
<td class='text-left nobr' title="<?php echo $bug2Link->title?>"><?php echo html::a($bugLink, $bug2Link->title);?></td>
<td><?php echo $this->processStatus('bug', $bug);?></td>
<td><?php echo zget($users, $bug2Link->openedBy);?></td>
+1 -1
View File
@@ -466,7 +466,7 @@ class build extends control
$html .= html::a("javascript:loadExecutionBuilds($executionID)", $this->lang->refresh);
die($html);
}
die(html::select('build', array('') + $builds, $build, "class='form-control' onchange='loadTestReports(this.value)'"));
die(html::select('build', array('') + $builds, $build, "class='form-control'"));
}
if($varName == 'dropdownList')
{
-2
View File
@@ -215,8 +215,6 @@ $lang->module = 'Module';
$lang->priv = 'Privilege';
$lang->other = 'Other';
$lang->estimation = 'Estimation';
$lang->issue = 'Issue';
$lang->risk = 'Risk';
$lang->measure = 'Report';
$lang->treeView = 'Tree View';
$lang->groupView = 'Group View';
-2
View File
@@ -215,8 +215,6 @@ $lang->module = '模块';
$lang->priv = '权限';
$lang->other = '其他';
$lang->estimation = '估算';
$lang->issue = '问题';
$lang->risk = '风险';
$lang->measure = '度量';
$lang->treeView = '树状图';
$lang->groupView = '分组视图';
-2
View File
@@ -210,8 +210,6 @@ $lang->module = '模組';
$lang->priv = '權限';
$lang->other = '其他';
$lang->estimation = '估算';
$lang->issue = '問題';
$lang->risk = '風險';
$lang->measure = '度量';
$lang->treeView = '樹狀圖';
$lang->groupView = '分組視圖';
+1 -1
View File
@@ -232,7 +232,7 @@ class company extends control
$userIdPairs[''] = $this->lang->company->user;
$this->view->userIdPairs = $userIdPairs;
$accountPairs = $this->user->getPairs('noclosed|nodeleted|noletter|all');
$accountPairs = $this->user->getPairs('nodeleted|noletter|all');
$accountPairs[''] = '';
/* The header and position. */
+88 -59
View File
@@ -263,50 +263,68 @@ class convert extends control
/**
* Import jira notice.
*
* @param string $type db|file
* @param string $mehotd db|file
* @access public
* @return void
*/
public function importNotice($type = 'db')
public function importNotice($method = 'db')
{
if($_POST)
if($this->server->request_method == 'POST')
{
$dbName = $this->post->dbName;
if(!$dbName)
if($method == 'db')
{
$response['result'] = 'fail';
$response['message'] = $this->lang->convert->jira->dbNameEmpty;
return print($this->send($response));
$dbName = $this->post->dbName;
if(!$dbName)
{
$response['result'] = 'fail';
$response['message'] = $this->lang->convert->jira->dbNameEmpty;
return print($this->send($response));
}
if(!$this->convert->dbExists($dbName))
{
$response['result'] = 'fail';
$response['message'] = $this->lang->convert->jira->invalidDB;
return print($this->send($response));
}
if(!$this->convert->tableExistsOfJira($dbName, 'nodeassociation'))
{
$response['result'] = 'fail';
$response['message'] = $this->lang->convert->jira->invalidTable;
return print($this->send($response));
}
$this->session->set('jiraDB', $dbName);
$link = $this->createLink('convert', 'mapJira2Zentao', "method=db&dbName={$this->post->dbName}");
}
else
{
$this->convert->splitFile();
$link = $this->createLink('convert', 'mapJira2Zentao', "method=file");
}
if(!$this->convert->dbExists($dbName))
{
$response['result'] = 'fail';
$response['message'] = $this->lang->convert->jira->invalidDB;
return print($this->send($response));
}
$this->session->set('jiraDB', $dbName);
$response['result'] = 'success';
$response['message'] = $this->lang->saveSuccess;
$response['locate'] = $this->createLink('convert', 'mapJira2Zentao', "type=db&dbName={$this->post->dbName}");
$response['locate'] = $link;
return print($this->send($response));
}
$this->view->title = $this->lang->convert->jira->method;
$this->view->type = $type;
$this->view->title = $this->lang->convert->jira->method;
$this->view->method = $method;
$this->display();
}
/**
* Map jira objects to zentao.
*
* @param string $type db|file
* @param string $method db|file
* @param string $dbName
* @param int $step
* @access public
* @return void
*/
public function mapJira2Zentao($type = 'db', $dbName = '')
public function mapJira2Zentao($method = 'db', $dbName = '', $step = 1)
{
$this->app->loadLang('story');
$this->app->loadLang('bug');
@@ -314,40 +332,51 @@ class convert extends control
if($_POST)
{
$this->session->set('jiraRelation', $_POST);
foreach($_POST as $key => $value) $_SESSION['jiraRelation'][$key] = $value;
$step = $step + 1;
$link = $step == 5 ? $this->createLink('convert', 'initJiraUser', "method=$method") : inlink('mapJira2Zentao', "method=$method&dbName=$dbName&step=$step");
$response['result'] = 'success';
$response['message'] = $this->lang->saveSuccess;
$response['locate'] = $this->createLink('convert', 'initJiraUser', "type=$type");
$response['locate'] = $link;
return print($this->send($response));
}
if($type == 'db')
if($method == 'db')
{
$dbh = $this->convert->connectDB($dbName);
$issueTypePairs = $this->dao->dbh($dbh)->select('ID,pname')->from(JIRA_ISSUETYPE)->fetchPairs();
$linkTypePairs = $this->dao->dbh($dbh)->select('ID,LINKNAME')->from(JIRA_ISSUELINKTYPE)->fetchPairs();
$resolutions = $this->dao->dbh($dbh)->select('ID,pname')->from(JIRA_RESOLUTION)->fetchPairs();
$statusList = $this->dao->dbh($dbh)->select('ID,pname')->from(JIRA_ISSUESTATUS)->fetchPairs();
$issueTypeList = $this->dao->dbh($dbh)->select('*')->from(JIRA_ISSUETYPE)->fetchAll('ID');
$linkTypeList = $this->dao->dbh($dbh)->select('*, LINKNAME as linkname')->from(JIRA_ISSUELINKTYPE)->fetchAll('ID');
$resolutionList = $this->dao->dbh($dbh)->select('*')->from(JIRA_RESOLUTION)->fetchAll('ID');
$statusList = $this->dao->dbh($dbh)->select('*')->from(JIRA_ISSUESTATUS)->fetchAll('ID');
}
else
{
$issueTypeList = $this->convert->getJiraDataFromFile('issuetype');
$linkTypeList = $this->convert->getJiraDataFromFile('issuelinktype');
$resolutionList = $this->convert->getJiraDataFromFile('resolution');
$statusList = $this->convert->getJiraDataFromFile('status');
}
$this->view->title = $this->lang->convert->jira->mapJira2Zentao;
$this->view->issueTypePairs = $issueTypePairs;
$this->view->linkTypePairs = $linkTypePairs;
$this->view->resolutions = $resolutions;
$this->view->issueTypeList = $issueTypeList;
$this->view->linkTypeList = $linkTypeList;
$this->view->resolutionList = $resolutionList;
$this->view->statusList = $statusList;
$this->view->method = $method;
$this->view->step = $step;
$this->display();
}
/**
* Init jira user.
*
* @param string $type db|file
* @param string $method db|file
* @access public
* @return void
*/
public function initJiraUser($type = 'db')
public function initJiraUser($method = 'db')
{
$this->app->loadLang('user');
@@ -360,6 +389,13 @@ class convert extends control
return print($this->send($response));
}
if(strlen(trim($this->post->password1)) < 6)
{
$response['result'] = 'fail';
$response['message'] = $this->lang->convert->jira->passwordLess;
return print($this->send($response));
}
if($this->post->password1 != $this->post->password2)
{
$response['result'] = 'fail';
@@ -373,7 +409,7 @@ class convert extends control
$response['result'] = 'success';
$response['message'] = $this->lang->saveSuccess;
$response['locate'] = $this->createLink('convert', 'importJira', "type=$type");
$response['locate'] = $this->createLink('convert', 'importJira', "method=$method");
return print($this->send($response));
}
@@ -395,32 +431,25 @@ class convert extends control
{
set_time_limit(0);
if($method = 'db')
{
if(helper::isAjaxRequest())
if(helper::isAjaxRequest())
{
$result = $method == 'db' ? $this->convert->importJiraFromDB($type, $lastID, $createTable) : $this->convert->importJiraFromFile($type, $lastID, $createTable);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if(isset($result['finished']) and $result['finished'])
{
$result = $this->convert->importJiraFromDB($type, $lastID, $createTable);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if(isset($result['finished']) and $result['finished'])
{
return print $this->send(array('result' => 'finished', 'message' => $this->lang->convert->jira->importSuccessfully));
}
else
{
$type = zget($this->lang->convert->jira->objectList, $result['type'], $result['type']);
return print $this->send(array('result' => 'finished', 'message' => $this->lang->convert->jira->importSuccessfully));
}
else
{
$type = zget($this->lang->convert->jira->objectList, $result['type'], $result['type']);
$response['result'] = 'unfinished';
$response['message'] = sprintf($this->lang->convert->jira->importResult, $type, $type, $result['count']);
$response['type'] = $type;
$response['count'] = $result['count'];
$response['next'] = inlink('importJira', "method=$method&type={$result['type']}&lastID={$result['lastID']}");
return print $this->send($response);
}
}
}
else
{
//$this->convert->importJiraFromFile();
$response['result'] = 'unfinished';
$response['message'] = sprintf($this->lang->convert->jira->importResult, $type, $type, $result['count']);
$response['type'] = $type;
$response['count'] = $result['count'];
$response['next'] = inlink('importJira', "method=$method&type={$result['type']}&lastID={$result['lastID']}");
return print $this->send($response);
}
}
$this->view->type = $type;
+16
View File
@@ -0,0 +1,16 @@
.main-header > ul {display: inline-block; padding: 0; margin: 0;}
.main-header > ul > li {position: relative;}
.main-header > ul > li.active > a {background-color: #1CA5FF; color: #fff}
.main-header > ul > li > a {min-width: 120px; padding: 5px 20px; text-align: center; transition: none; color: #838a9d}
.main-header > ul > li.active > a:hover,
.main-header > ul > li.active > a:focus {background-color: #1CA5FF;}
.main-header > ul > li .step-number {display: inline-block; width: 14px; height: 14px; border-radius: 7px; text-align: center; line-height: 12px; border: 1px solid #A6AAB8; color: #A6AAB8; font-size: 12px; font-weight: bold; margin-right: 3px;}
.main-header > ul > li + li > a {padding-left: 35px;}
.main-header > ul > li + li:before,
.main-header > ul > li + li:after {content: ' '; width: 0; height: 0; border-style: solid; border-width: 15px 0 15px 15px; border-color: transparent transparent transparent #a6aab8; position: absolute; left: 0; top: 0;}
.main-header > ul > li + li:after {border-left-color: #fff; left: -1px;}
.main-header > ul > li.active + li:after {border-left-color: #1ca5ff;}
.main-header > ul > li:hover + li:after {border-left-color: #eceef4;}
.main-header > ul > li.active:hover + li:after {border-left-color: #1CA5FF;}
.nav-primary>li.active>a:hover {color: #fff}
.main-header {margin-top: -15px !important;}
+74 -1
View File
@@ -116,4 +116,77 @@ $lang->convert->issue->zentao = 'ZenTao';
$lang->convert->issue->goto = 'Convert To';
$lang->convert->jira = new stdclass();
$lang->convert->jira->import = 'Import Jira';
$lang->convert->jira->method = 'Chose Import Method';
$lang->convert->jira->next = 'Next';
$lang->convert->jira->importFromDB = 'Import From Database';
$lang->convert->jira->importFromFile = 'Import From File';
$lang->convert->jira->mapJira2Zentao = 'Map Jira To Zentao';
$lang->convert->jira->dbNameNotice = 'Please type jira database';
$lang->convert->jira->importNotice = 'Notice: Importing data is risky! Make sure to complete the following steps in sequence before merging.';
$lang->convert->jira->dbDesc = '<p>If your Jira uses MySQL database, please choose this way.</p>';
$lang->convert->jira->fileDesc = '<p>Choose this method if your Jira uses a non-MySQL database.</p>';
$lang->convert->jira->jiraObject = 'Jira Issues';
$lang->convert->jira->zentaoObject = 'Zentao Object';
$lang->convert->jira->jiraLinkType = 'Jira Relates';
$lang->convert->jira->zentaoLinkType = 'Zentao Link Type';
$lang->convert->jira->jiraResolution = 'Jira Resolution';
$lang->convert->jira->zentaoResolution = 'Zentao Resolution';
$lang->convert->jira->zentaoReason = 'Zentao Story Closed Reason';
$lang->convert->jira->jiraStatus = 'Jira Issues Status';
$lang->convert->jira->storyStatus = 'Zentao Story Status';
$lang->convert->jira->storyStage = 'Zentao Story Stage';
$lang->convert->jira->bugStatus = 'Zentao Bug Status';
$lang->convert->jira->taskStatus = 'Zentao Task Status';
$lang->convert->jira->initJiraUser = 'Init Jira User';
$lang->convert->jira->importJira = 'Import Jira';
$lang->convert->jira->start = 'Start';
$lang->convert->jira->dbNameEmpty = 'Jira database name cannot be empty!';
$lang->convert->jira->invalidDB = 'Invalid database name!';
$lang->convert->jira->invalidTable = 'This database is not a Jira database!';
$lang->convert->jira->passwordNotice = "<span class='text-red'>Set the default password of Jira users after they are imported into ZenTao. Users can modify the password in ZenTao later.</span>";
$lang->convert->jira->groupNotice = "<span class='text-red'>Set the default permission grouping of Jira users after importing into ZenTao.</span>";
$lang->convert->jira->passwordDifferent = 'Password do not match!';
$lang->convert->jira->passwordEmpty = 'Password can not be empty!';
$lang->convert->jira->passwordLess = 'Password must not be less than six characters!';
$lang->convert->jira->importSuccessfully = 'Done!';
$lang->convert->jira->importResult = "Import <strong class='text-red'>%s</strong> data, <strong class='%scount'>%s</strong> pieces of data processed";
$lang->convert->jira->importing = 'Data is being imported, please do not switch to other pages.';
$lang->convert->jira->importingAB = 'Data Import';
$lang->convert->jira->imported = 'Data Import Complete';
$lang->convert->jira->zentaoObjectList[''] = '';
$lang->convert->jira->zentaoObjectList['task'] = 'Task';
$lang->convert->jira->zentaoObjectList['requirement'] = 'Requirement';
$lang->convert->jira->zentaoObjectList['story'] = 'Story';
$lang->convert->jira->zentaoObjectList['bug'] = 'Bug';
$lang->convert->jira->zentaoLinkTypeList['subTaskLink'] = 'Parent-Child Task';
$lang->convert->jira->zentaoLinkTypeList['subStoryLink'] = 'Parent-Child Story';
$lang->convert->jira->zentaoLinkTypeList['duplicate'] = 'Duplicate';
$lang->convert->jira->zentaoLinkTypeList['relates'] = 'Relates';
$lang->convert->jira->steps[1] = 'Objects';
$lang->convert->jira->steps[2] = 'Relates';
$lang->convert->jira->steps[3] = 'Resolution';
$lang->convert->jira->steps[4] = 'Status';
$lang->convert->jira->importSteps['db'][1] = 'Backup ZenTao database, backup Jira database.';
$lang->convert->jira->importSteps['db'][2] = 'Using ZenTao when importing data will cause performance pressure on the server, please try to ensure that no one is using ZenTao when importing data.';
$lang->convert->jira->importSteps['db'][3] = 'Import the Jira database into the Mysql used by ZenTao, the name is distinguished from the ZenTao database.';
$lang->convert->jira->importSteps['db'][4] = "Put the Jira <strong class='text-red'> attachments</strong> directory under <strong class='text-red'>%s</strong>, Make sure you have enough disk space on the ZenTao server.";
$lang->convert->jira->importSteps['db'][5] = "After the above steps are completed, please enter the Jira database name to proceed to the next step.";
$lang->convert->jira->importSteps['file'][1] = 'Backup ZenTao database, backup Jira database.';
$lang->convert->jira->importSteps['file'][2] = 'Using ZenTao when importing data will cause performance pressure on the server, please try to ensure that no one is using ZenTao when importing data.';
$lang->convert->jira->importSteps['file'][3] = "Put the Jira backup file <strong class='text-red'>entities.xml</strong> under <strong class='text-red'>%s</strong>.";
$lang->convert->jira->importSteps['file'][4] = "Put the Jira <strong class='text-red'> attachments</strong> directory under <strong class='text-red'>%s</strong>, Make sure you have enough disk space on the ZenTao server.";
$lang->convert->jira->importSteps['file'][5] = "After the above steps are completed, click Next.";
$lang->convert->jira->objectList['user'] = 'User';
$lang->convert->jira->objectList['project'] = 'Project';
$lang->convert->jira->objectList['issue'] = 'Issue';
$lang->convert->jira->objectList['build'] = 'Build';
$lang->convert->jira->objectList['issuelink'] = 'Issue Link';
$lang->convert->jira->objectList['action'] = 'Action';
$lang->convert->jira->objectList['file'] = 'File';
+14 -2
View File
@@ -120,7 +120,7 @@ $lang->convert->jira->method = '选择导入方式';
$lang->convert->jira->next = '下一步';
$lang->convert->jira->importFromDB = '从数据库导入';
$lang->convert->jira->importFromFile = '从文件导入';
$lang->convert->jira->mapJira2Zentao = '设置对应关系';
$lang->convert->jira->mapJira2Zentao = '设置Jira与禅道数据对应关系';
$lang->convert->jira->dbNameNotice = '请输入Jira数据库名字';
$lang->convert->jira->importNotice = '注意:导入数据有风险!请务必确保如下操作步骤依次完成,再进行合并。';
$lang->convert->jira->dbDesc = '<p>如果您的Jira使用Mysql数据库, 请选择此方式</p>';
@@ -143,13 +143,19 @@ $lang->convert->jira->start = '开始导入';
$lang->convert->jira->dbNameEmpty = 'Jira数据库名字不能为空!';
$lang->convert->jira->invalidDB = '无效的数据库名!';
$lang->convert->jira->invalidTable = '本数据库非Jira数据库!';
$lang->convert->jira->passwordNotice = "<span class='text-red'>设置Jira用户导入到禅道后的默认密码,用户后续可以在禅道中自行修改密码。</span>";
$lang->convert->jira->groupNotice = "<span class='text-red'>设置Jira用户导入到禅道后的默认权限分组。</span>";
$lang->convert->jira->passwordDifferent = '两次密码不一致!';
$lang->convert->jira->passwordEmpty = '密码不能为空!';
$lang->convert->jira->passwordLess = '密码不能少于六位!';
$lang->convert->jira->importSuccessfully = 'Jira导入完成!';
$lang->convert->jira->importResult = "导入 <strong class='text-red'>%s</strong> 数据, 已处理 <strong class='%scount'>%s</strong> 条记录;";
$lang->convert->jira->importing = '数据导入中,请不要切换其它页面';
$lang->convert->jira->importingAB = '数据导入中';
$lang->convert->jira->imported = '数据导入完成';
$lang->convert->jira->zentaoObjectList[''] = '';
$lang->convert->jira->zentaoObjectList['task'] = '任务';
$lang->convert->jira->zentaoObjectList['requirement'] = '用户需求';
$lang->convert->jira->zentaoObjectList['story'] = '软件需求';
@@ -160,14 +166,20 @@ $lang->convert->jira->zentaoLinkTypeList['subStoryLink'] = '父-子需求';
$lang->convert->jira->zentaoLinkTypeList['duplicate'] = '重复对象';
$lang->convert->jira->zentaoLinkTypeList['relates'] = '互相关联';
$lang->convert->jira->steps[1] = '对象';
$lang->convert->jira->steps[2] = '对象关联关系';
$lang->convert->jira->steps[3] = '解决方案';
$lang->convert->jira->steps[4] = '状态';
$lang->convert->jira->importSteps['db'][1] = '备份禅道数据库,备份Jira数据库。';
$lang->convert->jira->importSteps['db'][2] = '导入数据时使用禅道会给服务器造成性能压力,请尽量保证导入数据时无人使用禅道。';
$lang->convert->jira->importSteps['db'][3] = '将Jira数据库导入到禅道使用的Mysql中,名字和禅道数据库区别开来。';
$lang->convert->jira->importSteps['db'][4] = "将Jira附件目录<strong class='text-red'> attachments</strong> 放到 <strong class='text-red'>%s</strong> 下,确保禅道服务器磁盘空间足够。";
$lang->convert->jira->importSteps['db'][5] = "上述步骤完成后,请输入Jira数据库名字进行下一步。";
$lang->convert->jira->importSteps['file'][1] = '备份禅道数据库,备份Jira数据库。';
$lang->convert->jira->importSteps['file'][2] = '导入数据时使用禅道会给服务器造成性能压力,请尽量保证导入数据时无人使用禅道。';
$lang->convert->jira->importSteps['file'][3] = "将Jira的备份文件 <strong class='text-red'>entities.xml</strong> 放到 <strong class='text-red'>%s</strong> 下。";
$lang->convert->jira->importSteps['file'][3] = "将Jira的备份文件 <strong class='text-red'>entities.xml</strong> 放到 <strong class='text-red'>%s</strong> 下,并给该目录读写权限";
$lang->convert->jira->importSteps['file'][4] = "将Jira附件目录<strong class='text-red'> attachments</strong> 放到 <strong class='text-red'>%s</strong> 下,确保禅道服务器磁盘空间足够。";
$lang->convert->jira->importSteps['file'][5] = "上述步骤完成后,点击下一步。";
+449 -40
View File
@@ -52,6 +52,7 @@ class convertModel extends model
/**
* Check table exits or not.
*
* @param string $table
* @access public
* @return bool
*/
@@ -61,6 +62,21 @@ class convertModel extends model
return $this->dbh->query($sql)->fetch();
}
/**
* Check table of jira databases exits or not.
*
* @param string $dbName
* @param string $table
* @access public
* @return bool
*/
public function tableExistsOfJira($dbName, $table)
{
$this->connectDB($dbName);
$sql = "SHOW tables like '$table'";
return $this->dao->dbh($this->sourceDBH)->query($sql)->fetch();
}
/**
* Save the max id of every table. Thus when we convert again, when can delete id larger then the saved max id.
*
@@ -95,13 +111,15 @@ class convertModel extends model
}
/**
* Get jira data.
* Get jira data from db.
*
* @param int $module
* @param string $module
* @param int $lastID
* @param int $limit
* @access public
* @return void
*/
public function getJiraData($module = '', $lastID = 0, $limit = 0)
public function getJiraDataFromDB($module = '', $lastID = 0, $limit = 0)
{
$dataList = array();
if($module == 'user')
@@ -165,6 +183,207 @@ class convertModel extends model
return $dataList;
}
/**
* Get jira data from file.
*
* @param sting $module
* @param int $lastID
* @param int $limit
* @access public
* @return void
*/
public function getJiraDataFromFile($module, $lastID = 0, $limit = 0)
{
$fileName = $module;
if($module == 'build') $filename = 'version';
if($module == 'file') $filename = 'fileattachment';
$filePath = $this->app->getTmpRoot() . 'jirafile/' . $fileName . '.xml';
if(!file_exists($filePath)) return array();
$xmlContent = file_get_contents($filePath);
$xmlContent = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $xmlContent);
$parsedXML = simplexml_load_string($xmlContent, SimpleXMLElement::class, LIBXML_NOCDATA);
$dataList = array();
$parsedXML = $this->object2Array($parsedXML);
foreach($parsedXML as $key => $xmlArray)
{
if(strtolower($key) != strtolower($fileName)) continue;
foreach($xmlArray as $key => $attributes)
{
$desc = isset($attributes['description']) ? $attributes['description'] : '';
$summary = isset($attributes['summary']) ? $attributes['summary'] : '';
$body = isset($attributes['body']) ? $attributes['body'] : '';
if(is_numeric($key))
{
foreach($attributes as $value)
{
if(!is_array($value)) continue;
if(!empty($desc)) $value['description'] = $desc;
if(!empty($summary)) $value['summary'] = $summary;
if(!empty($body)) $value['body'] = $body;
$dataList[$value['id']] = $value;
}
}
else
{
$dataList[$attributes['id']] = $attributes;
}
}
}
if($limit)
{
$dataList = array_slice($dataList, $lastID, $limit, true);
if(empty($dataList)) return array();
}
foreach($dataList as $key => $data)
{
if($module == 'user')
{
$user = new stdclass();
$user->account = $data['lowerUserName'];
$user->realname = $data['lowerDisplayName'];
$user->email = $data['emailAddress'];
$user->join = $data['createdDate'];
$dataList[$key] = $user;
}
elseif($module == 'project')
{
$project = new stdclass();
$project->ID = $data['id'];
$project->pname = $data['name'];
$project->pkey = $data['key'];
$project->ORIGINALKEY = $data['originalkey'];
$project->DESCRIPTION = $data['description'];
$project->LEAD = $data['lead'];
$dataList[$key] = $project;
}
elseif($module == 'issue')
{
$issue = new stdclass();
$issue->ID = $data['id'];
$issue->SUMMARY = $data['summary'];
$issue->PRIORITY = $data['priority'];
$issue->PROJECT = $data['project'];
$issue->issuestatus = $data['status'];
$issue->CREATED = $data['created'];
$issue->CREATOR = $data['creator'];
$issue->issuetype = $data['type'];
$issue->ASSIGNEE = isset($data['assignee']) ? $data['assignee'] : '';
$issue->RESOLUTION = isset($data['resolution']) ? $data['resolution'] : '';
$issue->issuenum = $data['number'];
$issue->DESCRIPTION = isset($data['description']) ? $data['description'] : '';
$dataList[$key] = $issue;
}
elseif($module == 'build')
{
$build = new stdclass();
$build->ID = $data['id'];
$build->PROJECT = $data['project'];
$build->vname = $data['name'];
$build->RELEASEDATE = isset($data['releasedate']) ? $data['releasedate'] : '';
$build->DESCRIPTION = isset($data['description']) ? $data['description'] : '';
$dataList[$key] = $build;
}
elseif($module == 'issuelink')
{
$issueLink = new stdclass();
$issueLink->LINKTYPE = $data['linktype'];
$issueLink->SOURCE = $data['source'];
$issueLink->DESTINATION = $data['destination'];
$dataList[$key] = $issueLink;
}
elseif($module == 'action')
{
$action = new stdclass();
$action->issueid = $data['issue'];
$action->actionbody = $data['body'];
$action->AUTHOR = $data['author'];
$action->CREATED = $data['created'];
$dataList[$key] = $action;
}
elseif($module == 'file')
{
$file = new stdclass();
$file->issueid = $data['issue'];
$file->ID = $data['id'];
$file->FILENAME = $data['filename'];
$file->MIMETYPE = $data['mimetype'];
$file->FILESIZE = $data['filesize'];
$file->CREATED = $data['created'];
$file->AUTHOR = $data['author'];
$dataList[$key] = $file;
}
}
return $dataList;
}
/**
* Get version group from jira file.
*
* @access public
* @return void
*/
public function getVersionGroup()
{
$xmlContent = file_get_contents($this->app->getTmpRoot() . 'jirafile/nodeassociation.xml');
$xmlContent = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $xmlContent);
$parsedXML = simplexml_load_string($xmlContent, SimpleXMLElement::class, LIBXML_NOCDATA);
$dataList = array();
$parsedXML = $this->object2Array($parsedXML);
foreach($parsedXML as $key => $xmlArray)
{
if(strtolower($key) != 'nodeassociation') continue;
foreach($xmlArray as $key => $attributes)
{
foreach($attributes as $value)
{
if(!is_array($value)) continue;
if($value['sinkNodeEntity'] != 'Version') continue;
$dataList[$value['sinkNodeId']][] = $value['sinkNodeId'];
$dataList[$value['sinkNodeId']][] = $value['sourceNodeId'];
}
}
}
return $dataList;
}
/**
* Convert object to array.
*
* @param object $parsedXML
* @access public
* @return void
*/
public function object2Array($parsedXML)
{
if(is_object($parsedXML))
{
$parsedXML = (array)$parsedXML;
}
if(is_array($parsedXML))
{
foreach($parsedXML as $key => $value) $parsedXML[$key] = $this->object2Array($value);
}
return $parsedXML;
}
/**
* Import jira from db.
*
@@ -190,7 +409,7 @@ class convertModel extends model
while(true)
{
$dataList = $this->getJiraData($module, $lastID, $limit);
$dataList = $this->getJiraDataFromDB($module, $lastID, $limit);
if(empty($dataList))
{
@@ -210,17 +429,56 @@ class convertModel extends model
}
}
/* Set project min start date. */
$executionProject = $this->dao->dbh($this->dbh)->select('id,project')->from(TABLE_PROJECT)->where('type')->eq('sprint')->andWhere('project')->ne(0)->fetchPairs();
$minOpenedDateList = $this->dao->dbh($this->dbh)->select('id,execution,min(openedDate) as minOpenedDate')->from(TABLE_TASK)->where('execution')->in(array_keys($executionProject))->groupBy('execution')->fetchPairs('execution', 'minOpenedDate');
$this->afterExec();
return array('finished' => true);
}
foreach($minOpenedDateList as $executionID => $minOpenedDate)
/**
* Import jira from file.
*
* @param string $type
* @param int $lastID
* @param bool $createTable
* @access public
* @return void
*/
public function importJiraFromFile($type = '', $lastID = 0, $createTable = false)
{
if($createTable) $this->createTmpTable4Jira();
$limit = 1000;
$nextObject = false;
if(empty($type)) $type = key($this->lang->convert->jira->objectList);
foreach($this->lang->convert->jira->objectList as $module => $moduleName)
{
$projectID = $executionProject[$executionID];
$this->dao->update(TABLE_PROJECT)->set('begin')->eq($minOpenedDate)->where('id')->eq($projectID)->orWhere('id')->eq($executionID)->exec();
if($module != $type and !$nextObject) continue;
if($module == $type) $nextObject = true;
while(true)
{
$dataList = $this->getJiraDataFromFile($module, $lastID, $limit);
if(empty($dataList))
{
$lastID = 0;
break;
}
if($module == 'user') $this->importJiraUser($dataList, 'file');
if($module == 'project') $this->importJiraProject($dataList, 'file');
if($module == 'issue') $this->importJiraIssue($dataList, 'file');
if($module == 'build') $this->importJiraBuild($dataList, 'file');
if($module == 'issuelink') $this->importJiraIssueLink($dataList, 'file');
if($module == 'action') $this->importJiraAction($dataList, 'file');
if($module == 'file') $this->importJiraFile($dataList, 'file');
$offset = $lastID + $limit;
return array('type' => $module, 'count' => count($dataList), 'lastID' => $offset);
}
}
$this->dbh->exec("DROP TABLE" . JIRA_TMPRELATION);
$this->afterExec();
return array('finished' => true);
}
@@ -228,10 +486,11 @@ class convertModel extends model
* Import jira user.
*
* @param object $dataList
* @param string $method
* @access public
* @return void
*/
public function importJiraUser($dataList)
public function importJiraUser($dataList, $method = 'db')
{
$localUsers = $this->dao->dbh($this->dbh)->select('account')->from(TABLE_USER)->where('deleted')->eq('0')->fetchPairs();
$userConfig = $this->session->jiraUser;
@@ -250,7 +509,7 @@ class convertModel extends model
$user->type = 'inside';
$user->join = $data->join;
$this->dao->dbh($this->dbh)->insert(TABLE_USER)->data($user, 'group')->exec();
$this->dao->dbh($this->dbh)->replace(TABLE_USER)->data($user, 'group')->exec();
if(!dao::isError())
{
@@ -267,10 +526,11 @@ class convertModel extends model
* Import jira project.
*
* @param object $dataList
* @param string $method
* @access public
* @return void
*/
public function importJiraProject($dataList)
public function importJiraProject($dataList, $method = 'db')
{
global $app;
$app->loadConfig('execution');
@@ -295,8 +555,8 @@ class convertModel extends model
$project->acl = 'open';
$project->end = date('Y-m-d', time() + 24 * 3600);
$project->PM = $this->getJiraAccount($data->LEAD);
$project->openedBy = $this->getJiraAccount($data->LEAD);
$project->PM = $this->getJiraAccount($data->LEAD, $method);
$project->openedBy = $this->getJiraAccount($data->LEAD, $method);
$project->openedDate = $now;
$project->openedVersion = $this->config->version;
@@ -398,15 +658,20 @@ class convertModel extends model
$projectRelation['BType'] = 'zproject';
$projectRelation['AID'] = $id;
$projectRelation['BID'] = $projectID;
$this->dao->dbh($this->dbh)->insert(JIRA_TMPRELATION)->data($projectRelation)->exec();
$executionRelation['AType'] = 'jproject';
$executionRelation['BType'] = 'zexecution';
$executionRelation['AID'] = $id;
$executionRelation['BID'] = $executionID;
$this->dao->dbh($this->dbh)->insert(JIRA_TMPRELATION)->data($executionRelation)->exec();
$keyRelation['AType'] = 'joldkey';
$keyRelation['BType'] = 'jnewkey';
$keyRelation['AID'] = $data->ORIGINALKEY;
$keyRelation['BID'] = $data->pkey;
$keyRelation['extra'] = $data->ID;
$this->dao->dbh($this->dbh)->insert(JIRA_TMPRELATION)->data($keyRelation)->exec();
}
}
@@ -414,10 +679,11 @@ class convertModel extends model
* Import jira issue.
*
* @param object $dataList
* @param string $method
* @access public
* @return void
*/
public function importJiraIssue($dataList)
public function importJiraIssue($dataList, $method = 'db')
{
$relations = $this->session->jiraRelation;
@@ -430,6 +696,11 @@ class convertModel extends model
->andWhere('BType')->eq('zproject')
->fetchPairs();
$projectKeys = $this->dao->dbh($this->dbh)->select('extra as ID, AID as oldKey, BID as newKey')->from(JIRA_TMPRELATION)
->where('AType')->eq('joldkey')
->andWhere('BType')->eq('jnewkey')
->fetchAll('ID');
$projectProduct = $this->dao->dbh($this->dbh)->select('project,product')->from(TABLE_PROJECTPRODUCT)
->where('project')->in(array_values($projectRelation))
->fetchPairs();
@@ -452,8 +723,6 @@ class convertModel extends model
if(!empty($relations['zentaoResolution'][$id])) $resolutionList[$jiraCode] = $relations['zentaoResolution'][$id];
}
$projectKeys = $this->dao->dbh($this->sourceDBH)->select('ID, ORIGINALKEY as oldKey, pkey as newKey')->from(JIRA_PROJECT)->fetchAll('ID');
foreach($dataList as $id => $data)
{
$issueType = isset($issueTypeList[$data->issuetype]) ? $issueTypeList[$data->issuetype] : 'task';
@@ -476,9 +745,9 @@ class convertModel extends model
$story->version = 1;
$story->stage = $this->convertStage($data->issuestatus);
$story->status = $this->convertStatus('story', $data->issuestatus);
$story->openedBy = $this->getJiraAccount($data->CREATOR);
$story->openedBy = $this->getJiraAccount($data->CREATOR, $method);
$story->openedDate = substr($data->CREATED, 0, 19);
$story->assignedTo = $this->getJiraAccount($data->ASSIGNEE);
$story->assignedTo = $this->getJiraAccount($data->ASSIGNEE, $method);
if($data->RESOLUTION)
{
@@ -543,9 +812,9 @@ class convertModel extends model
$task->pri = $data->PRIORITY;
$task->status = $this->convertStatus('task', $data->issuestatus);
$task->desc = $data->DESCRIPTION;
$task->openedBy = $this->getJiraAccount($data->CREATOR);
$task->openedBy = $this->getJiraAccount($data->CREATOR, $method);
$task->openedDate = substr($data->CREATED, 0, 19);
$task->assignedTo = $this->getJiraAccount($data->ASSIGNEE);
$task->assignedTo = $this->getJiraAccount($data->ASSIGNEE, $method);
if($data->RESOLUTION)
{
$task->closedReason = zget($reasonList, $data->RESOLUTION, '');
@@ -585,10 +854,10 @@ class convertModel extends model
$bug->pri = $data->PRIORITY;
$bug->status = $this->convertStatus('bug', $data->issuestatus);
$bug->steps = $data->DESCRIPTION;
$bug->openedBy = $this->getJiraAccount($data->CREATOR);
$bug->openedBy = $this->getJiraAccount($data->CREATOR, $method);
$bug->openedDate = substr($data->CREATED, 0, 19);
$bug->openedBuild = 'trunk';
$bug->assignedTo = $this->getJiraAccount($data->ASSIGNEE);
$bug->assignedTo = $this->getJiraAccount($data->ASSIGNEE, $method);
if($data->RESOLUTION)
{
@@ -638,10 +907,11 @@ class convertModel extends model
* Import jira build.
*
* @param object $dataList
* @param string $method
* @access public
* @return void
*/
public function importJiraBuild($dataList)
public function importJiraBuild($dataList, $method = 'db')
{
$issueObjectType = $this->dao->dbh($this->dbh)->select('AID,extra')->from(JIRA_TMPRELATION)
->where('AType')->eq('jissueid')
@@ -667,7 +937,7 @@ class convertModel extends model
->where('project')->in(array_values($projectRelation))
->fetchPairs();
$versionGroup = $this->dao->dbh($this->sourceDBH)->select('SINK_NODE_ID as versionID, SOURCE_NODE_ID as issueID')->from(JIRA_NODEASSOCIATION)->where('SINK_NODE_ENTITY')->eq('Version')->fetchGroup('versionID');
$versionGroup = $method == 'db' ? $this->dao->dbh($this->sourceDBH)->select('SINK_NODE_ID as versionID, SOURCE_NODE_ID as issueID')->from(JIRA_NODEASSOCIATION)->where('SINK_NODE_ENTITY')->eq('Version')->fetchGroup('versionID') : $this->getVersionGroup();
foreach($dataList as $data)
{
@@ -704,9 +974,10 @@ class convertModel extends model
{
foreach($versionGroup[$versionID] as $issue)
{
$issueType = zget($issueObjectType, $issue->issueID, '');
$issueID = $method == 'db' ? $issue->issueID : $issue;
$issueType = zget($issueObjectType, $issueID, '');
if(!$issueType || ($issueType != 'story' and $issueType != 'bug')) continue;
$objectID = $issueType == 'bug' ? zget($issueBugs, $issue->issueID) : zget($issueStories, $issue->issueID);
$objectID = $issueType == 'bug' ? zget($issueBugs, $issueID) : zget($issueStories, $issueID);
$field = $issueType == 'story' ? 'stories' : 'bugs';
if($issueType == 'story')
@@ -726,10 +997,11 @@ class convertModel extends model
* Import jira issue link.
*
* @param object $dataList
* @param string $method
* @access public
* @return void
*/
public function importJiraIssueLink($dataList)
public function importJiraIssueLink($dataList, $method = 'db')
{
$issueLinkTypeList = array();
$relations = $this->session->jiraRelation;
@@ -849,10 +1121,11 @@ class convertModel extends model
* Import jira action.
*
* @param object $dataList
* @param string $method
* @access public
* @return void
*/
public function importJiraAction($dataList)
public function importJiraAction($dataList, $method = 'db')
{
$issueStories = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)
->where('AType')->eq('jstory')
@@ -891,6 +1164,8 @@ class convertModel extends model
$comment = $data->actionbody;
if(empty($comment)) continue;
if(!isset($issueObjectType[$issueID])) continue;
$objectType = $issueObjectType[$issueID];
if($objectType == 'task') $objectID = $issueTasks[$issueID];
if($objectType == 'bug') $objectID = $issueBugs[$issueID];
@@ -901,7 +1176,7 @@ class convertModel extends model
$action = new stdclass();
$action->objectType = $objectType;
$action->objectID = $objectID;
$action->actor = $this->getJiraAccount($data->AUTHOR);
$action->actor = $this->getJiraAccount($data->AUTHOR, $method);
$action->action = 'commented';
$action->date = substr($data->CREATED, 0, 19);
$action->comment = $comment;
@@ -913,10 +1188,11 @@ class convertModel extends model
* Import jira file.
*
* @param object $dataList
* @param string $method
* @access public
* @return void
*/
public function importJiraFile($dataList)
public function importJiraFile($dataList, $method = 'db')
{
$this->loadModel('file');
@@ -948,6 +1224,8 @@ class convertModel extends model
foreach($dataList as $fileAttachment)
{
$issueID = $fileAttachment->issueid;
if(!isset($issueObjectType[$issueID])) continue;
$objectType = $issueObjectType[$issueID];
if($objectType != 'bug' and $objectType != 'task' and $objectType != 'story') continue;
@@ -967,7 +1245,7 @@ class convertModel extends model
$file->size = $fileAttachment->FILESIZE;
$file->objectType = $objectType;
$file->objectID = $objectID;
$file->addedBy = $this->getJiraAccount($fileAttachment->AUTHOR);
$file->addedBy = $this->getJiraAccount($fileAttachment->AUTHOR, $method);
$file->addedDate = substr($fileAttachment->CREATED, 0, 19);
$this->dao->dbh($this->dbh)->insert(TABLE_FILE)->data($file)->exec();
@@ -1023,25 +1301,135 @@ class convertModel extends model
* Get jira account.
*
* @param string $userKey
* @param string $method
* @access public
* @return void
*/
public function getJiraAccount($userKey)
public function getJiraAccount($userKey, $method = 'db')
{
if(strpos($userKey, 'JIRAUSER') === false) return $userKey;
return $this->dao->dbh($this->sourceDBH)->select('lower_user_name')->from(JIRA_USER)->where('user_key')->eq($userKey)->fetch('lower_user_name');
if($method == 'db')
{
return $this->dao->dbh($this->sourceDBH)->select('lower_user_name')->from(JIRA_USER)->where('user_key')->eq($userKey)->fetch('lower_user_name');
}
else
{
$appUsers = $this->getJiraAppUser();
return zget($appUsers, $userKey, $userKey);
}
}
/**
* Get jira app user pairs.
*
* @access public
* @return void
*/
public function getJiraAppUser()
{
$xmlContent = file_get_contents($this->app->getTmpRoot() . 'jirafile/applicationuser.xml');
$xmlContent = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $xmlContent);
$parsedXML = simplexml_load_string($xmlContent, SimpleXMLElement::class, LIBXML_NOCDATA);
$pairs = array();
$parsedXML = $this->object2Array($parsedXML);
foreach($parsedXML as $key => $xmlArray)
{
if(strtolower($key) != 'applicationuser') continue;
foreach($xmlArray as $key => $attributes)
{
if(is_numeric($key))
{
foreach($attributes as $value)
{
if(!is_array($value)) continue;
if(!isset($value['userKey'])) continue;
$pairs[$value['userKey']] = $value['lowerUserName'];
}
}
else
{
$pairs[$attributes['userKey']] = $attributes['lowerUserName'];
}
}
}
return $pairs;
}
/**
* Split jira file.
*
* @access public
* @return void
*/
public function splitFile()
{
$filePath = $this->app->getTmpRoot() . 'jirafile/';
$fileName = 'entities.xml';
$file = $filePath . $fileName;
$handle = fopen($file, "r");
$usingData = array();
$headerList = array('<Action', '<Project', '<Status', '<Resolution', '<User', '<Issue', '<ChangeGroup', '<ChangeItem', '<IssueLink', '<IssueLinkType', '<FileAttachment', '<Version', '<IssueType', '<NodeAssociation', '<ApplicationUser');
$footerList = array('<Action' => '</Action>', '<Project' => '</Project>', '<Status' => '</Status>', '<Resolution' => '</Resolution>', '<User' => '</User>', '<Issue' => '</Issue>', '<ChangeGroup' => '</ChangeGroup>', '<ChangeItem' => '</ChangeItem>', '<IssueLink' => '</IssueLink>', '<IssueLinkType' => '</IssueLinkType>', '<FileAttachment' => '</FileattAchment>', '<Version' => '</Version>', '<IssueType' => '</IssueType>', '<NodeAssociation' => '</NodeAssociation>', '<ApplicationUser' => '</ApplicationUser>');
while(!feof($handle))
{
$itemStr = fgets($handle);
foreach($headerList as $object)
{
$itemName = $object;
$itemName .= ' ';
if(strpos($itemStr, $itemName) === false) continue;
if(strpos($itemStr, '/>') === false)
{
$end = $footerList[$object];
while(true)
{
$followItemStr = fgets($handle);
$itemStr .= $followItemStr;
if(strpos($itemStr, $end) !== false) break;
}
}
$object = str_replace('<', '', $object);
$object = strtolower($object);
$data = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $itemStr);
if(!file_exists($filePath . $object . '.xml')) $data = "<?xml version='1.0' encoding='UTF-8'?>\n <entity-engine-xml>\n" . $data;
file_put_contents($filePath . $object . '.xml', $data, FILE_APPEND);
}
}
foreach($headerList as $object)
{
$object = str_replace('<', '', $object);
$object = strtolower($object);
$filename = $filePath . $object . '.xml';
if(file_exists($filename)) file_put_contents($filename, '</entity-engine-xml>', FILE_APPEND);
}
fclose($handle);
}
/**
* Create tmp table for import jira.
*
* @access public
* @return void
*/
public function createTmpTable4Jira()
{
$sql = <<<EOT
CREATE TABLE `jiratmprelation`(
`id` int(8) NOT NULL AUTO_INCREMENT,
`AType` char(30) NOT NULL,
`AID` mediumint(8) NOT NULL,
`AID` char(30) NOT NULL,
`BType` char(30) NOT NULL,
`BID` mediumint(8) NOT NULL,
`BID` char(30) NOT NULL,
`extra` char(30) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `relation` (`AType`,`BType`,`AID`,`BID`)
@@ -1054,4 +1442,25 @@ EOT;
}
catch(Exception $e){}
}
/**
* After exec.
*
* @access public
* @return void
*/
public function afterExec()
{
/* Set project min start date. */
$executionProject = $this->dao->dbh($this->dbh)->select('id,project')->from(TABLE_PROJECT)->where('type')->eq('sprint')->andWhere('project')->ne(0)->fetchPairs();
$minOpenedDateList = $this->dao->dbh($this->dbh)->select('id,execution,min(openedDate) as minOpenedDate')->from(TABLE_TASK)->where('execution')->in(array_keys($executionProject))->groupBy('execution')->fetchPairs('execution', 'minOpenedDate');
foreach($minOpenedDateList as $executionID => $minOpenedDate)
{
$projectID = $executionProject[$executionID];
$this->dao->update(TABLE_PROJECT)->set('begin')->eq($minOpenedDate)->where('id')->eq($projectID)->orWhere('id')->eq($executionID)->exec();
}
$this->dbh->exec("DROP TABLE" . JIRA_TMPRELATION);
}
}
+9 -4
View File
@@ -13,28 +13,33 @@
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-content'>
<div class='main-header'>
<h2><i class='icon-import'></i> <?php echo $lang->convert->jira->importJira;?></h2>
<h2 class="importData"><i class='icon-import'></i> <?php echo $lang->convert->jira->importJira;?></h2>
</div>
<div>
<div class='form-group'>
<ul id='resultBox'></ul>
<ul id='resultBox'>
<li class='text-red hidden'><strong><?php echo $lang->convert->jira->importing;?></strong></li>
</ul>
</div>
<div class='from-group'><?php echo html::a(inlink('importJira', "method=db&type=user&lastID=0&createTable=true"), $lang->convert->jira->start, '', "class='btn btn-primary' id='execButton'");?></div>
<div class='from-group'><?php echo html::a(inlink('importJira', "method=$method&type=user&lastID=0&createTable=true"), $lang->convert->jira->start, '', "class='btn btn-primary' id='execButton'");?></div>
</div>
</div>
</div>
<script>
$(document).ready(function()
{
$('#execButton').click(function()
{
$('#execButton').hide();
$('#resultBox .text-red').removeClass('hidden');
$('.importData').html('<?php echo $lang->convert->jira->importingAB;?>');
$.getJSON($(this).attr('href'), function(response)
{
if(response.result == 'finished')
{
$('#resultBox').append("<li class='text-success'>" + response.message + "</li>");
$('#resultBox li.text-red').hide();
$('.importData').html('<?php echo $lang->convert->jira->imported;?>');
return false;
}
else
+9 -9
View File
@@ -17,24 +17,24 @@ li .form-control {margin-top: 10px}
<div id='mainContent' class='main-content'>
<div class='main-header'>
<h2>
<?php echo $type == 'db' ? $lang->convert->jira->importFromDB : $lang->convert->jira->importFromFile;?>
<?php echo $method == 'db' ? $lang->convert->jira->importFromDB : $lang->convert->jira->importFromFile;?>
</h2>
</div>
<form class='form-ajax' method='post'>
<div class='panel-title text-center text-red'><?php echo $lang->convert->jira->importNotice;?></div>
<div class='panel-body'>
<ol>
<li><?php echo $lang->convert->jira->importSteps[$type][1];?></li>
<li><?php echo $lang->convert->jira->importSteps[$type][2];?></li>
<?php if($type == 'db'):?>
<li><?php echo $lang->convert->jira->importSteps[$type][3];?></li>
<li><?php echo $lang->convert->jira->importSteps[$method][1];?></li>
<li><?php echo $lang->convert->jira->importSteps[$method][2];?></li>
<?php if($method == 'db'):?>
<li><?php echo $lang->convert->jira->importSteps[$method][3];?></li>
<?php else:?>
<li><?php echo sprintf($lang->convert->jira->importSteps[$type][3], $app->getTmpRoot() . 'jirafile');?></li>
<li><?php echo sprintf($lang->convert->jira->importSteps[$method][3], $app->getTmpRoot() . 'jirafile');?></li>
<?php endif;?>
<li><?php echo sprintf($lang->convert->jira->importSteps[$type][4], $app->getTmpRoot());?></li>
<li><?php echo sprintf($lang->convert->jira->importSteps[$method][4], $app->getTmpRoot());?></li>
<li>
<?php echo $lang->convert->jira->importSteps[$type][5];?>
<?php if($type == 'db'):?>
<?php echo $lang->convert->jira->importSteps[$method][5];?>
<?php if($method == 'db'):?>
<?php echo html::input('dbName', '', "class='form-control w-200px' placeholder={$lang->convert->jira->dbNameNotice}");?>
<?php endif;?>
</li>
@@ -46,5 +46,4 @@
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+38 -18
View File
@@ -10,13 +10,27 @@
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-content'>
<div class='main-header'>
<h2>
<?php echo $lang->convert->jira->mapJira2Zentao;?>
</h2>
<div id="mainMenu" class="clearfix">
<div class="btn-toolbar pull-left">
<?php echo html::a("javascript::void(0);", "<span class='text'>" . $lang->convert->jira->mapJira2Zentao . '</span>', '', "class='btn btn-link btn-active-text'");?>
</div>
</div>
<div id='mainContent' class='main-content'>
<form class='main-form form-ajax' method='post'>
<div class='main-header text-center'>
<ul class='nav nav-primary'>
<?php foreach($lang->convert->jira->steps as $key => $label):?>
<?php $active = $step == $key ? 'class="active"' : '';?>
<li <?php echo $active;?>>
<a><?php echo $label;?></a>
</li>
<?php endforeach;?>
</ul>
<div class='btn-toolbar pull-right'>
<?php echo html::submitButton($lang->convert->jira->next);?>
</div>
</div>
<?php if($step == 1):?>
<table class='table table-form'>
<thead>
<tr class='text-center'>
@@ -25,15 +39,18 @@
</tr>
</thead>
<tbody>
<?php foreach($issueTypePairs as $id => $name):?>
<?php foreach($issueTypeList as $id => $issueType):?>
<?php $value = $method == 'db' ? $issueType->pname : $issueType['name'];?>
<tr>
<td><?php echo html::select('jiraObject[]', $issueTypePairs, $id, "class='form-control chosen'");?></td>
<td><?php echo html::select('jiraObject[]', array($id => $value), $id, "class='form-control chosen'");?></td>
<td><?php echo html::select('zentaoObject[]', $lang->convert->jira->zentaoObjectList, '', "class='form-control chosen'");?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<hr />
<?php endif;?>
<?php if($step == 2):?>
<table class='table table-form'>
<thead>
<tr class='text-center'>
@@ -42,15 +59,18 @@
</tr>
</thead>
<tbody>
<?php foreach($linkTypePairs as $id => $name):?>
<?php foreach($linkTypeList as $id => $linkType):?>
<?php $value = $method == 'db' ? $linkType->linkname : $linkType['linkname'];?>
<tr>
<td><?php echo html::select('jiraLinkType[]', $linkTypePairs, $id, "class='form-control chosen'");?></td>
<td><?php echo html::select('jiraLinkType[]', array($id => $value), $id, "class='form-control chosen'");?></td>
<td><?php echo html::select('zentaoLinkType[]', $lang->convert->jira->zentaoLinkTypeList, '', "class='form-control chosen'");?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<hr />
<?php endif;?>
<?php if($step == 3):?>
<table class='table table-form'>
<thead>
<tr class='text-center'>
@@ -60,9 +80,10 @@
</tr>
</thead>
<tbody>
<?php foreach($resolutions as $id => $name):?>
<?php foreach($resolutionList as $id => $resolution):?>
<?php $value = $method == 'db' ? $resolution->pname : $resolution['name'];?>
<tr>
<td><?php echo html::select('jiraResolution[]', $resolutions, $id, "class='form-control chosen'");?></td>
<td><?php echo html::select('jiraResolution[]', array($id => $value), $id, "class='form-control chosen'");?></td>
<td><?php echo html::select('zentaoResolution[]', $lang->bug->resolutionList, '', "class='form-control chosen'");?></td>
<td><?php echo html::select('zentaoReason[]', $lang->story->reasonList, '', "class='form-control chosen'");?></td>
</tr>
@@ -70,6 +91,8 @@
</tbody>
</table>
<hr />
<?php endif;?>
<?php if($step == 4):?>
<table class='table table-form'>
<thead>
<tr class='text-center'>
@@ -81,9 +104,10 @@
</tr>
</thead>
<tbody>
<?php foreach($statusList as $id => $name):?>
<?php foreach($statusList as $id => $status):?>
<?php $value = $method == 'db' ? $status->pname : $status['name'];?>
<tr>
<td><?php echo html::select('jiraStatus[]', $statusList, $id, "class='form-control chosen'");?></td>
<td><?php echo html::select('jiraStatus[]', array($id => $value), $id, "class='form-control chosen'");?></td>
<td><?php echo html::select('storyStatus[]', $lang->story->statusList, '', "class='form-control chosen'");?></td>
<td><?php echo html::select('storyStage[]', $lang->story->stageList, '', "class='form-control chosen'");?></td>
<td><?php echo html::select('taskStatus[]', $lang->task->statusList, '', "class='form-control chosen'");?></td>
@@ -91,11 +115,7 @@
</tr>
<?php endforeach;?>
</tbody>
<tfoot>
<tr>
<td colspan="5" class="text-center form-actions"><?php echo html::submitButton($lang->convert->jira->next);?></td>
</tr>
</tfoot>
</table>
<?php endif;?>
</form>
</div>
@@ -15,7 +15,7 @@
</style>
<div id='mainMenu' class='clearfix'>
<div class='pull-right'>
<?php if(common::hasPriv('custom', 'setstoryconcept')) echo html::a($this->createLink('custom', 'setstoryconcept', '', '', true), $lang->custom->setStoryConcept, '', "class='btn btn-primary iframe' data-width=50%");?>
<?php if(common::hasPriv('custom', 'setstoryconcept')) echo html::a($this->createLink('custom', 'setstoryconcept', '', '', true), $lang->custom->setStoryConcept, '', "class='btn btn-primary iframe' data-width=" . ($this->config->URAndSR ? "590px" : "405px"));?>
</div>
</div>
<div id='mainContent' class='main-row'>
@@ -45,7 +45,7 @@
<td class='text-left SRBox'><?php echo $URSR['SRName'];?></td>
<td class='c-actions'>
<?php $disabled = $key == $config->custom->URSR ? "disabled=disabled" : '';?>
<?php if(common::hasPriv('custom', 'editStoryConcept')) echo html::a($this->createLink('custom', 'editStoryConcept', "id=$key", '', true), "<i class='icon icon-edit'></i>", '', "class='btn iframe' data-width=50% title={$lang->edit}");?>
<?php if(common::hasPriv('custom', 'editStoryConcept')) echo html::a($this->createLink('custom', 'editStoryConcept', "id=$key", '', true), "<i class='icon icon-edit'></i>", '', "class='btn iframe' data-width=" . ($this->config->URAndSR ? "480px" : "330px") . " title={$lang->edit}");?>
<?php if(common::hasPriv('custom', 'deleteStoryConcept')) echo html::a($this->createLink('custom', 'deleteStoryConcept', "id=$key"), "<i class='icon icon-trash'></i>", 'hiddenwin', "class='btn' $disabled title={$lang->delete} data-group='admin'");?>
</td>
</tr>
+1 -1
View File
@@ -23,7 +23,7 @@
<?php if($this->config->URAndSR):?>
<td class='w-200px'><strong><?php echo $lang->custom->URConcept;?></strong></th>
<?php endif;?>
<td class='w-200px'><strong><?php echo $lang->custom->SRConcept;?></strong></th>
<td class='<?php echo $this->config->URAndSR ? "w-200px" : "w-250px";?>'><strong><?php echo $lang->custom->SRConcept;?></strong></th>
<td></td><td></td>
</tr>
<tr>
+3 -3
View File
@@ -52,8 +52,7 @@ EOT;
<?php if($this->config->URAndSR):?>
<td class='w-200px'><strong><?php echo $lang->custom->URConcept;?></strong></th>
<?php endif;?>
<td class='w-200px'><strong><?php echo $lang->custom->SRConcept;?></strong></th>
<td></td><td></td>
<td<?php echo $this->config->URAndSR ? " class='w-200px'": " class='w-250px'";?>><strong><?php echo $lang->custom->SRConcept;?></strong></th>
</tr>
<tr>
<?php if($this->config->URAndSR):?>
@@ -65,7 +64,8 @@ EOT;
</td>
</tr>
<tr>
<td class='text-center' colspan='4'><?php echo html::submitButton();?></td>
<?php if ($this->config->URAndSR) echo "<td></td>";?>
<td class=<?php echo !$this->config->URAndSR ? "'text-center' colspan='2'" : "text-left";?>><?php echo html::submitButton();?></td>
</tr>
</table>
</div>
+2 -1
View File
@@ -2024,7 +2024,7 @@ class execution extends control
{
if($this->execution->isClickable($execution, $action)) $executionActions[] = $action;
}
$userList = array();
$users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$avatarPairs = $this->user->getAvatarPairs();
@@ -2066,6 +2066,7 @@ class execution extends control
$this->view->allPlans = $allPlans;
$this->view->kanbanData = $kanbanData;
$this->view->executionActions = $executionActions;
$this->view->kanban = $this->lang->execution->kanban;
$this->display();
}
+1 -1
View File
@@ -11,4 +11,4 @@ th.c-pri{width:50px;}
.c-status, .c-user {width: 70px;}
.c-category {width: 80px;}
.c-actions-7 {width: 190px;}
.closed-story {color:#e5e5e5;}
.closed-story {color: #e5e5e5;}
+1 -1
View File
@@ -363,7 +363,7 @@ class executionModel extends model
$this->dao->insert(TABLE_EXECUTION)->data($sprint)
->autoCheck($skipFields = 'begin,end')
->batchcheck($this->config->execution->create->requiredFields, 'notempty')
->checkIF((!empty($sprint->name) and $this->config->systemMode == 'new'), 'name', 'unique', "`type` in ('sprint','stage') and `project` = $sprintProject")
->checkIF((!empty($sprint->name) and $this->config->systemMode == 'new'), 'name', 'unique', "`type` in ('sprint','stage', 'kanban') and `project` = $sprintProject")
->checkIF(!empty($sprint->code), 'code', 'unique', "`type` in ('sprint','stage')")
->checkIF($sprint->begin != '', 'begin', 'date')
->checkIF($sprint->end != '', 'end', 'date')
+1 -1
View File
@@ -119,7 +119,7 @@
</td>
<td class='text-left c-name <?php if(!empty($execution->children)) echo 'has-child';?> flex' title='<?php echo $execution->name?>'>
<?php if($config->systemMode == 'new'):?>
<span class='project-type-label label label-outline <?php echo $execution->type == 'stage' ? 'label-warning' : 'label-info';?>'><?php echo $lang->execution->typeList[$execution->type]?></span>
<span class='project-type-label label label-outline <?php echo $execution->type == 'stage' ? 'label-warning' : 'label-info';?>'><?php echo $lang->execution->typeList[$execution->type]?></span>
<?php endif;?>
<?php
$executionLink = $execution->projectModel == 'kanban' ? html::a($this->createLink('execution', 'kanban', 'executionID=' . $execution->id), $execution->name, '', "class='text-ellipsis'") : html::a($this->createLink('execution', 'task', 'execution=' . $execution->id), $execution->name, '', "class='text-ellipsis'");
+1 -1
View File
@@ -201,7 +201,7 @@ js::set('hasTaskButton', $hasTaskButton);
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="icon icon-close"></i></button>
<h4 class="modal-title"><?php echo $lang->execution->linkStoryByPlan;?></h4><?php echo '(' . $lang->execution->linkStoryByPlanTips . ')';?>
<h4 class="modal-title"><?php echo $lang->execution->linkStoryByPlan;?></h4><?php echo str_replace($lang->executionCommon, $kanban, $lang->execution->linkStoryByPlanTips);?>
</div>
<div class="modal-body">
<div class='input-group'>
+3 -3
View File
@@ -610,7 +610,7 @@ class gitlabModel extends model
{
$url .= '&owned=true';
}
elseif(!empty($minRole))
elseif(!empty($minRole) and isset($this->config->gitlab->accessLevel[$minRole]))
{
$url .= '&min_access_level=' . $this->config->gitlab->accessLevel[$minRole];
}
@@ -625,7 +625,7 @@ class gitlabModel extends model
$results = json_decode(commonModel::http($url . "&statistics=true&order_by={$order}&sort={$sort}&page={$page}&per_page=100&all_available=true"));
if(!is_array($results)) break;
if(!empty($results)) $allResults = array_merge($allResults, $results);
if(count($results) < 100 or $page > 10) break;
if(count($results) < 100) break;
}
return $allResults;
@@ -701,7 +701,7 @@ class gitlabModel extends model
$results = json_decode(commonModel::http($url . "&simple={$simple}&page={$page}&per_page=100"));
if(!is_array($results)) break;
if(!empty($results)) $allResults = array_merge($allResults, $results);
if(count($results) < 100 or $page > 10) break;
if(count($results) < 100) break;
}
return $allResults;
+2
View File
@@ -33,6 +33,7 @@ class kanban extends control
$this->view->browseType = $browseType;
$this->view->pager = $pager;
$this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$this->view->userIdPairs = $this->user->getPairs('noletter|nodeleted|showid');
$this->view->usersAvatar = $this->user->getAvatarPairs();
$this->display();
@@ -1299,6 +1300,7 @@ class kanban extends control
{
$this->view->cards = $this->kanban->getCardsByObject('region', $regionID, 1);
$this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$this->view->userIdPairs = $this->user->getPairs('noletter|nodeleted|showid');
$this->view->usersAvatar = $this->user->getAvatarPairs();
$this->display();
+4 -4
View File
@@ -206,10 +206,10 @@ $lang->kanban->defaultColumn['doing'] = 'doing';
$lang->kanban->defaultColumn['done'] = 'done';
$lang->kanban->defaultColumn['closed'] = 'close';
$this->lang->kanban->laneTypeList = array();
$this->lang->kanban->laneTypeList['story'] = $lang->SRCommon;
$this->lang->kanban->laneTypeList['bug'] = 'Bug';
$this->lang->kanban->laneTypeList['task'] = 'Task';
$lang->kanban->laneTypeList = array();
$lang->kanban->laneTypeList['story'] = $lang->SRCommon;
$lang->kanban->laneTypeList['bug'] = 'Bug';
$lang->kanban->laneTypeList['task'] = 'Task';
$lang->kanban->storyColumn = array();
$lang->kanban->storyColumn['backlog'] = 'Backlog';
+4 -4
View File
@@ -206,10 +206,10 @@ $lang->kanban->defaultColumn['doing'] = '进行中';
$lang->kanban->defaultColumn['done'] = '已完成';
$lang->kanban->defaultColumn['closed'] = '已关闭';
$this->lang->kanban->laneTypeList = array();
$this->lang->kanban->laneTypeList['story'] = $lang->SRCommon;
$this->lang->kanban->laneTypeList['bug'] = 'Bug';
$this->lang->kanban->laneTypeList['task'] = '任务';
$lang->kanban->laneTypeList = array();
$lang->kanban->laneTypeList['story'] = $lang->SRCommon;
$lang->kanban->laneTypeList['bug'] = 'Bug';
$lang->kanban->laneTypeList['task'] = '任务';
$lang->kanban->storyColumn = array();
$lang->kanban->storyColumn['backlog'] = 'Backlog';
+4 -4
View File
@@ -157,10 +157,10 @@ $lang->kanban->error->childLimitNote = '子列在製品數量之和不能大於
$lang->kanban->defaultColumn = array('未開始', '進行中', '已完成', '已關閉');
$this->lang->kanban->laneTypeList = array();
$this->lang->kanban->laneTypeList['story'] = $lang->SRCommon;
$this->lang->kanban->laneTypeList['bug'] = 'Bug';
$this->lang->kanban->laneTypeList['task'] = '任務';
$lang->kanban->laneTypeList = array();
$lang->kanban->laneTypeList['story'] = $lang->SRCommon;
$lang->kanban->laneTypeList['bug'] = 'Bug';
$lang->kanban->laneTypeList['task'] = '任務';
$lang->kanban->storyColumn = array();
$lang->kanban->storyColumn['backlog'] = 'Backlog';
+1 -1
View File
@@ -2036,7 +2036,7 @@ class kanbanModel extends model
$lane->type = $type;
$lane->execution = $executionID;
$this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec();
$laneID = $this->dao->lastInsertId();
$this->createExecutionColumns($laneID, $type, $executionID);
}
+2 -2
View File
@@ -127,13 +127,13 @@
$count ++;
?>
<div title="<?php echo $users[$member];?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member], 'account' => $member)); ?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member], 'account' => $member), 'avatar-circle avatar-' . zget($userIdPairs, $member)); ?>
</div>
<?php endforeach;?>
<?php if($teamCount > 3):?>
<?php if($teamCount > 4) echo '<span>…</span>';?>
<div title="<?php echo $users[end($teamPairs)];?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[end($teamPairs)], 'account' => end($teamPairs))); ?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[end($teamPairs)], 'account' => end($teamPairs)), 'avatar-circle avatar-' . zget($userIdPairs, end($teamPairs))); ?>
</div>
<?php endif;?>
</div>
+1 -1
View File
@@ -109,7 +109,7 @@
foreach($assignedToList as $account)
{
if($count > 2) continue;
echo html::smallAvatar(array('avatar' => $usersAvatar[$account], 'account' => $account));
echo html::smallAvatar(array('avatar' => $usersAvatar[$account], 'account' => $account), 'avatar-circle avatar-' . zget($userIdPairs, $account));
$count ++;
}
?>
+2
View File
@@ -94,6 +94,7 @@ $lang->misc->feature->downloadFile = 'Download introduction';
$lang->misc->feature->tutorialDesc = '<p>ZenTao 15.0 has new functions, and you know how to use it through the "<strong>Tutorial</strong>".</p><p>Click your [<span style="color: #0c60e1">Avatar-Theme-Young Blue</span>] to set it.</p>';
$lang->misc->feature->themeDesc = '<p>ZenTao 15.0+ a new "Youth Blue" theme, the pages are more beautiful and the experience is more friendly.</p><p>Click your [<span style="color: #0c60e1">Avatar-Theme-Young Blue</span>] to set it.</p>';
$lang->misc->releaseDate['16.4'] = '2022-02-15';
$lang->misc->releaseDate['16.3'] = '2022-01-26';
$lang->misc->releaseDate['16.2'] = '2022-01-17';
$lang->misc->releaseDate['16.1'] = '2022-01-11';
@@ -167,6 +168,7 @@ $lang->misc->releaseDate['7.2.stable'] = '2015-05-22';
$lang->misc->releaseDate['7.1.stable'] = '2015-03-07';
$lang->misc->releaseDate['6.3.stable'] = '2014-11-07';
$lang->misc->feature->all['16.4'][] = array('title' => 'Implement JIRA import function and improve plug-in extension mechanism.', 'desc' => '');
$lang->misc->feature->all['16.3'][] = array('title' => 'Kanban adds related plan/release/version/iteration functions, and the detail experience is optimized, fix bugs.', 'desc' => '');
$lang->misc->feature->all['16.2'][] = array('title' => 'Add kanban model project, fix bugs.', 'desc' => '');
$lang->misc->feature->all['16.1'][] = array('title' => 'Plan to add status management and Kanban view, upgrade process optimization, fix bugs.', 'desc' => '');
+2
View File
@@ -94,6 +94,7 @@ $lang->misc->feature->downloadFile = '下载新版本功能介绍文档';
$lang->misc->feature->tutorialDesc = "<p>禅道15系列新增了多项功能,您可以通过“<strong>新手引导教程</strong>”快速了解禅道的基本使用方法。</p><p>通过鼠标经过 [<span style='color: #0c60e1'>头像-新手引导</span>],点击新手引导,即可进入新手引导教程。</p>";
$lang->misc->feature->themeDesc = "<p>禅道15系列上线了全新的“青春蓝”主题,页面呈现更加美观,体验更加友好。</p><p>通过鼠标经过 [<span style='color: #0c60e1'>头像-主题-青春蓝</span>],点击青春蓝,即可设置成功。</p>";
$lang->misc->releaseDate['16.4'] = '2022-02-15';
$lang->misc->releaseDate['16.3'] = '2022-01-26';
$lang->misc->releaseDate['16.2'] = '2022-01-17';
$lang->misc->releaseDate['16.1'] = '2022-01-11';
@@ -167,6 +168,7 @@ $lang->misc->releaseDate['7.2.stable'] = '2015-05-22';
$lang->misc->releaseDate['7.1.stable'] = '2015-03-07';
$lang->misc->releaseDate['6.3.stable'] = '2014-11-07';
$lang->misc->feature->all['16.4'][] = array('title' => '实现JIRA导入功能,完善插件扩展机制。', 'desc' => '');
$lang->misc->feature->all['16.3'][] = array('title' => '看板增加关联计划/发布/版本/迭代功能,细节体验优化。', 'desc' => '');
$lang->misc->feature->all['16.2'][] = array('title' => '新增专业研发看板,可以创建看板模型项目,修复Bug。', 'desc' => '');
$lang->misc->feature->all['16.1'][] = array('title' => '计划增加状态管理和看板视图,升级流程优化,修复Bug。', 'desc' => '');
+2 -2
View File
@@ -163,7 +163,7 @@ class mr extends control
if(!$this->app->user->admin)
{
$groupIDList = array(0 => 0);
$groups = $this->gitlab->apiGetGroups($MR->gitlabID, 'name_asc', $this->config->gitlab->accessLevel['developer']);
$groups = $this->gitlab->apiGetGroups($MR->gitlabID, 'name_asc', 'developer');
foreach($groups as $group) $groupIDList[] = $group->id;
$sourceProject = $this->gitlab->apiGetSingleProject($MR->gitlabID, $MR->sourceProject);
$isDeveloper = $this->gitlab->checkUserAccess($MR->gitlabID, 0, $sourceProject, $groupIDList, 'developer');
@@ -919,7 +919,7 @@ class mr extends control
}
$groupIDList = array(0 => 0);
$groups = $this->gitlab->apiGetGroups($gitlabID, 'name_asc', $this->config->gitlab->accessLevel['developer']);
$groups = $this->gitlab->apiGetGroups($gitlabID, 'name_asc', 'developer');
foreach($groups as $group) $groupIDList[] = $group->id;
foreach($projects as $key => $project)
{
+1 -1
View File
@@ -118,7 +118,7 @@ class mrModel extends model
$groupIDList = array(0 => 0);
if(!$this->app->user->admin)
{
$groups = $this->gitlab->apiGetGroups($gitlabID, 'name_asc', $this->config->gitlab->accessLevel['reporter']);
$groups = $this->gitlab->apiGetGroups($gitlabID, 'name_asc', 'reporter');
foreach($groups as $group) $groupIDList[] = $group->id;
}
$allGroups[$gitlabID] = $groupIDList;
+1 -2
View File
@@ -1,2 +1 @@
.c-hours {width: 80px !important;}
.c-progress {width: 80px;}
.c-hours, .c-progress {width: 80px !important;}
+1 -1
View File
@@ -14,7 +14,7 @@ $(document).ready(function()
{
window.parent.$('#userNav .avatar, #menu-avatar').html('<img src="' + avatar + '"/>');
window.parent.$('#mainContent>.cell>.main-header>.avatar').html('<img src="' + avatar + '"/>');
window.parent.$('#mainContent .avatar-' + account).html('<img src="' + avatar + '"/>');
window.parent.$('#mainContent .avatar-' + userID).html('<img src="' + avatar + '"/>');
}
$('#avatarUploadBtn').tooltip();
+1 -1
View File
@@ -12,7 +12,7 @@
?>
<?php include '../../common/view/header.html.php';?>
<?php js::set('avatar', $this->app->user->avatar);?>
<?php js::set('account', $this->app->user->account);?>
<?php js::set('userID', $this->app->user->id);?>
<div id='mainContent'>
<div class='cell'>
<div class='main-header text-center'>
+19 -4
View File
@@ -366,10 +366,25 @@ function renderKanbanItem(item, $item)
'</div>'
].join('')).appendTo($item);
var $time = $dateBox.children('.time');
if(item.begin != '2030-01-01' && item.end != '2030-01-01')
var $time = $dateBox.children('.time');
var beginTimeShort = $.zui.formatDate(begin, 'MM-dd');
var beginTimeLong = $.zui.formatDate(begin, 'yyyy-MM-dd');
var endTimeShort = $.zui.formatDate(end, 'MM-dd');
var endTimeLong = $.zui.formatDate(end, 'yyyy-MM-dd')
var to = productplanLang.to;
var undetermined = productplanLang.future;
var undetermindeDate = '2030-01-01';
if(item.begin != undetermindeDate && item.end != undetermindeDate)
{
$time.text($.zui.formatDate(begin, 'MM-dd') + ' ' + productplanLang.to + ' ' + $.zui.formatDate(end, 'MM-dd')).attr('title', $.zui.formatDate(begin, 'yyyy-MM-dd') + productplanLang.to + $.zui.formatDate(end, 'yyyy-MM-dd')).show();
$time.text(beginTimeShort + ' ' + to + ' ' + endTimeShort).attr('title', beginTimeLong + to + endTimeLong).show();
}
else if(item.begin != undetermindeDate && item.end == undetermindeDate)
{
$time.text(beginTimeShort + ' ' + to + ' ' + undetermined).attr('title', beginTimeLong + to).show();
}
else if(item.begin == undetermindeDate && item.end != undetermindeDate)
{
$time.text(undetermined + ' ' + to + ' ' + endTimeShort).attr('title', to + endTimeLong).show();
}
else if(item.begin != '2030-01-01' && item.end == '2030-01-01')
{
@@ -381,7 +396,7 @@ function renderKanbanItem(item, $item)
}
else
{
$time.text(productplanLang.future).attr('title', productplanLang.future).show();
$time.text(undetermined).attr('title', undetermined).show();
}
/* Output plan desc information. */
+3 -28
View File
@@ -59,6 +59,7 @@ class program extends control
$this->view->status = $status;
$this->view->orderBy = $orderBy;
$this->view->users = $this->user->getPairs('noletter');
$this->view->userIdPairs = $this->user->getPairs('noletter|showid');
$this->view->usersAvatar = $this->user->getAvatarPairs();
$this->view->programType = $programType;
$this->view->PMList = $PMList;
@@ -489,35 +490,9 @@ class program extends control
* @access public
* @return void
*/
public function createStakeholder($programID = 0, $dept = '', $parentIdList = '')
public function createStakeholder($programID = 0)
{
if($_POST)
{
$this->program->createStakeholder($programID);
die(js::locate($this->createLink('program', 'stakeholder', "programID=$programID"), 'parent'));
}
$this->program->setMenu($programID);
$this->loadModel('user');
$this->lang->program->switcherMenu = $this->program->getSwitcher($programID, true);
$this->loadModel('dept');
$deptUsers = $dept === '' ? array() : $this->dept->getDeptUserPairs($dept);
$this->view->title = $this->lang->program->createStakeholder;
$this->view->position[] = $this->lang->program->createStakeholder;
$this->view->programID = $programID;
$this->view->program = $this->program->getByID($programID);
$this->view->users = $this->loadModel('user')->getPairs('nodeleted|noclosed');
$this->view->deptUsers = $deptUsers;
$this->view->dept = $dept;
$this->view->depts = array('' => '') + $this->dept->getOptionMenu();
$this->view->stakeholders = $this->program->getStakeholders($programID, 't1.id_desc');
$this->view->parentStakeholders = $this->program->getStakeholdersByPrograms($parentIdList);
$this->display();
return print($this->fetch('stakeholder', 'create', "objectID=$programID"));
}
/**
+1 -1
View File
@@ -66,7 +66,7 @@
<td class='c-status'><span class="status-program status-<?php echo $program->status?>"><?php echo zget($lang->project->statusList, $program->status, '');?></span></td>
<td>
<?php if(!empty($program->PM)):?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$program->PM], 'account' => $program->PM)); ?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$program->PM], 'account' => $program->PM), 'avatar-circle avatar-' . zget($userIdPairs, $program->PM)); ?>
<?php $userID = isset($PMList[$program->PM]) ? $PMList[$program->PM]->id : '';?>
<?php $userName = zget($users, $program->PM);?>
<?php echo html::a($this->createLink('user', 'profile', "userID=$userID", '', true), $userName, '', "title='{$userName}' data-toggle='modal' data-type='iframe' data-width='600'");?>
+5 -3
View File
@@ -262,9 +262,10 @@ class project extends control
$this->view->executionActions = $executionActions;
}
$this->view->title = $this->lang->project->common . $this->lang->colon . $this->lang->project->index;
$this->view->position[] = $this->lang->project->index;
$this->view->project = $project;
$this->view->title = $this->lang->project->common . $this->lang->colon . $this->lang->project->index;
$this->view->position[] = $this->lang->project->index;
$this->view->project = $project;
$this->view->userIdPairs = $this->loadModel('user')->getPairs('nodeleted|showid');
$this->display();
}
@@ -308,6 +309,7 @@ class project extends control
$this->view->programTree = $this->project->getTreeMenu(0, array('projectmodel', 'createManageLink'), 0, 'list');
$this->view->programs = array('0' => '') + $this->program->getParentPairs();
$this->view->users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted');
$this->view->userIdPairs = $this->loadModel('user')->getPairs('nodeleted|showid');
$this->view->usersAvatar = $this->user->getAvatarPairs();
$this->view->browseType = $browseType;
$this->view->projectType = $projectType;
+2 -2
View File
@@ -155,13 +155,13 @@
$count ++;
?>
<a href='<?php echo helper::createLink('project', 'team', "projectID=$projectID");?>' title="<?php echo $users[$member];?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member], 'account' => $member)); ?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member], 'account' => $member), 'avatar-circle avatar-' . zget($userIdPairs, $member)); ?>
</a>
<?php endforeach;?>
<?php if($project->teamCount > 3):?>
<?php if($project->teamCount > 4) echo '<span>…</span>';?>
<a href='<?php echo helper::createLink('project', 'team', "projectID=$projectID");?>' title="<?php echo $users[$member];?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[end($project->teamMembers)], 'account' => $member)); ?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[end($project->teamMembers)], 'account' => $member), 'avatar-circle avatar-' . zget($userIdPairs, $member)); ?>
</a>
<?php endif;?>
</div>
+2 -2
View File
@@ -93,7 +93,7 @@
<?php if($count > 2) break;?>
<?php $count ++;?>
<div title="<?php echo $member->realname;?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member->account], 'account' => $member->account)); ?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member->account], 'account' => $member->account), 'avatar-circle avatar-' . zget($userIdPairs, $member->account)); ?>
</div>
<?php endforeach;?>
<?php if(count($members) > 4):?>
@@ -102,7 +102,7 @@
<?php if(count($members) > 3):?>
<?php $lastMember = end($members);?>
<div title="<?php echo $lastMember->realname;?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$lastMember->account], 'account' => $lastMember->account)); ?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$lastMember->account], 'account' => $lastMember->account), 'avatar-circle avatar-' . zget($userIdPairs, $lastMember->account)); ?>
</div>
<?php endif;?>
</div>
+1 -1
View File
@@ -1146,7 +1146,7 @@ class repo extends control
$projects = $this->gitlab->apiGetProjects($gitlabID, $filter ? 'false' : 'true');
$groupIDList = array(0 => 0);
$groups = $this->gitlab->apiGetGroups($gitlabID, 'name_asc', $this->config->gitlab->accessLevel['developer']);
$groups = $this->gitlab->apiGetGroups($gitlabID, 'name_asc', 'developer');
foreach($groups as $group) $groupIDList[] = $group->id;
if($filter == 'IS_DEVELOPER')
{
+1
View File
@@ -32,6 +32,7 @@
.modal-iframe .modal-content .modal-header .modal-title .icon-eye-open {display: none;}
.modal-iframe .modal-content .modal-header .modal-title a.btn {border-color: transparent; background: none; padding: 0px; border-radius: 2px; color: #036; padding: 4px 7px;}
.modal-iframe .modal-content .modal-header .modal-title a.btn .icon-eye-open {display: inline;}
.modal-iframe .modal-header .close {top: 17px !important;}
.input-group-btn.goback {padding-left: 10px;}
.input-group-btn.select {min-width: 60px; margin-left: -2px;}
.input-group .chosen-container {min-width: 250px;}
+3
View File
@@ -0,0 +1,3 @@
.table-report .table-nest-icon {color: #000; font-weight: 700;}
.table-report .label-pri {border: 4px solid #D4333F; height: 25px; width: 25px; border-radius: 25px; margin-left: 5px;}
.table-report .label-pri .label-full {display: block; transform: translate(-20%, 6%); background-color: #D4333F; width: 15px; height: 15px; border-radius: 15px;}
+2
View File
@@ -67,8 +67,10 @@ $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] = "Malformed key for Project Key,Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.";
$lang->sonarqube->errorLang[2] = "Could not create Project, key already exists: %s";
$lang->sonarqube->issue = new stdclass();
$lang->sonarqube->issue->message = 'Issue Name';
+14 -7
View File
@@ -30,26 +30,33 @@
</span>
</div>
</div>
<table class="table table-data">
<table class="table table-data table-report">
<thead>
<tr class="text-center">
<th><?php echo $lang->sonarqube->report->bugs;?></th>
<th><?php echo $lang->sonarqube->report->vulnerabilities;?></th>
<th class="w-150px"><?php echo $lang->sonarqube->report->security_hotspots_reviewed;?></th>
<th><?php echo $lang->sonarqube->report->code_smells;?></th>
<th><?php echo "<span class='table-nest-icon icon icon-bug'></span>" . $lang->sonarqube->report->bugs;?></th>
<th class="w-140px"><?php echo "<span class='table-nest-icon icon icon-unlock'></span>" . $lang->sonarqube->report->vulnerabilities;?></th>
<th class="w-180px"><?php echo "<span class='table-nest-icon icon icon-shield'></span>" . $lang->sonarqube->report->security_hotspots_reviewed;?></th>
<th class="w-130px"><?php echo "<span class='table-nest-icon icon icon-frown'></span>" . $lang->sonarqube->report->code_smells;?></th>
<th><?php echo $lang->sonarqube->report->coverage;?></th>
<th><?php echo $lang->sonarqube->report->duplicated_lines_density;?></th>
<th><?php echo $lang->sonarqube->report->ncloc;?></th>
</tr>
</thead>
<tbody>
<?php
$emptyRing = "<span class='label-pri'></span>";
$fullRing = "<span class='label-pri'><span class='label-full'></span></span>";
$coverageIcon = $measures['coverage'] != '0.0%' ? $fullRing : $emptyRing;
$duplicationIcon = $measures['duplicated_lines_density'] != '0.0%' ? $fullRing : $emptyRing;
?>
<tr class="text-center">
<td><?php echo $measures['bugs'];?></td>
<td><?php echo $measures['vulnerabilities'];?></td>
<td><?php echo $measures['security_hotspots_reviewed'];?></td>
<td><?php echo $measures['code_smells'];?></td>
<td><?php echo $measures['coverage'];?></td>
<td><?php echo $measures['duplicated_lines_density'];?></td>
<td><?php echo $measures['coverage'] . $coverageIcon;?></td>
<td><?php echo $measures['duplicated_lines_density'] . $duplicationIcon;?></td>
<td><?php echo $measures['ncloc'];?></td>
</tr>
</tbody>
+1 -1
View File
@@ -334,7 +334,7 @@ class story extends control
$this->view->users = $users;
$this->view->moduleID = $moduleID ? $moduleID : (int)$this->cookie->lastStoryModule;
$this->view->moduleOptionMenu = $moduleOptionMenu;
$this->view->plans = $this->loadModel('productplan')->getPairsForStory($productID, $branch, 'skipParent|unexpired');
$this->view->plans = str_replace('2030-01-01', $this->lang->story->undetermined, $this->loadModel('productplan')->getPairsForStory($productID, $branch, 'skipParent|unexpired'));
$this->view->planID = $planID;
$this->view->source = $source;
$this->view->sourceNote = $sourceNote;
-2
View File
@@ -11,8 +11,6 @@
*/
global $config;
$lang->story->create = "Story hinzufügen";
$lang->story->createStory = "Story hinzufügen";
$lang->story->createRequirement = 'Create Requirement';
$lang->story->requirement = zget($lang, 'URCommon', "Requirement");
$lang->story->story = zget($lang, 'SRCommon', "Story");
+1 -2
View File
@@ -11,8 +11,6 @@
*/
global $config;
$lang->story->create = "Create Story";
$lang->story->createStory = 'Create Story';
$lang->story->createRequirement = 'Create Requirement';
$lang->story->requirement = zget($lang, 'URCommon', "Requirement");
$lang->story->story = zget($lang, 'SRCommon', "Story");
@@ -68,6 +66,7 @@ $lang->story->viewAll = "See All";
$lang->story->toTask = 'Convert to Task';
$lang->story->batchToTask = 'Batch Convert to Task';
$lang->story->convertRelations = 'Convert Relations';
$lang->story->undetermined = 'undetermined';
$lang->story->editAction = "Edit {$lang->SRCommon}";
$lang->story->changeAction = "Change {$lang->SRCommon}";
-2
View File
@@ -11,8 +11,6 @@
*/
global $config;
$lang->story->create = "Créer Story";
$lang->story->createStory = "Créer Story";
$lang->story->createRequirement = 'Create Requirement';
$lang->story->requirement = zget($lang, 'URCommon', "Requirement");
$lang->story->story = zget($lang, 'SRCommon', "Story");
-2
View File
@@ -11,8 +11,6 @@
*/
global $config;
$lang->story->create = "Create Story";
$lang->story->createStory = 'Create Story';
$lang->story->createRequirement = 'Create Requirement';
$lang->story->requirement = zget($lang, 'UrCommon', "Requirement");
$lang->story->story = zget($lang, 'SrCommon', "Story");
+1 -2
View File
@@ -11,8 +11,6 @@
*/
global $config;
$lang->story->create = "{$lang->SRCommon}";
$lang->story->createStory = "{$lang->SRCommon}";
$lang->story->createRequirement = "{$lang->SRCommon}";
$lang->story->requirement = zget($lang, 'URCommon', "需求");
$lang->story->story = zget($lang, 'SRCommon', "故事");
@@ -68,6 +66,7 @@ $lang->story->viewAll = '查看全部';
$lang->story->toTask = '转任务';
$lang->story->batchToTask = '批量转任务';
$lang->story->convertRelations = '换算关系';
$lang->story->undetermined = '待定';
$lang->story->editAction = "编辑{$lang->SRCommon}";
$lang->story->changeAction = "变更{$lang->SRCommon}";
-2
View File
@@ -11,8 +11,6 @@
*/
global $config;
$lang->story->create = "{$lang->SRCommon}";
$lang->story->createStory = "{$lang->SRCommon}";
$lang->story->createRequirement = "{$lang->SRCommon}";
$lang->story->requirement = zget($lang, 'URCommon', "需求");
$lang->story->story = zget($lang, 'SRCommon', "故事");
+15
View File
@@ -573,4 +573,19 @@ class testreportModel extends model
->andWhere('id')->in(trim($storyIdList, ','))
->fetchAll('id');
}
/**
* Get pairs.
*
* @param int $productID
* @access public
* @return array
*/
public function getPairs($productID = 0)
{
return $this->dao->select('id,title')->from(TABLE_TESTREPORT)
->beginIF($productID)->where('product')->eq($productID)->fi()
->orderBy('id_desc')
->fetchPairs();
}
}
+7 -7
View File
@@ -756,7 +756,7 @@ class testtask extends control
$executions = empty($productID) ? array() : $this->product->getExecutionPairsByProduct($productID, 0, 'id_desc', $projectID);
$executionID = $task->execution;
$builds = empty($productID) ? array() : $this->loadModel('build')->getBuildPairs($productID, 'all', 'noempty,notrunk', $executionID, 'execution');
$testreports = $this->testtask->getTestReportPairsByBuild($task->build);
$testreports = $this->loadModel('testreport')->getPairs($task->product);
$this->view->task = $task;
$this->view->executions = $executions;
@@ -1435,7 +1435,7 @@ class testtask extends control
$testTasks = $this->testtask->getUserTestTaskPairs($account, 0, $status);
if($id) return print(html::select("testtasks[$id]", $testTasks, '', 'class="form-control"'));
return print(html::select('testtask', $testTasks, '', 'class=form-control'));
return print(html::select('testtask', $testTasks, '', 'class="form-control"'));
}
/**
@@ -1453,16 +1453,16 @@ class testtask extends control
}
/**
* Ajax get test report.
* Ajax: Get test report by productID.
*
* @param int $buildID
* @param int $productID
* @access public
* @return void
*/
public function ajaxGetTestReports($buildID)
public function ajaxGetTestReports($productID)
{
/* Testreport list. */
$pairs = $this->testtask->getTestReportPairsByBuild($buildID);
return print(html::select('testreport', $pairs, '', "class='form-control chosen'"));
$pairs = $this->loadModel('testreport')->getPairs($productID);
return print(html::select('testreport', array('') + $pairs, '', "class='form-control chosen'"));
}
}
+3 -3
View File
@@ -106,13 +106,13 @@ function loadExecutionBuilds(executionID, selected)
/**
* Load test report.
*
* @param int buildID
* @param int productID
* @access public
* @return void
*/
function loadTestReports(buildID)
function loadTestReports(productID)
{
link = createLink('testtask', 'ajaxGetTestReports', 'buildID=' + buildID);
link = createLink('testtask', 'ajaxGetTestReports', 'productID=' + productID);
$.get(link, function(data)
{
if(!data) data = '<select id="testreport" name="testreport" class="form-control"></select>';
+3 -1
View File
@@ -7,7 +7,8 @@
function loadProductRelated()
{
loadExecutions($('#product').val());
buildData = '<select id="build" name="build" class="form-control" onchange="loadTestReports(this.value)"></select>';
loadTestReports($('#product').val());
buildData = '<select id="build" name="build" class="form-control"></select>';
$('#build').replaceWith(buildData);
$('#build_chosen').remove();
$("#build").chosen();
@@ -37,5 +38,6 @@ function loadExecutions(productID)
$(function()
{
adjustPriBoxWidth();
loadTestReports($('#product').val());
if($('#execution').val() != 0) loadExecutionBuilds($('#execution').val());
});
+2 -1
View File
@@ -1069,7 +1069,8 @@ class testtaskModel extends model
$testtaskPairs = array();
while($testtask = $stmt->fetch())
{
$testtaskPairs[$testtask->id] = $testtask->execution . ' / ' . $testtask->name;
if($testtask->execution) $testtask->execution .= " / ";
$testtaskPairs[$testtask->id] = $testtask->execution . $testtask->name;
}
return $testtaskPairs;
}
+2 -2
View File
@@ -30,7 +30,7 @@
<?php else:?>
<tr class='hide'>
<th class='w-80px'><?php echo $lang->testtask->product;?></th>
<td class='w-p35-f'><?php echo html::input('product', $productID, "class='form-control'");?></td><td></td>
<td class='w-p35-f'><?php echo html::input('product', $productID, "class='form-control' onchange='loadTestReports(this.value)'");?></td><td></td>
</tr>
<?php endif;?>
<tr>
@@ -41,7 +41,7 @@
<th class='w-80px'><?php echo $lang->testtask->build;?></th>
<td class='w-p35-f'>
<div class='input-group' id='buildBox'>
<?php echo html::select('build', empty($builds) ? '' : $builds, $build, "class='form-control chosen' onchange='loadTestReports(this.value)'");?>
<?php echo html::select('build', empty($builds) ? '' : $builds, $build, "class='form-control chosen'");?>
<?php if(isset($executionID) and $executionID and empty($builds)):?>
<span class='input-group-addon'><?php echo html::a(helper::createLink('build', 'create', "executionID=$executionID&productID=$productID&projectID=$projectID", '', true), $lang->build->create, '', "data-toggle='modal' data-type='iframe' data-width='95%'")?> </span>
</div>
+2 -2
View File
@@ -36,7 +36,7 @@
</tr>
<tr>
<th><?php echo $lang->testtask->build;?></th>
<td class='w-p35-f'><span id='buildBox'><?php echo html::select('build', $builds, $task->build, "class='form-control chosen' onchange='loadTestReports(this.value)'");?></span></td>
<td class='w-p35-f'><span id='buildBox'><?php echo html::select('build', $builds, $task->build, "class='form-control chosen'");?></span></td>
<td></td>
</tr>
<tr>
@@ -69,7 +69,7 @@
</tr>
<tr>
<th><?php echo $lang->testtask->testreport;?></th>
<td><?php echo html::select('testreport', $testreports, $task->testreport, "class='form-control chosen'");?></td>
<td><?php echo html::select('testreport', array('') + $testreports, $task->testreport, "class='form-control chosen'");?></td>
</tr>
<tr>
<th><?php echo $lang->testtask->name;?></th>
+1
View File
@@ -151,3 +151,4 @@ $lang->upgrade->fromVersions['16_0_beta1'] = '16.0.beta1';
$lang->upgrade->fromVersions['16_0'] = '16.0';
$lang->upgrade->fromVersions['16_1'] = '16.1';
$lang->upgrade->fromVersions['16_2'] = '16.2';
$lang->upgrade->fromVersions['16_3'] = '16.3';
+5
View File
@@ -756,6 +756,10 @@ class upgradeModel extends model
$this->updateSpaceTeam();
$this->updateDocField();
$this->appendExec('16_2');
case '16_3':
$this->saveLogs('Execute 16_3');
$this->execSQL($this->getUpgradeFile('16.3'));
$this->appendExec('16_3');
}
$this->deletePatch();
@@ -966,6 +970,7 @@ class upgradeModel extends model
case '16_0': $confirmContent .= file_get_contents($this->getUpgradeFile('16.0'));
case '16_1': $confirmContent .= file_get_contents($this->getUpgradeFile('16.1'));
case '16_2': $confirmContent .= file_get_contents($this->getUpgradeFile('16.2'));
case '16_3': $confirmContent .= file_get_contents($this->getUpgradeFile('16.3'));
}
return str_replace('zt_', $this->config->db->prefix, $confirmContent);
}
+10 -3
View File
@@ -119,9 +119,16 @@ class userModel extends model
/* Cycle the user records to append the first letter of his account. */
foreach($users as $account => $user)
{
$firstLetter = ucfirst(substr($user->account, 0, 1)) . ':';
if(strpos($params, 'noletter') !== false or !empty($this->config->isINT)) $firstLetter = '';
$users[$account] = $firstLetter . (($user->deleted and strpos($params, 'realname') === false) ? $user->account : ($user->realname ? $user->realname : $user->account));
if(strpos($params, 'showid') !== false)
{
$users[$account] = $user->id;
}
else
{
$firstLetter = ucfirst(substr($user->account, 0, 1)) . ':';
if(strpos($params, 'noletter') !== false or !empty($this->config->isINT)) $firstLetter = '';
$users[$account] = $firstLetter . (($user->deleted and strpos($params, 'realname') === false) ? $user->account : ($user->realname ? $user->realname : $user->account));
}
}
/* Append empty, closed, and guest users. */
+5 -5
View File
@@ -21,22 +21,22 @@ if(!$result) $result = 'return null';
r($result) && p() && e('return null'); //使用空的数据查询群组
$gitlabID = 1;
$result = $gitlab->apiGetGroups($gitlabID, $orderBy, $minRole);
$result = $gitlab->apiGetGroups($gitlabID, $orderBy, $minRole);
if($result) $result = 'return true';
r($result) && p() && e('return true'); //使用正确的gitlabID查询群组
$orderBy = 'abc';
$result = $gitlab->apiGetGroups($gitlabID, $orderBy, $minRole);
$result = $gitlab->apiGetGroups($gitlabID, $orderBy, $minRole);
if(is_array($result)) $result = 'return true';
r($result) && p() && e('return true'); //使用错误的orderBy查询群组
$orderBy = 'name_asc';
$minRole = 'owner';
$result = $gitlab->apiGetGroups($gitlabID, $orderBy, $minRole);
$result = $gitlab->apiGetGroups($gitlabID, $orderBy, $minRole);
if(is_array($result)) $result = 'return true';
r($result) && p() && e('return true'); //通过gitlabID,orderBy查询群组
$minRole = 'abc';
$result = $gitlab->apiGetGroups($gitlabID, $orderBy, $minRole);
if(is_array($result) && empty($result)) $result = 'return true';
$result = $gitlab->apiGetGroups($gitlabID, $orderBy, $minRole);
if(is_array($result)) $result = 'return true';
r($result) && p() && e('return true'); //通过错误的minRole查询群组
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
/**
title=测试 sonarqubeModel::apiCreateProject();
cid=1
pid=1
使用空的sonarqubeID、项目对象创建sonarqube项目 >> return false
使用空的sonarqubeID、正确的项目对象创建sonarqube项目 >> return false
使用正确的sonarqubeID,项目对象创建sonarqube项目 >> unit_test
使用重复的项目对象创建sonarqube项目 >> Could not create Project, key already exists: unit_test
*/
$sonarqube = $tester->loadModel('sonarqube');
$sonarqubeID = 0;
$project = new stdclass();
$result = $sonarqube->apiCreateProject($sonarqubeID, $project);
if($result === false) $result = 'return false';
r($result) && p() && e('return false'); //使用空的sonarqubeID、项目对象创建sonarqube项目
$project->projectName = 'unit_test';
$project->projectKey = 'unit_test';
$result = $sonarqube->apiCreateProject($sonarqubeID, $project);
if($result === false) $result = 'return false';
r($result) && p() && e('return false'); //使用空的sonarqubeID、正确的项目对象创建sonarqube项目
$sonarqubeID = 2;
$sonarqube->apiDeleteProject($sonarqubeID, $project->projectKey);
$result = $sonarqube->apiCreateProject($sonarqubeID, $project);
r($result) && p('project:key') && e('unit test'); //使用正确的sonarqubeID,项目对象创建sonarqube项目
$result = $sonarqube->apiCreateProject($sonarqubeID, $project);
r($result->errors) && p('0:msg') && e("Could not create Project, key already exists: unit_test"); //使用重复的项目对象创建sonarqube项目
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
/**
title=测试 sonarqubeModel::apiGetIssues();
cid=1
pid=1
通过sonarqubeID,获取SonarQube问题列表 >> 1
通过sonarqubeID,项目key获取SonarQube问题列表 >> 1
当sonarqubeID为0时,获取SonarQube问题列表 >> return empty
*/
$sonarqube = $tester->loadModel('sonarqube');
$sonarqubeID = 2;
$result = $sonarqube->apiGetIssues($sonarqubeID);
r(isset($result[0]->message)) && p() && e('1'); //通过sonarqubeID,获取SonarQube问题列表
$projectKey = 'zentaopms';
$result = $sonarqube->apiGetIssues($sonarqubeID, $projectKey);
r(isset($result[0]->message)) && p() && e('1'); //通过sonarqubeID,项目key获取SonarQube问题列表
$sonarqubeID = 0;
$result = $sonarqube->apiGetIssues($sonarqubeID);
if(empty($result)) $result = 'return empty';
r($result) && p() && e('return empty'); //当sonarqubeID为0时,获取SonarQube问题列表
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
/**
title=测试 sonarqubeModel::createProject();
cid=1
pid=1
使用空的项目数据,空的sonarqubeID创建Sonarqube项目 >> 『项目名称』不能为空
使用错误项目数据,正确的sonarqubeID创建Sonarqube项目 >> return error
使用正确的项目数据,空的sonarqubeID创建Sonarqube项目 >> return false
使用正确的sonarqubeID和Sonarqube项目信息创建Sonarqube项目 >> 1
*/
$sonarqube = $tester->loadModel('sonarqube');
$sonarqubeID = 0;
$_POST['projectName'] = '';
$_POST['projectKey'] = '';
$sonarqube->createProject($sonarqubeID);
r(dao::getError()) && p('projectName:0') && e('『项目名称』不能为空'); //使用空的项目数据,空的sonarqubeID创建Sonarqube项目
$sonarqubeID = 2;
$_POST['projectName'] = 'unit_test';
$_POST['projectKey'] = '@#';
$sonarqube->createProject($sonarqubeID);
$errors = dao::getError();
if(array_shift($errors) == "项目标识的格式不正确。允许的字符为字母、数字、'-'、''、'.'和“:”,至少有一个非数字。") $result = 'return error';
r($result) && p() && e("return error"); //使用错误项目数据,正确的sonarqubeID创建Sonarqube项目
$sonarqubeID = 0;
$_POST['projectName'] = 'unit_test';
$_POST['projectKey'] = 'unit_test';
$result = $sonarqube->createProject($sonarqubeID);
if(empty($result)) $result = 'return false';
r($result) && p() && e('return false'); //使用正确的项目数据,空的sonarqubeID创建Sonarqube项目
dao::getError();
$sonarqubeID = 2;
$sonarqube->apiDeleteProject($sonarqubeID, $_POST['projectKey']);
$result = $sonarqube->createProject($sonarqubeID);
r($result) && p() && e(1); //使用正确的sonarqubeID和Sonarqube项目信息创建Sonarqube项目
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
/**
title=测试 sonarqubeModel::getCacheFile();
cid=1
pid=1
使用正确的sonarqubeID,项目key获取缓存文件 >> 1
*/
$sonarqube = $tester->loadModel('sonarqube');
$sonarqubeID = 2;
$projectKey = 'unit_test';
$result = $sonarqube->getCacheFile($sonarqubeID, $projectKey);
r(strPos($result, '/' . $sonarqubeID . '-' ) !== false) && p('') && e(1); //使用正确的sonarqubeID,项目key获取缓存文件
+1 -1
View File
@@ -84,4 +84,4 @@ a.showMoreImage:hover {opacity: 1;}
#headerActions .dropdown-menu {top: 35px; z-index: 2000;}
#headerActions .dropdown-menu:before, #headerActions .dropdown-menu:hover:before {position: absolute; top: -9px; right: 50%; width: 0; height: 0; content: ' '; border-color: transparent transparent #fff transparent; border-style: solid; border-width: 0 10px 10px 10px;}
.icon-size-width:before {content: "\e9c5"; transform: rotate(90deg); -ms-transform: rotate(90deg); -moz-transform: rotate(90deg); -webkit-transform: rotate(90deg); -o-transform: rotate(90deg);
.icon-size-width:before {content: "\e9c5"; transform: rotate(90deg); -ms-transform: rotate(90deg); -moz-transform: rotate(90deg); -webkit-transform: rotate(90deg); -o-transform: rotate(90deg)};
File diff suppressed because one or more lines are too long
Binary file not shown.
+6
View File
@@ -222,9 +222,15 @@
<glyph unicode="&#xe9c7;" glyph-name="publish, send" d="M450.158-149.754c-2.8 0-5.7 0.5-8.3 1.5-9.799 3.4-16.599 12.299-17.299 22.599l-13.8 259.386-375.179 113.693c-20.199 5.9-34.398 23.998-35.498 44.997-1.2 21.099 10.699 40.697 30.099 49.297l958.646 391.278c3.1 1.3 6.399 1.9 9.699 1.9 5.9 0 11.699-2.1 16.199-5.9 7.2-6 10.599-15.499 8.799-24.699l-155.991-797.455c-2.4-11.3-8.599-21.499-17.599-28.898-8.9-7.299-19.999-11.3-31.499-11.3-10.2 0-20.099 3.1-28.398 9l-188.089 130.493-132.592-147.292c-4.9-5.5-11.899-8.599-19.199-8.599zM955.13 621.803l-479.673-465.474 342.081-237.586 137.593 703.060zM463.557 102.332l8.599-163.591 87.495 96.994-96.094 66.596zM902.532 642.302l-848.953-346.681 374.679-113.493 474.273 460.174z" />
<glyph unicode="&#xe9c8;" glyph-name="college" d="M528-206.629c-0.441-0.027-0.953-0.041-1.472-0.041-5.431 0-10.469 1.691-14.613 4.576l0.085-0.055-432.64 245.12c-4.754 2.64-8.64 6.32-11.445 10.752l-0.075 0.128c-21.561 36.718-34.297 80.871-34.297 128s12.736 91.282 34.951 129.207l-0.654-1.209c3.001 3.835 6.608 7.033 10.704 9.502l0.176 0.098 433.28 247.68c4.373 2.715 9.678 4.325 15.36 4.325s10.987-1.609 15.486-4.398l-0.126 0.073 432.64-245.12c7.451-3.87 12.878-10.763 14.686-19.015l0.034-0.185c0.549-2.126 0.864-4.565 0.864-7.077 0-5.931-1.753-11.454-4.773-16.075l0.069 0.112c-16.869-27.993-26.848-61.792-26.848-97.92s9.979-69.927 27.33-98.789l-0.485 0.869c2.544-4.377 4.046-9.632 4.046-15.239 0-2.542-0.309-5.010-0.889-7.371l0.043 0.21c-2.315-8.039-7.602-14.576-14.565-18.48l-0.155-0.080-431.36-245.12c-4.306-2.809-9.577-4.48-15.239-4.48-0.041 0-0.085 0-0.128 0h0.007zM117.12 90.971l410.88-232.32 392.32 222.080c-13.55 29.947-21.445 64.933-21.445 101.76s7.895 71.813 22.085 103.353l-0.64-1.593-392.32 222.080-410.88-232.32c-14.615-26.448-23.216-57.979-23.216-91.52s8.601-65.072 23.717-92.512l-0.501 0.992zM501.12-191.269c-21.934 36.624-34.903 80.798-34.903 128s12.969 91.376 35.541 129.152l-0.638-1.152 51.84-32.64-25.6 16.64 25.6-16c-16.869-27.993-26.848-61.792-26.848-97.92s9.979-69.927 27.33-98.789l-0.485 0.869zM528 247.131c-0.327-0.011-0.709-0.018-1.093-0.018-5.472 0-10.61 1.431-15.061 3.938l0.153-0.080-432.64 245.76c-9.287 5.074-15.481 14.775-15.481 25.92s6.194 20.846 15.328 25.842l0.153 0.078 432.64 245.76c4.446 2.418 9.737 3.84 15.36 3.84s10.914-1.422 15.531-3.925l-0.171 0.085 433.28-245.76c9.287-5.074 15.481-14.775 15.481-25.92s-6.194-20.846-15.328-25.842l-0.153-0.078-432.64-245.76c-4.457-2.37-9.737-3.785-15.344-3.84h-0.016zM156.16 522.971l371.84-210.56 371.2 210.56-371.2 209.92zM501.12 36.571c-21.561 36.718-34.297 80.871-34.297 128s12.736 91.282 34.951 129.207l-0.654-1.207 51.84-32.64c-16.869-27.993-26.848-61.792-26.848-97.92s9.979-69.927 27.33-98.789l-0.485 0.869zM528 20.571c-0.327-0.011-0.709-0.018-1.093-0.018-5.472 0-10.61 1.431-15.061 3.938l0.153-0.080-432.64 245.12c-4.894 2.699-8.821 6.626-11.445 11.371l-0.075 0.149c-21.934 36.624-34.903 80.798-34.903 128s12.969 91.376 35.541 129.152l-0.638-1.152 51.84-32.64c-16.171-27.538-25.723-60.654-25.723-96s9.552-68.462 26.215-96.905l-0.491 0.905 410.88-232.32 392.32 222.72c-13.465 29.824-21.312 64.656-21.312 101.321 0 47.92 13.401 92.709 36.658 130.825l-0.626-1.106 25.6-16 26.24-16.64c-16.752-27.918-26.656-61.602-26.656-97.6s9.906-69.682 27.143-98.473l-0.485 0.873c2.562-4.498 4.071-9.883 4.071-15.621 0-2.631-0.318-5.191-0.917-7.639l0.046 0.219c-2.715-7.842-7.886-14.254-14.576-18.473l-0.144-0.087-434.56-240c-4.457-2.37-9.737-3.785-15.344-3.84h-0.016z" />
<glyph unicode="&#xe9c9;" glyph-name="tree" d="M783.988 725.523h-350.156v-62.111h1.034v-339.283h-192.268c2.628-10.143 3.955-20.578 3.948-31.056 0-10.727-1.373-21.134-3.948-31.056h192.268v-343.422h-1.034v-62.111h351.033c-2.879 9.759-4.337 19.881-4.33 30.055 0 11.174 1.735 21.943 4.941 32.057h-288.499v343.424h288.186c-3.077 10.065-4.636 20.532-4.628 31.056v0c-0.008 10.524 1.552 20.991 4.628 31.056h-288.186v339.283h289.551c-3.977 11.308-6.004 23.209-5.994 35.196 0 9.298 1.203 18.318 3.453 26.913v0zM242.598 324.13c-13.768 53.047-61.971 92.218-119.326 92.218-68.082 0-123.273-55.191-123.273-123.273s55.191-123.273 123.273-123.273c57.354 0 105.558 39.169 119.326 92.218v0c2.628 10.143 3.955 20.578 3.948 31.056 0 10.725-1.372 21.131-3.948 31.054v0zM784.866-143.516c12.957-43.884 53.554-75.913 101.637-75.913 58.525 0 105.967 47.443 105.967 105.967s-47.443 105.967-105.967 105.967c-47.349 0-87.441-31.057-101.026-73.91v0c-3.284-10.368-4.95-21.181-4.941-32.057v-0.003c-0.007-10.174 1.452-20.296 4.331-30.054l-0.001 0.003zM785.163 262.019c13.275-43.37 53.622-74.912 101.339-74.912 58.525 0 105.967 47.443 105.967 105.967s-47.443 105.967-105.967 105.967c-47.717 0-88.064-31.542-101.339-74.912v0c-3.077-10.065-4.636-20.532-4.628-31.056v0c-0.008-10.524 1.552-20.99 4.628-31.054v0zM783.988 725.523v0c-2.298-8.787-3.459-17.833-3.453-26.915v-0.005c-0.010-11.987 2.017-23.888 5.995-35.196 14.513-41.222 53.789-70.771 99.974-70.771 58.525 0 105.967 47.443 105.967 105.967s-47.443 105.967-105.967 105.967c-49.223 0-90.604-33.563-102.514-79.052l-0.002 0.004z" />
<glyph unicode="&#xe9ca;" glyph-name="shield" d="M498.592 802.026c8.616 3.393 18.198 3.393 26.811 0l402.286-158.475c14.609-5.755 23.914-20.193 23.12-35.874l-15.913-314.552c-7.393-146.117-81.65-280.697-201.325-364.862l-200.533-141.034c-12.624-8.877-29.462-8.877-42.083 0.005l-200.521 141.061c-119.621 84.151-193.858 218.679-201.271 364.745l-15.974 314.634c-0.797 15.683 8.507 30.125 23.12 35.881l402.282 158.472zM861.846 296.821l14.586 288.307-364.433 143.563v-866.839l179.496 126.237c101.263 71.216 164.096 185.088 170.351 308.729v0.002z" />
<glyph unicode="&#xe9cb;" glyph-name="list" d="M345.841 658.312h629.406c26.925 0 48.753 21.828 48.753 48.753s-21.828 48.753-48.753 48.753h-629.406c-26.925 0-48.753-21.828-48.753-48.753s21.828-48.753 48.753-48.753zM345.827 269.482h627.096c26.925 0 48.753 21.828 48.753 48.753s-21.828 48.753-48.753 48.753h-627.096c-26.925 0-48.753-21.828-48.753-48.753s21.828-48.753 48.753-48.753zM975.247-23.998h-629.406c-26.925 0-48.753-21.828-48.753-48.753s21.828-48.753 48.753-48.753h629.406c26.925 0 48.753 21.828 48.753 48.753s-21.828 48.753-48.753 48.753zM0 707.065v0c0-53.851 43.655-97.506 97.506-97.506s97.506 43.655 97.506 97.506v0c0 53.851-43.655 97.506-97.506 97.506s-97.506-43.655-97.506-97.506v0zM0 318.818v0c0-53.851 43.655-97.506 97.506-97.506s97.506 43.655 97.506 97.506v0c0 53.851-43.655 97.506-97.506 97.506s-97.506-43.655-97.506-97.506v0zM0-72.476v0c0-53.851 43.655-97.506 97.506-97.506s97.506 43.655 97.506 97.506v0c0 53.851-43.655 97.506-97.506 97.506s-97.506-43.655-97.506-97.506v0z" />
<glyph unicode="&#xe9cc;" glyph-name="gantt" d="M217.356 638.873h424.226c29.549 0 44.324-14.774 44.324-44.324v-81.392c0-29.549-14.774-44.324-44.324-44.324h-424.226c-29.549 0-44.324 14.774-44.324 44.324v81.392c0 29.549 14.774 44.324 44.324 44.324zM217.356 119.902h424.226c29.549 0 44.324-14.774 44.324-44.324v-81.392c0-29.549-14.774-44.324-44.324-44.324h-424.226c-29.549 0-44.324 14.774-44.324 44.324v81.392c0 29.549 14.774 44.324 44.324 44.324zM440.436 379.393h357.253c32.723 0 49.085-16.362 49.085-49.085v-71.87c0-32.723-16.362-49.085-49.085-49.085h-357.253c-32.723 0-49.085 16.362-49.085 49.085v71.87c0 32.723 16.362 49.085 49.085 49.085v0zM927.656-219.429h-831.3c-53.19 0.062-96.293 43.166-96.356 96.356v831.286c0.061 53.19 43.163 96.294 96.353 96.358h831.302c53.19-0.062 96.293-43.166 96.356-96.356v-831.286c-0.061-53.19-43.163-96.294-96.353-96.359h-0.002zM96.356 725.219v0c-9.391 0-17.004-7.613-17.004-17.004 0 0 0 0 0 0v-831.289c0-9.391 7.613-17.004 17.004-17.004h831.3c9.391 0 17.004 7.613 17.004 17.004v831.289c0 9.391-7.613 17.004-17.004 17.004h-831.3z" />
<glyph unicode="&#xe9cd;" glyph-name="group-view" d="M101.777 625.942h90.353c25.963 0 46.734 20.771 46.734 46.734s-20.771 46.734-46.734 46.734h-90.353c-56.081 0-101.777-45.696-101.777-101.777v-595.083c0-56.081 45.696-101.777 101.777-101.777h90.353c25.963 0 46.734 20.771 46.734 46.734s-20.771 46.734-46.734 46.734h-90.353c-5.193 0-8.308 4.154-8.308 8.308v250.288h98.661c25.963 0 46.734 20.771 46.734 46.734s-20.771 46.734-46.734 46.734h-98.661v251.327c0 4.154 3.116 8.308 8.308 8.308zM981.42 444.198h-638.702c-23.886 0-43.619-19.732-43.619-43.619v-163.051c0-23.886 19.732-43.619 43.619-43.619h637.663c23.886 0 43.619 19.732 43.619 43.619v163.051c0 23.886-18.694 43.619-42.58 43.619v0zM421.647 287.378c0-9.347-8.308-16.617-18.694-16.617h-2.077c-9.347 0-16.617 7.27-16.617 16.617v64.389c0 9.347 7.27 16.617 16.617 16.617h2.077c10.385 0 18.694-7.27 18.694-16.617v-64.389zM940.917 311.265c0-22.848-17.655-40.503-40.503-40.503h-352.065c-22.848 0-40.503 17.655-40.503 40.503v16.617c0 22.848 17.655 40.503 40.503 40.503h352.065c22.848 0 40.503-17.655 40.503-40.503v-16.617zM981.42 804.571h-638.702c-23.886 0-43.619-19.732-43.619-43.619v-163.051c0-23.886 19.732-43.619 43.619-43.619h637.663c23.886 0 43.619 19.732 43.619 43.619v164.089c0 23.886-18.694 42.58-42.58 42.58zM421.647 647.752c0-9.347-8.308-16.617-18.694-16.617h-2.077c-9.347 0-16.617 7.27-16.617 16.617v64.389c0 9.347 7.27 16.617 16.617 16.617h2.077c10.385 0 18.694-7.27 18.694-16.617v-64.389zM940.917 671.638c0-22.848-17.655-40.503-40.503-40.503h-352.065c-22.848 0-40.503 17.655-40.503 40.503v16.617c0 22.848 17.655 40.503 40.503 40.503h352.065c22.848 0 40.503-17.655 40.503-40.503v-16.617zM981.42 82.786h-638.702c-23.886 0-43.619-19.732-43.619-43.619v-163.051c0-23.886 19.732-43.619 43.619-43.619h637.663c23.886 0 43.619 19.732 43.619 43.619v164.089c0 23.886-18.694 42.58-42.58 42.58zM421.647-74.033c0-9.347-8.308-16.617-18.694-16.617h-2.077c-9.347 0-16.617 7.27-16.617 16.617v64.389c0 9.347 7.27 16.617 16.617 16.617h2.077c10.385 0 18.694-7.27 18.694-16.617v-64.389zM940.917-50.147c0-22.848-17.655-40.503-40.503-40.503h-352.065c-22.848 0-40.503 17.655-40.503 40.503v16.617c0 22.848 17.655 40.503 40.503 40.503h352.065c22.848 0 40.503-17.655 40.503-40.503v-16.617z" />
<glyph unicode="&#xe9ce;" glyph-name="meh" d="M731.429 182.857c0-20-16.571-36.571-36.571-36.571h-365.714c-20 0-36.571 16.571-36.571 36.571s16.571 36.571 36.571 36.571h365.714c20 0 36.571-16.571 36.571-36.571zM438.857 438.857c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM731.429 438.857c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM877.714 292.571c0 201.714-164 365.714-365.714 365.714s-365.714-164-365.714-365.714 164-365.714 365.714-365.714 365.714 164 365.714 365.714zM950.857 292.571c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
<glyph unicode="&#xe9cf;" glyph-name="frown" d="M721.143 102.285c6.286-19.429-4.571-39.429-23.429-45.714-19.429-6.286-40 4.571-46.286 24-18.857 61.143-75.429 102.286-139.429 102.286s-120.571-41.143-139.429-102.286c-6.286-19.429-26.857-30.286-45.714-24-19.429 6.286-30.286 26.286-24 45.714 28.571 92 112.571 153.714 209.143 153.714s180.571-61.714 209.143-153.714zM438.857 438.857c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM731.429 438.857c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM877.714 292.571c0 201.714-164 365.714-365.714 365.714s-365.714-164-365.714-365.714 164-365.714 365.714-365.714 365.714 164 365.714 365.714zM950.857 292.571c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
<glyph unicode="&#xe9d0;" glyph-name="smile" d="M721.143 190.285c-28.571-92-112.571-153.714-209.143-153.714s-180.571 61.714-209.143 153.714c-6.286 19.429 4.571 39.429 24 45.714 18.857 6.286 39.429-4.571 45.714-24 18.857-61.143 75.429-102.286 139.429-102.286s120.571 41.143 139.429 102.286c6.286 19.429 26.857 30.286 46.286 24 18.857-6.286 29.714-26.286 23.429-45.714zM438.857 438.857c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM731.429 438.857c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM877.714 292.571c0 201.714-164 365.714-365.714 365.714s-365.714-164-365.714-365.714 164-365.714 365.714-365.714 365.714 164 365.714 365.714zM950.857 292.571c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
<glyph unicode="&#xe9d1;" glyph-name="unlock-solid" d="M365.714 606.040v-20.898c0-20.198-16.374-36.571-36.571-36.571s-36.571 16.374-36.571 36.571v20.898c0 89.447 72.512 161.959 161.96 161.959h114.937c89.45 0 161.96-72.512 161.96-161.959v-167.184h120.163c54.824 0 99.266-44.442 99.266-99.266v-423.183c0-54.824-44.442-99.266-99.266-99.266h-679.183c-54.822 0-99.265 44.442-99.265 99.266v423.183c0 54.824 44.443 99.266 99.265 99.266h485.877v167.184c0 49.053-39.763 88.816-88.817 88.816h-114.937c-49.053 0-88.817-39.763-88.817-88.816zM512 256c-20.198 0-36.571-16.373-36.571-36.571v-182.857c0-20.198 16.373-36.571 36.571-36.571s36.571 16.373 36.571 36.571v182.857c0 20.198-16.373 36.571-36.571 36.571z" />
<glyph unicode="&#xe9d2;" glyph-name="lock-solid" d="M585.143 768c80.79 0 146.286-65.494 146.286-146.286v-182.857h109.714c60.595 0 109.714-49.119 109.714-109.714v-402.286c0-60.595-49.119-109.714-109.714-109.714h-658.286c-60.594 0-109.714 49.119-109.714 109.714v402.286c0 60.595 49.121 109.714 109.714 109.714h109.714v182.857c0 80.791 65.494 146.286 146.286 146.286h146.286zM512 256c-20.199 0-36.571-16.374-36.571-36.571v0-182.857c0-20.199 16.373-36.571 36.571-36.571s36.571 16.374 36.571 36.571v0 182.857c0 20.199-16.374 36.571-36.571 36.571zM365.714 621.714v-182.857h292.571v182.857c0 40.395-32.746 73.143-73.143 73.143h-146.286c-40.397 0-73.143-32.747-73.143-73.143z" />
<glyph unicode="&#xf016;" glyph-name="file, file-empty" horiz-adv-x="878" d="M838.857 587.428c21.143-21.143 38.857-63.429 38.857-93.714v-658.286c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v914.286c0 30.286 24.571 54.857 54.857 54.857h512c30.286 0 72.571-17.714 93.714-38.857zM585.143 726.857v-214.857h214.857c-3.429 9.714-8.571 19.429-12.571 23.429l-178.857 178.857c-4 4-13.714 9.143-23.429 12.571zM804.571-146.286v585.143h-237.714c-30.286 0-54.857 24.571-54.857 54.857v237.714h-438.857v-877.714h731.429z" />
<glyph unicode="&#xf02d;" glyph-name="book" d="M948.764 510.653c13.529-19.482 17.318-44.918 9.741-69.81l-148.822-490.3c-13.529-45.999-61.152-81.716-107.693-81.716h-499.501c-55.199 0-114.186 43.835-134.21 100.116-8.658 24.352-8.658 48.165-1.083 68.728 1.083 10.823 3.248 21.647 3.789 34.635 0.541 8.658-4.329 15.694-3.248 22.188 2.165 12.988 13.529 22.188 22.188 36.799 16.235 27.058 34.635 70.893 40.587 99.033 2.706 10.281-2.706 22.188 0 31.388 2.706 10.281 12.988 17.859 18.401 27.6 14.612 24.894 33.554 73.058 36.259 98.493 1.083 11.364-4.329 23.811-1.083 32.471 3.789 12.447 15.694 17.859 23.811 28.682 12.988 17.859 34.635 69.269 37.881 97.952 1.083 9.199-4.329 18.401-2.706 28.14 2.165 10.281 15.153 21.106 23.811 33.554 22.728 33.554 27.058 107.693 95.787 88.211l-0.541-1.623c9.199 2.165 18.401 4.871 27.6 4.871h411.831c25.434 0 48.165-11.364 61.694-30.306 14.070-19.482 17.318-44.918 9.741-70.352l-148.281-490.3c-25.434-83.34-39.505-101.74-108.235-101.74h-470.277c-7.035 0-15.694-1.623-20.565-8.117-4.329-6.495-4.871-11.364-0.541-23.269 10.823-31.388 48.165-37.881 77.93-37.881h499.501c20.024 0 43.293 11.364 49.247 30.846l162.352 534.135c3.248 10.281 3.248 21.106 2.706 30.846 12.447-4.871 23.811-12.447 31.929-23.269zM372.959 509.57c-3.248-9.741 2.165-17.318 11.905-17.318h329.032c9.199 0 19.482 7.576 22.728 17.318l11.364 34.635c3.248 9.741-2.165 17.318-11.905 17.318h-329.032c-9.199 0-19.482-7.576-22.728-17.318zM328.043 371.031c-3.248-9.741 2.165-17.318 11.905-17.318h329.032c9.199 0 19.482 7.576 22.728 17.318l11.364 34.635c3.248 9.741-2.165 17.318-11.905 17.318h-329.032c-9.199 0-19.482-7.576-22.728-17.318z" />
<glyph unicode="&#xf064;" glyph-name="share" d="M960.285 412.526c0-8.506-3.502-16.511-9.505-22.514l-256.163-256.163c-6.003-6.003-14.008-9.505-22.514-9.505-17.512 0-32.020 14.509-32.020 32.020v128.082h-112.072c-215.638 0-357.227-41.527-357.227-280.178 0-20.513 1.001-41.025 2.501-61.54 0.5-8.005 2.501-17.011 2.501-25.015 0-9.505-6.003-17.512-16.010-17.512-7.004 0-10.506 3.502-14.008 8.506-7.505 10.506-13.009 26.518-18.511 38.025-28.517 64.041-63.54 155.598-63.54 225.643 0 56.036 5.504 113.572 26.518 166.606 69.545 172.609 273.673 201.628 437.778 201.628h112.072v128.082c0 17.512 14.509 32.020 32.020 32.020 8.506 0 16.511-3.502 22.514-9.505l256.163-256.163c6.003-6.003 9.505-14.008 9.505-22.514z" />

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.
Binary file not shown.