ajaxgeteditorcontent.html.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <?php
  2. /**
  3. * The create view file of repo module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2022 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @author Yanyi Cao
  7. * @package repo
  8. * @version $Id: create.html.php $
  9. */
  10. ?>
  11. <?php
  12. include $app->getModuleRoot() . 'common/view/header.lite.html.php';
  13. include $app->getModuleRoot() . 'common/view/form.html.php';
  14. include $app->getModuleRoot() . 'common/view/kindeditor.html.php';
  15. include 'header.review.html.php';
  16. $canLinkStory = common::hasPriv('repo', 'linkStory');
  17. $canLinkBug = common::hasPriv('repo', 'linkBug');
  18. $canLinkTask = common::hasPriv('repo', 'linkTask');
  19. $canUnlinkObject = common::hasPriv('repo', 'unlink');
  20. $canAddBug = common::hasPriv('repo', 'addBug');
  21. js::set('jsRoot', $jsRoot);
  22. js::set('clientLang', $app->clientLang);
  23. js::set('fileExt', $this->config->repo->fileExt);
  24. js::set('file', $pathInfo);
  25. js::set('blameTmpl', $lang->repo->blameTmpl);
  26. js::set('repoID', $repoID);
  27. js::set('showEditor', $showEditor);
  28. js::set('canLinkStory', $canLinkStory);
  29. js::set('canLinkBug', $canLinkBug);
  30. js::set('canLinkTask', $canLinkTask);
  31. js::set('objectID', 0);
  32. js::set('objectType', 'story');
  33. js::set('pageType', $type);
  34. js::set('revision', $revision);
  35. js::set('sourceRevision', $oldRevision);
  36. js::set('encodePath', $this->repo->encodePath($entry));
  37. js::set('canAddBug', $canAddBug);
  38. js::set('createLang', $lang->repo->addIssue);
  39. if($showEditor) js::set('codeContent', $content);
  40. js::import($jsRoot . '/zui/tabs/tabs.min.js');
  41. js::import($jsRoot . 'monaco-editor/min/vs/loader.js');
  42. ?>
  43. <div id="monacoEditor" class='repoCode'>
  44. <?php if(strpos($config->repo->images, "|$suffix|") !== false):?>
  45. <div class='image'><img src='data:image/<?php echo $suffix?>;base64,<?php echo $content?>' /></div>
  46. <?php elseif($suffix == 'binary'):?>
  47. <div class='binary'><?php echo html::a($this->repo->createLink('download', "repoID=$repoID&path=" . $this->repo->encodePath($entry) . "&fromRevision=$revision"), "<i class='icon-download'></i>", 'hiddenwin', "title='{$lang->repo->download}'"); ?></div>
  48. <?php else:?>
  49. <div id="codeContainer"></div>
  50. <?php endif;?>
  51. <div id="log">
  52. <div class="history"></div>
  53. <div class="pull-right action-btn">
  54. <div class="btn btn-close pull-right"><i class="icon icon-close"></i></div>
  55. <?php if($canLinkStory or $canLinkBug or $canLinkTask):?>
  56. <div class="dropdown pull-right">
  57. <button class="btn" type="button" data-toggle="context-dropdown"><i class="icon icon-ellipsis-v icon-rotate-90"></i></button>
  58. <ul class="dropdown-menu">
  59. <?php
  60. if($canLinkStory) echo '<li id="linkStory">' . html::a('javascript:;', '<i class="icon icon-lightbulb"></i> ' . $lang->repo->linkStory) . '</li>';
  61. if($canLinkBug) echo '<li id="linkBug">' . html::a('javascript:;', '<i class="icon icon-bug"></i> ' . $lang->repo->linkBug) . '</li>';
  62. if($canLinkTask) echo '<li id="linkTask">' . html::a('javascript:;', '<i class="icon icon-todo"></i> ' . $lang->repo->linkTask) . '</li>';
  63. ?>
  64. </ul>
  65. </div>
  66. <?php endif;?>
  67. </div>
  68. </div>
  69. <div id="related">
  70. <div class="main-col main">
  71. <div class="content panel">
  72. <div class='btn-toolbar'>
  73. <div class="btn btn-left pull-left"><i class="icon icon-chevron-left"></i></div>
  74. <div class="btn btn-right pull-right"><i class="icon icon-chevron-right"></i></div>
  75. <div class='panel-title'>
  76. <div class="tabs w-10" id="relationTabs"></div>
  77. </div>
  78. </div>
  79. </div>
  80. </div>
  81. <div class="table-empty-tip">
  82. <p><?php echo $lang->repo->notRelated;?></p>
  83. </div>
  84. </div>
  85. </div>
  86. <div id='reviewBugs' class='hidden'>
  87. <div class="modal-header">
  88. <button type="button" class="close" data-dismiss="modal"><i class="icon icon-close"></i></button>
  89. <h4 class="modal-title"><?php echo $lang->repo->viewBugs;?></h4>
  90. </div>
  91. <div class='review-bug-list'></div>
  92. </div>
  93. <?php include $app->getModuleRoot() . 'common/view/footer.lite.html.php';?>
  94. <script>
  95. var editor = null;
  96. var viewZoneId = null;
  97. var bugViewZoneId = null;
  98. var modifiedEditor = null;
  99. var diffContent = null;
  100. var blames = null;
  101. var globalCommit = '';
  102. var codeHeight = $(window).innerHeight() - $('#mainHeader').height() - $('#appsBar').height() - $('#fileTabs .tabs-navbar').height();
  103. var editorWidth = $(window).innerWidth() - 150;
  104. var zoneLeft = '65px'
  105. if(codeHeight > 0) $.cookie('codeContainerHeight', codeHeight);
  106. $('#codeContainer').css('height', $.cookie('codeContainerHeight'));
  107. /**
  108. * Get relation by commit.
  109. *
  110. * @param string $commit
  111. * @access public
  112. * @return void
  113. */
  114. function getRelation(commit)
  115. {
  116. $('.table-empty-tip').show();
  117. $('#codeContainer').css('height', codeHeight / 5 * 3);
  118. var relatedHeight = codeHeight / 5 * 2 - $('#log').height() - 10;
  119. $('#related').css('height', relatedHeight);
  120. $tabs = $('#relationTabs').data('zui.tabs');
  121. if($tabs) $tabs.closeAll();
  122. $.post(createLink('repo', 'ajaxGetCommitRelation', 'repoID=' + repoID + '&commit=' + commit), function(data)
  123. {
  124. var titleList = JSON.parse(data).titleList;
  125. var tabs = [];
  126. $.each(titleList, function(i, titleObj)
  127. {
  128. var tab = setTab(titleObj);
  129. if($tabs)
  130. {
  131. $tabs.open(tab);
  132. }
  133. else
  134. {
  135. tabs.push(tab);
  136. }
  137. });
  138. if(titleList.length > 0)
  139. {
  140. if($tabs)
  141. {
  142. $tabs.open(setTab(titleList[0]));
  143. }
  144. else
  145. {
  146. $('#relationTabs').tabs({tabs: tabs});
  147. }
  148. $('.table-empty-tip').hide();
  149. $('#related button').show();
  150. }
  151. else
  152. {
  153. if(!canLinkStory && !canLinkBug && !canLinkTask) $('#related button').hide();
  154. }
  155. arrowTabs('relationTabs', 1);
  156. });
  157. globalCommit = commit;
  158. var linkStory = createLink('repo', 'linkStory', 'repoID=' + repoID + '&commit=' + commit, '', true);
  159. var linkBug = createLink('repo', 'linkBug', 'repoID=' + repoID + '&commit=' + commit, '', true);
  160. var linkTask = createLink('repo', 'linkTask', 'repoID=' + repoID + '&commit=' + commit, '', true);
  161. $('#linkStory a').attr('data-link', linkStory);
  162. $('#linkBug a').attr('data-link', linkBug);
  163. $('#linkTask a').attr('data-link', linkTask);
  164. $('#related').show();
  165. }
  166. <?php if($canUnlinkObject):?>
  167. $('#relationTabs').on('onLoad', function(event, tab) {
  168. var objectInfo = tab.id.split('-');
  169. objectID = objectInfo[0];
  170. objectType = objectInfo[1];
  171. unlink = createLink('repo', 'unlink', 'repoID=' + repoID + '&commit=' + globalCommit + '&objectID=' + objectID + '&objectType=' + objectType);
  172. $('#relationTabs ul li[data-id=' + tab.id + '] span.title').after('<a title="<?php echo $lang->repo->unlink;?>" class="unlinks" data-link="' + unlink + '"><i class="icon-unlink"></i></a>');
  173. });
  174. <?php endif;?>
  175. /**
  176. * Set tab data.
  177. *
  178. * @param object $titleObj
  179. * @access public
  180. * @return object
  181. */
  182. function setTab(titleObj)
  183. {
  184. return {
  185. id: titleObj.type + '-' + titleObj.id,
  186. title: titleObj.title,
  187. icon: titleObj.type == 'story' ? 'icon-lightbulb' : (titleObj.type == 'task' ? 'icon-check-sign' : 'icon-bug'),
  188. type: 'iframe',
  189. url: createLink('repo', 'ajaxGetRelationInfo', 'objectID=' + titleObj.id + '&objectType=' + titleObj.type)
  190. };
  191. }
  192. /**
  193. * Init bugs.
  194. *
  195. * @access public
  196. * @return void
  197. */
  198. function initBugs()
  199. {
  200. var selector = '#codeContainer .margin-view-overlays > div';
  201. if(pageType == 'diff') selector = '#codeContainer .modified-in-monaco-diff-editor .margin-view-overlays > div';
  202. $.each($(selector), function(i)
  203. {
  204. var lineNumber = $(this).find('.line-numbers').text();
  205. $(this).addClass('line-number-' + lineNumber).addClass('content-line').attr('data-line', lineNumber);
  206. if(!$(this).find('.add-bug').length) $(this).append('<span class="view-line-icon text-primary add-bug hidden icon-create-' + lineNumber + '" data-line="' + lineNumber + '" title="' + createLang + '"><i class="icon icon-plus-bold text-primary strong"></i></span>');
  207. var $viewLine = pageType == 'diff' ? $('.modified .view-lines:not(".line-delete")') : $('.view-lines');
  208. $viewLine.find('.view-line').eq(i).attr('class', '').addClass('view-line-' + lineNumber + ' view-line view-content-line').attr('data-line', lineNumber);
  209. });
  210. if(bugs)
  211. {
  212. var lineBugs, bugsCount, i, hasBugIcon, $lineNumberDom;
  213. for(var line in bugs)
  214. {
  215. if(line)
  216. {
  217. lineBugs = bugs[line];
  218. bugsCount = lineBugs.length;
  219. var $viewNumberDom = pageType == 'diff' ? $('.modified .view-lines .view-line-' + line) : $('.view-lines .view-line-' + line);
  220. if($viewNumberDom.find('.view-line-icon .bug-count').length) $viewNumberDom.find('.view-line-icon.view-bugs').remove();
  221. $viewNumberDom.append('<span class="view-line-icon view-bugs text-primary icon-bug-' + line + '" data-line="' + line + '"><i class="icon icon-audit"></i><strong class="bug-count">' + bugsCount + '</strong></span>');
  222. }
  223. }
  224. }
  225. }
  226. /**
  227. * Update diff editor inline style.
  228. *
  229. * @param bool display
  230. * @access public
  231. * @return void
  232. */
  233. function updateEditorInline(display){
  234. if(display)
  235. {
  236. zoneLeft = '20px';
  237. editorWidth = $(window).innerWidth() / 2;
  238. }
  239. else
  240. {
  241. zoneLeft = '50px';
  242. editorWidth = $(window).innerWidth() - 150;
  243. }
  244. modifiedEditor.updateOptions({renderSideBySide: display});
  245. setTimeout(initBugs, 100);
  246. }
  247. /**
  248. * Show commit info.
  249. *
  250. * @access public
  251. * @return void
  252. */
  253. function showCommitInfo()
  254. {
  255. var link = createLink('repo', 'ajaxGetCommitInfo');
  256. var data = {
  257. repoID : repoID,
  258. entry : encodePath,
  259. revision : revision,
  260. sourceRevision: sourceRevision,
  261. line : 0,
  262. returnType : 'json'
  263. };
  264. $.post(link, data, function(res)
  265. {
  266. if(!res) return;
  267. res = JSON.parse(res);
  268. blames = res.blames;
  269. })
  270. }
  271. /**
  272. * Show blame info and relations.
  273. *
  274. * @param int line
  275. * @access public
  276. * @return void
  277. */
  278. function showBlameAndRelation(line)
  279. {
  280. /* Show bugs in this line. */
  281. if(!blames) return;
  282. if(diffContent)
  283. {
  284. var lineIndex = jQuery.inArray(line, diffContent.line.new);
  285. line = diffContent.line.new[lineIndex];
  286. }
  287. var blame = blames[line];
  288. if(!blame) return;
  289. var p_line = parseInt(line);
  290. while(!blame.revision)
  291. {
  292. p_line--;
  293. blame = blames[p_line];
  294. }
  295. if($('#log').data('line') == p_line) return;
  296. var time = blame.time != 'unknown' ? blame.time : '';
  297. var user = blame.committer != 'unknown' ? blame.committer : '';
  298. var version = blame.revision.toString().substring(0, 10);
  299. var content = blameTmpl.replace('%line', line).replace('%time', time).replace('%name', user).replace('%version', version).replace('%comment', blame.message);
  300. $('.history').html(content);
  301. $('#log').data('line', line);
  302. $('#log').css('display', 'flex');
  303. getRelation(blame.revision);
  304. }
  305. $(function()
  306. {
  307. $('.btn-left').click(function() {arrowTabs('relationTabs', 1);});
  308. $('.btn-right').click(function() {arrowTabs('relationTabs', -2);});
  309. if(showEditor)
  310. {
  311. require.config({
  312. paths: {vs: jsRoot + 'monaco-editor/min/vs'},
  313. 'vs/nls': {
  314. availableLanguages: {
  315. '*': clientLang
  316. }
  317. }
  318. });
  319. require(['vs/editor/editor.main'], function ()
  320. {
  321. var lang = 'php';
  322. $.each(fileExt, function(langName, ext)
  323. {
  324. if(ext.indexOf('.' + file.extension) !== -1) lang = langName;
  325. });
  326. if(pageType == 'diff')
  327. {
  328. diffContent = parent.getDiffs(file.dirname + '/' + file.basename);
  329. modifiedEditor = monaco.editor.createDiffEditor(document.getElementById('codeContainer'),
  330. {
  331. folding: false,
  332. language: lang,
  333. readOnly: true,
  334. autoIndent: true,
  335. contextmenu: true,
  336. automaticLayout: true,
  337. minimap: {enabled: false},
  338. showFoldingControls: 'never',
  339. lineNumbers: function(number)
  340. {
  341. var newlc = diffContent.line.new;
  342. var oldlc = diffContent.line.old;
  343. return newlc[number - 1];
  344. }
  345. });
  346. modifiedEditor.setModel({
  347. original: monaco.editor.createModel(diffContent.code.old.trim("\n"), lang),
  348. modified: monaco.editor.createModel(diffContent.code.new.trim("\n"), lang),
  349. });
  350. editor = modifiedEditor.getModifiedEditor();
  351. var getOriginalEditor = modifiedEditor.getOriginalEditor();
  352. getOriginalEditor.onMouseDown(function(obj)
  353. {
  354. showBlameAndRelation(obj.target.position.lineNumber);
  355. })
  356. }
  357. else
  358. {
  359. editor = monaco.editor.create(document.getElementById('codeContainer'),
  360. {
  361. value: codeContent.toString(),
  362. folding: false,
  363. language: lang,
  364. readOnly: true,
  365. autoIndent: true,
  366. contextmenu: true,
  367. automaticLayout: true,
  368. minimap: {enabled: false},
  369. showFoldingControls: 'never'
  370. });
  371. }
  372. editor.onDidScrollChange(function()
  373. {
  374. setTimeout(initBugs, 100);
  375. });
  376. editor.addAction({
  377. id: "editor.action.addBug",
  378. label: createLang,
  379. contextMenuGroupId: 'customCommand',
  380. run() {
  381. var position = editor.getPosition();
  382. showBugForm(position.lineNumber)
  383. }
  384. });
  385. editor.onMouseMove(function(obj)
  386. {
  387. var line = obj.target.position ? obj.target.position.lineNumber : 0;
  388. if($(obj.target.element).closest('.view-content-line').length) line = $(obj.target.element).closest('.view-content-line').attr('data-line');
  389. if($(obj.target.element).closest('.content-line').length) line = $(obj.target.element).closest('.content-line').attr('data-line');
  390. if($(obj.target.element).parent().hasClass('view-line-icon')) return;
  391. if($(obj.target.element).hasClass('add-bug')) return;
  392. $('.content-line .add-bug').addClass('hidden');
  393. $('.line-number-' + line + ' .add-bug').removeClass('hidden');
  394. });
  395. editor.onMouseDown(function(obj)
  396. {
  397. closeRelation();
  398. var line = obj.target.position ? obj.target.position.lineNumber : $(obj.target.element).closest('.view-content-line').data('line');
  399. if($(obj.target.element).hasClass('icon-plus-bold') || $(obj.target.element).closest('.add-bug').length)
  400. {
  401. showBugForm(line);
  402. return;
  403. }
  404. if($(obj.target.element).parent().hasClass('view-line-icon'))
  405. {
  406. showBugs(line);
  407. return;
  408. }
  409. if(!line || $('div[widgetid="bugForm' + line + '"]').html()) return;
  410. hiddenForm();
  411. showBlameAndRelation(line);
  412. setTimeout(initBugs, 100);
  413. });
  414. setTimeout(function()
  415. {
  416. initBugs();
  417. var hash = parent.location.hash;
  418. if(hash)
  419. {
  420. var reg = "^#L\\d+$";
  421. if(hash.match(reg))
  422. {
  423. var line = parseInt(hash.substr(2));
  424. if(pageType == 'diff') line = jQuery.inArray(line, diffContent.line.new) + 1;
  425. }
  426. }
  427. }, 500);
  428. });
  429. }
  430. $('#linkStory a, #linkBug a, #linkTask a').on('click', function()
  431. {
  432. var link = $(this).attr('data-link');
  433. parent.loadLinkPage(link);
  434. });
  435. $('#relationTabs').on('click', '.unlinks',function()
  436. {
  437. var link = $(this).attr('data-link');
  438. var tabID = $(this).attr('data-tabID');
  439. $.post(link, function(data)
  440. {
  441. data = JSON.parse(data);
  442. if(data.result)
  443. {
  444. $tabs = $('#relationTabs').data('zui.tabs');
  445. if($tabs) $tabs.close(tabID);
  446. $('#relationTabs #tab-nav-item-' + tabID).remove();
  447. getRelation(data.revision);
  448. }
  449. else
  450. {
  451. alert(data.message);
  452. }
  453. })
  454. });
  455. $('#relationTabs').on('onOpen', function(event, tab)
  456. {
  457. $('#tab-nav-item-' + tab.id).attr('title', tab.title);
  458. var relatedHeight = codeHeight / 5 * 2 - $('#log').height() - 45;
  459. $('#relationTabs iframe').css('height', relatedHeight);
  460. });
  461. /* Get file commits. */
  462. showCommitInfo();
  463. });
  464. </script>