* Refactor repo pages.

This commit is contained in:
zhaoke
2023-07-31 17:10:41 +08:00
parent 9894d392cc
commit 6dca989859
56 changed files with 4542 additions and 441 deletions
+3
View File
@@ -478,3 +478,6 @@ $config->featureGroup->other = array('devops', 'kanban');
$config->bi = new stdclass();
$config->bi->pickerHeight = 150;
$config->hasDropmenuApps = array('program', 'project', 'product', 'execution', 'qa', 'admin');
$config->excludeDropmenuList = array('program-browse', 'product-all', 'product-index', 'execution-all', 'project-browse', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create');
+2
View File
@@ -498,6 +498,8 @@ $lang->pager->totalCount = "Total: <strong>{recTotal}</strong> items";
$lang->pager->pageSize = "<strong>{recPerPage}</strong> per page";
$lang->pager->itemsRange = "From <strong>{start}</strong> to <strong>{end}</strong>";
$lang->pager->pageOfTotal = "Page <strong>{page}</strong> of <strong>{totalPage}</strong>";
$lang->pager->totalCountAB = "Total: {recTotal} items";
$lang->pager->pageSizeAB = "{recPerPage} per page";
$lang->colorPicker = new stdclass();
$lang->colorPicker->errorTip = 'Not a valid color value';
+2
View File
@@ -512,6 +512,8 @@ $lang->pager->totalCount = "Total: <strong>{recTotal}</strong> items";
$lang->pager->pageSize = "<strong>{recPerPage}</strong> per page";
$lang->pager->itemsRange = "From <strong>{start}</strong> to <strong>{end}</strong>";
$lang->pager->pageOfTotal = "Page <strong>{page}</strong> of <strong>{totalPage}</strong>";
$lang->pager->totalCountAB = "Total: {recTotal} items";
$lang->pager->pageSizeAB = "{recPerPage} per page";
$lang->colorPicker = new stdclass();
$lang->colorPicker->errorTip = 'Not a valid color value';
+2
View File
@@ -498,6 +498,8 @@ $lang->pager->totalCount = "Total: <strong>{recTotal}</strong> lignes";
$lang->pager->pageSize = "<strong>{recPerPage}</strong> par page";
$lang->pager->itemsRange = "De <strong>{start}</strong> à <strong>{end}</strong>";
$lang->pager->pageOfTotal = "Page <strong>{page}</strong> sur <strong>{totalPage}</strong>";
$lang->pager->totalCountAB = "Total: {recTotal} lignes";
$lang->pager->pageSizeAB = "{recPerPage} par page";
$lang->colorPicker = new stdclass();
$lang->colorPicker->errorTip = "Ce n'est pas une valeur de couleur valide";
+2
View File
@@ -512,6 +512,8 @@ $lang->pager->totalCount = '共 <strong>{recTotal}</strong> 项';
$lang->pager->pageSize = '每页 <strong>{recPerPage}</strong> 项';
$lang->pager->itemsRange = '第 <strong>{start}</strong> ~ <strong>{end}</strong> 项';
$lang->pager->pageOfTotal = '第 <strong>{page}</strong>/<strong>{totalPage}</strong> 页';
$lang->pager->totalCountAB = '共 {recTotal} 项';
$lang->pager->pageSizeAB = '每页 {recPerPage} 项';
$lang->colorPicker = new stdclass();
$lang->colorPicker->errorTip = '不是有效的颜色值';
+18 -2
View File
@@ -1,4 +1,6 @@
<?php
global $lang;
$config->program = new stdclass();
$config->program->suffix['c'] = "cpp";
$config->program->suffix['cpp'] = "cpp";
@@ -66,8 +68,9 @@ $config->repo->gitlab->apiPath = "%s/api/v4/projects/%s/repository/";
$config->repo->gitea = new stdclass;
$config->repo->gitea->apiPath = "%s/api/v1/repos/%s/";
$config->repo->gitServiceList = array('gitlab', 'gitea', 'gogs');
$config->repo->gitTypeList = array('Gitlab', 'Gitea', 'Gogs', 'Git');
$config->repo->gitServiceList = array('gitlab', 'gitea', 'gogs');
$config->repo->gitServiceTypeList = array('Gitlab', 'Gitea', 'Gogs');
$config->repo->gitTypeList = array('Gitlab', 'Gitea', 'Gogs', 'Git');
$config->repo->rules['module']['task'] = 'Task';
$config->repo->rules['module']['bug'] = 'Bug';
@@ -151,3 +154,16 @@ $config->repo->fileExt["vb"] = array('.vb');
$config->repo->fileExt["verilog"] = array('.v', '.vh');
$config->repo->fileExt["xml"] = array('.xml', '.dtd', '.ascx', '.csproj', '.config', '.wxi', '.wxl', '.wxs', '.xaml', '.svg', '.svgz', '.opf', '.xsl');
$config->repo->fileExt["yaml"] = array('.yaml', '.yml');
$config->repo->search['module'] = 'repo';
$config->repo->search['fields']['name'] = $lang->repo->name;
$config->repo->search['fields']['id'] = $lang->repo->id;
$config->repo->search['fields']['product'] = $lang->repo->product;
$config->repo->search['fields']['projects'] = $lang->repo->projects;
$config->repo->search['fields']['SCM'] = $lang->repo->SCM;
$config->repo->search['fields']['desc'] = $lang->repo->desc;
$config->repo->search['params']['name'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->repo->search['params']['product'] = array('operator' => 'include', 'control' => 'select', 'values' => '');
$config->repo->search['params']['projects'] = array('operator' => 'include', 'control' => 'select', 'values' => '');
$config->repo->search['params']['SCM'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->repo->scmList);
+222
View File
@@ -0,0 +1,222 @@
<?php
global $lang, $app;
$app->loadLang('sonarqube');
$config->repo->dtable = new stdclass();
$config->repo->dtable->fieldList['name']['name'] = 'name';
$config->repo->dtable->fieldList['name']['title'] = $lang->repo->name;
$config->repo->dtable->fieldList['name']['type'] = 'title';
$config->repo->dtable->fieldList['name']['width'] = '0.2';
$config->repo->dtable->fieldList['product']['name'] = 'productNames';
$config->repo->dtable->fieldList['product']['title'] = $lang->repo->product;
$config->repo->dtable->fieldList['product']['type'] = 'text';
$config->repo->dtable->fieldList['product']['sortType'] = true;
$config->repo->dtable->fieldList['product']['width'] = '136';
$config->repo->dtable->fieldList['project']['name'] = 'projectNames';
$config->repo->dtable->fieldList['project']['title'] = $lang->repo->projects;
$config->repo->dtable->fieldList['project']['type'] = 'text';
$config->repo->dtable->fieldList['project']['sortType'] = true;
$config->repo->dtable->fieldList['project']['width'] = '136';
$config->repo->dtable->fieldList['scm']['name'] = 'SCM';
$config->repo->dtable->fieldList['scm']['title'] = $lang->repo->type;
$config->repo->dtable->fieldList['scm']['type'] = 'scm';
$config->repo->dtable->fieldList['scm']['map'] = $lang->repo->scmList;
$config->repo->dtable->fieldList['scm']['group'] = 1;
$config->repo->dtable->fieldList['path']['name'] = 'codePath';
$config->repo->dtable->fieldList['path']['title'] = $lang->repo->path;
$config->repo->dtable->fieldList['path']['type'] = 'text';
$config->repo->dtable->fieldList['path']['width'] = '260';
$config->repo->dtable->fieldList['path']['group'] = 1;
$config->repo->dtable->fieldList['lastSubmit']['name'] = 'lastSubmitTime';
$config->repo->dtable->fieldList['lastSubmit']['title'] = $lang->repo->lastSubmitTime;
$config->repo->dtable->fieldList['lastSubmit']['type'] = 'datetime';
$config->repo->dtable->fieldList['lastSubmit']['width'] = '128';
$config->repo->dtable->fieldList['job']['name'] = 'job';
$config->repo->dtable->fieldList['job']['hidden'] = true;
$config->repo->dtable->fieldList['actions']['name'] = 'actions';
$config->repo->dtable->fieldList['actions']['title'] = $lang->actions;
$config->repo->dtable->fieldList['actions']['type'] = 'actions';
$config->repo->dtable->fieldList['actions']['width'] = '132';
$config->repo->dtable->fieldList['actions']['menu'] = array('execJob', 'reportView', 'edit', 'delete');
$config->repo->dtable->fieldList['actions']['list']['edit']['icon'] = 'edit';
$config->repo->dtable->fieldList['actions']['list']['edit']['hint'] = $lang->repo->edit;
$config->repo->dtable->fieldList['actions']['list']['execJob']['icon'] = 'sonarqube';
$config->repo->dtable->fieldList['actions']['list']['execJob']['hint'] = $lang->sonarqube->execJob;
$config->repo->dtable->fieldList['actions']['list']['execJob']['url'] = helper::createLink('sonarqube', 'execJob', "jobID={jobID}");
$config->repo->dtable->fieldList['actions']['list']['execJob']['data-toggle'] = 'modal';
$config->repo->dtable->fieldList['actions']['list']['reportView']['icon'] = 'audit';
$config->repo->dtable->fieldList['actions']['list']['reportView']['hint'] = $lang->sonarqube->reportView;
$config->repo->dtable->fieldList['actions']['list']['reportView']['url'] = helper::createLink('sonarqube', 'reportView', "jobID={jobID}");
$config->repo->dtable->fieldList['actions']['list']['reportView']['data-toggle'] = 'modal';
$config->repo->dtable->fieldList['actions']['list']['delete']['icon'] = 'trash';
$config->repo->dtable->fieldList['actions']['list']['delete']['hint'] = $lang->repo->delete;
$config->repo->dtable->fieldList['actions']['list']['delete']['data-toggle'] = 'modal';
$config->repo->repoDtable = new stdclass();
$config->repo->repoDtable->fieldList['name']['name'] = 'name';
$config->repo->repoDtable->fieldList['name']['title'] = $lang->repo->name;
$config->repo->repoDtable->fieldList['name']['minWidth'] = '160';
$config->repo->repoDtable->fieldList['name']['sortType'] = false;
$config->repo->repoDtable->fieldList['name']['type'] = 'shortTitle';
$config->repo->repoDtable->fieldList['name']['fixed'] = false;
$config->repo->repoDtable->fieldList['name']['hint'] = true;
$config->repo->repoDtable->fieldList['name']['checkbox'] = false;
$config->repo->repoDtable->fieldList['revision']['name'] = 'revision';
$config->repo->repoDtable->fieldList['revision']['title'] = $lang->repo->revisions;
$config->repo->repoDtable->fieldList['revision']['sortType'] = false;
$config->repo->repoDtable->fieldList['revision']['width'] = '90';
$config->repo->repoDtable->fieldList['revision']['hint'] = true;
$config->repo->repoDtable->fieldList['time']['name'] = 'date';
$config->repo->repoDtable->fieldList['time']['title'] = $lang->repo->time;
$config->repo->repoDtable->fieldList['time']['sortType'] = false;
$config->repo->repoDtable->fieldList['time']['type'] = 'date';
$config->repo->repoDtable->fieldList['time']['hint'] = true;
$config->repo->repoDtable->fieldList['committer']['name'] = 'account';
$config->repo->repoDtable->fieldList['committer']['title'] = $lang->repo->committer;
$config->repo->repoDtable->fieldList['committer']['sortType'] = false;
$config->repo->repoDtable->fieldList['committer']['type'] = 'user';
$config->repo->repoDtable->fieldList['committer']['hint'] = true;
$config->repo->repoDtable->fieldList['comment']['name'] = 'originalComment';
$config->repo->repoDtable->fieldList['comment']['title'] = $lang->repo->comment;
$config->repo->repoDtable->fieldList['comment']['sortType'] = false;
$config->repo->repoDtable->fieldList['comment']['type'] = 'text';
$config->repo->repoDtable->fieldList['comment']['hint'] = true;
$config->repo->commentDtable = new stdclass();
$config->repo->commentDtable->fieldList['id']['title'] = '';
$config->repo->commentDtable->fieldList['id']['name'] = '';
$config->repo->commentDtable->fieldList['id']['type'] = 'checkID';
$config->repo->commentDtable->fieldList['id']['fixed'] = false;
$config->repo->commentDtable->fieldList['id']['sortType'] = false;
$config->repo->commentDtable->fieldList['id']['checkbox'] = true;
$config->repo->commentDtable->fieldList['id']['width'] = '25';
$config->repo->commentDtable->fieldList['revision']['name'] = 'revision';
$config->repo->commentDtable->fieldList['revision']['title'] = $lang->repo->revisions;
$config->repo->commentDtable->fieldList['revision']['sortType'] = false;
$config->repo->commentDtable->fieldList['revision']['type'] = 'title';
$config->repo->commentDtable->fieldList['revision']['fixed'] = false;
$config->repo->commentDtable->fieldList['revision']['width'] = '100';
$config->repo->commentDtable->fieldList['revision']['hint'] = true;
$config->repo->commentDtable->fieldList['commit']['name'] = 'commit';
$config->repo->commentDtable->fieldList['commit']['title'] = $lang->repo->commit;
$config->repo->commentDtable->fieldList['commit']['sortType'] = false;
$config->repo->commentDtable->fieldList['commit']['width'] = '40';
$config->repo->commentDtable->fieldList ['commit']['hint'] = true;
$config->repo->commentDtable->fieldList['time'] = $config->repo->repoDtable->fieldList['time'];
$config->repo->commentDtable->fieldList['committer'] = $config->repo->repoDtable->fieldList['committer'];
$config->repo->commentDtable->fieldList['comment'] = $config->repo->repoDtable->fieldList['comment'];
$config->repo->commentDtable->fieldList['comment']['name'] = 'originalComment';
$config->repo->commentDtable->fieldList['committer']['name'] = 'committer';
$config->repo->logDtable = new stdclass();
$config->repo->logDtable->fieldList['id']['hidden'] = true;
$config->repo->logDtable->fieldList['revision']['type'] = 'revision';
$config->repo->logDtable->fieldList['revision']['width'] = '100';
$config->repo->logDtable->fieldList['revision']['checkbox'] = true;
$config->repo->logDtable->fieldList['revision']['nestedToggle'] = false;
$config->repo->logDtable->fieldList['commit']['type'] = 'number';
$config->repo->logDtable->fieldList['commit']['width'] = '50';
$config->repo->logDtable->fieldList['commit']['sortType'] = false;
$config->repo->logDtable->fieldList['date']['name'] = 'time';
$config->repo->logDtable->fieldList['date']['type'] = 'datetime';
$config->repo->logDtable->fieldList['date']['sortType'] = false;
$config->repo->logDtable->fieldList['committer']['name'] = 'committer';
$config->repo->logDtable->fieldList['comment']['type'] = 'html';
$config->repo->logDtable->fieldList['comment']['width'] = '600';
$config->repo->blameDtable = new stdclass();
$config->repo->blameDtable->fieldList['revision']['type'] = 'revision';
$config->repo->blameDtable->fieldList['revision']['width'] = '120';
$config->repo->blameDtable->fieldList['commit']['type'] = 'number';
$config->repo->blameDtable->fieldList['commit']['sortType'] = false;
$config->repo->blameDtable->fieldList['commit']['width'] = '80';
$config->repo->blameDtable->fieldList['committer']['name'] = 'committer';
$config->repo->blameDtable->fieldList['committer']['width'] = '150';
$config->repo->blameDtable->fieldList['line']['type'] = 'number';
$config->repo->blameDtable->fieldList['line']['sortType'] = false;
$config->repo->blameDtable->fieldList['line']['width'] = '80';
$config->repo->blameDtable->fieldList['content']['title'] = $lang->repo->code;
$config->repo->blameDtable->fieldList['content']['type'] = 'html';
$config->repo->blameDtable->fieldList['content']['width'] = '700';
$app->loadLang('task');
$app->loadModuleConfig('task');
$config->repo->taskDtable = new stdclass();
$config->repo->taskDtable->fieldList = array();
$config->repo->taskDtable->fieldList['id']['name'] = 'id';
$config->repo->taskDtable->fieldList['id']['title'] = $lang->idAB;
$config->repo->taskDtable->fieldList['id']['type'] = 'id';
$config->repo->taskDtable->fieldList['id']['checkbox'] = true;
$config->repo->taskDtable->fieldList['pri']['title'] = $lang->priAB;
$config->repo->taskDtable->fieldList['pri']['type'] = 'pri';
$config->repo->taskDtable->fieldList['pri']['sortType'] = true;
$config->repo->taskDtable->fieldList['pri']['show'] = true;
$config->repo->taskDtable->fieldList['pri']['group'] = 1;
$config->repo->taskDtable->fieldList['pri']['fixed'] = 'left';
$config->repo->taskDtable->fieldList ['name']['flex'] = 1;
$config->repo->taskDtable->fieldList ['name']['nestedToggle'] = false;
$config->repo->taskDtable->fieldList ['name']['sortType'] = true;
$config->repo->taskDtable->fieldList ['name']['required'] = true;
$config->repo->taskDtable->fieldList['name']['data-size'] = 'lg';
$config->repo->taskDtable->fieldList['name']['data-toggle'] = 'modal';
$config->repo->taskDtable->fieldList ['name']['fixed'] = 'left';
$config->repo->taskDtable->fieldList ['name']['type'] = 'nestedTitle';
$config->repo->taskDtable->fieldList ['name']['link'] = array('module' => 'task', 'method' => 'view', 'params' => 'taskID={id}');
$config->repo->taskDtable->fieldList['name']['title'] = $lang->task->name;
$config->repo->taskDtable->fieldList['finishedBy']['title'] = $lang->task->finishedByAB;
$config->repo->taskDtable->fieldList['finishedBy']['type'] = 'user';
$config->repo->taskDtable->fieldList['finishedBy']['sortType'] = true;
$config->repo->taskDtable->fieldList['finishedBy']['show'] = true;
$config->repo->taskDtable->fieldList['finishedBy']['group'] = 4;
$config->repo->taskDtable->fieldList['assignedTo']['type'] = 'assign';
$config->repo->taskDtable->fieldList['assignedTo']['sortType'] = true;
$config->repo->taskDtable->fieldList['assignedTo']['show'] = true;
$config->repo->taskDtable->fieldList['assignedTo']['group'] = 3;
$config->repo->taskDtable->fieldList['assignedTo']['currentUser'] = $app->user->account;
$config->repo->taskDtable->fieldList['assignedTo']['title'] = $lang->task->assignedTo;
$config->repo->taskDtable->fieldList['assignedTo']['assignLink'] = array('module' => 'task', 'method' => 'assignTo', 'params' => 'executionID={execution}&taskID={id}');
$config->repo->taskDtable->fieldList['status']['title'] = $lang->statusAB;
$config->repo->taskDtable->fieldList['status']['type'] = 'status';
$config->repo->taskDtable->fieldList['status']['statusMap'] = $lang->task->statusList;
$config->repo->taskDtable->fieldList['status']['sortType'] = true;
$config->repo->taskDtable->fieldList['status']['show'] = true;
$config->repo->taskDtable->fieldList['status']['group'] = 1;
+19
View File
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
$config->repo->form = new stdclass();
$config->repo->form->create = array();
$config->repo->form->create['product'] = array('required' => true, 'type' => 'array');
$config->repo->form->create['projects'] = array('required' => false, 'type' => 'array', 'default' => array());
$config->repo->form->create['SCM'] = array('required' => true, 'type' => 'string', 'filter' => 'trim');
$config->repo->form->create['serviceHost'] = array('required' => true, 'type' => 'int');
$config->repo->form->create['serviceProject'] = array('required' => false, 'type' => 'string', 'default' => '');
$config->repo->form->create['name'] = array('required' => true, 'type' => 'string', 'filter' => 'trim');
$config->repo->form->create['path'] = array('required' => false, 'type' => 'string', 'default' => '');
$config->repo->form->create['encoding'] = array('required' => true, 'type' => 'string');
$config->repo->form->create['client'] = array('required' => false, 'type' => 'string', 'default' => '');
$config->repo->form->create['account'] = array('required' => false, 'type' => 'string', 'default' => '');
$config->repo->form->create['password'] = array('required' => false, 'type' => 'string', 'default' => '');
$config->repo->form->create['encrypt'] = array('required' => false, 'type' => 'string', 'default' => '');
$config->repo->form->create['desc'] = array('required' => false, 'type' => 'string', 'default' => '');
+219 -264
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
.m-repo-ajaxgeteditorcontent {overflow: hidden;}
#log .tip-circular {background: #2e7fff; border-radius: 10px; padding: 0px 7px;}
#log {margin-top: 8px; display: none; padding: 5px; justify-content: space-between; height: 25px; line-height: 20px; background: #efefef;}
#log .history {margin-left: 5px; max-width: 90%;}
#log .action-btn .btn {border: none; background: none;}
#related {display: none; margin-top: 2px; background: #fff; height: 0px;}
#relationTabs .nav > li > a {padding: 8px 1px; display: flex; white-space: pre;}
#relationTabs .nav-tabs > li > a > span {margin-right: 5px;}
#relationTabs .tab-pane {display: none;}
#relationTabs {background: #FFFFFF;}
#relationTabs .tab-pane.active {display: block;}
#relationTabs .tab-nav-item {max-width: none !important;}
#relationTabs .tab-nav-item .icon {line-height: inherit;}
#relationTabs {overflow: hidden; padding-bottom: 10px; position: relative; height: 100%;}
#relationTabs > .nav-tabs {position: absolute; display: flex;}
#relationTabs .tab-nav-link .title {max-width: 300px; display: inline-block; white-space: nowrap; width: 100%; overflow: hidden; text-overflow: ellipsis; padding: 0 5px;}
#relationTabs{width: 100%}
#relationTabs .nav-tabs > li > a.active > span {background: #E6F0FF;color: #2e7fff;}
#relationTabs .nav-tabs > li > a > i, #relationTabs .nav-tabs > li > a > span {background: #EDEEF2; font-weight: normal; padding: 1px 5px;}
#relationTabs .nav-tabs > li > a > span > i {margin-right: 8px;}
#relationTabs .nav-tabs > li > .unlinks {padding-left: 0;color: #169}
#relationTabs .nav-tabs > li > .unlinks:hover {text-decoration: underline;color: #C61A1A;}
#relationTabs .nav-tabs > li > .unlinks > i {background: none;padding: 0;}
#relationTabs .nav-tabs>.nav-item>a.active:after{border: none;}
#relationTabs .tab-content{margin-top: 40px;}
#related .content, #related .btn {background-color: #F4F5F7; border: none;}
#related .btn.pull-right {margin-right: 0;z-index: 1;}
#related .btn.pull-left {z-index: 1;}
#related .nav-tabs > li > a.active:before {background: none; height: 0;}
#related .table-empty-tip {padding: 35px 10px;}
.table-empty-tip {padding: 80px 10px;text-align: center;background: #fff;}
.repoCode .binary a .icon-download {font-size: 50px;}
.repoCode .binary, .repoCode .image {text-align: center;}
.repoCode .binary a {margin: 100px 0px; display: block;}
.repoCode .image {margin-top: 10px;}
#codeContainer{height: 100%;}
#related .nav-item {gap: 0;}
.nav-tabs>li>a.active {font-weight: 700;color: #313c52!important}
+1
View File
@@ -0,0 +1 @@
#featureBar .nav .btn>.icon {color: #ffffff;}
+7
View File
@@ -0,0 +1,7 @@
.repo-downloadCode{margin: 8px 10px 8px 0;}
.repo-select{margin-right: 10px;}
.repo-comment{width: 100%; display: block; word-break: keep-all; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;}
.dtable-cell-html{width: 100%;}
.container .sidebar{width: 550px;}
#actionBar .dropdown-menu {margin-top: 10px; padding: 10px;}
#actionBar>.dropdown {position: absolute!important;}
+53
View File
@@ -0,0 +1,53 @@
#fileTabs .nav-tabs {overflow: hidden;position: absolute; height: 40px;background: #efefef;}
#fileTabs .nav-tabs > li.nav-item > a {z-index: 1; background: none; padding: 1.7rem; cursor: pointer !important; margin-left: -25px; margin-top: 12px;}
#fileTabs .nav-tabs > li > a::after {content: ''; position: absolute; top: 2px; left: 10px; width: 85%; right: 6px; bottom: 0; background: #f8f8f8; border: none; border-radius: 10px 10px 0 0; transform: perspective(0.5rem) rotateX(2deg); z-index: -1;}
#fileTabs .nav-tabs > li > a.active span {font-weight: 700; color: #2b80ff!important;}
#fileTabs .nav-tabs > li > a.active::after {background: #fff;}
#fileTabs > .nav-tabs {overflow: hidden; padding-bottom: 10px; position: relative; height: 40px; background: #efefef;}
#fileTabs > .nav-tabs {position: absolute; display: flex; padding-left: 25px;}
#fileTabs .monaco-close{margin-right: 18px;}
#fileTabs .nav-tabs > li > a > span{margin-left: 18px;font-weight: 700;color: #313c52!important;}
#related .btn-right, #related .btn-left {padding-top: 9px;}
#log .action-btn {margin-top: -7px;}
.repoCode .btn.btn-right, .repoCode .btn.btn-left {margin-right: 0; padding: 6px 6px;}
#related .btn-right, #related .btn-left {padding-top: 9px;}
.btn-left, .btn-right {display: none;}
#linkObject{display: none;}
#related .btn-right, #related .btn-left {padding-top: 9px;}
#monacoTree .tree-item-content span {cursor: pointer;}
.directory {background-image:url('theme/default/images/repo/dir.png');}
.file {background-image:url('theme/default/images/repo/txt.png');}
.mini-icon {display: inline-block; height: 16px; width: 16px; background-color: transparent; background-position: 0 0; background-repeat: no-repeat; vertical-align: text-bottom; margin-right: 5px;}
#monacoTree .tree-item-content {cursor: pointer;}
.sync-content {margin: 10px}
.sync-main-content {padding: 20px;background-color: #fff;border-radius: 4px;}
.sync-line {border-top: 1px solid #e4e4e4; margin: 10px 0;}
.sync-main-content div {width: 100%;}
.sync-main-content h3 {font-size: 16px; font-weight: 700;}
.sync-main-content p {color: #313c52;}
.sync-main-content .alert > [class*="icon-"] {font-size: 42px;opacity: 0.8;}
.repo-linkstory-title {background-color: #e6f0ff; color: #2e7fff; padding: 12px 6px 12px;}
#repoBranchDropMenu .dropmenu-btn {justify-content: space-between; width: 100%;}
#monacoTabs .nav > li > a {padding: 8px 10px; display: flex; white-space: pre;}
#monacoTabs .nav-tabs > li > a > span {margin-right: 5px;}
#monacoTabs .tab-pane {display: none;}
#monacoTabs {background: #FFFFFF;}
#monacoTabs .tab-pane.active {display: block;}
#monacoTabs .tab-nav-item {max-width: none !important;}
#monacoTabs .tab-nav-item .icon {line-height: inherit;}
#monacoTabs {overflow: hidden; position: relative; height: 100%;}
#monacoTabs > .nav-tabs {position: absolute; display: flex; padding-right: 20px;}
#fileTabs .arrow-left, #fileTabs .arrow-right {line-height: 28px; padding: 6px; width: 26px; background: #efefef; cursor: pointer;}
#fileTabs .arrow-right {right: 40px; cursor: pointer;}
#monacoTabs ul {min-width: 100%; padding-left: 25px; z-index: 1;}
#monacoTabs .tab-content {margin-top: 40px;}
#monacoTabsContainer {background: #efefef;}
.monaco-dropmenu{background: #efefef; height: 40px; line-height: 40px;}
#fileTabs > .btn {background: #efefef; border: none; --tw-ring-shadow: none;}
#fileTabs > .btn-right {padding-right: 40px;}
#repoBranchDropMenu > .dropmenu-btn {background: white; --tw-ring-color: none; color: inherit; margin-bottom: 10px; background: white;}
.repo-select #repoBranchDropMenu > .dropmenu-btn {background: inherit; --tw-ring-color: none; color: inherit; margin-bottom: 0;}
#repoBranchDropMenu > .dropmenu-btn > .icon-angle-right {display: none;}
#log .action-btn .caret {display: none;}
#fileTabs .monaco-dropmenu .caret {display: none;}
#monacoTabs .gap-x-5 {-moz-column-gap: 0; column-gap: 0;}
+18
View File
@@ -0,0 +1,18 @@
.diff-back-btn {border: none; --tw-ring-shadow: none}
.diff-back-btn::after {border: none;}
.diff-back-btn::before {border: none;}
.label-info {background-color: #37b2fe}
.label-info[href]:focus,.label-info[href]:hover {color: #fff; background-color: #049efe}
.label-info.label-outline {color: #37b2fe; background: 0 0; border: 1px solid #37b2fe}
.label {padding: .2em .6em .2em; color: #fff;text-align: center; white-space: nowrap; vertical-align: middle; border-radius: .25em;}
.diff-label {border: none; --tw-ring-shadow: none; margin-left: 5px; margin-right: 10px}
.label-exchange {background-color: #566F7C; cursor: pointer;}
#fileTabs .tab-pane {display: none;}
#fileTabs .tab-pane.active {display: block;}
#fileTabs .nav-item {max-width: none !important;}
.sidebar .tree{background-color: white;}
.container .sidebar{width: 240px;}
.tree li.tree-item.selected{color: #438EFF !important;}
#monacoTree{overflow: auto;}
.monaco-dropmenu{background: #efefef;}
+3
View File
@@ -0,0 +1,3 @@
.form-batch-row-actions>button:first-child {display: none;}
#repoList tr td:last-child, #repoList tr th:last-child {text-align: right;}
#repoList tr td:first-child {text-align: center;}
+1
View File
@@ -0,0 +1 @@
#featureBar .nav .btn>.icon {color: #ffffff;}
+1
View File
@@ -0,0 +1 @@
.btn-active-text {font-weight: 700; color: #1e6aeb; background: #e6eaf1; --tw-ring-color: var(--color-transparent);}
+7
View File
@@ -0,0 +1,7 @@
#fileTabs .tab-pane {display: none;}
#fileTabs .tab-pane.active {display: block;}
#fileTabs .nav-item {max-width: none !important;}
.sidebar .tree{background-color: white;}
.container .sidebar{width: 240px;}
.tree li.tree-item.selected{color: #438EFF !important;}
#monacoTree{overflow: auto;}
+236
View File
@@ -0,0 +1,236 @@
var blames = null;
var codeHeight = 0;
window.onMouseDown = function(obj)
{
showBlameAndRelation(obj.target.position.lineNumber);
}
/**
* 显示关联信息弹窗。
* Show blame info and relations.
*
* @param int line
* @access public
* @return void
*/
function showBlameAndRelation(line)
{
if(!blames) return;
if(pageType == 'diff') line = diffContent.line.new[line -1];
var blame = blames[line];
if(!blame) return;
var p_line = parseInt(line);
while(!blame.revision)
{
p_line--;
blame = blames[p_line];
}
if($('#log').data('line') == line) return;
var time = blame.time != 'unknown' ? blame.time : '';
var user = blame.committer != 'unknown' ? blame.committer : '';
var version = blame.revision.toString().substring(0, 10);
var content = blameTmpl.replace('%line', line).replace('%time', time).replace('%name', user).replace('%version', version).replace('%comment', blame.message);
$('.history').html(content);
$('#log').data('line', line);
$('#log').css('display', 'flex');
getRelation(blame.revision);
}
/**
* 获取关联信息。
* Get relation by commit.
*
* @param string $commit
* @access public
* @return void
*/
window.getRelation = function(commit)
{
$('.table-empty-tip').show();
$('#codeContainer').css('height', codeHeight / 5 * 3);
var relatedHeight = codeHeight / 5 * 2 - $('#log').height() - 10;
$('#related').css('height', relatedHeight);
$('#relationTabs ul li').remove();
$('#relationTabs .tab-content .tab-pane').remove();
$.post($.createLink('repo', 'ajaxGetCommitRelation', 'repoID=' + repoID + '&commit=' + commit), function(data)
{
var titleList = JSON.parse(data).titleList;
$.each(titleList, function(i, titleObj)
{
openTab(titleObj);
});
if(titleList.length > 0)
{
$('.table-empty-tip').hide();
$('#related button').show();
$('#relationTabs ul li a').first().trigger('click');
}
else
{
if(!canLinkStory && !canLinkBug && !canLinkTask) $('#related button').hide();
}
arrowTabs('relationTabs', 1);
});
globalCommit = commit;
var linkStory = $.createLink('repo', 'linkStory', 'repoID=' + repoID + '&commit=' + commit, '', true);
var linkBug = $.createLink('repo', 'linkBug', 'repoID=' + repoID + '&commit=' + commit, '', true);
var linkTask = $.createLink('repo', 'linkTask', 'repoID=' + repoID + '&commit=' + commit, '', true);
$('#linkStory').data('link', linkStory);
$('#linkBug').data('link', linkBug);
$('#linkTask').data('link', linkTask);
$('#related').show();
}
/**
* 打开关联信息tab。
* Open tab.
*
* @param object $titleObj
* @access public
* @return void
*/
function openTab(titleObj)
{
var tabTemplate = `<li class="nav-item"><a class="font-medium" href="{href}" data-toggle="tab"><span><i class="icon icon-{prefixIcon}"></i>{title}</span></a><a title="{unlinkTitle}" class="unlinks" data-link="{unlinkHref}"><i class="icon icon-unlink"></i></a></li>`;
var eleId = titleObj.type + '-' + titleObj.id;
var prefixIcon = titleObj.type == 'story' ? 'lightbulb' : (titleObj.type == 'task' ? 'check-sign' : 'bug');
var unlinkHref = canUnlinkObject ? $.createLink('repo', 'unlink', 'repoID=' + repoID + '&commit=' + globalCommit + '&objectID=' + titleObj.type + '&objectType=' + titleObj.id) : '';
var tabHtml = tabTemplate.replace('{title}', titleObj.title)
.replace('{unlinkHref}', unlinkHref)
.replace('{unlinkTitle}', unlinkTitle)
.replace('{href}', '#related-' + eleId)
.replace('{prefixIcon}', prefixIcon);
$('#relationTabs>ul').append(tabHtml);
var height = getRelationTabHeight();
$('#relationTabs .tab-content').append("<div id='related-" + eleId + "' class='tab-pane active in'><iframe class='repo-iframe' src='" + $.createLink('repo', 'ajaxGetRelationInfo', 'objectID=' + titleObj.id + '&objectType=' + titleObj.type) + "' width='100%' height='" + height + "' scrolling='no'></iframe></div>")
}
/**
* 获取关联信息弹窗高度。
* Get relation tab height.
*
* @param object $titleObj
* @access public
* @return void
*/
function getRelationTabHeight()
{
var relatedHeight = $('#related').height();
var tabHeight = $('#related #relationTabs>ul').height();
relatedHeight = parseInt(relatedHeight) ? parseInt(relatedHeight) : 0;
tabHeight = parseInt(tabHeight) ? parseInt(tabHeight) : 0;
return relatedHeight - tabHeight;
}
/**
* 修改比对差异方式。
* Update diff editor inline style.
*
* @param bool display
* @access public
* @return void
*/
window.updateEditorInline = function(display)
{
modifiedEditor.updateOptions({renderSideBySide: display});
}
/**
* 获取提交信息。
* Show commit info.
*
* @access public
* @return void
*/
function showCommitInfo()
{
var link = $.createLink('repo', 'ajaxGetCommitInfo');
var data = {
repoID : repoID,
entry : encodePath,
revision : revision,
sourceRevision: sourceRevision,
line : 0,
returnType : 'json'
};
$.post(link, data, function(res)
{
res = JSON.parse(res);
blames = res.blames;
})
}
/* 初始化数据 */
$(function()
{
setTimeout(() => {
initPage();
}, 200);
});
/**
* 初始化页面。
* Init page.
*
* @access public
* @return void
*/
function initPage()
{
codeHeight = $.cookie.get('codeContainerHeight');
$('#codeContainer').css('height', $.cookie.get('codeContainerHeight'));
$('.btn-left').on('click', function() {arrowTabs('relationTabs', 1);});
$('.btn-right').on('click', function() {arrowTabs('relationTabs', -2);});
$('#linkStory, #linkBug, #linkTask').on('click', function()
{
var link = $(this).data('link');
parent.loadLinkPage(link);
});
$('#relationTabs').on('click', '.unlinks', function()
{
var link = $(this).data('link');
$.post(link, function(data)
{
data = JSON.parse(data);
if(data.result)
{
getRelation(data.revision);
}
else
{
alert(data.message);
}
})
})
$('#relationTabs').on('onOpen', function(event, tab)
{
$('#tab-nav-item-' + tab.id).attr('title', tab.title);
var relatedHeight = codeHeight / 5 * 2 - $('#log').height() - 45;
$('#relationTabs iframe').css('height', relatedHeight);
});
/* Get file commits. */
showCommitInfo();
}
+248
View File
@@ -0,0 +1,248 @@
var revisionMap = {};
var checkedIds = [];
window.renderCell = function(result, {col, row})
{
if(col.name === 'name')
{
var iconHtml = '<span class="' + (row.data.kind == 'dir' ? 'directory' : 'file') + ' mini-icon"></span>';
result[0] = {html:iconHtml + '<a href="' + row.data.link + '">' + row.data.name + '</a>'};
return result;
}
if(col.name === 'originalComment')
{
result[0] = {html:'<span class="repo-comment">' + row.data.comment + '</span>'};
return result;
}
return result;
};
/**
* commit表格渲染跳转链接。
* Render jump link of version.
*
* @access public
* @return void
*/
window.renderCommentCell = function(result, {col, row})
{
if(col.name === 'revision')
{
result[0] = {html:'<a href="' + row.data.link + '">' + row.data.revision + '</a>', style:{flexDirection:"column"}};
return result;
}
if(col.name === 'originalComment')
{
result[0] = {html:'<span class="repo-comment">' + row.data.comment + '</span>', style:{flexDirection:"column"}};
return result;
}
return result;
};
/* Open download page when downZip btn click. */
$('.downloadZip-btn').on('click', function()
{
var link = $.createLink('repo', 'downloadCode', 'repoID=' + repoID + '&branch=' + branch);
window.open(link);
})
/* Refresh page when repo changed. */
$('#repo-select').on('change', function()
{
var index = $('#repo-select').prop('selectedIndex');
if(menus[index - 1].url != undefined)
{
openUrl(menus[index - 1].url);
}
})
/**
* 当选中两行时禁用其他行。
* Disable checkable attribution when checked rows equal 2.
*
* @param object changes
* @access public
* @return void
*/
window.checkedChange = function(changes)
{
checkedIds = getCurrentCheckedIds();
if(checkedIds.length < 2)
{
$('.btn-diff').addClass('disabled')
}
else
{
$('.btn-diff').removeClass('disabled')
}
}
/**
* 跳转比较差异页面。
* Redirect to diff page.
*
* @access public
* @return void
*/
window.diffClick = function()
{
var checkedIds = getCurrentCheckedIds();
var newDiffLink = diffLink.replace('{oldRevision}', revisionMap[checkedIds[1]]);
newDiffLink = newDiffLink.replace('{newRevision}', revisionMap[checkedIds[0]]);
$.cookie.set('sideRepoSelected', checkedIds.join(','))
openUrl(newDiffLink);
}
/**
* 当选中数量等于2,则禁用其他所有行。
* When the selected row equals 2, disable all other rows.
*
* @param int rowID
* @access public
* @return bool
*/
window.canRowCheckable = function(rowID)
{
const dtable = zui.DTable.query('#repo-comments-table');
if(dtable == undefined) return;
var data = dtable.$.props.data;
if(data.length == 0) return true;
initRevisionMap(data);
var currentCheckedIds = getCurrentCheckedIds();
if(currentCheckedIds.length < 2) return true;
if(currentCheckedIds.indexOf(rowID) == -1) return 'disabled'
return true;
}
/**
* 检测revisionMap是否跟当前页面数据一致,不一致重新生成。
* Regenerate revisionMap when revisionMap is not in current list.
*
* @param array
* @access public
* @return void
*/
function initRevisionMap(data)
{
if(revisionMap[data[data.length - 1].id] !== undefined) return;
revisionMap = {};
for (var i = 0; i < data.length; i++) revisionMap[data[i].id] = data[i].revision;
/* Check rows where id in cookie. */
setTimeout(function()
{
checkColInCurrentPage()
}, 100);
}
/**
* 选中当前页码对应的行。
* Select the rows to the current page.
*
* @access public
* @return void
*/
function checkColInCurrentPage()
{
const dtable = zui.DTable.query('#repo-comments-table');
const checkedIds = $.cookie.get('sideRepoSelected') ? $.cookie.get('sideRepoSelected').split(',') : [];
var currentCheckedIds = [];
if(revisionMap[checkedIds[0]]) currentCheckedIds.push(checkedIds[0]);
if(revisionMap[checkedIds[1]]) currentCheckedIds.push(checkedIds[1]);
dtable.$.toggleCheckRows(Object.keys(revisionMap), false);
if(currentCheckedIds.length > 0)
{
dtable.$.toggleCheckRows(currentCheckedIds, true);
}
else
{
dtable.$.toggleCheckRows(Object.keys(revisionMap).slice(0, 2), true);
}
window.checkedChange();
}
/**
* 获取当前页码选中的行。
* Get checked rows in current page.
*
* @access public
* @return array
*/
function getCurrentCheckedIds()
{
const dtable = zui.DTable.query('#repo-comments-table');
if(dtable.$ == undefined) return [];
var checkedIds = dtable.$.getChecks();
var currentCheckedIds = [];
for (var i = 0; i < checkedIds.length; i++)
{
if(revisionMap[checkedIds[i]]) currentCheckedIds.push(checkedIds[i]);
}
if(currentCheckedIds.length > 2) currentCheckedIds = currentCheckedIds.slice(0, 2);
return currentCheckedIds;
}
$('.copy-btn').on('click', function()
{
var copyText = $(this).parent().parent().find('input');
copyText[0].select();
document.execCommand("Copy");
copyText[0].selectionStart = copyText[0].selectionEnd;
copyText[0].blur();
$(that).tooltip('show');
var that = this;
setTimeout(function()
{
$(that).tooltip('hide');
}, 2000)
})
/* 此方法在页面被替换时执行。 */
// function onPageUnmount()
// {
// /* 删除全局变量。 */
// delete window.canRowCheckable;
// delete window.diffClick;
// delete window.checkedChange;
// delete window.renderCommentCell;
// delete window.renderCell;
// }
window.afterPageUpdate = function()
{
setTimeout(function()
{
$('.copy-btn').tooltip({
trigger: 'click',
placement: 'bottom',
title: copied,
tipClass: 'success',
});
}, 200);
};
+223
View File
@@ -0,0 +1,223 @@
$(function()
{
$('#log').on('click', '.btn-close', closeRelation);
});
/**
* 切换仓库。
* Swtich repo.
*
* @param int $repoID
* @param string $module
* @param string $method
* @access public
* @return void
*/
function switchRepo(repoID, module, method)
{
if(typeof(eventKeyCode) == 'undefined') eventKeyCode = 0;
if(eventKeyCode > 0 && eventKeyCode != 13) return false;
/* The project id is a string, use it as the project model. */
if(isNaN(repoID))
{
$.cookie.set('projectMode', repoID, {expires:config.cookieLife, path:config.webRoot});
repoID = 0;
}
if(method != 'settings') method ="browse";
link = createLink(module, method, 'repoID=' + repoID);
location.href=link;
}
/**
* 切换分支。
* Switch branch for git.
*
* @param string $branchID
* @access public
* @return void
*/
function switchBranch(branchID)
{
$.cookie.set('repoBranch', branchID, {expires:config.cookieLife, path:config.webRoot});
$.cookie.set('repoRefresh', 1, {expires:config.cookieLife, path:config.webRoot});
location.href=location.href;
}
var distance = 0;
/**
* 左右切换关联信息。
* Aarrow tabs area.
*
* @param string domID
* @param number shift 1|-1
* @param bool hideRightBtn
* @access public
* @return void
*/
function arrowTabs(domID, shift, hideRightBtn)
{
if($('#' + domID).html() == '') return;
var hasParent = $('#' + domID + ' .btn-left').length;
var $leftBtn = hasParent ? $('#' + domID + ' .btn-left') : $('.btn-left');
var $rightBtn = hasParent ? $('#' + domID + ' .btn-right') : $('.btn-right');
$leftBtn.show();
$rightBtn.show();
if(hideRightBtn) $rightBtn.hide();
var tabItemWidth = 0;
if($('#' + domID + ' > .nav-tabs')[0]) tabItemWidth = $('#' + domID + ' > .nav-tabs')[0].clientWidth;
var tabsWidth = $('#' + domID)[0].clientWidth;
if($('#' + domID + ' .close-bugs').length) tabsWidth = tabsWidth * 0.7;
if(tabItemWidth <= tabsWidth)
{
$leftBtn.hide();
$rightBtn.hide();
$('#' + domID + ' > .nav-tabs')[0].style.transform = 'translateX(0px)';
return;
}
distance += tabsWidth * shift * 0.2;
if(distance > 0) distance = 0;
if(distance == 0)
{
$leftBtn.hide();
}
if((tabItemWidth + distance) <= tabsWidth * 0.75)
{
$rightBtn.hide();
return arrowTabs(domID, 1, true);
}
if(domID == 'monacoTabs' && distance < -60) distance = distance + 60;
$('#' + domID + ' > .nav-tabs')[0].style.transform = 'translateX('+ distance +'px)';
}
/**
* 关闭关联信息tab。
* Close commit relations.
*
* @access public
* @return void
*/
function closeRelation()
{
$('#relationTabs ul li').remove();
$('#relationTabs .tab-content .tab-pane').remove();
$('.history').html('');
$('#log').data('line', 0);
$('#log').hide();
$('#codeContainer').css('height', codeHeight);
$('#related').css('height', 0);
};
/**
* 获取tabs内容高度。
* Get tab-content height.
*
* @access public
* @return void
*/
function getIframeHeight()
{
if(iframeHeight) return iframeHeight;
var windowHeight = $(window).height();
var headerHeight = parseInt($('#header').height());
var mainNavbar = parseInt($('#mainNavbar').height());
var featureBar = parseInt($('#featureBar').height());
var tabsbar = parseInt($('.nav-tabs').height());
var mainMenuHeight = parseInt($('#mainMenu').css('padding-top')) + parseInt($('#mainMenu').css('padding-bottom'));
var appTabsHeight = parseInt($('#appTabs').height());
var appsBarHeight = parseInt($('#appsBar').height());
featureBar = featureBar ? featureBar : 0;
appsBarHeight = appsBarHeight ? appsBarHeight : 0;
appTabsHeight = appTabsHeight ? appTabsHeight : 0;
mainMenuHeight = mainMenuHeight ? mainMenuHeight : 0;
mainNavbar = mainNavbar ? mainNavbar : 0;
iframeHeight = windowHeight - headerHeight - appsBarHeight - appTabsHeight - mainMenuHeight - mainNavbar - tabsbar - 20 - featureBar;
return iframeHeight;
}
/**
* 获取左侧边栏高度。
* Get sidebar height.
*
* @access public
* @return void
*/
function getSidebarHeight()
{
if(sidebarHeight) return sidebarHeight;
var windowHeight = $(window).height();
var headerHeight = parseInt($('#header').height());
var mainNavbar = parseInt($('#mainNavbar').height());
var featureBar = parseInt($('#featureBar').height());
var repoMenu = parseInt($('#repoBranchDropMenu').height());
var mainMenuHeight = parseInt($('#mainMenu').css('padding-top')) + parseInt($('#mainMenu').css('padding-bottom'));
var appTabsHeight = parseInt($('#appTabs').height());
var appsBarHeight = parseInt($('#appsBar').height());
repoMenu = repoMenu ? repoMenu : 0;
featureBar = featureBar ? featureBar : 0;
appsBarHeight = appsBarHeight ? appsBarHeight : 0;
appTabsHeight = appTabsHeight ? appTabsHeight : 0;
mainMenuHeight = mainMenuHeight ? mainMenuHeight : 0;
mainNavbar = mainNavbar ? mainNavbar : 0;
sidebarHeight = windowHeight - headerHeight - appsBarHeight - appTabsHeight - mainMenuHeight - mainNavbar - repoMenu - featureBar;
return sidebarHeight;
}
/**
* 查找选中元素所有的父元素及选中元素的id。
* Find parent nodes and item id of selected tree item.
*
* @access public
* @return string
*/
function findItemInTreeItems(list, key, level) {
for (const item of list) {
if(level === 0)
{
parentTree = [item.key];
}
if (item.key === key) return item;
if (item.children && item.children.length > 0)
{
const findedItem = findItemInTreeItems(item.children, key);
if (findedItem)
{
parentTree.push(item.key);
return findedItem;
}
}
}
}
/**
* 展开树状结构。
* Expand tree node.
*
* @access public
* @return void
*/
function expandTree()
{
const treeObj = $('#monacoTree').parent().data('zui.Tree');
if(treeObj == undefined || treeObj.$ == undefined) return;
for (const key of parentTree) treeObj.$.expand(key);
}
+107
View File
@@ -0,0 +1,107 @@
$(function()
{
onScmChange();
});
function onProductChange(event)
{
var projects = $('[name="projects[]"]').val();
var products = $('[name="product[]"]').val();
$.post($.createLink('repo', 'ajaxProjectsOfProducts'), {products, projects}, function(response)
{
var data = JSON.parse(response);
var $projects = $('#projects').zui('picker');
$projects.render({items: data});
$projects.$.clear();
});
}
function onHostChange()
{
var host = $('[name=serviceHost]').val();
var url = $.createLink('repo', 'ajaxGetProjects', "host=" + host);
if(host == '') return false;
toggleLoading('#serviceProject', true);
$.get(url, function(response)
{
var data = JSON.parse(response);
var $project = $('#serviceProject').zui('picker');
$project.render({items: data});
$project.$.clear();
toggleLoading('#serviceProject', false);
});
}
function onProjectChange()
{
$option = $('#serviceProject option').eq($('#serviceProject').prop('selectedIndex'));
$('#name').val($option.data('name'));
}
/**
* Changed SCM.
*
* @param string $scm
* @access public
* @return void
*/
function onScmChange()
{
var scm = $('[name=SCM]').val();
if(!scm)
{
for(i in scmList)
{
scm = i;
break;
}
}
(scm == 'Git') ? $('.tips-git').removeClass('hidden') : $('.tips-git').addClass('hidden');
if(scm == 'Git' || scm == 'Gitea' || scm == 'Gogs')
{
$('.account-fields').hide();
$('#path').attr('placeholder', pathGitTip);
$('#client').attr('placeholder', clientGitTip);
}
else
{
$('.account-fields').show();
$('#path').attr('placeholder', pathSvnTip);
$('#client').attr('placeholder', clientSvnTip);
}
if(scm == 'Git' || scm == 'Subversion')
{
$('.service').toggle(false);
$('.hide-service').toggle(true);
}
else
{
$('.service').toggle(true);
if(scm == 'Gitea' || scm == 'Gogs')
{
$('.hide-service').each(function()
{
if(!$(this).hasClass('hide-git')) $(this).toggle(true);
});
$('.hide-git').toggle(false);
}
else
{
$('.hide-service').toggle(false);
}
var url = $.createLink('repo', 'ajaxGetHosts', "scm=" + scm);
$.get(url, function(response)
{
var data = JSON.parse(response);
var $hostPicker = $('#serviceHost').zui('picker');
$hostPicker.render({items: data});
$hostPicker.$.clear();
onHostChange();
});
}
}
+245
View File
@@ -0,0 +1,245 @@
var iframeHeight = 0;
var sidebarHeight = 0;
var tabTemp;
var diffAppose = $.cookie.get('renderSideBySide') == 'true';
/* Close tab. */
$('#monacoTabs').on('click', '.monaco-close', function()
{
var eleId = $(this).parent().attr('href');
var tabsEle = $(this).parent().parent().parent();
var isActive = $(this).parent().hasClass('active');
$(this).parent().parent().remove();
$(eleId).remove();
$('#' + eleId.substring(5)).parent().removeClass('selected');
if(isActive) tabsEle.children().last().find('a').trigger('click');
});
window.afterPageUpdate = function()
{
setTimeout(function()
{
var fileAsId = file.replace(/=/g, '-');
/* Resize moaco height. */
$('#monacoTree').css('height', getSidebarHeight() - 8 + 'px');
/* Init tab template. */
if(!tabTemp) tabTemp = $('#monacoTabs ul li').first().clone();
/* Load default tab content. */
var height = getIframeHeight();
$('#tab-' + fileAsId).html("<iframe class='repo-iframe' src='" + $.createLink('repo', 'ajaxGetDiffEditorContent', urlParams.replace('%s', fileAsId)) + "' width='100%' height='" + height + "' scrolling='no'></iframe>")
/* Select default tree item. */
const currentElement = findItemInTreeItems(tree, fileAsId, 0);
console.log(1111, $('#' + currentElement.id))
expandTree();
if(currentElement != undefined) setTimeout(() =>
{
$('#' + currentElement.id).parent().addClass('selected');
}, 100);
$('.btn-left').on('click', function() {arrowTabs('monacoTabs', 1);});
$('.btn-right').on('click', function() {arrowTabs('monacoTabs', -2);});
$('#repoDownloadCode').on('click', function()
{
var url = $(this).data('link');
var activeFilePath = $('#monacoTabs .nav-item .active').attr('href').substring(5).replace(/-/g, '=');
window.open(url.replace('{path}', activeFilePath));
return;
})
}, 300);
};
/**
* 点击左侧菜单打开详情tab。
* Open new tab when click tree item.
*
* @access public
* @return void
*/
window.treeClick = function(info)
{
if (info.item.items && info.item.items.length > 0) return;
$('#' + info.item.id).parent().addClass('selected');
openTab(info.item.key, info.item.text);
arrowTabs('monacoTabs', -2);
}
/**
* Change code encoding.
*
* @param string encoding
* @access public
* @return void
*/
function changeEncoding(encoding)
{
$('#encoding').val(encoding);
$('#encoding').parents('form').submit();
}
/**
* Html code decode.
*
* @param string str
* @access public
* @return string
*/
function htmlspecialchars_decode(str){
str = str.replace(/&amp;/g, '&');
str = str.replace(/&lt;/g, '<');
str = str.replace(/&gt;/g, '>');
str = str.replace(/&quot;/g, "''");
str = str.replace(/&#039;/g, "'");
return str;
}
/**
* Get diffs by file name.
*
* @param string fileName
* @access public
* @return object
*/
window.getDiffs = function(fileName)
{
if(fileName.indexOf('./') === 0) fileName = fileName.substring(2);
var result = {
'code': {'new': '', 'old': ''},
'line': {'new': [], 'old': []}
};
$.each(diffs, function(i, diff)
{
if(diff.fileName == fileName)
{
if(!diff.contents || typeof diff.contents[0].lines != 'object') return result;
$.each(diff.contents, function(k, content)
{
var lines = content.lines;
$.each(lines, function(l, code)
{
if(code.type == 'all' || code.type == 'new')
{
result.code.new += htmlspecialchars_decode(code.line.substring(1)) + "\n";
result.line.new.push(parseInt(code.newlc));
}
if(code.type == 'all' || code.type == 'old')
{
result.code.old += htmlspecialchars_decode(code.line.substring(1)) + "\n";
result.line.old.push(parseInt(code.oldlc));
}
})
})
return result;
}
});
return result;
}
/**
* 打开新tab。
* Open new tab.
*
* @access public
* @return void
*/
function openTab(entry, name)
{
var eleId = 'tab-' + entry.replace(/=/g, '-');
var element = document.getElementById(eleId);
if (element)
{
$("a[href='" + '#' + eleId + "']").trigger('click');
return;
}
var newTab = tabTemp.clone();
newTab.find('a').attr('href', '#' + eleId);
newTab.find('span').text(name);
$('#monacoTabs .nav-tabs').append(newTab);
var height = getIframeHeight();
$('#monacoTabs .tab-content').append("<div id='" + eleId + "' class='tab-pane active in'><iframe class='repo-iframe' src='" + $.createLink('repo', 'ajaxGetDiffEditorContent', urlParams.replace('%s', entry)) + "' width='100%' height='" + height + "' scrolling='no'></iframe></div>")
setTimeout(() => {
$("a[href='" + '#' + eleId + "']").trigger('click');
updateEditorInline('#' + eleId);
}, 100);
}
function updateEditorInline(eleId)
{
$.cookie.set('renderSideBySide', diffAppose);
if(typeof $(eleId + ' iframe')[0].contentWindow.updateEditorInline == 'function')
{
$(eleId + ' iframe')[0].contentWindow.updateEditorInline(diffAppose);
}
}
$('#monacoTabs .nav-item a').on('click', function()
{
var eleId = $(this).attr('href');
$(eleId + ' iframe')[0].contentWindow.updateEditorInline(diffAppose);
});
$(document).ready(function()
{
if(diffAppose)
{
$('.dropdown-menu #inline').show();
$('.dropdown-menu #appose').hide();
}
else
{
$('.dropdown-menu #appose').show();
$('.dropdown-menu #inline').hide();
}
$('.btn-left').on('click', function() {arrowTabs('monacoTabs', 1);});
$('.btn-right').on('click', function() {arrowTabs('monacoTabs', -2);});
});
$('.inline-appose').on('click', function()
{
$('.inline-appose').hide();
diffAppose = !diffAppose;
if(diffAppose)
{
$('.dropdown-menu #inline').show();
}
else
{
$('.dropdown-menu #appose').show();
}
var tabID = $('#monacoTabs .nav-item .active').attr('href');
updateEditorInline(tabID);
return;
});
$(".label-exchange").on('click', function()
{
var newDiffLink = diffLink.replace('{oldRevision}', newRevision);
newDiffLink = newDiffLink.replace('{newRevision}', oldRevision);
openUrl(newDiffLink);
});
/**
* 在当前页面用modal加载链接。
* Load link object page.
*
* @param string $link
* @access public
* @return void
*/
window.loadLinkPage = function(link)
{
$('#linkObject').attr('href', link);
$('#linkObject').trigger('click');
}
+110
View File
@@ -0,0 +1,110 @@
$(function()
{
onScmChange(true);
});
function onProductChange(event)
{
var projects = $('[name="projects[]"]').val();
var products = $('[name="product[]"]').val();
$.post($.createLink('repo', 'ajaxProjectsOfProducts'), {products, projects}, function(response)
{
var data = JSON.parse(response);
var $projects = $('#projects').zui('picker');
$projects.render({items: data});
$projects.$.clear();
});
}
function onHostChange()
{
var host = $('[name=serviceHost]').val();
var url = $.createLink('repo', 'ajaxGetProjects', "host=" + host);
if(host == '') return false;
toggleLoading('#serviceProject', true);
$.get(url, function(response)
{
var data = JSON.parse(response);
var $project = $('#serviceProject').zui('picker');
$project.render({items: data});
$project.$.clear();
toggleLoading('#serviceProject', false);
});
}
function onProjectChange()
{
$option = $('#serviceProject option').eq($('#serviceProject').prop('selectedIndex'));
$('#name').val($option.data('name'));
}
/**
* Changed SCM.
*
* @param bool $isFirstRequest
* @access public
* @return void
*/
function onScmChange(isFirstRequest = false)
{
var scm = $('[name=SCM]').val();
if(!scm)
{
for(i in scmList)
{
scm = i;
break;
}
}
(scm == 'Git') ? $('.tips-git').removeClass('hidden') : $('.tips-git').addClass('hidden');
if(scm == 'Git' || scm == 'Gitea' || scm == 'Gogs')
{
$('.account-fields').hide();
$('#path').attr('placeholder', pathGitTip);
$('#client').attr('placeholder', clientGitTip);
}
else
{
$('.account-fields').show();
$('#path').attr('placeholder', pathSvnTip);
$('#client').attr('placeholder', clientSvnTip);
}
if(scm == 'Git' || scm == 'Subversion')
{
$('.service').toggle(false);
$('.hide-service').toggle(true);
}
else
{
$('.service').toggle(true);
if(scm == 'Gitea' || scm == 'Gogs')
{
$('.hide-service').each(function()
{
if(!$(this).hasClass('hide-git')) $(this).toggle(true);
});
$('.hide-git').toggle(false);
}
else
{
$('.hide-service').toggle(false);
}
if(!isFirstRequset)
{
var url = $.createLink('repo', 'ajaxGetHosts', "scm=" + scm);
$.get(url, function(response)
{
var data = JSON.parse(response);
var $hostPicker = $('#serviceHost').zui('picker');
$hostPicker.render({items: data});
$hostPicker.$.clear();
onHostChange();
});
}
}
}
+46
View File
@@ -0,0 +1,46 @@
$(function()
{
checkItem();
$('.form-batch-row-actions .form-batch-btn').each(function()
{
if($(this).data('type') == 'add') $(this).addClass('hidden');
});
});
/**
* Check the project.
*
* @access public
* @return void
*/
function checkItem()
{
if(!$("#repoList tbody tr").length) $("#submit").prop('disabled', true);
}
/**
* Change server.
*
* @access public
* @return void
*/
function selectServer(event)
{
var server = $(event.target).val();
console.log(server);
if(server) loadPage($.createLink('repo', 'import', 'server=' + server));
}
function loadProductProjects(event)
{
const $target = $(event.target);
const $currentRow = $target.closest('tr');
const products = $target.val();
const projects = $currentRow.find('.form-batch-input[data-name="projects"]');
const projectIds = $(projects).val();
$.post($.createLink('repo', 'ajaxProjectsOfProducts'), {products : products, projects: projectIds, number : 1}, function(response)
{
$(projects).html(response);
});
}
+23
View File
@@ -0,0 +1,23 @@
window.createSortLink = function(col)
{
var sort = col.name + '_asc';
if(sort == orderBy) sort = col.name + '_desc';
return sortLink.replace('{orderBy}', sort);
}
$(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer .batch-btn', function(e)
{
const dtable = zui.DTable.query(e.target);
const checkedList = dtable.$.getChecks();
if(!checkedList.length) return;
const tabType = $(this).data('type');
const postData = [];
postData[`${tabType}[]`] = checkedList;
$.ajaxSubmit({
url: $(this).data('url'),
data: postData
});
});
+23
View File
@@ -0,0 +1,23 @@
window.createSortLink = function(col)
{
var sort = col.name + '_asc';
if(sort == orderBy) sort = col.name + '_desc';
return sortLink.replace('{orderBy}', sort);
}
$(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer .batch-btn', function(e)
{
const dtable = zui.DTable.query(e.target);
const checkedList = dtable.$.getChecks();
if(!checkedList.length) return;
const tabType = $(this).data('type');
const postData = [];
postData[`${tabType}[]`] = checkedList;
$.ajaxSubmit({
url: $(this).data('url'),
data: postData
});
});
+23
View File
@@ -0,0 +1,23 @@
window.createSortLink = function(col)
{
var sort = col.name + '_asc';
if(sort == orderBy) sort = col.name + '_desc';
return sortLink.replace('{orderBy}', sort);
}
$(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer .batch-btn', function(e)
{
const dtable = zui.DTable.query(e.target);
const checkedList = dtable.$.getChecks();
if(!checkedList.length) return;
const tabType = $(this).data('type');
const postData = [];
postData[`${tabType}[]`] = checkedList;
$.ajaxSubmit({
url: $(this).data('url'),
data: postData
});
});
+106
View File
@@ -0,0 +1,106 @@
var revisionMap = {};
var checkedIds = [];
/**
* 当选中两行时禁用其他行。
* Disable checkable attribution when checked rows equal 2.
*
* @param object changes
* @access public
* @return void
*/
window.checkedChange = function(changes)
{
checkedIds = getCurrentCheckedIds();
if(checkedIds.length < 2)
{
$('.btn-diff').addClass('disabled')
}
else
{
$('.btn-diff').removeClass('disabled')
}
}
/**
* 当选中数量等于2,则禁用其他所有行。
* When the selected row equals 2, disable all other rows.
*
* @param int rowID
* @access public
* @return bool
*/
window.canRowCheckable = function(rowID)
{
const dtable = zui.DTable.query('#repo-logs-table');
if(dtable == undefined) return;
var data = dtable.$.props.data;
if(data.length == 0) return true;
initRevisionMap(data);
var currentCheckedIds = getCurrentCheckedIds();
if(currentCheckedIds.length < 2) return true;
if(currentCheckedIds.indexOf(rowID) == -1) return 'disabled'
return true;
}
/**
* 检测revisionMap是否跟当前页面数据一致,不一致重新生成。
* Regenerate revisionMap when revisionMap is not in current list.
*
* @param array
* @access public
* @return void
*/
function initRevisionMap(data)
{
if(revisionMap[data[data.length - 1].id] !== undefined) return;
revisionMap = {};
for (var i = 0; i < data.length; i++) revisionMap[data[i].id] = data[i].revision;
}
/**
* 跳转比较差异页面。
* Redirect to diff page.
*
* @access public
* @return void
*/
window.diffClick = function()
{
var checkedIds = getCurrentCheckedIds();
var newDiffLink = diffLink.replace('{oldRevision}', revisionMap[checkedIds[1]]);
newDiffLink = newDiffLink.replace('{newRevision}', revisionMap[checkedIds[0]]);
openUrl(newDiffLink);
}
/**
* 获取当前页码选中的行。
* Get checked rows in current page.
*
* @access public
* @return array
*/
function getCurrentCheckedIds()
{
const dtable = zui.DTable.query('#repo-logs-table');
if(dtable.$ == undefined) return [];
var checkedIds = dtable.$.getChecks();
var currentCheckedIds = [];
for (var i = 0; i < checkedIds.length; i++)
{
if(revisionMap[checkedIds[i]]) currentCheckedIds.push(checkedIds[i]);
}
if(currentCheckedIds.length > 2) currentCheckedIds = currentCheckedIds.slice(0, 2);
return currentCheckedIds;
}
+135
View File
@@ -0,0 +1,135 @@
var iframeHeight = 0;
var sidebarHeight = 0;
var tabTemp;
var parentTree = [];
/* Close tab. */
$('#monacoTabs').on('click', '.monaco-close', function()
{
var eleId = $(this).parent().attr('href');
var tabsEle = $(this).parent().parent().parent();
var isActive = $(this).parent().hasClass('active');
$(this).parent().parent().remove();
$(eleId).remove();
$('#' + eleId.substring(5)).parent().removeClass('selected');
if(isActive) tabsEle.children().last().find('a').trigger('click');
arrowTabs('monacoTabs', -1);
});
window.afterPageUpdate = function()
{
setTimeout(function()
{
var fileAsId = file.replace(/=/g, '-');
/* Resize moaco height. */
$('#monacoTree').css('height', getSidebarHeight() - 8 + 'px');
/* Init tab template. */
if(!tabTemp) tabTemp = $('#monacoTabs ul li').first().clone();
/* Load default tab content. */
var height = getIframeHeight();
$('#tab-' + fileAsId).html("<iframe class='repo-iframe' src='" + $.createLink('repo', 'ajaxGetEditorContent', urlParams.replace('%s', fileAsId)) + "' width='100%' height='" + height + "' scrolling='no'></iframe>")
/* Select default tree item. */
const currentElement = findItemInTreeItems(tree, fileAsId, 0);
if(currentElement != undefined) $('#' + currentElement.id).parent().addClass('selected');
if(['Git', 'Gitlab', 'Gogs', 'Gitea'].indexOf(repo.SCM) == -1)
{
$('#sourceSwapper').hide();
}
expandTree();
$('.btn-left').on('click', function() {arrowTabs('monacoTabs', 1);});
$('.btn-right').on('click', function() {arrowTabs('monacoTabs', -2);});
/* Reload current page when click dropmeu. */
$('body').on('click', '.dropmenu-tree .dropmenu-item', function()
{
var branchOrTag = $(this).attr('title');
var url = $(this).data('url');
if(url != 'javascript:;') return;
if(branchOrTag != $.cookie.get('repoBranch')) $.cookie.set('repoBranch', branchOrTag);
openUrl(currentLink);
})
$('.repoDropDownMenu').on('click', function()
{
var url = $(this).data('link');
var activeFilePath = $('#monacoTabs .nav-item .active').attr('href').substring(5).replace(/-/g, '=');
if(url.indexOf('blame') >=0 && url.indexOf('download') == -1)
{
openUrl(url.replace('{path}', activeFilePath));
}
else
{
window.open(url.replace('{path}', activeFilePath));
}
return;
})
}, 200);
};
/**
* 点击左侧菜单打开详情tab。
* Open new tab when click tree item.
*
* @access public
* @return void
*/
window.treeClick = function(info)
{
if (info.item.items && info.item.items.length > 0) return;
$('#' + info.item.id).parent().addClass('selected');
openTab(info.item.key, info.item.text);
arrowTabs('monacoTabs', -2);
}
/**
* 打开新tab。
* Open new tab.
*
* @access public
* @return void
*/
function openTab(entry, name)
{
var eleId = 'tab-' + entry.replace(/=/g, '-');
var element = document.getElementById(eleId);
if (element)
{
$("a[href='" + '#' + eleId + "']").trigger('click');
return;
}
var newTab = tabTemp.clone();
newTab.find('a').attr('href', '#' + eleId);
newTab.find('span').text(name);
$('#monacoTabs .nav-tabs').append(newTab);
var height = getIframeHeight();
$('#monacoTabs .tab-content').append("<div id='" + eleId + "' class='tab-pane active in'><iframe class='repo-iframe' src='" + $.createLink('repo', 'ajaxGetEditorContent', urlParams.replace('%s', entry)) + "' width='100%' height='" + height + "' scrolling='no'></iframe></div>")
setTimeout(() => {
$("a[href='" + '#' + eleId + "']").trigger('click');
}, 100);
}
/**
* 在当前页面用modal加载链接。
* Load link object page.
*
* @param string $link
* @access public
* @return void
*/
window.loadLinkPage = function(link)
{
$('#linkObject').attr('href', link);
$('#linkObject').trigger('click');
}
+15
View File
@@ -0,0 +1,15 @@
function syncComments()
{
$.get(link, function(data)
{
if(data == 'finish') return openUrl(browseLink);
var count = parseInt(data);
if(isNaN(count)) count = 0;
$('#commits').html(parseInt($('#commits').html()) + count);
setTimeout(syncComments, 100);
});
}
setTimeout(syncComments, 500);
+6 -2
View File
@@ -28,7 +28,7 @@ $lang->repo->branch = 'Branch';
$lang->repo->tag = 'Tag';
$lang->repo->addWebHook = 'Add Webhook';
$lang->repo->apiGetRepoByUrl = 'API: Get repo by URL';
$lang->repo->blameTmpl = 'Code for line <strong>%line</strong>, <span class="tip-circular"></span> %name commited at %time, %version %comment';
$lang->repo->blameTmpl = 'Code for line <strong>%line</strong>: %name commited at %time, %version %comment';
$lang->repo->notRelated = 'There is currently no related ZenTao object';
$lang->repo->browseAction = 'Browse Repo';
@@ -51,6 +51,7 @@ $lang->repo->import = 'Import';
$lang->repo->importName = 'Name after import';
$lang->repo->importServer = 'Please select a server';
$lang->repo->gitlabList = 'Gitlab Repo';
$lang->repo->batchCreate = 'Batch create repo';
$lang->repo->submit = 'Submit';
$lang->repo->cancel = 'Cancel';
@@ -101,7 +102,7 @@ $lang->repo->diff = 'Diff';
$lang->repo->diffAB = 'Diff';
$lang->repo->diffAll = 'Diff All';
$lang->repo->viewDiff = 'View diff';
$lang->repo->allLog = 'All Revisions';
$lang->repo->allLog = 'All Commits';
$lang->repo->location = 'Location';
$lang->repo->file = 'File';
$lang->repo->action = 'Action';
@@ -120,6 +121,7 @@ $lang->repo->linkBug = 'Link Bug';
$lang->repo->linkTask = 'Link Task';
$lang->repo->unlink = 'Unlink';
$lang->repo->viewBugs = 'View Bugs';
$lang->repo->lastSubmitTime = 'Final submission time';
$lang->repo->title = 'Title';
$lang->repo->status = 'Status';
@@ -239,3 +241,5 @@ $lang->repo->typeList['performance'] = 'Performance';
$lang->repo->typeList['security'] = 'Security';
$lang->repo->typeList['redundancy'] = 'Redundancy';
$lang->repo->typeList['logicError'] = 'Logic Error';
$lang->repo->featureBar['maintain']['all'] = 'All';
+6 -2
View File
@@ -28,7 +28,7 @@ $lang->repo->branch = 'Branch';
$lang->repo->tag = 'Tag';
$lang->repo->addWebHook = 'Add Webhook';
$lang->repo->apiGetRepoByUrl = 'API: Get repo by URL';
$lang->repo->blameTmpl = 'Code for line <strong>%line</strong>, <span class="tip-circular"></span> %name commited at %time, %version %comment';
$lang->repo->blameTmpl = 'Code for line <strong>%line</strong>: %name commited at %time, %version %comment';
$lang->repo->notRelated = 'There is currently no related ZenTao object';
$lang->repo->browseAction = 'Browse Repo';
@@ -51,6 +51,7 @@ $lang->repo->import = 'Import';
$lang->repo->importName = 'Name after import';
$lang->repo->importServer = 'Please select a server';
$lang->repo->gitlabList = 'Gitlab Repo';
$lang->repo->batchCreate = 'Batch create repo';
$lang->repo->submit = 'Submit';
$lang->repo->cancel = 'Cancel';
@@ -101,7 +102,7 @@ $lang->repo->diff = 'Diff';
$lang->repo->diffAB = 'Diff';
$lang->repo->diffAll = 'Diff All';
$lang->repo->viewDiff = 'View diff';
$lang->repo->allLog = 'All Revisions';
$lang->repo->allLog = 'All Commits';
$lang->repo->location = 'Location';
$lang->repo->file = 'File';
$lang->repo->action = 'Action';
@@ -120,6 +121,7 @@ $lang->repo->linkBug = 'Link Bug';
$lang->repo->linkTask = 'Link Task';
$lang->repo->unlink = 'Unlink';
$lang->repo->viewBugs = 'View Bugs';
$lang->repo->lastSubmitTime = 'Final submission time';
$lang->repo->title = 'Title';
$lang->repo->status = 'Status';
@@ -239,3 +241,5 @@ $lang->repo->typeList['performance'] = 'Performance';
$lang->repo->typeList['security'] = 'Security';
$lang->repo->typeList['redundancy'] = 'Redundancy';
$lang->repo->typeList['logicError'] = 'Logic Error';
$lang->repo->featureBar['maintain']['all'] = 'All';
+6 -2
View File
@@ -28,7 +28,7 @@ $lang->repo->branch = 'Branch';
$lang->repo->tag = 'Tag';
$lang->repo->addWebHook = 'Add Webhook';
$lang->repo->apiGetRepoByUrl = 'API: Get repo by URL';
$lang->repo->blameTmpl = 'Code for line <strong>%line</strong>, <span class="tip-circular"></span> %name commited at %time, %version %comment';
$lang->repo->blameTmpl = 'Code for line <strong>%line</strong>: %name commited at %time, %version %comment';
$lang->repo->notRelated = 'There is currently no related ZenTao object';
$lang->repo->browseAction = 'Browse Repo';
@@ -51,6 +51,7 @@ $lang->repo->import = 'Import';
$lang->repo->importName = 'Name after import';
$lang->repo->importServer = 'Please select a server';
$lang->repo->gitlabList = 'Gitlab Repo';
$lang->repo->batchCreate = 'Batch create repo';
$lang->repo->submit = 'Soumettre';
$lang->repo->cancel = 'Annuler';
@@ -101,7 +102,7 @@ $lang->repo->diff = 'Diff';
$lang->repo->diffAB = 'Diff';
$lang->repo->diffAll = 'Toutes Diff';
$lang->repo->viewDiff = 'Voir diff';
$lang->repo->allLog = 'Toutes Révisions';
$lang->repo->allLog = 'All Commits';
$lang->repo->location = 'Localisation';
$lang->repo->file = 'Fichier';
$lang->repo->action = 'Action';
@@ -120,6 +121,7 @@ $lang->repo->linkBug = 'Link Bug';
$lang->repo->linkTask = 'Link Task';
$lang->repo->unlink = 'Unlink';
$lang->repo->viewBugs = 'View Bugs';
$lang->repo->lastSubmitTime = 'Final submission time';
$lang->repo->title = 'Titre';
$lang->repo->status = 'Statut';
@@ -239,3 +241,5 @@ $lang->repo->typeList['performance'] = 'Performance';
$lang->repo->typeList['security'] = 'Securité';
$lang->repo->typeList['redundancy'] = 'Redondance';
$lang->repo->typeList['logicError'] = 'Erreur Logique';
$lang->repo->featureBar['maintain']['all'] = 'All';
+3 -2
View File
@@ -168,8 +168,7 @@ $lang->repo->placeholder->gitlabHost = 'Input url of gitlab';
$lang->repo->notice = new stdclass();
$lang->repo->notice->syncing = 'Đang đồng bộ. Vui lòng đợi ...';
$lang->repo->notice->syncComplete = 'Đồng bộ hóa hoàn tất, nhảy ...';
$lang->repo->notice->syncFailed = 'Đồng bộ hóa không thành công.';
$lang->repo->notice->syncComplete = 'Synchronized. Now redirecting ...';
$lang->repo->notice->syncedCount = 'The number of records synchronized is ';
$lang->repo->notice->delete = 'Bạn có muốn xóa this repo?';
$lang->repo->notice->successDelete = 'Repository is removed.';
@@ -231,3 +230,5 @@ $lang->repo->typeList['performance'] = 'Hiệu suất';
$lang->repo->typeList['security'] = 'Bảo mật';
$lang->repo->typeList['redundancy'] = 'Redundancy';
$lang->repo->typeList['logicError'] = 'Logic Error';
$lang->repo->featureBar['maintain']['all'] = 'All';
+6 -2
View File
@@ -28,7 +28,7 @@ $lang->repo->branch = '分支';
$lang->repo->tag = '标签';
$lang->repo->addWebHook = '添加Webhook';
$lang->repo->apiGetRepoByUrl = '接口:通过URL获取代码库';
$lang->repo->blameTmpl = '第 <strong>%line</strong> 行代码相关信息,<span class="tip-circular"></span> %name 于 %time 提交 %version %comment';
$lang->repo->blameTmpl = '第 <strong>%line</strong> 行代码相关信息: %name 于 %time 提交 %version %comment';
$lang->repo->notRelated = '暂时没有关联禅道对象';
$lang->repo->browseAction = '浏览代码库';
@@ -51,6 +51,7 @@ $lang->repo->import = '导入';
$lang->repo->importName = '导入后的名称';
$lang->repo->importServer = '请选择服务器';
$lang->repo->gitlabList = 'Gitlab代码库';
$lang->repo->batchCreate = '批量添加代码库';
$lang->repo->submit = '提交';
$lang->repo->cancel = '取消';
@@ -101,7 +102,7 @@ $lang->repo->diff = '比较差异';
$lang->repo->diffAB = '比较';
$lang->repo->diffAll = '全部比较';
$lang->repo->viewDiff = '查看差异';
$lang->repo->allLog = '所有版本';
$lang->repo->allLog = '所有提交';
$lang->repo->location = '位置';
$lang->repo->file = '文件';
$lang->repo->action = '操作';
@@ -120,6 +121,7 @@ $lang->repo->linkBug = '关联Bug';
$lang->repo->linkTask = '关联任务';
$lang->repo->unlink = '取消关联';
$lang->repo->viewBugs = '查看Bug';
$lang->repo->lastSubmitTime = '最后提交时间';
$lang->repo->title = '标题';
$lang->repo->status = '状态';
@@ -239,3 +241,5 @@ $lang->repo->typeList['performance'] = '性能';
$lang->repo->typeList['security'] = '安全';
$lang->repo->typeList['redundancy'] = '冗余';
$lang->repo->typeList['logicError'] = '逻辑错误';
$lang->repo->featureBar['maintain']['all'] = '全部';
+2
View File
@@ -214,3 +214,5 @@ $lang->repo->typeList['performance'] = '性能';
$lang->repo->typeList['security'] = '安全';
$lang->repo->typeList['redundancy'] = '冗餘';
$lang->repo->typeList['logicError'] = '邏輯錯誤';
$lang->repo->featureBar['maintain']['all'] = '全部';
+207 -163
View File
@@ -44,7 +44,7 @@ class repoModel extends model
/* Check the privilege. */
if($repoID)
{
$repo = $this->getRepoByID($repoID);
$repo = $this->getByID($repoID);
if(empty($repo))
{
echo(js::alert($this->lang->repo->error->noFound));
@@ -58,10 +58,10 @@ class repoModel extends model
}
if(!in_array(strtolower($repo->SCM), $this->config->repo->gitServiceList)) unset($this->lang->devops->menu->mr);
if(count($repos) > 1) $this->lang->switcherMenu = $this->getSwitcher($repoID);
// if(count($repos) > 1) $this->lang->switcherMenu = $this->getSwitcher($repoID);
}
common::setMenuVars('devops', $repoID);
if(!in_array($this->app->methodName, array('maintain', 'create', 'edit','import'))) common::setMenuVars('devops', $repoID);
if(!session_id()) session_start();
$this->session->set('repoID', $repoID);
session_write_close();
@@ -81,7 +81,7 @@ class repoModel extends model
if(!in_array($currentModule, $this->config->repo->switcherModuleList)) return '';
if($currentModule == 'repo' and !in_array($currentMethod, $this->config->repo->switcherMethodList)) return '';
$currentRepo = $this->getRepoByID($repoID);
$currentRepo = $this->getByID($repoID);
$currentRepoName = $currentRepo->name;
if($this->app->viewType == 'mhtml' and $repoID)
@@ -111,10 +111,38 @@ class repoModel extends model
* @access public
* @return array
*/
public function getList($projectID = 0, $SCM = '', $orderBy = 'id_desc', $pager = null, $getCodePath = false)
public function getList($projectID = 0, $SCM = '', $orderBy = 'id_desc', $pager = null, $getCodePath = false, $lastSubmitTime = false, $type = '', $param = 0)
{
$repoQuery = '';
if($type == 'bySearch')
{
$queryID = $param;
$queryName = 'repoQuery';
if($queryID && $queryID != 'myQueryID')
{
$query = $this->loadModel('search')->getQuery($queryID);
if($query)
{
$this->session->set($queryName, $query->sql);
$this->session->set($queryName . 'Form', $query->form);
}
else
{
$this->session->set($queryName, ' 1 = 1');
}
}
else
{
if($this->session->$queryName == false) $this->session->set($queryName, ' 1 = 1');
}
$repoQuery = $this->session->$queryName;
}
$repos = $this->dao->select('*')->from(TABLE_REPO)
->where('deleted')->eq('0')
->beginIF(!empty($repoQuery))->andWhere($repoQuery)->fi()
->beginIF($SCM)->andWhere('SCM')->eq($SCM)->fi()
->orderBy($orderBy)
->page($pager)
@@ -154,6 +182,12 @@ class repoModel extends model
}
}
if($lastSubmitTime)
{
$lastRevision = $this->repoTao->getLastRevision($repo->id);
$repo->lastSubmitTime = isset($lastRevision->time) ? $lastRevision->time : '';
}
if(in_array(strtolower($repo->SCM), $this->config->repo->gitServiceList)) $repo = $this->processGitService($repo, $getCodePath);
}
@@ -189,52 +223,24 @@ class repoModel extends model
}
/**
* 创建版本库。
* Create a repo.
*
* @param object $repo
* @param bool $isPipelineServer
* @access public
* @return bool
* @return int|false
*/
public function create()
public function create(object $repo, bool $isPipelineServer): int|false
{
if(!$this->checkClient()) return false;
if(!$this->checkConnection()) return false;
$isPipelineServer = in_array(strtolower($this->post->SCM), $this->config->repo->gitServiceList) ? true : false;
$data = fixer::input('post')
->setIf($isPipelineServer, 'password', $this->post->serviceToken)
->setIf($this->post->SCM == 'Gitlab', 'path', '')
->setIf($this->post->SCM == 'Gitlab', 'client', '')
->setIf($this->post->SCM == 'Gitlab', 'extra', $this->post->serviceProject)
->setIf($isPipelineServer, 'prefix', '')
->setIf($this->post->SCM == 'Git', 'account', '')
->setIf($this->post->SCM == 'Git', 'password', '')
->skipSpecial('path,client,account,password')
->setDefault('product', '')
->join('product', ',')
->setDefault('projects', '')->join('projects', ',')
->get();
$data->acl = empty($data->acl) ? '' : json_encode($data->acl);
if($data->SCM == 'Subversion')
{
$scm = $this->app->loadClass('scm');
$scm->setEngine($data);
$info = $scm->info('');
$infoRoot = urldecode($info->root);
$data->prefix = empty($infoRoot) ? '' : trim(str_ireplace($infoRoot, '', str_replace('\\', '/', $data->path)), '/');
if($data->prefix) $data->prefix = '/' . $data->prefix;
}
if($data->encrypt == 'base64') $data->password = base64_encode($data->password);
$this->dao->insert(TABLE_REPO)->data($data, $skip = 'serviceToken')
$this->dao->insert(TABLE_REPO)->data($repo, 'serviceToken')
->batchCheck($this->config->repo->create->requiredFields, 'notempty')
->batchCheckIF($data->SCM != 'Gitlab', 'path,client', 'notempty')
->batchCheckIF($repo->SCM != 'Gitlab', 'path,client', 'notempty')
->batchCheckIF($isPipelineServer, 'serviceHost,serviceProject', 'notempty')
->batchCheckIF($data->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty')
->check('name', 'unique', "`SCM` = '{$data->SCM}'")
->checkIF($isPipelineServer, 'serviceProject', 'unique', "`SCM` = '{$data->SCM}' and `serviceHost` = '{$data->serviceHost}'")
->checkIF(!$isPipelineServer, 'path', 'unique', "`SCM` = '{$data->SCM}' and `serviceHost` = '{$data->serviceHost}'")
->batchCheckIF($repo->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty')
->check('name', 'unique', "`SCM` = '{$repo->SCM}'")
->checkIF($isPipelineServer, 'serviceProject', 'unique', "`SCM` = '{$repo->SCM}' and `serviceHost` = '{$repo->serviceHost}'")
->checkIF(!$isPipelineServer, 'path', 'unique', "`SCM` = '{$repo->SCM}' and `serviceHost` = '{$repo->serviceHost}'")
->autoCheck()
->exec();
@@ -242,46 +248,23 @@ class repoModel extends model
$this->rmClientVersionFile();
$repoID = $this->dao->lastInsertID();
if($this->post->SCM == 'Gitlab')
{
/* Add webhook. */
$repo = $this->getRepoByID($repoID);
$this->loadModel('gitlab')->addPushWebhook($repo);
}
return $repoID;
return $this->dao->lastInsertID();
}
/**
* 批量创建版本库。
* Batch create repos.
*
* @param array $repos
* @access public
* @return bool
*/
public function batchCreate()
public function batchCreate(array $repos, int $serviceHost): bool
{
$repos = fixer::input('post')->get();
$dataAry = array();
foreach($repos as $key => $val)
{
if(strpos($key, 'serviceProject') === 0)
{
$i = substr($key, 14);
if(empty($repos->{'product' . $i})) dao::$errors['product' . $i][] = sprintf($this->lang->error->notempty, $this->lang->repo->product);
if(empty($repos->{'name' . $i})) dao::$errors['name' . $i][] = sprintf($this->lang->error->notempty, $this->lang->repo->name);
if(!empty($repos->{'product' . $i}) and !empty($repos->{'name' . $i})) $dataAry[] = array('serviceProject' => $repos->{'serviceProject' . $i}, 'product' => implode(',', $repos->{'product' . $i}), 'name' => $repos->{'name' . $i}, 'projects' => empty($repos->projects[$i]) ? '' : implode(',', $repos->projects[$i]));
}
}
if(dao::isError()) return false;
foreach($dataAry as $data)
foreach($repos as $data)
{
$repo = new stdclass();
$repo->serviceHost = $repos->serviceHost;
$repo->serviceHost = $serviceHost;
$repo->serviceProject = $data['serviceProject'];
$repo->product = $data['product'];
$repo->name = $data['name'];
@@ -305,11 +288,11 @@ class repoModel extends model
if($repo->SCM == 'Gitlab')
{
/* Add webhook. */
$repo = $this->getRepoByID($repoID);
$repo = $this->getByID($repoID);
$this->loadModel('gitlab')->addPushWebhook($repo);
}
$actionID = $this->loadModel('action')->create('repo', $repoID, 'created');
$this->loadModel('action')->create('repo', $repoID, 'created');
}
return true;
@@ -324,7 +307,7 @@ class repoModel extends model
*/
public function update($id)
{
$repo = $this->getRepoByID($id);
$repo = $this->getByID($id);
if($repo->client != $this->post->client and !$this->checkClient()) return false;
if(!$this->checkConnection()) return false;
@@ -363,7 +346,7 @@ class repoModel extends model
}
if($data->encrypt == 'base64') $data->password = base64_encode($data->password);
$this->dao->update(TABLE_REPO)->data($data, $skip = 'serviceToken')
$this->dao->update(TABLE_REPO)->data($data, 'serviceToken')
->batchCheck($this->config->repo->edit->requiredFields, 'notempty')
->batchCheckIF($data->SCM != 'Gitlab', 'path,client', 'notempty')
->batchCheckIF($isPipelineServer, 'serviceHost,serviceProject', 'notempty')
@@ -376,7 +359,7 @@ class repoModel extends model
$this->rmClientVersionFile();
if($data->SCM == 'Gitlab') $data->path = $this->getRepoByID($id)->path;
if($data->SCM == 'Gitlab') $data->path = $this->getByID($id)->path;
if($repo->path != $data->path)
{
@@ -524,47 +507,70 @@ class repoModel extends model
/**
* Get repos group by repo type.
*
* @param string type
* @param string $type
* @param int $projectID
* @param string $repoType
* @access public
* @return array
*/
public function getRepoGroup($type, $projectID = 0)
public function getRepoGroup(string $type, int $projectID = 0, string $repoType = ''): array
{
$repoPairs = $this->getRepoPairs($type, $projectID);
$repos = $this->dao->select('*')->from(TABLE_REPO)
->where('deleted')->eq(0)
->beginIF($repoType == 'git')->andWhere('SCM')->in($this->config->repo->gitServiceTypeList)->fi()
->fetchAll();
$repos = array();
foreach($this->lang->repo->scmList as $scmType => $scm) $repos[$scmType] = array();
foreach($repoPairs as $id => $repo)
$productIds = $productItems = array();
foreach($repos as $repo)
{
if(strpos($repo, '[gitlab]') !== false)
$productIds = array_merge($productIds, explode(',', $repo->product));
}
$products = $this->loadModel('product')->getByIdList(array_filter(array_unique($productIds)));
foreach($products as $productID => $product)
{
$productItem = array();
$productItem['id'] = $productID;
$productItem['type'] = 'product';
$productItem['text'] = $product->name;
$productItem['items'] = array();
$productItems[$productID] = $productItem;
}
/* Get project products. */
$projectProductIds = ($type == 'project' or $type == 'execution') ? $this->loadModel('product')->getProductIDByProject((int)$projectID, false) : array();
/* Get repo data for dropmenu. */
$repoPairs = array();
foreach($repos as $repo)
{
$repo->acl = json_decode($repo->acl);
$scm = $repo->SCM == 'Subversion' ? 'svn' : strtolower($repo->SCM);
if($this->checkPriv($repo))
{
$repo = str_replace('[gitlab]', '', $repo);
$repos['Gitlab'][$id] = $repo;
}
elseif(strpos($repo, '[gogs]') !== false)
{
$repo = str_replace('[gogs]', '', $repo);
$repos['Gogs'][$id] = $repo;
}
elseif(strpos($repo, '[gitea]') !== false)
{
$repo = str_replace('[gitea]', '', $repo);
$repos['Gitea'][$id] = $repo;
}
elseif(strpos($repo, '[svn]') !== false)
{
$repo = str_replace('[svn]', '', $repo);
$repos['SVN'][$id] = $repo;
}
elseif(strpos($repo, '[git]') !== false)
{
$repo = str_replace('[git]', '', $repo);
$repos['Git'][$id] = $repo;
$repoItem = array();
$repoItem['id'] = $repo->id;
$repoItem['text'] = $repo->name;
$repoItem['keys'] = zget(common::convert2Pinyin(array($repo->name)), $repo->name, '');
$repoProducts = explode(',', $repo->product);
foreach($repoProducts as $productID)
{
if(($type == 'project' or $type == 'execution') and $projectID)
{
if(!in_array($productID, $projectProductIds)) continue;
}
if(strpos(",$repo->product,", ",$productID,") !== false)
{
if(!isset($repoPairs[$productID])) $repoPairs[$productID] = $productItems[$productID];
$repoPairs[$productID]['items'][] = $repoItem;
}
}
}
}
return $repos;
return $repoPairs;
}
/**
@@ -574,8 +580,9 @@ class repoModel extends model
* @access public
* @return object
*/
public function getRepoByID($repoID)
public function getByID($repoID)
{
if(empty($repoID)) return new stdclass();
$repo = $this->dao->select('*')->from(TABLE_REPO)->where('id')->eq($repoID)->fetch();
if(!$repo) return false;
@@ -637,7 +644,7 @@ class repoModel extends model
$conditions = array();
foreach($matches as $matched) $conditions[] = "(`client`='$matched->gitlab' and `path`='$matched->project')";
$conditions = '(' . join(' OR ', $conditions). ')';
$conditions = '(' . implode(' OR ', $conditions). ')';
$matchedRepos = $this->dao->select('*')->from(TABLE_REPO)->where('SCM')->eq('Gitlab')
->andWhere($conditions)
@@ -716,7 +723,7 @@ class repoModel extends model
$conditions = array();
foreach($matches as $matched) $conditions[] = "(`client`='$matched->gitlab' and `path`='$matched->project')";
$conditions = '(' . join(' OR ', $conditions). ')';
$conditions = '(' . implode(' OR ', $conditions). ')';
$matchedRepos = $this->dao->select('*')->from(TABLE_REPO)->where('SCM')->eq('Gitlab')
->andWhere($conditions)
@@ -742,7 +749,7 @@ class repoModel extends model
public function getByIdList($idList)
{
$repos = $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq(0)->andWhere('id')->in($idList)->fetchAll();
foreach($repos as $i => $repo)
foreach($repos as $repo)
{
if($repo->encrypt == 'base64') $repo->password = base64_decode($repo->password);
$repo->acl = json_decode($repo->acl);
@@ -803,6 +810,7 @@ class repoModel extends model
*/
public function getCommits($repo, $entry, $revision = 'HEAD', $type = 'dir', $pager = null, $begin = 0, $end = 0)
{
if(!isset($repo->id)) return array();
if($repo->SCM == 'Gitlab') return $this->loadModel('gitlab')->getCommits($repo, $entry, $revision, $type, $pager, $begin, $end);
$entry = ltrim($entry, '/');
@@ -859,6 +867,7 @@ class repoModel extends model
$repoComment->originalComment = $repoComment->comment;
$repoComment->comment = $this->replaceCommentLink($repoComment->comment);
}
return $comments;
}
@@ -886,7 +895,7 @@ class repoModel extends model
->fetch();
if(empty($lastComment)) return null;
$repo = $this->getRepoByID($repoID);
$repo = $this->getByID($repoID);
if($repo->SCM == 'Git' and $lastComment->commit != $count)
{
$this->fixCommit($repo->id);
@@ -984,7 +993,7 @@ class repoModel extends model
*/
public function getProductsByRepo($repoID)
{
$repo = $this->getRepoByID($repoID);
$repo = $this->getByID($repoID);
if(empty($repo)) return array();
return $this->dao->select('id,name')->from(TABLE_PRODUCT)
@@ -1347,7 +1356,7 @@ class repoModel extends model
*/
public function setRepoBranch($branch)
{
setcookie("repoBranch", $branch, 0, $this->config->webRoot, '', $this->config->cookieSecure);
helper::setcookie("repoBranch", $branch, 0, $this->config->webRoot, '', $this->config->cookieSecure);
$_COOKIE['repoBranch'] = $branch;
}
@@ -1428,7 +1437,6 @@ class repoModel extends model
$blk = substr($content, 0, 512);
return (
false ||
substr_count($blk, "^\r\n")/512 > 0.3 ||
substr_count($blk, "^ -~")/512 > 0.3 ||
substr_count($blk, "\x00") > 0
@@ -1521,7 +1529,7 @@ class repoModel extends model
exec($versionCommand, $versionOutput, $versionResult);
if($versionResult)
{
$message = sprintf($this->lang->repo->error->output, $versionCommand, $versionResult, join("<br />", $versionOutput));
$message = sprintf($this->lang->repo->error->output, $versionCommand, $versionResult, implode("<br />", $versionOutput));
dao::$errors['client'] = $this->lang->repo->error->cmd . "<br />" . nl2br($message);
return false;
}
@@ -1539,7 +1547,7 @@ class repoModel extends model
$command = "$client info --username $account --password $password --non-interactive --trust-server-cert-failures=cn-mismatch --trust-server-cert --no-auth-cache $path 2>&1";
if(version_compare($svnVersion, '1.9', '<')) $command = "$client info --username $account --password $password --non-interactive --trust-server-cert --no-auth-cache $path 2>&1";
}
else if(stripos($path, 'file://') === 1)
elseif(stripos($path, 'file://') === 1)
{
$command = "$client info --non-interactive --no-auth-cache $path 2>&1";
}
@@ -1551,7 +1559,7 @@ class repoModel extends model
exec($command, $output, $result);
if($result)
{
$message = sprintf($this->lang->repo->error->output, $command, $result, join("<br />", $output));
$message = sprintf($this->lang->repo->error->output, $command, $result, implode("<br />", $output));
if(stripos($message, 'Expected FS format between') !== false and strpos($message, 'found format') !== false)
{
dao::$errors['client'] = $this->lang->repo->error->clientVersion;
@@ -1614,7 +1622,7 @@ class repoModel extends model
exec($command, $output, $result);
if($result)
{
dao::$errors['submit'] = $this->lang->repo->error->connect . "<br />" . sprintf($this->lang->repo->error->output, $command, $result, join("<br />", $output));
dao::$errors['submit'] = $this->lang->repo->error->connect . "<br />" . sprintf($this->lang->repo->error->output, $command, $result, implode("<br />", $output));
return false;
}
}
@@ -1631,9 +1639,6 @@ class repoModel extends model
public function replaceCommentLink($comment)
{
$rules = $this->processRules();
$stories = array();
$tasks = array();
$bugs = array();
$storyReg = '/' . $rules['storyReg'] . '/i';
$taskReg = '/' . $rules['taskReg'] . '/i';
$bugReg = '/' . $rules['bugReg'] . '/i';
@@ -1948,7 +1953,7 @@ class repoModel extends model
}
else
{
$this->task->recordEstimate($taskID);
$this->task->recordWorkhour($taskID);
}
$action->action = $scm == 'svn' ? 'svncommited' : 'gitcommited';
@@ -2168,7 +2173,7 @@ class repoModel extends model
{
$record = array();
$record['execution'] = $executionID;
$record['product'] = isset($products[$taskID]) ? "," . join(',', array_keys($products[$taskID])) . "," : ",0,";
$record['product'] = isset($products[$taskID]) ? "," . implode(',', array_keys($products[$taskID])) . "," : ",0,";
$records[$taskID] = $record;
}
return $records;
@@ -2221,7 +2226,8 @@ class repoModel extends model
{
if($getCodePath) $project = $this->loadModel('gitlab')->apiGetSingleProject($repo->serviceHost, $repo->serviceProject);
$repo->path = $service ? sprintf($this->config->repo->{$service->type}->apiPath, $service->url, $repo->serviceProject) : '';
$repo->path = (!$repo->path && $service) ? sprintf($this->config->repo->{$service->type}->apiPath, $service->url, $repo->serviceProject) : $repo->path;
$repo->apiPath = sprintf($this->config->repo->{$service->type}->apiPath, $service->url, $repo->serviceProject);
$repo->client = $service ? $service->url : '';
$repo->password = $service ? $service->token : '';
$repo->codePath = isset($project->web_url) ? $project->web_url : $repo->path;
@@ -2382,7 +2388,7 @@ class repoModel extends model
/* Get file commits by repo. */
if($repo->SCM != 'Subversion' and empty($branch)) $branch = $this->cookie->repoBranch;
$fileCommits = $this->dao->select('t1.id,t1.path,t1.type,t1.action,t1.oldPath,t1.parent,t2.revision,t2.`comment`,t2.committer,t2.time')->from(TABLE_REPOFILES)->alias('t1')
$fileCommits = $this->dao->select('t1.id,t1.path,t1.type,t1.action,t1.oldPath,t1.parent,t2.revision,t2.comment,t2.committer,t2.time')->from(TABLE_REPOFILES)->alias('t1')
->leftJoin(TABLE_REPOHISTORY)->alias('t2')->on('t1.revision=t2.id')
->leftJoin(TABLE_REPOBRANCH)->alias('t3')->on('t2.id=t3.revision')
->where('t1.repo')->eq($repo->id)
@@ -2526,14 +2532,11 @@ class repoModel extends model
}
$infos = array('revision' => $lastRevision, 'files' => $allFiles);
if($cacheFile)
if($cacheFile && !file_exists($cacheFile . '.lock'))
{
if(!file_exists($cacheFile . '.lock'))
{
touch($cacheFile . '.lock');
file_put_contents($cacheFile, serialize($infos));
unlink($cacheFile . '.lock');
}
touch($cacheFile . '.lock');
file_put_contents($cacheFile, serialize($infos));
unlink($cacheFile . '.lock');
}
}
else
@@ -2602,7 +2605,7 @@ class repoModel extends model
{
$files = array();
$id = 0;
foreach($allFiles as $key => $file)
foreach($allFiles as $file)
{
$fileName = explode('/', $file);
$parent = '';
@@ -2612,40 +2615,24 @@ class repoModel extends model
$parentID = $parent == '' ? 0 : $files[$parent]['id'];
$parent .= $parent == '' ? $path : '/' . $path;
if(!isset($files[$parent])){
if(!isset($files[$parent]))
{
$id++;
$id = str_replace('=', '-', base64_encode(urlencode($parent)));
$files[$parent] = array(
'id' => $id,
'parent' => $parentID,
'name' => $path,
'path' => $parent,
'key' => $key,
'key' => $id,
);
}
}
}
sort($files);
$fileTree = $this->buildTree($files);
$html = '<ul data-name="filesTree" data-ride="tree" data-initial-state="preserve" id="modules" class="tree">';
foreach($fileTree as $file)
{
$html .= "<li class='open' data-id='{$file['id']}'>";
if(isset($file['children']))
{
$html .= "<i class='icon icon-folder'></i> {$file['name']}";
$html .= $this->getFrontFiles($file['children']);
}
else
{
$html .= "<span class='item doc-title text-ellipsis'><i class='icon icon-file-text-alt'></i> " . html::a('#filePath' . $file['key'], $file['name'], '', "class='repoFileName' data-path='{$file['path']}' title='{$file['path']}'") . '</span>';
}
$html .= '</li>';
}
$html .= '</ul>';
return $html;
return $this->buildTree($files);
}
/**
@@ -2733,7 +2720,7 @@ class repoModel extends model
$output = new stdClass();
$scm = $this->app->loadClass('scm');
$repo = $this->getRepoByID($repoID);
$repo = $this->getByID($repoID);
if(!$repo) return $output;
$scm->setEngine($repo);
@@ -2806,8 +2793,8 @@ class repoModel extends model
}
}
$stories = empty($storyIDs) ? array() : $this->loadModel('story')->getByList($storyIDs);
$bugs = empty($bugIDs) ? array() : $this->loadModel('bug')->getByList($bugIDs);
$tasks = empty($taskIDs) ? array() : $this->loadModel('task')->getByList($taskIDs);
$bugs = empty($bugIDs) ? array() : $this->loadModel('bug')->getByIdList($bugIDs);
$tasks = empty($taskIDs) ? array() : $this->loadModel('task')->getByIdList($taskIDs);
$titleList = array();
foreach($relationList as $key => $relation)
@@ -2868,7 +2855,7 @@ class repoModel extends model
public function insertDeleteRecord($repoID)
{
set_time_limit(0);
$repo = $this->loadModel('repo')->getRepoByID($repoID);
$repo = $this->loadModel('repo')->getByID($repoID);
if(empty($repo)) return false;
$scm = $this->app->loadClass('scm');
@@ -2935,7 +2922,7 @@ class repoModel extends model
if($values)
{
$sql = 'INSERT INTO ' . TABLE_REPOFILES . ' (`repo`,`revision`,`path`,`oldPath`,`parent`,`type`,`action`) VALUES ' . trim($values, ',');
$result = $this->dao->exec($sql);
$this->dao->exec($sql);
}
$this->loadModel('setting')->setItem('system.repo.synced', $this->config->repo->synced . ',' . $repoID);
@@ -2974,7 +2961,7 @@ class repoModel extends model
*/
public function updateCommit($repoID, $objectID = 0, $branchID = 0)
{
$repo = $this->getRepoByID($repoID);
$repo = $this->getByID($repoID);
/* Update code commit history. */
$commentGroup = $this->loadModel('job')->getTriggerGroup('commit', array($repoID));
@@ -3027,6 +3014,63 @@ class repoModel extends model
return true;
}
/**
* 判断按钮是否可点击。
* Judge an action is clickable or not.
*
* @param object $repo
* @param string $action
* @access public
* @return bool
*/
public static function isClickable(object $repo, string $action): bool
{
$action = strtolower($action);
if($action == 'execjob') return $repo->exec == '';
if($action == 'reportview') return $repo->report == '';
return true;
}
/**
* 获取gitlab项目列表。
* Get gitlab projects.
*
* @param int $gitlabID
* @param string $projectIdList
* @param string $filter
* @access public
* @return array
*/
public function getGitlabProjects(int $gitlabID, string $projectIdList = '', string $filter = ''): array
{
$showAll = ($filter == 'ALL' and common::hasPriv('repo', 'create')) ? true : false;
if($this->app->user->admin or $showAll)
{
$projects = $this->loadModel('gitlab')->apiGetProjects($gitlabID, true, 0, 0, false);
}
else
{
$gitlabUser = $this->loadModel('gitlab')->getUserIDByZentaoAccount($gitlabID, $this->app->user->account);
if(!$gitlabUser) $this->send(array('message' => array()));
$projects = $this->gitlab->apiGetProjects($gitlabID, $filter ? 'false' : 'true');
$groupIDList = array(0 => 0);
$groups = $this->gitlab->apiGetGroups($gitlabID, 'name_asc', 'developer');
foreach($groups as $group) $groupIDList[] = $group->id;
if($filter == 'IS_DEVELOPER')
{
foreach($projects as $key => $project)
{
if(!$this->gitlab->checkUserAccess($gitlabID, 0, $project, $groupIDList, 'developer')) unset($projects[$key]);
}
}
}
return $projects;
}
/**
* Check str in array.
*
+28
View File
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* The tao file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
class repoTao extends repoModel
{
/**
* 获取最后一次提交信息。
* Get last revision.
*
* @param int $repoID
* @access protected
* @return object|false
*/
protected function getLastRevision(int $repoID): object|false
{
return $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->orderBy('time_desc')->fetch();
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/**
* The head switcher view file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
$data = array('repo' => array_values($repoGroup));
$tabs = array();
$tabs[] = array('name' => 'repo', 'text' => $lang->repo->codeRepo);
$json = array();
$json['data'] = $data;
$json['tabs'] = $tabs;
$json['searchHint'] = $lang->searchAB;
$json['labelMap'] = array('product' => $lang->product->common);
$json['link'] = array('repo' => sprintf($link, '{id}'));
$json['itemType'] = 'repo';
renderJson($json);
@@ -0,0 +1,159 @@
<?php
declare(strict_types=1);
/**
* The ajaxgeteditorcontent view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao<zhaoke@easycorp.ltd>
* @package repo
* @link http://www.zentao.net
*/
namespace zin;
$canLinkStory = common::hasPriv('repo', 'linkStory');
$canLinkBug = common::hasPriv('repo', 'linkBug');
$canLinkTask = common::hasPriv('repo', 'linkTask');
$canUnlinkObject = common::hasPriv('repo', 'unlink');
jsVar('unlinkTitle', $this->lang->repo->unlink);
jsVar('canUnlinkObject', $canUnlinkObject);
jsVar('fileExt', $this->config->repo->fileExt);
jsVar('file', $pathInfo);
jsVar('filePath', $entry);
jsVar('blameTmpl', $lang->repo->blameTmpl);
jsVar('repoID', $repoID);
jsVar('showEditor', $showEditor);
jsVar('canLinkStory', $canLinkStory);
jsVar('canLinkBug', $canLinkBug);
jsVar('canLinkTask', $canLinkTask);
jsVar('objectID', 0);
jsVar('objectType', 'story');
jsVar('pageType', $type);
jsVar('revision', $revision);
jsVar('sourceRevision', $oldRevision);
jsVar('encodePath', $this->repo->encodePath($entry));
if($showEditor) jsVar('codeContent', $content);
$lang = 'php';
foreach($this->config->repo->fileExt as $langName => $exts)
{
foreach($exts as $ext) if(isset($pathInfo['extension']) && str_contains('.' . $pathInfo['extension'], $ext)) $lang = $langName;
}
if(strpos($config->repo->images, "|$suffix|") !== false)
{
$wg = div
(
set::class('image'),
img(set::src('data:image/' . $suffix . ';base64,' . $content))
);
}
elseif($suffix == 'binary')
{
$wg = div
(
set::class('binary'),
a
(
set::href($this->repo->createLink('download', "repoID=$repoID&path=" . $this->repo->encodePath($entry) . "&fromRevision=$revision")),
icon('download'),
set::target('_blank'),
set::title($this->lang->repo->download),
)
);
}
else
{
$options = array(
'value' => $content,
'language' => $lang,
'readOnly' => true,
'autoIndent' => true,
'contextmenu' => true,
'automaticLayout' => true,
'EditorMinimapOptions' => array('enabled' => false),
);
if($type == 'diff') $options = array(
'language' => $lang,
'readOnly' => true,
'autoIndent' => true,
'contextmenu' => true,
'automaticLayout' => true,
'renderSideBySide' => false,
'EditorMinimapOptions' => array('enabled' => false),
);
$wg = monaco
(
set::id('codeContainer'),
set::options($options),
set::action($type == 'diff' ? 'diff' : 'create'),
$type == 'diff' ? set::diffContent(jsRaw('parent.getDiffs(filePath)')) : null,
set::onMouseDown('window.onMouseDown')
);
}
$dropMenus = array();
if($canLinkStory) $dropMenus[] = array('id' => 'linkStory', 'text' => $this->lang->repo->linkStory, 'icon' => 'lightbulb');
if($canLinkBug) $dropMenus[] = array('id' => 'linkBug', 'text' => $this->lang->repo->linkBug, 'icon' => 'bug');
if($canLinkTask) $dropMenus[] = array('id' => 'linkTask', 'text' => $this->lang->repo->linkTask, 'icon' => 'todo');
$logWg = div
(
set::id('log'),
div(set::class('history')),
div
(
set::class('action-btn pull-right'),
div(set::class('btn btn-close pull-right ghost text-black bg-light bg-opacity-50'), icon('close')),
!empty($dropMenus) ? dropdown
(
set::arrow(false),
set::staticMenu(true),
btn
(
setClass('ghost text-black bg-light bg-opacity-50'),
set::icon('ellipsis-v rotate-90'),
),
set::items
(
$dropMenus
),
) : '',
),
);
$relatedWg = div
(
set::id('related'),
div(set::class('btn btn-left pull-left'), icon('chevron-left')),
div(set::class('btn btn-right pull-right'), icon('chevron-right')),
div
(
set::class('panel-title'),
tabs
(
set::id('relationTabs'),
tabPane
(
set::key('tab1'),
set::title(''),
tableData()
),
),
),
div(set::class('table-empty-tip'), p($this->lang->repo->notRelated))
);
div
(
set::id('monacoEditor'),
set::class('repoCode'),
$wg,
$logWg,
$relatedWg
);
set::zui(true);
render('pageBase');
+106
View File
@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
/**
* The blame view file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
dropmenu(set::module('repo'), set::tab('repo'), set::objectID($repo->id));
/* Prepare breadcrumb navigation data. */
$base64BranchID = helper::safe64Encode(base64_encode($branchID));
$breadcrumbItems = array();
$breadcrumbItems[] = h::a
(
set::href($this->repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID")),
$repo->name,
);
$breadcrumbItems[] = h::span('/', setStyle('margin', '0 5px'));
$paths = explode('/', $entry);
$fileName = array_pop($paths);
$postPath = '';
foreach($paths as $pathName)
{
$postPath .= $pathName . '/';
$breadcrumbItems[] = h::a
(
set::href($this->repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID&path=" . $this->repo->encodePath($postPath))),
trim($pathName, '/'),
);
$breadcrumbItems[] = h::span('/', setStyle('margin', '0 5px'));
}
if($fileName) $breadcrumbItems[] = h::span($fileName);
$breadcrumbItems[] = h::span
(
setClass('ml-2 label secondary'),
html($revisionName),
);
foreach($blames as $key => $blame)
{
if(isset($blame['lines']))
{
if($repo->SCM != 'Subversion') $blames[$key]['revision'] = substr($blame['revision'], 0, 10);
}
$blames[$key]['content'] = htmlSpecialString($blame['content']);
}
foreach($blames as $key => $blame) $blames[$key] = (object)$blame;
$blames = initTableData($blames, $config->repo->blameDtable->fieldList, $this->repo);
$encodePath = $this->repo->encodePath($entry);
$encodes = array();
foreach($lang->repo->encodingList as $key => $val)
{
$encodes[] = array('text' => $val, 'url' => inlink('blame', "repoID=$repoID&objectID=$objectID&entry=$encodePath&revision=$revision&encoding=$key"));
}
$defaultEncode = $lang->repo->encodingList[$encoding];
featureBar(
backBtn
(
setClass('mr-5'),
set::icon('back'),
set::type('secondary'),
set::back('GLOBAL'),
$lang->goback
),
...$breadcrumbItems
);
panel
(
set::title($fileName),
to::headingActions(
btnGroup
(
btn($defaultEncode),
dropdown
(
btn(setClass('btn dropdown-toggle'),
setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))),
set::items($encodes),
set::arrow(true),
set::flip(true),
set::placement('bottom-end'),
),
),
),
dtable
(
set::cols($config->repo->blameDtable->fieldList),
set::data($blames),
),
);
render();
+260
View File
@@ -0,0 +1,260 @@
<?php
declare(strict_types=1);
/**
* The browse view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao<zhaoke@easycorp.ltd>
* @package repo
* @link http://www.zentao.net
*/
namespace zin;
jsVar('copied', $lang->repo->copied);
dropmenu(set::module('repo'), set::tab('repo'));
/* Prepare repo select data. */
$branchMenus = array();
$tagMenus = array();
$selected = '';
foreach($branches as $branchName)
{
$selected = ($branchName == $branchID and $branchOrTag == 'branch') ? $branchName : $selected;
$base64BranchID = helper::safe64Encode(base64_encode($branchName));
$branchLink = $this->createLink('repo', 'browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID");
$branchMenus[] = array('text' => $branchName, 'id' => $branchName, 'keys' => zget(common::convert2Pinyin(array($branchName), $branchName), ''), 'url' => $branchLink);
}
foreach($tags as $tagName)
{
$selected = ($tagName == $branchID and $branchOrTag == 'tag') ? $tagName : $selected;
$base64TagID = helper::safe64Encode(base64_encode($tagName));
$tagLink = $this->createLink('repo', 'browse', "repoID=$repoID&branchID=$base64TagID&objectID=$objectID&path=&revision=HEAD&refresh=0&branchOrTag=tag");
$tagMenus[] = array('text' => $tagName, 'id' => $tagName, 'keys' => zget(common::convert2Pinyin(array($tagName), $tagName), ''), 'url' => $tagLink);
}
$tabs = array(array('name' => 'branch', 'text' => $lang->repo->branch), array('name' => 'tag', 'text' => $lang->repo->tag));
$menuData = array('branch' => $branchMenus, 'tag' => $tagMenus);
/* Prepare breadcrumb navigation data. */
$base64BranchID = helper::safe64Encode(base64_encode($branchID));
$breadcrumbItems = array();
$breadcrumbItems[] = h::a
(
set::href($this->repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID")),
$repo->name,
);
$breadcrumbItems[] = h::span('/', setStyle('margin', '0 5px'));
$paths = explode('/', $path);
$fileName = array_pop($paths);
$postPath = '';
foreach($paths as $index => $pathName)
{
$postPath .= $pathName . '/';
$breadcrumbItems[] = h::a
(
set::href($this->repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID&path=" . $this->repo->encodePath($postPath))),
trim($pathName, '/'),
);
$breadcrumbItems[] = h::span('/', setStyle('margin', '0 5px'));
}
if($fileName) $breadcrumbItems[] = h::span($fileName);
/* zin: Define the set::module('repo') feature bar on main menu. */
featureBar(
formGroup
(
set::class('repo-select'),
set::required(true),
dropmenu
(
setID('repoBranchDropMenu'),
set::objectID($selected),
set::text($selected),
set::data(array('data' => $menuData, 'tabs' => $tabs)),
),
),
...$breadcrumbItems
);
/* zin: Define the toolbar on main menu. */
$refreshLink = $this->createLink('repo', 'browse', "repoID=$repoID&branchID=" . $base64BranchID . "&objectID=$objectID&path=" . $this->repo->encodePath($path) . "&revision=$revision&refresh=1");
$refreshItem = array('text' => $lang->refresh, 'url' => $refreshLink, 'class' => 'primary', 'icon' => 'refresh');
$tableData = initTableData($infos, $config->repo->repoDtable->fieldList, $this->repo);
$downloadWg = div
(
set::id('modal-downloadCode'),
set::title($lang->repo->downloadCode),
on('click', '', array('capture' => true, 'prevent' => true, 'stop' => true)),
!empty($cloneUrl->svn) ? div
(
p(set::class('repo-downloadCode'), $lang->repo->cloneUrl),
formRow
(
formGroup
(
set::width('450px'),
input
(
set::type('text'),
set::name('svnUrl'),
set::value($cloneUrl->svn),
set::readOnly(true),
),
),
formGroup
(
set::width('50px'),
btn
(
set::icon('copy'),
)
)
),
) : null,
!empty($cloneUrl->ssh) ? div
(
p(set::class('repo-downloadCode'), $lang->repo->sshClone),
formRow
(
formGroup
(
set::width('450px'),
input
(
set::type('text'),
set::name('sshUrl'),
set::value($cloneUrl->ssh),
set::readOnly(true),
),
),
formGroup
(
set::width('50px'),
btn
(
set::class('copy-btn'),
set::icon('copy'),
)
)
),
) : null,
!empty($cloneUrl->http) ? div
(
p(set::class('repo-downloadCode'), $lang->repo->httpClone),
formRow
(
formGroup
(
set::width('450px'),
input
(
set::type('text'),
set::name('httpUrl'),
set::value($cloneUrl->http),
set::readOnly(true),
),
),
formGroup
(
set::width('50px'),
btn
(
set::class('copy-btn'),
set::icon('copy'),
)
)
),
) : null,
div
(
setStyle(array('margin-top' => '20px')),
btn
(
set::icon('down-circle'),
set::class('downloadZip-btn'),
set::text($lang->repo->downloadZip),
)
),
);
toolbar
(
span(
set::class('last-sync-time'),
$lang->repo->notice->lastSyncTime . $cacheTime
),
item(set($refreshItem)),
dropdown
(
set::staticMenu(true),
btn
(
setClass('primary download-btn'),
set::icon('download'),
$lang->repo->download
),
to::items
(
array($downloadWg)
),
)
);
dtable
(
set::cols($config->repo->repoDtable->fieldList),
set::data($tableData),
set::onRenderCell(jsRaw('window.renderCell')),
set::canRowCheckable(jsRaw('function(rowID){return false;}')),
set::footPager(),
);
/* zin: Define the sidebar in main content. */
$encodePath = $this->repo->encodePath($path);
$diffLink = $this->repo->createLink('diff', "repoID=$repoID&objectID=$objectID&entry=" . $encodePath . "&oldrevision={oldRevision}&newRevision={newRevision}");
jsVar('repoID', $repoID);
jsVar('branch', $branchID);
jsVar('diffLink', $diffLink);
jsVar('sortLink', helper::createLink('repo', 'browse', "repoID={$repoID}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}"));
/* Disbale check all checkbox of table header */
$config->repo->commentDtable->fieldList['id']['checkbox'] = jsRaw('(rowID) => rowID !== \'HEADER\'');
$commentsTableData = initTableData($revisions, $config->repo->commentDtable->fieldList, $this->repo);
$readAllLink = $this->repo->createLink('log', "repoID=$repoID&objectID=$objectID&entry=" . $encodePath . "&revision=HEAD&type=$logType");
$footToolbar['items'][] = array('text' => $lang->repo->diff, 'class' => "btn primary size-sm btn-diff", 'btnType' => 'primary', 'onClick' => jsRaw('window.diffClick'));
$footToolbar['items'][] = array('text' => $lang->repo->allLog, 'url' => $readAllLink);
sidebar
(
set::side('right'),
dtable
(
set::id('repo-comments-table'),
set::cols($config->repo->commentDtable->fieldList),
set::data($commentsTableData),
set::onRenderCell(jsRaw('window.renderCommentCell')),
set::onCheckChange(jsRaw('window.checkedChange')),
set::canRowCheckable(jsRaw('function(rowID){return canRowCheckable(rowID);}')),
set::footToolbar($footToolbar),
set::footer(array('toolbar', 'flex', 'pager')),
set::footPager(usePager()),
set::showToolbarOnChecked(false),
),
);
render();
+202 -2
View File
@@ -1,6 +1,206 @@
<?php
declare(strict_types=1);
/**
* The create view file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
div('hello world');
jsVar('pathGitTip', $lang->repo->example->path->git);
jsVar('pathSvnTip', $lang->repo->example->path->svn);
jsVar('clientGitTip', $lang->repo->example->client->git);
jsVar('clientSvnTip', $lang->repo->example->client->svn);
jsVar('scmList', $lang->repo->scmList);
render('page');
formPanel
(
on::change('#product', 'onProductChange'),
on::change('#SCM', 'onScmChange'),
on::change('#serviceHost', 'onHostChange'),
on::change('#serviceProject', 'onProjectChange'),
set::title($lang->repo->createAction),
set::back('repo-maintain'),
formGroup
(
set::name("product[]"),
set::label($lang->story->product),
set::required(true),
set::control(array("type" => "picker","multiple" => true)),
set::items($products),
set::value(empty($objectID) ? '' : array_keys($products))
),
formGroup
(
set::name("projects[]"),
set::label($lang->repo->projects),
set::control(array("type" => "picker","multiple" => true)),
set::items($projects),
set::value($relatedProjects)
),
formRow
(
formGroup
(
set::width('1/2'),
set::id('SCM'),
set::name('SCM'),
set::label($lang->product->typeAB),
set::required(true),
set::value('Gitlab'),
set::control('picker'),
set::items($lang->repo->scmList)
),
h::span
(
setClass('tips-git leading-8 ml-2'),
html($lang->repo->syncTips),
),
),
formRow
(
setClass('service hide'),
formGroup
(
set::width('1/2'),
set::name("serviceHost"),
set::label($lang->repo->serviceHost),
set::required(true),
set::value(""),
set::control("picker"),
set::items($serviceHosts)
),
),
formRow
(
setClass('service hide'),
formGroup
(
set::width('1/2'),
set::name("serviceProject"),
set::label($lang->repo->serviceProject),
set::required(true),
set::control("picker"),
),
),
formGroup
(
set::width('1/2'),
set::name("name"),
set::label($lang->user->name),
set::required(true),
set::control("text")
),
formRow
(
setClass('hide-service hide-git'),
formGroup
(
set::name("path"),
set::label($lang->repo->path),
set::required(true),
set::control("text"),
set::placeholder($lang->repo->example->path->git),
),
),
formGroup
(
set::width('1/2'),
set::name("encoding"),
set::label($lang->repo->encoding),
set::required(true),
set::value("utf-8"),
set::control("text"),
set::placeholder($lang->repo->encodingsTips),
),
formRow
(
setClass('hide-service'),
formGroup
(
set::name("client"),
set::label($lang->repo->client),
set::required(true),
set::control("text")
),
),
formRow
(
setClass('account-fields hide-service'),
formGroup
(
set::name("account"),
set::label($lang->user->account),
set::control("text")
),
),
formRow
(
setClass('account-fields hide-service'),
formGroup
(
set::label($lang->user->password),
inputGroup
(
control(set(array
(
'name' => "password",
'id' => "password",
'value' => "",
'type' => "password"
))),
control(set(array
(
'name' => "encrypt",
'id' => "encrypt",
'value' => "base64",
'type' => "picker",
'items' => $lang->repo->encryptList
)))
)
)
),
formGroup
(
set::label($lang->repo->acl),
inputGroup
(
$lang->repo->group,
width('full'),
control(set(array
(
'name' => "acl[groups][]",
'id' => "aclgroups",
'value' => NULL,
'type' => "picker",
'items' => $groups,
'multiple' => true
)))
),
inputGroup
(
$lang->repo->user,
control(set(array
(
'name' => "acl[users][]",
'id' => "aclusers",
'value' => NULL,
'type' => "picker",
'items' => $users,
'multiple' => true
))),
setClass('mt-2')
)
),
formGroup
(
set::name("desc"),
set::label($lang->story->spec),
set::control("editor")
)
);
render();
+76
View File
@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
/**
* The diff view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao<zhaoke@easycorp.ltd>
* @package repo
* @link http://www.zentao.net
*/
namespace zin;
dropmenu(set::module('repo'), set::tab('repo'));
$browser = helper::getBrowser();
jsVar('browser', $browser['name']);
jsVar('edition', $config->edition);
$backURI = $this->session->repoView ? $this->session->repoView : $this->session->repoList;
$breadcrumbItems = array();
$breadcrumbItems[] = h::a
(
set::href($this->repo->createLink('browse', "repoID=$repoID&branchID=&objectID=$objectID"), $repo->name, '', "data-app='{$app->tab}'"),
$repo->name,
);
$breadcrumbItems[] = h::span('/', setStyle('margin', '0 5px'));
$paths = explode('/', $entry);
$fileName = array_pop($paths);
$postPath = '';
$base64BranchID = helper::safe64Encode(base64_encode($branchID));
foreach($paths as $pathName)
{
$postPath .= $pathName . '/';
$breadcrumbItems[] = h::a
(
set::href($this->repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID&path=" . $this->repo->encodePath($postPath))),
trim($pathName, '/'),
);
$breadcrumbItems[] = h::span('/', setStyle('margin', '0 5px'));
}
if($fileName) $breadcrumbItems[] = h::span($fileName);
if(strpos($repo->SCM, 'Subversion') === false)
{
$oldRevision = $oldRevision == '^' ? "$newRevision" : $oldRevision;
$revisionWg = h::span(setClass('label label-info diff-label'), substr($oldRevision, 0, 10) . " : " . substr($newRevision, 0, 10) . ' (' . zget($historys, $oldRevision, '') . ' : ' . zget($historys, $newRevision, ''));
}
else
{
$oldRevision = $oldRevision == '^' ? $newRevision - 1 : $oldRevision;
$revisionWg = h::span(setClass('label label-info'), $oldRevision . ':' . $newRevision);
}
$breadcrumbItems[] = $revisionWg;
$breadcrumbItems[] = span(setClass('label label-exchange'), icon('exchange'));
featureBar
(
backBtn(set::icon('back'), $backURI ? set::url($backURI) : null, setClass('bg-transparent diff-back-btn'), $lang->goback),
item(set::type('divider')),
...$breadcrumbItems,
);
if($diffs) include 'diffeditor.html.php';
jsVar('oldRevision', $oldRevision);
jsVar('newRevision', $newRevision);
+95
View File
@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
/**
* The diffeditor view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao<zhaoke@easycorp.ltd>
* @package repo
* @link http://www.zentao.net
*/
namespace zin;
$entry = count($diffs) ? $diffs[0]->fileName : '';
$currentEntry = $this->repo->encodePath($entry);
$fileInfo = $entry ? pathinfo($entry) : array();
$showBug = isset($showBug) ? $showBug : true;
$objectID = isset($objectID) ? $objectID : 0;
$tree = $this->repo->getFileTree($repo, '', $diffs);
$diffLink = $this->repo->createLink('diff', "repoID=$repoID&objectID=$objectID&entry=" . $file . "&oldrevision={oldRevision}&newRevision={newRevision}");
jsVar('diffs', $diffs);
jsVar('tree', $tree);
jsVar('file', $currentEntry);
jsVar('entry', $entry);
jsVar('diffLink', $diffLink);
jsVar('urlParams', "repoID=$repoID&objectID=$objectID&entry=%s&oldRevision=$oldRevision&newRevision=$newRevision&showBug=$showBug&encoding=$encoding");
featureBar();
$dropMenus = array();
if(common::hasPriv('repo', 'download')) $dropMenus[] = array('text' => $this->lang->repo->downloadDiff, 'icon' => 'download', 'data-link' => $this->repo->createLink('download', "repoID=$repoID&path={path}&fromRevision=$oldRevision&toRevision=$newRevision&type=path"), 'id' => 'repoDownloadCode');
$dropMenus[] = array('text' => $this->lang->repo->viewDiffList['inline'], 'icon' => 'snap-house', 'id' => 'inline', 'class' => 'inline-appose');
$dropMenus[] = array('text' => $this->lang->repo->viewDiffList['appose'], 'icon' => 'col-archive', 'id' => 'appose', 'class' => 'inline-appose');
div(
set::id('fileTabs'),
tabs
(
set::id('monacoTabs'),
set::class('relative'),
div(setStyle(array('position' => 'absolute', 'width' => '100%', 'height' => '35px', 'background' => '#efefef', 'top' => '0px'))),
tabPane
(
set::title($fileInfo['basename']),
set::active(true),
set::key('tab-' . str_replace('=', '-', $currentEntry)),
to::suffix
(
icon
(
'close',
set::class('monaco-close'),
)
),
div(set::id('tab-' . $currentEntry)),
),
dropdown
(
set::arrow(false),
set::staticMenu(true),
set::class('absolute top-0 right-0 z-10 monaco-dropmenu'),
btn
(
setClass('ghost text-black pull-right'),
set::icon('ellipsis-v rotate-90'),
),
set::items
(
$dropMenus
),
),
div(set::class('absolute top-0 left-0 z-20 arrow-left btn-left'), icon('chevron-left')),
div(set::class('absolute top-0 right-0 z-20 arrow-right btn-right'), icon('chevron-right')),
)
);
sidebar
(
set::side('left'),
tree
(
set::id('monacoTree'),
set::items($tree),
set::canSplit(false),
set::collapsedIcon('folder'),
set::expandedIcon('folder-open'),
set::normalIcon('file-text-alt'),
set::activeKey($entry),
set::onClickItem(jsRaw('window.treeClick')),
)
);
a(set::class('iframe'), setData('size', '1000px'), setData('toggle', 'modal'), set::id('linkObject'));
+212
View File
@@ -0,0 +1,212 @@
<?php
declare(strict_types=1);
/**
* The edit view file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
jsVar('pathGitTip', $lang->repo->example->path->git);
jsVar('pathSvnTip', $lang->repo->example->path->svn);
jsVar('clientGitTip', $lang->repo->example->client->git);
jsVar('clientSvnTip', $lang->repo->example->client->svn);
jsVar('scmList', $lang->repo->scmList);
formPanel
(
on::change('#product', 'onProductChange'),
on::change('#SCM', 'onScmChange'),
on::change('#serviceHost', 'onHostChange'),
on::change('#serviceProject', 'onProjectChange'),
set::title($lang->repo->edit),
set::back('repo-maintain'),
formGroup
(
set::name("product[]"),
set::label($lang->story->product),
set::required(true),
set::control(array("type" => "picker","multiple" => true)),
set::items($products),
set::value($repo->product)
),
formGroup
(
set::name("projects[]"),
set::label($lang->repo->projects),
set::control(array("type" => "picker","multiple" => true)),
set::items($relatedProjects),
set::value($repo->projects)
),
formRow
(
formGroup
(
set::width('1/2'),
set::name("SCM"),
set::label($lang->product->typeAB),
set::required(true),
set::value($repo->SCM),
set::control("picker"),
set::items($lang->repo->scmList)
),
h::span
(
setClass('tips-git leading-8 ml-2'),
html($lang->repo->syncTips),
),
),
formRow
(
setClass('service hide'),
formGroup
(
set::width('1/2'),
set::name("serviceHost"),
set::label($lang->repo->serviceHost),
set::required(true),
set::value(isset($repo->gitService) ? $repo->gitService : ''),
set::control("picker"),
set::items($serviceHosts)
),
),
formRow
(
setClass('service hide'),
formGroup
(
set::width('1/2'),
set::name("serviceProject"),
set::label($lang->repo->serviceProject),
set::required(true),
set::control("picker"),
set::items($projects),
set::value(isset($repo->project) ? $repo->project : ''),
),
),
formGroup
(
set::width('1/2'),
set::name("name"),
set::label($lang->user->name),
set::required(true),
set::control("text"),
set::value($repo->name),
),
formRow
(
setClass('hide-service hide-git'),
formGroup
(
set::name("path"),
set::label($lang->repo->path),
set::required(true),
set::control("text"),
set::placeholder($lang->repo->example->path->git),
set::value($repo->path),
),
),
formGroup
(
set::width('1/2'),
set::name("encoding"),
set::label($lang->repo->encoding),
set::required(true),
set::value($repo->encoding),
set::control("text"),
set::placeholder($lang->repo->encodingsTips),
),
formRow
(
setClass('hide-service'),
formGroup
(
set::name("client"),
set::label($lang->repo->client),
set::required(true),
set::control("text"),
set::value($repo->client),
),
),
formRow
(
setClass('account-fields hide-service'),
formGroup
(
set::name("account"),
set::label($lang->user->account),
set::control("text"),
set::value($repo->account),
),
),
formRow
(
setClass('account-fields hide-service'),
formGroup
(
set::label($lang->user->password),
inputGroup
(
control(set(array
(
'name' => "password",
'id' => "password",
'value' => $repo->password,
'type' => "password"
))),
control(set(array
(
'name' => "encrypt",
'id' => "encrypt",
'value' => $repo->encrypt,
'type' => "picker",
'items' => $lang->repo->encryptList
)))
)
)
),
formGroup
(
set::label($lang->repo->acl),
inputGroup
(
$lang->repo->group,
width('full'),
control(set(array
(
'name' => "acl[groups][]",
'id' => "aclgroups",
'value' => empty($repo->acl->groups) ? '' : implode(',', $repo->acl->groups),
'type' => "picker",
'items' => $groups,
'multiple' => true
)))
),
inputGroup
(
$lang->repo->user,
control(set(array
(
'name' => "acl[users][]",
'id' => "aclusers",
'value' => empty($repo->acl->users) ? '' : implode(',', $repo->acl->users),
'type' => "picker",
'items' => $users,
'multiple' => true
))),
setClass('mt-2')
)
),
formGroup
(
set::name("desc"),
set::label($lang->story->spec),
set::control("editor"),
set::value($repo->desc),
)
);
render();
+61
View File
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/**
* The import view file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
$items = array();
$items[] = array('name' => 'no', 'label' => $lang->user->abbr->id, 'control' => 'static', 'width' => '32px', 'class' => 'no');
$items[] = array('name' => 'serviceProject', 'hidden' => true);
$items[] = array('name' => 'name_with_namespace', 'label' => $lang->repo->repo, 'control' => 'static', 'width' => '264px');
$items[] = array('name' => 'name', 'label' => $lang->repo->importName);
$items[] = array('name' => 'product', 'label' => $lang->repo->product, 'control' => array('type' => 'picker', 'multiple' => true), 'items' => $products);
$items[] = array('name' => 'projects', 'label' => $lang->repo->projects, 'control' => array('type' => 'picker', 'multiple' => true), 'items' => $projects);
$no = 1;
foreach($repoList as $repo)
{
$repo->serviceProject = $repo->id;
$repo->no = $no ++;
}
featureBar
(
h::a
(
setClass('form-title'),
set::href($this->createLink('repo', 'import')),
$lang->repo->batchCreate,
),
picker
(
setClass('ml-3'),
width('200px'),
on::change('selectServer'),
set::name('servers'),
set::id('servers'),
set::placeholder($lang->repo->importServer),
set::items(array('' => '') + $gitlabPairs),
set::value($gitlab->id)
),
);
formBatchPanel
(
set::id('repoList'),
set::back('repo-maintain'),
set::mode(count($repoList) == 0 ? 'edit' : 'add'),
set::addRowIcon('false'),
set::items($items),
set::data($repoList),
set::maxRows(count($repoList)),
on::change('[data-name="product"]', 'loadProductProjects'),
);
render();
+60
View File
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/**
* The linkbug file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao <zhaoke@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
jsVar('orderBy', $orderBy);
jsVar('sortLink', createLink('repo', 'linkBug', "repoID=$repoID&revision=$revision&browseType=$browseType&param=$param&orderBy={orderBy}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}"));
detailHeader
(
to::prefix(''),
to::title
(
$lang->productplan->linkBug,
)
);
$footToolbar = array('items' => array
(
array('text' => $lang->productplan->linkBug, 'className' => 'batch-btn ajax-btn', 'data-url' => helper::createLink('repo', 'linkBug', "repoID=$repoID&revision=$revision&browseType=$browseType&param=$param&orderBy=$orderBy"))
), 'btnProps' => array('size' => 'sm', 'btnType' => 'secondary', 'data-type' => 'bugs'));
div(setID('searchFormPanel'), set('data-module', 'bug'), searchToggle(set::open(true), set::module('bug')));
div
(
set('class', 'repo-linkstory-title'),
icon('unlink'),
span
(
set('class', 'font-semibold ml-2'),
$lang->productplan->unlinkedBugs . "({$pager->recTotal})"
)
);
$cols = array();
foreach($config->release->dtable->defaultFields['linkBug'] as $field) $cols[$field] = zget($config->bug->dtable->fieldList, $field, array());
$cols['title']['data-size'] = 'lg';
$cols['title']['data-toggle'] = 'modal';
$allBugs = initTableData($allBugs, $cols);
$data = array_values($allBugs);
dtable
(
set::userMap($users),
set::data($data),
set::cols($cols),
set::checkable(true),
set::footToolbar($footToolbar),
set::sortLink(jsRaw('createSortLink')),
set::footPager(usePager()),
);
render();
+63
View File
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/**
* The linkstory file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao <zhaoke@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
jsVar('orderBy', $orderBy);
jsVar('sortLink', createLink('repo', 'linkStory', "repoID=$repoID&revision=$revision&browseType=$browseType&param=$param&orderBy={orderBy}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}"));
detailHeader
(
to::prefix(''),
to::title
(
$lang->repo->linkStory,
)
);
$footToolbar = array('items' => array
(
array('text' => $lang->repo->linkStory, 'className' => 'batch-btn ajax-btn', 'data-url' => helper::createLink('repo', 'linkStory', "repoID=$repoID&revision=$revision&browseType=$browseType&param=$param&orderBy=$orderBy"))
), 'btnProps' => array('size' => 'sm', 'btnType' => 'secondary', 'data-type' => 'stories'));
div(setID('searchFormPanel'), set('data-module', 'story'), searchToggle(set::open(true), set::module('story')));
div
(
set('class', 'repo-linkstory-title'),
icon('unlink'),
span
(
set('class', 'font-semibold ml-2'),
$lang->productplan->unlinkedStories . "({$pager->recTotal})"
)
);
$cols = array();
foreach($config->release->dtable->defaultFields['linkStory'] as $field) $cols[$field] = zget($config->release->dtable->story->fieldList, $field, array());
$cols = array_map(function($col){$col['show'] = true; return $col;}, $cols);
$cols['title']['link'] = $this->createLink('story', 'view', "storyID={id}");
$cols['title']['nestedToggle'] = false;
$cols['title']['data-size'] = 'lg';
$allStories = initTableData($allStories, $cols);
$data = array_values($allStories);
dtable
(
set::userMap($users),
set::data($data),
set::cols($cols),
set::checkable(true),
set::footToolbar($footToolbar),
set::sortLink(jsRaw('createSortLink')),
set::footPager(usePager()),
);
render();
+55
View File
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/**
* The linktask file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao <zhaoke@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
jsVar('orderBy', $orderBy);
jsVar('sortLink', createLink('repo', 'linkTask', "repoID=$repoID&revision=$revision&browseType=$browseType&param=$param&orderBy={orderBy}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}"));
detailHeader
(
to::prefix(''),
to::title
(
$lang->repo->linkTask,
)
);
$footToolbar = array('items' => array
(
array('text' => $lang->repo->linkTask, 'className' => 'batch-btn ajax-btn', 'data-url' => helper::createLink('repo', 'linkTask', "repoID=$repoID&revision=$revision&browseType=$browseType&param=$param&orderBy=$orderBy"))
), 'btnProps' => array('size' => 'sm', 'btnType' => 'secondary', 'data-type' => 'tasks'));
div(setID('searchFormPanel'), set('data-module', 'task'), searchToggle(set::open(true), set::module('task')));
div
(
set('class', 'repo-linkstory-title'),
icon('unlink'),
span
(
set('class', 'font-semibold ml-2'),
$lang->repo->unlinkedTasks . "({$pager->recTotal})"
)
);
$allTasks = initTableData($allTasks, $config->repo->taskDtable->fieldList);
$data = array_values($allTasks);
dtable
(
set::userMap($users),
set::data($data),
set::cols($config->repo->taskDtable->fieldList),
set::checkable(true),
set::footToolbar($footToolbar),
set::sortLink(jsRaw('createSortLink')),
set::footPager(usePager()),
);
render();
+83
View File
@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
/**
* The log view file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
dropmenu(set::module('repo'), set::tab('repo'));
$diffLink = $this->repo->createLink('diff', "repoID=$repoID&objectID=$objectID&entry=" . $this->repo->encodePath($entry) . "&oldrevision={oldRevision}&newRevision={newRevision}");
jsVar('diffLink', $diffLink);
/* Prepare breadcrumb navigation data. */
$base64BranchID = helper::safe64Encode(base64_encode($branchID));
$breadcrumbItems = array();
$breadcrumbItems[] = h::a
(
set::href($this->repo->createLink('log', "repoID=$repoID&objectID=$objectID")),
$repo->name,
);
$breadcrumbItems[] = h::span('/', setStyle('margin', '0 5px'));
$paths = explode('/', $entry);
$fileName = array_pop($paths);
$postPath = '';
foreach($paths as $pathName)
{
$postPath .= $pathName . '/';
$breadcrumbItems[] = h::a
(
set::href($this->repo->createLink('log', "repoID=$repoID&objectID=$objectID&entry=" . $this->repo->encodePath($postPath))),
trim($pathName, '/'),
);
$breadcrumbItems[] = h::span('/', setStyle('margin', '0 5px'));
}
if($fileName) $breadcrumbItems[] = h::span($fileName);
foreach($logs as $log)
{
$log->revision = substr($log->revision, 0, 10);
}
/* Disbale check all checkbox of table header */
$config->repo->logDtable->fieldList['revision']['checkbox'] = jsRaw('(rowID) => rowID !== \'HEADER\'');
$config->repo->logDtable->fieldList['revision']['link'] = array('module' => 'repo', 'method' => 'revision', 'params' => "repoID={$repoID}&objectID={$objectID}&revision={revision}");
$logs = initTableData($logs, $config->repo->logDtable->fieldList);
$footToolbar['items'][] = array('text' => $lang->repo->diff, 'class' => "btn primary size-sm btn-diff", 'btnType' => 'primary', 'onClick' => jsRaw('window.diffClick'));
featureBar(
backBtn
(
setClass('mr-5'),
set::icon('back'),
set::type('secondary'),
set::back('repo-browse'),
$lang->goback
),
...$breadcrumbItems
);
dtable
(
set::id('repo-logs-table'),
set::cols($config->repo->logDtable->fieldList),
set::data($logs),
set::onCheckChange(jsRaw('window.checkedChange')),
set::canRowCheckable(jsRaw('function(rowID){return canRowCheckable(rowID);}')),
set::footToolbar($footToolbar),
set::footer(array('toolbar', 'flex', 'pager')),
set::footPager(usePager('pager', 'noTotalCount')),
set::showToolbarOnChecked(false),
);
render();
+96
View File
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
/**
* The maintain view file of repo module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
namespace zin;
$createItem = array('text' => $lang->repo->createAction, 'url' => createLink('repo', 'create'));
$batchCreateItem = array('text' => $lang->repo->batchCreate, 'url' => createLink('repo', 'import'));
foreach($repoList as $repo)
{
$jobID = 0;
$repo->exec = 'disabled';
$repo->report = 'disabled';
if(isset($sonarRepoList[$repo->id]))
{
$repo->exec = '';
$repo->job = $sonarRepoList[$repo->id]->id;
if(in_array($repo->job, $successJobs)) $repo->report = '';
}
$repo->productNames = '';
$productList = explode(',', str_replace(' ', '', $repo->product));
if($productList)
{
foreach($productList as $productID)
{
if(!isset($products[$productID])) continue;
$repo->productNames .= ' ' . zget($products, $productID, $productID);
}
}
$repo->projectNames = '';
$projectList = explode(',', str_replace(' ', '', $repo->projects));
if($projectList)
{
foreach($projectList as $projectID)
{
if(!isset($projects[$projectID])) continue;
$repo->projectNames .= ' ' . zget($projects, $projectID, $projectID);
}
}
}
$config->repo->dtable->fieldList['name']['link'] = $this->createLink('repo', 'browse', "repoID={id}&branchID=&objectID={$objectID}");
$config->repo->dtable->fieldList['actions']['list']['edit']['url'] = $this->createLink('repo', 'edit', "repoID={id}&objectID={$objectID}");
$config->repo->dtable->fieldList['actions']['list']['delete']['url'] = $this->createLink('repo', 'delete', "repoID={id}&objectID={$objectID}");
$repos = initTableData($repoList, $config->repo->dtable->fieldList, $this->repo);
featureBar
(
set::current('all'),
li(searchToggle()),
);
toolBar
(
!hasPriv('repo', 'create') && hasPriv('repo', 'import') ? item(set($batchCreateItem + array
(
'icon' => 'plus',
'class' => 'btn primary',
))) : null,
!hasPriv('repo', 'import') && hasPriv('repo', 'create') ? item(set($createItem + array
(
'icon' => 'plus',
'class' => 'btn primary',
))) : null,
hasPriv('repo', 'import') && hasPriv('repo', 'create') ? btnGroup
(
btn(setClass('btn primary'), set::icon('plus'), set::url(createLink('repo', 'create')), $lang->repo->createAction),
dropDown
(
btn(setClass('btn primary dropdown-toggle'),
setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))),
set::placement('bottom-end'),
set::items(array($createItem, $batchCreateItem)),
),
) : null,
);
dtable
(
set::cols($config->repo->dtable->fieldList),
set::data($repos),
set::footPager(usePager()),
);
render();
+110
View File
@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
/**
* The monaco view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao<zhaoke@easycorp.ltd>
* @package repo
* @link http://www.zentao.net
*/
namespace zin;
dropmenu(set::module('repo'), set::tab('repo'));
$tree = $this->repo->getFileTree($repo);
jsVar('isonlybody', isonlybody());
jsVar('entry', $entry);
jsVar('repoID', $repoID);
jsVar('repo', $repo);
jsVar('revision', $revision);
jsVar('branchID', $branchID);
jsVar('branchMenus', $dropMenus);
jsVar('file', $file);
jsVar('tree', $tree);
jsVar('openedFiles', array($entry));
jsVar('urlParams', "repoID=$repoID&objectID=$objectID&entry=%s&revision=$revision&showBug=$showBug&encoding=$encoding");
jsVar('currentLink', $this->createLink('repo', 'view', "repoID=$repoID&objectID=$objectID&entry=$file"));
featureBar();
$monacoDropMenus = array();
if(common::hasPriv('repo', 'blame')) $monacoDropMenus[] = array('text' => $this->lang->repo->blame, 'icon' => 'blame', 'data-link' => $this->repo->createLink('blame', "repoID=$repoID&objectID=$objectID&entry={path}&revision=$revision&encoding=$encoding"), 'class' => 'repoDropDownMenu');
if(common::hasPriv('repo', 'download')) $monacoDropMenus[] = array('text' => $this->lang->repo->download, 'icon' => 'download', 'data-link' => $this->repo->createLink('download', "repoID=$repoID&path={path}&fromRevision=$revision"), 'class' => 'repoDropDownMenu');
$tabs = array(array('name' => 'branch', 'text' => $lang->repo->branch), array('name' => 'tag', 'text' => $lang->repo->tag));
$menuData = array('branch' => $dropMenus['branchMenus'], 'tag' => $dropMenus['tagMenus']);
div(
set::id('fileTabs'),
setStyle('position', 'relative'),
tabs
(
set::id('monacoTabs'),
set::class('relative'),
div(setStyle(array('position' => 'absolute', 'width' => '100%', 'height' => '40px', 'background' => '#efefef', 'top' => '0px'))),
tabPane
(
set::title($pathInfo['basename']),
set::active(true),
set::key('tab-' . str_replace('=', '-', $file)),
to::suffix
(
icon
(
'close',
set::class('monaco-close'),
)
),
div(set::id('tab-' . $file)),
),
dropdown
(
set::arrow(false),
set::staticMenu(true),
set::key('dropdown'),
set::class('absolute top-0 right-0 z-10 monaco-dropmenu'),
btn
(
setClass('ghost text-black pull-right'),
set::icon('ellipsis-v rotate-90'),
),
set::items
(
$monacoDropMenus
),
),
div(set::class('absolute top-0 left-0 z-20 arrow-left btn-left'), icon('chevron-left')),
div(set::class('absolute top-0 right-0 z-20 arrow-right btn-right'), icon('chevron-right')),
)
);
sidebar
(
set::side('left'),
dropmenu
(
setID('repoBranchDropMenu'),
on::click('window.changeBranch'),
on::change('window.changeBranch'),
set::objectID($dropMenus['selected']),
set::text($dropMenus['selected']),
set::data(array('data' => $menuData, 'tabs' => $tabs)),
),
tree
(
set::id('monacoTree'),
set::items($tree),
set::canSplit(false),
set::collapsedIcon('folder'),
set::expandedIcon('folder-open'),
set::normalIcon('file-text-alt'),
set::activeKey($entry),
set::onClickItem(jsRaw('window.treeClick')),
)
);
a(set::class('iframe'), setData('size', '1000px'), setData('toggle', 'modal'), set::id('linkObject'));
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* The showsynccommit view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Ke Zhao<zhaoke@easycorp.ltd>
* @package repo
* @link http://www.zentao.net
*/
namespace zin;
featureBar();
if(empty($branch))
{
$link = helper::createLink('repo', 'ajaxSyncCommit', "repoID=$repoID");
}
else
{
$link = helper::createLink('repo', 'ajaxSyncBranchCommit', "repoID=$repoID&branch=" . helper::safe64Encode(base64_encode($branch)));
}
jsVar('link', $link);
jsVar('browseLink', $browseLink);
jsVar('syncComplete', $lang->repo->notice->syncComplete);
div
(
setClass('sync-main-content'),
div
(
set::class('sync-content'),
div
(
setClass('alert light-pale flex items-center'),
icon(setClass('icon icon-info-sign icon-2x alert-icon'), 'check-sign'),
div
(
h3($lang->repo->notice->syncing),
div(setClass('sync-line')),
p($lang->repo->notice->syncedCount, span($version, set::id('commits')))
)
)
)
);
render();
+406
View File
@@ -0,0 +1,406 @@
<?php
declare(strict_types=1);
/**
* The zen file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zeng Gang<zenggang@easycorp.ltd>
* @package repo
* @link https://www.zentao.net
*/
class repoZen extends repo
{
/**
* 准备创建版本库的数据。
* Prepare create repo data.
*
* @param form $formData
* @param bool $isPipelineServer
* @access protected
* @return object|false
*/
protected function prepareCreate(form $formData, bool $isPipelineServer): object|false
{
if(!$this->checkClient()) return false;
if(!$this->checkConnection()) return false;
$repo = $formData
->setIf($isPipelineServer, 'password', $this->post->serviceToken)
->setIf($this->post->SCM == 'Gitlab', 'path', '')
->setIf($this->post->SCM == 'Gitlab', 'client', '')
->setIf($this->post->SCM == 'Gitlab', 'extra', $this->post->serviceProject)
->setIf($isPipelineServer, 'prefix', '')
->setIf($this->post->SCM == 'Git', 'account', '')
->setIf($this->post->SCM == 'Git', 'password', '')
->setIf(in_array($this->post->SCM, array('Gitea', 'Gogs')), 'path', $_POST['path'])
->setIf($this->post->acl != '', 'acl', json_encode($this->post->acl))
->setIf($this->post->encrypt == 'base64', 'password', base64_encode($this->post->password))
->skipSpecial('path,client,account,password')
->setDefault('product', '')->join('product', ',')
->setDefault('projects', '')->join('projects', ',')
->get();
if($repo->SCM == 'Subversion')
{
$scm = $this->app->loadClass('scm');
$scm->setEngine($repo);
$info = $scm->info('');
$infoRoot = urldecode($info->root);
$repo->prefix = empty($infoRoot) ? '' : trim(str_ireplace($infoRoot, '', str_replace('\\', '/', $repo->path)), '/');
if($repo->prefix) $repo->prefix = '/' . $repo->prefix;
}
return $repo;
}
/**
* 检查svn、git客户端。
* Check svn/git client.
*
* @access public
* @return bool
*/
protected function checkClient(): bool
{
if($this->post->SCM == 'Gitlab') return true;
if(!$this->config->features->checkClient) return true;
if(!$this->post->client)
{
dao::$errors['client'] = sprintf($this->lang->error->notempty, $this->lang->repo->client);
return false;
}
if(strpos($this->post->client, ' '))
{
dao::$errors['client'] = $this->lang->repo->error->clientPath;
return false;
}
$clientVersionFile = $this->session->clientVersionFile;
if(empty($clientVersionFile))
{
$clientVersionFile = $this->app->getLogRoot() . uniqid('version_') . '.log';
session_start();
$this->session->set('clientVersionFile', $clientVersionFile);
session_write_close();
}
if(file_exists($clientVersionFile)) return true;
$cmd = $this->post->client . " --version > $clientVersionFile";
dao::$errors['client'] = sprintf($this->lang->repo->error->safe, $clientVersionFile, $cmd);
return false;
}
/**
* 检查连接。
* Check connection
*
* @access public
* @return bool
*/
protected function checkConnection(): bool
{
if(empty($_POST)) return false;
$scm = $this->post->SCM;
$client = $this->post->client;
$account = $this->post->account;
$password = $this->post->password;
$encoding = strtoupper($this->post->encoding);
$path = $this->post->path;
if($encoding != 'UTF8' and $encoding != 'UTF-8') $path = helper::convertEncoding($path, 'utf-8', $encoding);
if($scm == 'Subversion')
{
/* Get svn version. */
$versionCommand = "$client --version --quiet 2>&1";
exec($versionCommand, $versionOutput, $versionResult);
if($versionResult)
{
$message = sprintf($this->lang->repo->error->output, $versionCommand, $versionResult, implode("<br />", $versionOutput));
dao::$errors['client'] = $this->lang->repo->error->cmd . "<br />" . nl2br($message);
return false;
}
$svnVersion = end($versionOutput);
$path = '"' . str_replace(array('%3A', '%2F', '+'), array(':', '/', ' '), urlencode($path)) . '"';
if(stripos($path, 'https://') === 1 or stripos($path, 'svn://') === 1)
{
if(version_compare($svnVersion, '1.6', '<'))
{
dao::$errors['client'] = $this->lang->repo->error->version;
return false;
}
$command = "$client info --username $account --password $password --non-interactive --trust-server-cert-failures=cn-mismatch --trust-server-cert --no-auth-cache $path 2>&1";
if(version_compare($svnVersion, '1.9', '<')) $command = "$client info --username $account --password $password --non-interactive --trust-server-cert --no-auth-cache $path 2>&1";
}
elseif(stripos($path, 'file://') === 1)
{
$command = "$client info --non-interactive --no-auth-cache $path 2>&1";
}
else
{
$command = "$client info --username $account --password $password --non-interactive --no-auth-cache $path 2>&1";
}
exec($command, $output, $result);
if($result)
{
$message = sprintf($this->lang->repo->error->output, $command, $result, implode("<br />", $output));
if(stripos($message, 'Expected FS format between') !== false and strpos($message, 'found format') !== false)
{
dao::$errors['client'] = $this->lang->repo->error->clientVersion;
return false;
}
if(preg_match('/[^\:\/A-Za-z0-9_\-\'\"\.]/', $path))
{
dao::$errors['encoding'] = $this->lang->repo->error->encoding . "<br />" . nl2br($message);
return false;
}
dao::$errors['submit'] = $this->lang->repo->error->connect . "<br>" . nl2br($message);
return false;
}
}
elseif(in_array($scm, array('Gitea', 'Gogs')))
{
if($this->post->name != '' and $this->post->serviceProject != '')
{
$module = strtolower($scm);
$project = $this->loadModel($module)->apiGetSingleProject($this->post->serviceHost, $this->post->serviceProject);
if(isset($project->tokenCloneUrl))
{
$path = $this->app->getAppRoot() . 'www/data/repo/' . $this->post->name . '_' . $module;
if(!realpath($path))
{
$cmd = 'git clone --progress -v "' . $project->tokenCloneUrl . '" "' . $path . '" > "' . $this->app->getTmpRoot() . "log/clone.progress.$module.{$this->post->name}.log\" 2>&1 &";
if(PHP_OS == 'WINNT') $cmd = "start /b $cmd";
exec($cmd);
}
$_POST['path'] = $path;
}
else
{
dao::$errors['serviceProject'] = $this->lang->repo->error->noCloneAddr;
return false;
}
}
}
elseif($scm == 'Git')
{
if(!is_dir($path))
{
dao::$errors['path'] = sprintf($this->lang->repo->error->noFile, $path);
return false;
}
if(!chdir($path))
{
if(!is_executable($path))
{
dao::$errors['path'] = sprintf($this->lang->repo->error->noPriv, $path);
return false;
}
dao::$errors['path'] = $this->lang->repo->error->path;
return false;
}
$command = "$client tag 2>&1";
exec($command, $output, $result);
if($result)
{
dao::$errors['submit'] = $this->lang->repo->error->connect . "<br />" . sprintf($this->lang->repo->error->output, $command, $result, implode("<br />", $output));
return false;
}
}
return true;
}
/**
* 构建创建版本库页面数据。
* Build form fields for create repo.
*
* @param int $objectID
* @access protected
* @return void
*/
protected function buildCreateForm(int $objectID): void
{
$repoID = $this->repo->saveState(0, $objectID);
$this->commonAction($repoID, $objectID);
$this->app->loadLang('action');
if($this->app->tab == 'project' or $this->app->tab == 'execution')
{
$products = $this->loadModel('product')->getProductPairsByProject($objectID);
}
else
{
$products = $this->loadModel('product')->getPairs('', 0, '', 'all');
}
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->create;
$this->view->groups = $this->loadModel('group')->getPairs();
$this->view->users = $this->loadModel('user')->getPairs('noletter|noempty|nodeleted|noclosed');
$this->view->products = $products;
$this->view->projects = $this->loadModel('product')->getProjectPairsByProductIDList(array_keys($products));
$this->view->relatedProjects = ($this->app->tab == 'project' or $this->app->tab == 'execution') ? array($objectID) : array();
$this->view->serviceHosts = $this->loadModel('gitlab')->getPairs();
$this->view->objectID = $objectID;
$this->display();
}
/**
* 准备批量创建版本库的数据。
* Prepare batch create repo data.
*
* @access protected
* @return array|false
*/
protected function prepareBatchCreate(): array|false
{
$data = array();
foreach($_POST as $key => $val)
{
if(strpos($key, 'serviceProject') === 0)
{
$i = substr($key, 14);
if($this->post->{'product' . $i} == '') dao::$errors['product' . $i][] = sprintf($this->lang->error->notempty, $this->lang->repo->product);
if($this->post->{'name' . $i} == '') dao::$errors['name' . $i][] = sprintf($this->lang->error->notempty, $this->lang->repo->name);
if(dao::isError()) continue;
$data[] = array('serviceProject' => $this->post->{'serviceProject' . $i}, 'product' => implode(',', $this->post->{'product' . $i}), 'name' => $this->post->{'name' . $i}, 'projects' => empty($_POST['projects'][$i]) ? '' : implode(',', $this->post->projects[$i]));
}
}
if(dao::isError()) return false;
return $data;
}
/**
* 获取gitlab还没存在禅道的项目列表。
* Get gitlab not exist repos.
*
* @param object $gitlab
* @access protected
* @return array
*/
protected function getGitlabNotExistRepos(object $gitlab): array
{
$repoList = array();
if(!empty($gitlab))
{
$repoList = $this->loadModel('gitlab')->apiGetProjects($gitlab->id);
$existRepoList = $this->dao->select('serviceProject,name')->from(TABLE_REPO)
->where('SCM')->eq(ucfirst($gitlab->type))
->andWhere('serviceHost')->eq($gitlab->id)
->fetchPairs();
foreach($repoList as $key => $repo)
{
if(isset($existRepoList[$repo->id])) unset($repoList[$key]);
}
}
return $repoList;
}
/**
* 获取版本库文件列表信息。
* Get repo files info.
*
* @param object $repo
* @param string $path
* @param string $branchID
* @param int $refresh
* @param string $revision
* @param object $lastRevision
* @access protected
* @return array
*/
protected function getFilesInfo(object $repo, string $path, string $branchID, int $refresh, string $revision, object $lastRevision): array
{
$cacheFile = $this->repo->getCacheFile($repo->id, $path, $branchID);
$cacheRefreshTime = isset($lastRevision->time) ? date('Y-m-d H:i', strtotime($lastRevision->time)) : date('Y-m-d H:i');
$this->scm->setEngine($repo);
if($refresh or !$cacheFile or !file_exists($cacheFile) or filemtime($cacheFile) < strtotime($cacheRefreshTime))
{
if($repo->SCM == 'Gitlab')
{
$infos = $this->repo->getFileList($repo, $branchID, $path);
}
else
{
$infos = $this->scm->ls($path, $revision);
$revisionList = array_column($infos, 'revision', 'revision');
$comments = $this->repo->getHistory($repo->id, $revisionList);
foreach($infos as $info)
{
if(isset($comments[$info->revision]))
{
$comment = $comments[$info->revision];
$info->comment = $comment->comment;
}
}
}
if($cacheFile) file_put_contents($cacheFile, serialize($infos), LOCK_EX);
}
else
{
$infos = unserialize(file_get_contents($cacheFile));
}
foreach($infos as $info)
{
$info->originalComment = $info->comment;
$info->comment = $this->repo->replaceCommentLink($info->comment);
}
return $infos;
}
/**
* 获取分支与tag下拉菜单组件配置。
* Get items of branch and tags menu.
*
* @param object $repo
* @param string $branchID
* @access protected
* @return array
*/
protected function getBranchAndTagItems(object $repo, string $branchID): array
{
/* Set branch or tag for git. */
$branches = $tags = array();
if(!in_array($repo->SCM, $this->config->repo->gitTypeList)) return array();
$scm = $this->app->loadClass('scm');
$scm->setEngine($repo);
$branches = $scm->branch();
$initTags = $scm->tags('');
foreach($initTags as $tag) $tags[$tag] = $tag;
$selected = '';
$branchMenus = array();
$tagMenus = array();
foreach($branches as $branchName)
{
$selected = ($branchName == $branchID) ? $branchName : $selected;
$branchMenus[] = array('text' => $branchName, 'id' => $branchName, 'keys' => zget(common::convert2Pinyin(array($branchName)), $branchName, ''), 'url' => 'javascript:;');
}
foreach($tags as $tagName)
{
$selected = ($tagName == $branchID) ? $tagName : $selected;
$tagMenus[] = array('text' => $tagName, 'id' => $tagName, 'keys' => zget(common::convert2Pinyin(array($tagName)), $tagName, ''), 'url' => 'javascript:;');
}
return array('branchMenus' => $branchMenus, 'tagMenus' => $tagMenus, 'selected' => $selected);
}
}