diff --git a/lib/base/front/front.class.php b/lib/base/front/front.class.php index b71849b423..13f4c0842c 100644 --- a/lib/base/front/front.class.php +++ b/lib/base/front/front.class.php @@ -1232,7 +1232,7 @@ EOT; } /* Fix value is '0123' error. */ - if(is_numeric($value) and !preg_match('/^0[1-9]/', $value)) + if(is_numeric($value) and !preg_match('/^0[0-9]+/', $value)) { $js .= "{$prefix}{$key} = {$value};"; } diff --git a/module/repo/control.php b/module/repo/control.php index 4fd22cf037..67ba225f8e 100644 --- a/module/repo/control.php +++ b/module/repo/control.php @@ -247,6 +247,52 @@ class repo extends control return print(js::reload('parent')); } + /** + * Get diff editor content by ajax. + * + * @param int $repoID + * @param int $objectID + * @param string $entry + * @param string $oldRevision + * @param string $newRevision + * @param string $showBug + * @param string $encoding + * @access public + * @return void + */ + public function ajaxGetDiffEditorContent($repoID, $objectID = 0, $entry = '', $oldRevision = '', $newRevision = '', $showBug = 'false', $encoding = '') + { + $file = $entry; + $repo = $this->repo->getRepoByID($repoID); + $entry = urldecode($this->repo->decodePath($entry)); + $revision = str_replace('*', '-', $oldRevision); + $nRevision = str_replace('*', '-', $newRevision); + + $entry = urldecode($entry); + $pathInfo = pathinfo($entry); + $encoding = empty($encoding) ? $repo->encoding : $encoding; + $encoding = strtolower(str_replace('_', '-', $encoding)); + + $this->scm->setEngine($repo); + $info = $this->scm->info($entry, $nRevision); + $path = $entry ? $info->path : ''; + + $this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->diff; + $this->view->type = 'diff'; + $this->view->encoding = str_replace('-', '_', $encoding); + $this->view->repoID = $repoID; + $this->view->objectID = $objectID; + $this->view->repo = $repo; + $this->view->revision = $nRevision; + $this->view->file = $file; + $this->view->content = ''; + $this->view->pathInfo = $pathInfo; + $this->view->suffix = 'c'; + $this->view->blames = array(); + $this->view->showEditor = true; + $this->display('repo', 'ajaxGetEditorContent'); + } + /** * Get editor content by ajax. * diff --git a/module/repo/css/diff.css b/module/repo/css/diff.css index 7124f55959..be4e7fcbe2 100644 --- a/module/repo/css/diff.css +++ b/module/repo/css/diff.css @@ -24,3 +24,13 @@ table.diff {margin-bottom: 0px;} .body-modal #mainMenu {padding-bottom: 40px;} .repoCode td.code {white-space: inherit;} +.repoCode .content, .repoCode .btn {background-color: #F4F5F7; border: none;} +.repoCode .nav-tabs > li.active > a:before {background: none; height: 0;} + +#fileTabs .tab-pane {display: none;} +#fileTabs .tab-pane.active {display: block;} +#fileTabs .tab-nav-item {max-width: none !important;} +#fileTabs > .tabs-navbar {overflow: hidden; padding-bottom: 10px; position: relative; height: 35px;} +#fileTabs > .tabs-navbar > .nav-tabs {position: absolute; display: flex;} +#filesTree #modules {margin-top: 5px;} +#filesTree {overflow-y: auto;} diff --git a/module/repo/js/common.js b/module/repo/js/common.js index 645f1ea834..737427acdf 100644 --- a/module/repo/js/common.js +++ b/module/repo/js/common.js @@ -124,3 +124,51 @@ $(function() }); } }) + +/** + * Set pane height. + * + * @access public + * @return void + */ +function setHeight() +{ + var paneHeight = $(window).height() - 120; + $('#fileTabs .tab-pane').css('height', paneHeight + 'px') + + paneHeight += 45; + if($('.label-exchange').html()) paneHeight -= 50; + $('#filesTree').css('height', paneHeight + 'px') +} +setHeight(); + +$(document).on('click', '.repoFileName', function() +{ + var path = encodeURIComponent($(this).data('path')); + var name = $(this).text(); + var $tabs = $('#fileTabs').data('zui.tabs'); + if(openedFiles.indexOf(path) == -1) openedFiles.push(path); + + $tabs.open(createTab(name, path)); + setHeight(); + arrowTabs('fileTabs', -2); +}); + +/* Remove file path for opened files. */ +$('#fileTabs').on('onClose', function(event, tab) { + var filepath = decodeURIComponent(Base64.decode(tab.id.replaceAll('-', '='))); + var index = openedFiles.indexOf(filepath); + if(index > -1) + { + openedFiles.splice(index, 1) + $('[data-path="' + filepath + '"]').closest('li').removeClass('selected'); + } + + if(index == openedFiles.length) arrowTabs('fileTabs', -2); +}); + +/* Append file path into the title. */ +$('#fileTabs').on('onLoad', function(event, tab) { + var filepath = Base64.decode(tab.id.replaceAll('-', '=')); + $('#tab-nav-item-' + tab.id).attr('title', decodeURIComponent(filepath)); +}); diff --git a/module/repo/js/diff.js b/module/repo/js/diff.js index b95eb6e738..054dd01302 100644 --- a/module/repo/js/diff.js +++ b/module/repo/js/diff.js @@ -1,11 +1,122 @@ -$(document).ready(function() -{ - $("#inline").click(function(){$('#arrange').val('inline');this.form.submit();}); - $("#appose").click(function(){$('#arrange').val('appose');this.form.submit();}); - $(".label-exchange").click(function(){ $('#exchange').submit();}); -}); function changeEncoding(encoding) { $('#encoding').val(encoding); $('#encoding').parents('form').submit(); } + +function getDiffs(fileName) +{ + var result = {'new': '', 'old': ''}; + $.each(diffs, function(i, diff) + { + if(diff.fileName == fileName) + { + if(typeof diff.contents[0].lines != 'object') return result; + + var lines = diff.contents[0].lines; + $.each(lines, function(l, code) + { + if(code.type == 'new') + { + result.new += code.line.substring(2) + "\n"; + } + else + { + result.old += code.line.substring(2) + "\n"; + } + }) + return result; + } + }); + + return result; +} + +/** + * Create file tab. + * + * @param string filename + * @param string filepath + * @access public + * @return object + */ +function createTab(filename, filepath) +{ + $('[data-path="' + decodeURIComponent(filepath) + '"]').closest('li').addClass('selected'); + var tabID = Base64.encode(filepath).replaceAll('=', '-'); + return { + title: filename, + id: tabID, + type: 'iframe', + url: createLink('repo', 'ajaxGetDiffEditorContent', urlParams.replace('%s', Base64.encode(encodeURIComponent(filepath)))) + }; +} + +$(document).ready(function() +{ + $("#inline").click(function(){$('#arrange').val('inline');this.form.submit();}); + $("#appose").click(function(){$('#arrange').val('appose');this.form.submit();}); + $(".label-exchange").click(function(){ $('#exchange').submit();}); + + $('.btn-left').click(function() {arrowTabs('fileTabs', 1);}); + $('.btn-right').click(function() {arrowTabs('fileTabs', -2);}); + if(file) $('#fileTabs').tabs({tabs: [createTab(file['basename'], entry)]}); + + /** + * Set pane height. + * + * @access public + * @return void + */ + function setHeight() + { + var paneHeight = $(window).height() - 120; + $('#fileTabs .tab-pane').css('height', paneHeight + 'px') + $('#filesTree').css('height', paneHeight + 45) + } + setHeight(); + + $(document).on('click', '.repoFileName', function() + { + var path = encodeURIComponent($(this).data('path')); + var name = $(this).text(); + var $tabs = $('#fileTabs').data('zui.tabs'); + if(openedFiles.indexOf(path) == -1) openedFiles.push(path); + + $tabs.open(createTab(name, path)); + setHeight(); + arrowTabs('fileTabs', -2); + }); + + /* Remove file path for opened files. */ + $('#fileTabs').on('onClose', function(event, tab) { + var filepath = decodeURIComponent(Base64.decode(tab.id.replaceAll('-', '='))); + var index = openedFiles.indexOf(filepath); + if(index > -1) + { + openedFiles.splice(index, 1) + $('[data-path="' + filepath + '"]').closest('li').removeClass('selected'); + } + + if(index == openedFiles.length) arrowTabs('fileTabs', -2); + }); + + /* Append file path into the title. */ + $('#fileTabs').on('onLoad', function(event, tab) { + var filepath = Base64.decode(tab.id.replaceAll('-', '=')); + $('#tab-nav-item-' + tab.id).attr('title', decodeURIComponent(filepath)); + }); +}); + +/** + * Load link object page. + * + * @param string $link + * @access public + * @return void + */ +function loadLinkPage(link) +{ + $('#linkObject').attr('href', link); + $('#linkObject').click() +} diff --git a/module/repo/model.php b/module/repo/model.php index 7619c87258..ef7c89a0b8 100644 --- a/module/repo/model.php +++ b/module/repo/model.php @@ -2365,27 +2365,34 @@ class repoModel extends model * * @param object $repo * @param string $branch - * @param string $parent + * @param array $diffs * @access public * @return string */ - public function getFileTree($repo, $branch = '') + public function getFileTree($repo, $branch = '', $diffs = array()) { - if($repo->SCM != 'Subversion' and empty($branch)) $branch = $this->cookie->repoBranch; - $files = $this->dao->select('t1.path,t1.action')->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) - ->andWhere('t1.type')->eq('file') - ->andWhere('left(t2.comment, 12)')->ne('Merge branch') - ->beginIF($repo->SCM != 'Subversion' and $branch)->andWhere('t3.branch')->eq($branch)->fi() - ->orderBy('t2.`time` asc') - ->fetchPairs(); - $allFiles = array(); - foreach($files as $file => $action) + if(empty($diffs)) { - if($action != 'D') $allFiles[] = $file; + if($repo->SCM != 'Subversion' and empty($branch)) $branch = $this->cookie->repoBranch; + $files = $this->dao->select('t1.path,t1.action')->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) + ->andWhere('t1.type')->eq('file') + ->andWhere('left(t2.comment, 12)')->ne('Merge branch') + ->beginIF($repo->SCM != 'Subversion' and $branch)->andWhere('t3.branch')->eq($branch)->fi() + ->orderBy('t2.`time` asc') + ->fetchPairs(); + + foreach($files as $file => $action) + { + if($action != 'D') $allFiles[] = $file; + } + } + else + { + foreach($diffs as $diff) $allFiles[] = $diff->fileName; } return $this->buildFileTree($allFiles); diff --git a/module/repo/view/ajaxgeteditorcontent.html.php b/module/repo/view/ajaxgeteditorcontent.html.php index 19390a3b15..976f168328 100644 --- a/module/repo/view/ajaxgeteditorcontent.html.php +++ b/module/repo/view/ajaxgeteditorcontent.html.php @@ -27,9 +27,10 @@ js::set('canLinkBug', $canLinkBug); js::set('canLinkTask', $canLinkTask); js::set('objectID', 0); js::set('objectType', 'story'); +js::set('pageType', $type); if($showEditor) { - js::set('codeContent', trim($content)); + js::set('codeContent', $content); js::set('blames', $blames); } js::import($jsRoot . '/zui/tabs/tabs.min.js'); @@ -193,24 +194,51 @@ $(function() if(ext.indexOf('.' + file.extension) !== -1) lang = langName; }); - var editor = monaco.editor.create(document.getElementById('codeContainer'), + if(pageType == 'diff') { - autoIndent: true, - value: codeContent.toString(), - language: lang, - contextmenu: true, - EditorMinimapOptions: { - enabled: false - }, - readOnly: true, - automaticLayout: true - }); + var diffContent = parent.getDiffs(file.basename); + var editor = monaco.editor.createDiffEditor(document.getElementById('codeContainer'), + { + tabCompletion: 'on', + autoIndent: true, + language: lang, + contextmenu: true, + EditorMinimapOptions: { + enabled: false + }, + readOnly: true, + automaticLayout: true + }); + + editor.setModel({ + original: monaco.editor.createModel(diffContent.old, lang), + modified: monaco.editor.createModel(diffContent.new, lang), + }); + } + else + { + var editor = monaco.editor.create(document.getElementById('codeContainer'), + { + tabCompletion: 'on', + autoIndent: true, + value: codeContent.toString(), + language: lang, + contextmenu: true, + EditorMinimapOptions: { + enabled: false + }, + readOnly: true, + automaticLayout: true + }); + } editor.onMouseDown(function(obj) { var line = obj.target.position.lineNumber; - var blame = blames[line]; + var blame = blames[line]; + if(!blame) return; + var p_line = parseInt(line); while(!blame.revision) { diff --git a/module/repo/view/diff.html.php b/module/repo/view/diff.html.php index 5bff77e99f..b918742401 100644 --- a/module/repo/view/diff.html.php +++ b/module/repo/view/diff.html.php @@ -11,6 +11,10 @@ + +
@@ -154,6 +159,9 @@
+ + +