diff --git a/module/repo/css/browse.ui.css b/module/repo/css/browse.ui.css
new file mode 100644
index 0000000000..eae6e23750
--- /dev/null
+++ b/module/repo/css/browse.ui.css
@@ -0,0 +1,3 @@
+.sidebar-comments{width: 530px;}
+.repo-downloadCode{margin: 8px 10px 8px 0;}
+.repo-select{margin-left: 1rem;}
\ No newline at end of file
diff --git a/module/repo/css/common.ui.css b/module/repo/css/common.ui.css
new file mode 100644
index 0000000000..10c06361a0
--- /dev/null
+++ b/module/repo/css/common.ui.css
@@ -0,0 +1,3 @@
+.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: 2px;}
\ No newline at end of file
diff --git a/module/repo/js/browse.ui.js b/module/repo/js/browse.ui.js
new file mode 100644
index 0000000000..ac367d254f
--- /dev/null
+++ b/module/repo/js/browse.ui.js
@@ -0,0 +1,86 @@
+window.renderCell = function(result, {col, row})
+{
+ if(col.name === 'name')
+ {
+ var iconHtml = '';
+ result[0] = {html:iconHtml + '' + row.data.name + '', style:{flexDirection:"column"}};
+
+ return result;
+ }
+
+ return result;
+};
+
+window.renderCommentCell = function(result, {col, row})
+{
+ if(col.name === 'revision')
+ {
+ result[0] = {html:'' + row.data.revision + '', 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)
+ {
+ window.location.href = menus[index - 1].url;
+ }
+})
+
+/**
+ * 当选中两行时禁用其他行。
+ * Disable checkable attribution when checked rows equal 2.
+ *
+ * @param object changes
+ * @access public
+ * @return void
+ */
+window.checkedChange = function(changes)
+{
+ const dtable = zui.DTable.query('#repo-comments-table');
+ var checkedIds = dtable == undefined ? [] : dtable.$.getChecks()
+
+ if(checkedIds.length < 2)
+ {
+ $('.btn-diff').addClass('disabled')
+ }
+ else
+ {
+ $('.btn-diff').removeClass('disabled')
+ }
+}
+
+window.diffClick = function()
+{
+ const dtable = zui.DTable.query('#repo-comments-table');
+ var checkedIds = dtable.$.getChecks();
+ var newDiffLink = diffLink.replace('{oldRevision}', revisionMap[checkedIds[1]]);
+ newDiffLink = newDiffLink.replace('{newRevision}', revisionMap[checkedIds[0]]);
+
+ console.log('checkedIds:', checkedIds);
+
+ window.location.href = newDiffLink;
+}
+
+$(function()
+{
+ setTimeout(function()
+ {
+ const dtable = zui.DTable.query('#repo-comments-table');
+ dtable.$.toggleCheckRows(Object.keys(revisionMap).slice(0, 2));
+ window.checkedChange();
+ }, 100);
+})
\ No newline at end of file
diff --git a/module/repo/ui/browse.html.php b/module/repo/ui/browse.html.php
new file mode 100644
index 0000000000..0673b2d9b0
--- /dev/null
+++ b/module/repo/ui/browse.html.php
@@ -0,0 +1,239 @@
+
+ * @package repo
+ * @link http://www.zentao.net
+ */
+
+namespace zin;
+
+/* Prepare repo select data. */
+$menus = 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");
+
+ $menus[] = array('text' => $branchName, 'value' => $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");
+
+ $menus[] = array('text' => $tagName, 'value' => $tagName, 'url' => $tagLink);
+}
+
+/* Prepare breadcrumb navigation data. */
+$base64BranchID = helper::safe64Encode(base64_encode($branchID));
+$breadcrumbItems = array();
+$breadcrumbItems[] = h::a
+(
+ setClass('form-title'),
+ set::href($this->repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID")),
+ $repo->name,
+);
+$breadcrumbItems[] = h::span('/');
+
+$paths = explode('/', $path);
+$fileName = array_pop($paths);
+$postPath = '';
+foreach($paths as $index => $pathName)
+{
+ $postPath .= $pathName . '/';
+ $breadcrumbItems[] = h::a
+ (
+ setClass('form-title'),
+ set::href($this->repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID&path=" . $this->repo->encodePath($postPath))),
+ trim($pathName, '/'),
+ );
+ $breadcrumbItems[] = h::span('/');
+}
+if($fileName) $breadcrumbItems[] = h::span($fileName);
+
+/* zin: Define the set::module('repo') feature bar on main menu. */
+featureBar(
+ formGroup
+ (
+ set::width('200px'),
+ set::class('repo-select'),
+ set::required(true),
+ select
+ (
+ set::id('repo-select'),
+ set::items($menus),
+ set::value($selected)
+ )
+ ),
+ ...$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');
+$downloadItem = array('text' => $lang->repo->downloadCode, 'url' => '#modalDownloadCode', 'class' => 'primary download-btn', 'icon' => 'download', 'data-toggle' => 'modal');
+
+$tableData = initTableData($infos, $config->repo->repoDtable->fieldList, $this->repo);
+
+toolbar
+(
+ span(
+ set::class('last-sync-time'),
+ $lang->repo->notice->lastSyncTime . $cacheTime
+ ),
+ item(set($refreshItem)),
+ item(set($downloadItem)),
+);
+
+modalTrigger
+(
+ modal
+ (
+ set::id('modalDownloadCode'),
+ set::title($lang->repo->downloadCode),
+ $cloneUrl->svn ? div
+ (
+ p(set::class('repo-downloadCode'), $lang->repo->cloneUrl),
+ formRow
+ (
+ formGroup
+ (
+ set::width('2/3'),
+ input
+ (
+ set::type('text'),
+ set::value($cloneUrl->svn),
+ set::readOnly(true),
+ ),
+ ),
+ formGroup
+ (
+ set::width('1/6'),
+ btn
+ (
+ set::icon('copy'),
+ )
+ )
+ ),
+ ) : null,
+
+ $cloneUrl->ssh ? div
+ (
+ p(set::class('repo-downloadCode'), $lang->repo->sshClone),
+ formRow
+ (
+ formGroup
+ (
+ set::width('2/3'),
+ input
+ (
+ set::type('text'),
+ set::value($cloneUrl->ssh),
+ set::disabled(true),
+ ),
+ ),
+ formGroup
+ (
+ set::width('1/6'),
+ btn
+ (
+ set::icon('copy'),
+ )
+ )
+ ),
+ ) : null,
+
+ $cloneUrl->http ? div
+ (
+ p(set::class('repo-downloadCode'), $lang->repo->httpClone),
+ formRow
+ (
+ formGroup
+ (
+ set::width('2/3'),
+ input
+ (
+ set::type('text'),
+ set::value($cloneUrl->http),
+ set::disabled(true),
+ ),
+ ),
+ formGroup
+ (
+ set::width('1/6'),
+ btn
+ (
+ set::icon('copy'),
+ )
+ )
+ ),
+ ) : null,
+
+ to::footer
+ (
+ set::footerClass('flex-center'),
+ btn
+ (
+ set::class('downloadZip-btn'),
+ set::text($lang->repo->downloadZip),
+ )
+ ),
+ )
+);
+
+dtable
+(
+ set::cols($config->repo->repoDtable->fieldList),
+ set::data($tableData),
+ set::onRenderCell(jsRaw('window.renderCell')),
+ 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}");
+$revisionMap = array();
+foreach($revisions as $revision) $revisionMap[$revision->id] = $revision->revision;
+
+jsVar('repoID', $repoID);
+jsVar('branch', $branchID);
+jsVar('menus', $menus);
+jsVar('revisionMap', $revisionMap);
+jsVar('diffLink', $diffLink);
+jsVar('sortLink', helper::createLink('repo', 'browse', "repoID={$repoID}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}"));
+
+
+$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 secondary size-sm btn-diff", 'btnType' => 'primary', 'onClick' => jsRaw('window.diffClick'));
+$footToolbar['items'][] = array('text' => $lang->repo->allLog, 'url' => $readAllLink);
+
+sidebar
+(
+ set::side('right'),
+ cell(set::class('sidebar-comments')),
+ 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::checkInfo(jsRaw('function(){return {html:\'\'}}')),
+ set::footToolbar($footToolbar),
+ set::footPager(usePager()),
+ set::showToolbarOnChecked(false),
+ ),
+);
+
+render();
diff --git a/module/repo/zen.php b/module/repo/zen.php
index d7878bea78..e8e97f0915 100644
--- a/module/repo/zen.php
+++ b/module/repo/zen.php
@@ -121,7 +121,7 @@ class repoZen extends repo
exec($versionCommand, $versionOutput, $versionResult);
if($versionResult)
{
- $message = sprintf($this->lang->repo->error->output, $versionCommand, $versionResult, join("
", $versionOutput));
+ $message = sprintf($this->lang->repo->error->output, $versionCommand, $versionResult, implode("
", $versionOutput));
dao::$errors['client'] = $this->lang->repo->error->cmd . "
" . nl2br($message);
return false;
}
@@ -151,7 +151,7 @@ class repoZen extends repo
exec($command, $output, $result);
if($result)
{
- $message = sprintf($this->lang->repo->error->output, $command, $result, join("
", $output));
+ $message = sprintf($this->lang->repo->error->output, $command, $result, implode("
", $output));
if(stripos($message, 'Expected FS format between') !== false and strpos($message, 'found format') !== false)
{
dao::$errors['client'] = $this->lang->repo->error->clientVersion;
@@ -214,7 +214,7 @@ class repoZen extends repo
exec($command, $output, $result);
if($result)
{
- dao::$errors['submit'] = $this->lang->repo->error->connect . "
" . sprintf($this->lang->repo->error->output, $command, $result, join("
", $output));
+ dao::$errors['submit'] = $this->lang->repo->error->connect . "
" . sprintf($this->lang->repo->error->output, $command, $result, implode("
", $output));
return false;
}
}
@@ -323,7 +323,7 @@ class repoZen extends repo
* @access protected
* @return array
*/
- protected function getFilesInfo(object $repo, string $path, string $branchID, int $refresh, string $revison, object $lastRevision): 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');
@@ -338,7 +338,7 @@ class repoZen extends repo
{
$infos = $this->scm->ls($path, $revision);
$revisionList = array_column($infos, 'revision', 'revision');
- $comments = $this->repo->getHistory($repoID, $revisionList);
+ $comments = $this->repo->getHistory($repo->id, $revisionList);
foreach($infos as $info)
{
if(isset($comments[$info->revision]))