PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/websvn-2.3.3/listing.php

#
PHP | 346 lines | 261 code | 47 blank | 38 comment | 79 complexity | aad7586ec1a18613274feb6ff5fc33c3 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. // WebSVN - Subversion repository viewing via the web using PHP
  3. // Copyright (C) 2004-2006 Tim Armes
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation; either version 2 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. //
  19. // --
  20. //
  21. // listing.php
  22. //
  23. // Show the listing for the given repository/path/revision
  24. require_once 'include/setup.php';
  25. require_once 'include/svnlook.php';
  26. require_once 'include/utils.php';
  27. require_once 'include/template.php';
  28. require_once 'include/bugtraq.php';
  29. function removeURLSeparator($url) {
  30. return preg_replace('#(\?|&(amp;)?)$#', '', $url);
  31. }
  32. function urlForPath($fullpath, $passRevString) {
  33. global $config, $rep;
  34. $isDir = $fullpath{strlen($fullpath) - 1} == '/';
  35. if ($isDir) {
  36. if ($config->treeView) {
  37. $url = $config->getURL($rep, $fullpath, 'dir').$passRevString;
  38. $id = anchorForPath($fullpath);
  39. $url .= '#'.$id.'" id="'.$id;
  40. } else {
  41. $url = $config->getURL($rep, $fullpath, 'dir').$passRevString;
  42. }
  43. } else {
  44. $url = $config->getURL($rep, $fullpath, 'file').$passRevString;
  45. }
  46. return removeURLSeparator($url);
  47. }
  48. function showDirFiles($svnrep, $subs, $level, $limit, $rev, $peg, $listing, $index, $treeview = true) {
  49. global $config, $lang, $rep, $passrev, $peg, $passRevString;
  50. $path = '';
  51. if (!$treeview) {
  52. $level = $limit;
  53. }
  54. // TODO: Fix node links to use the path and number of peg revision (if exists)
  55. // This applies to file detail, log, and RSS -- leave the download link as-is
  56. for ($n = 0; $n <= $level; $n++) {
  57. $path .= $subs[$n].'/';
  58. }
  59. // List each file in the current directory
  60. $loop = 0;
  61. $last_index = 0;
  62. $accessToThisDir = $rep->hasReadAccess($path, false);
  63. // If using flat view and not at the root, create a '..' entry at the top.
  64. if (!$treeview && count($subs) > 2) {
  65. $parentPath = $subs;
  66. unset($parentPath[count($parentPath) - 2]);
  67. $parentPath = implode('/', $parentPath);
  68. if ($rep->hasReadAccess($parentPath, false)) {
  69. $listing[$index]['rowparity'] = $index % 2;
  70. $listing[$index]['path'] = $parentPath;
  71. $listing[$index]['filetype'] = 'dir';
  72. $listing[$index]['filename'] = '..';
  73. $listing[$index]['fileurl'] = urlForPath($parentPath, $passRevString);
  74. $listing[$index]['filelink'] = '<a href="'.$listing[$index]['fileurl'].'">'.$listing[$index]['filename'].'</a>';
  75. $listing[$index]['level'] = 0;
  76. $listing[$index]['node'] = 0; // t-node
  77. $listing[$index]['revision'] = $rev;
  78. $listing[$index]['revurl'] = $config->getURL($rep, $parentPath, 'revision').'rev='.$rev.'&amp;isdir=1';
  79. global $vars;
  80. $listing[$index]['date'] = $vars['date'];
  81. $listing[$index]['age'] = datetimeFormatDuration(time() - strtotime($vars['date']), true, true);
  82. $index++;
  83. }
  84. }
  85. $openDir = false;
  86. $logList = $svnrep->getList($path, $rev, $peg);
  87. if ($logList) {
  88. $downloadRevAndPeg = createRevAndPegString($rev, $peg ? $peg : $rev);
  89. foreach ($logList->entries as $entry) {
  90. $isDir = $entry->isdir;
  91. if (!$isDir && $level != $limit) {
  92. continue; // Skip any files outside the current directory
  93. }
  94. $file = $entry->file;
  95. $isDirString = ($isDir) ? 'isdir=1&amp;' : '';
  96. // Only list files/directories that are not designated as off-limits
  97. $access = ($isDir) ? $rep->hasReadAccess($path.$file, true)
  98. : $accessToThisDir;
  99. if ($access) {
  100. $listing[$index]['rowparity'] = $index % 2;
  101. if ($isDir) {
  102. $listing[$index]['filetype'] = ($openDir) ? 'diropen' : 'dir';
  103. $openDir = isset($subs[$level + 1]) && (!strcmp($subs[$level + 1].'/', $file) || !strcmp($subs[$level + 1], $file));
  104. } else {
  105. $listing[$index]['filetype'] = strtolower(strrchr($file, '.'));
  106. $openDir = false;
  107. }
  108. $listing[$index]['isDir'] = $isDir;
  109. $listing[$index]['openDir'] = $openDir;
  110. $listing[$index]['level'] = ($treeview) ? $level : 0;
  111. $listing[$index]['node'] = 0; // t-node
  112. $listing[$index]['path'] = $path.$file;
  113. $listing[$index]['filename'] = $file;
  114. if ($isDir) {
  115. $listing[$index]['fileurl'] = urlForPath($path.$file, $passRevString);
  116. } else {
  117. $listing[$index]['fileurl'] = urlForPath($path.$file, createDifferentRevAndPegString($passrev, $peg));
  118. }
  119. $listing[$index]['filelink'] = '<a href="'.$listing[$index]['fileurl'].'">'.$listing[$index]['filename'].'</a>';
  120. if ($isDir) {
  121. $listing[$index]['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.$passRevString;
  122. } else {
  123. $listing[$index]['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.createDifferentRevAndPegString($passrev, $peg);
  124. }
  125. if ($treeview) {
  126. $listing[$index]['compare_box'] = '<input type="checkbox" name="compare[]" value="'.$path.$file.'@'.$passrev.'" onclick="checkCB(this)" />';
  127. }
  128. if ($config->showLastModInListing()) {
  129. $listing[$index]['committime'] = $entry->committime;
  130. $listing[$index]['revision'] = $entry->rev;
  131. $listing[$index]['author'] = $entry->author;
  132. $listing[$index]['age'] = $entry->age;
  133. $listing[$index]['date'] = $entry->date;
  134. $listing[$index]['revurl'] = $config->getURL($rep, $path.$file, 'revision').$isDirString.createRevAndPegString($entry->rev, $peg ? $peg : $rev);
  135. }
  136. if ($rep->isDownloadAllowed($path.$file)) {
  137. $downloadurl = $config->getURL($rep, $path.$file, 'dl').$isDirString.$downloadRevAndPeg;
  138. if ($isDir) {
  139. $listing[$index]['downloadurl'] = $downloadurl;
  140. $listing[$index]['downloadplainurl'] = '';
  141. } else {
  142. $listing[$index]['downloadplainurl'] = $downloadurl;
  143. $listing[$index]['downloadurl'] = '';
  144. }
  145. } else {
  146. $listing[$index]['downloadplainurl'] = '';
  147. $listing[$index]['downloadurl'] = '';
  148. }
  149. if ($rep->isRssEnabled()) {
  150. // RSS should always point to the latest revision, so don't include rev
  151. $listing[$index]['rssurl'] = $config->getURL($rep, $path.$file, 'rss').$isDirString.createRevAndPegString('', $peg);
  152. }
  153. $loop++;
  154. $index++;
  155. $last_index = $index;
  156. if ($isDir && ($level != $limit)) {
  157. // @todo remove the alternate check with htmlentities when assured that there are not side effects
  158. if (isset($subs[$level + 1]) && (!strcmp($subs[$level + 1].'/', $file) || !strcmp(htmlentities($subs[$level + 1], ENT_QUOTES).'/', htmlentities($file)))) {
  159. $listing = showDirFiles($svnrep, $subs, $level + 1, $limit, $rev, $peg, $listing, $index);
  160. $index = count($listing);
  161. }
  162. }
  163. }
  164. }
  165. }
  166. // For an expanded tree, give the last entry an "L" node to close the grouping
  167. if ($treeview && $last_index != 0) {
  168. $listing[$last_index - 1]['node'] = 1; // l-node
  169. }
  170. return $listing;
  171. }
  172. function showTreeDir($svnrep, $path, $rev, $peg, $listing) {
  173. global $vars, $config;
  174. $subs = explode('/', $path);
  175. // For directory, the last element in the subs is empty.
  176. // For file, the last element in the subs is the file name.
  177. // Therefore, it is always count($subs) - 2
  178. $limit = count($subs) - 2;
  179. for ($n = 0; $n < $limit; $n++) {
  180. $vars['last_i_node'][$n] = false;
  181. }
  182. $vars['compare_box'] = ''; // Set blank once in case tree view is not enabled.
  183. return showDirFiles($svnrep, $subs, 0, $limit, $rev, $peg, $listing, 0, $config->treeView);
  184. }
  185. // Make sure that we have a repository
  186. if ($rep) {
  187. $svnrep = new SVNRepository($rep);
  188. if (!empty($rev)) {
  189. $info = $svnrep->getInfo($path, $rev, $peg);
  190. if ($info) {
  191. $path = $info->path;
  192. $peg = (int)$info->rev;
  193. }
  194. }
  195. $history = $svnrep->getLog($path, 'HEAD', 1, false, 2, ($path == '/') ? '' : $peg);
  196. if (!$history) {
  197. unset($vars['error']);
  198. $history = $svnrep->getLog($path, '', '', false, 2, ($path == '/') ? '' : $peg);
  199. if (!$history) {
  200. header('HTTP/1.x 404 Not Found', true, 404);
  201. $vars['error'] = $lang['NOPATH'];
  202. }
  203. }
  204. $youngest = ($history && isset($history->entries[0])) ? $history->entries[0]->rev : 0;
  205. // Unless otherwise specified, we get the log details of the latest change
  206. $lastChangedRev = ($passrev) ? $passrev : $youngest;
  207. if ($lastChangedRev != $youngest) {
  208. $history = $svnrep->getLog($path, $lastChangedRev, 1, false, 2, $peg);
  209. }
  210. $logEntry = ($history && isset($history->entries[0])) ? $history->entries[0] : 0;
  211. $headlog = $svnrep->getLog('/', '', '', true, 1);
  212. $headrev = ($headlog && isset($headlog->entries[0])) ? $headlog->entries[0]->rev : 0;
  213. // If we're not looking at a specific revision, get the HEAD revision number
  214. // (the revision of the rest of the tree display)
  215. if (empty($rev)) {
  216. $rev = $headrev;
  217. }
  218. if ($path == '' || $path{0} != '/') {
  219. $ppath = '/'.$path;
  220. } else {
  221. $ppath = $path;
  222. }
  223. createPathLinks($rep, $ppath, $passrev, $peg);
  224. $passRevString = createRevAndPegString($passrev, $peg);
  225. $isDirString = 'isdir=1&amp;';
  226. $revurl = $config->getURL($rep, $path != '/' ? $path : '', 'dir');
  227. $revurlSuffix = $path != '/' ? '#'.anchorForPath($path) : '';
  228. if ($rev < $youngest) {
  229. if ($path == '/') {
  230. $vars['goyoungesturl'] = $config->getURL($rep, '', 'dir');
  231. } else {
  232. $vars['goyoungesturl'] = $config->getURL($rep, $path, 'dir').createRevAndPegString($youngest, $peg ? $peg: $rev).$revurlSuffix;
  233. }
  234. $vars['goyoungestlink'] = '<a href="'.$vars['goyoungesturl'].'"'.($youngest ? ' title="'.$lang['REV'].' '.$youngest.'"' : '').'>'.$lang['GOYOUNGEST'].'</a>';
  235. $history2 = $svnrep->getLog($path, $rev, $youngest, false, 2, $peg);
  236. if (isset($history2->entries[1])) {
  237. $nextRev = $history2->entries[1]->rev;
  238. if ($nextRev != $youngest) {
  239. $vars['nextrev'] = $nextRev;
  240. $vars['nextrevurl'] = $revurl.createRevAndPegString($nextRev, $peg).$revurlSuffix;
  241. }
  242. }
  243. unset($vars['error']);
  244. }
  245. if (isset($history->entries[1])) {
  246. $prevRev = $history->entries[1]->rev;
  247. $prevPath = $history->entries[1]->path;
  248. $vars['prevrev'] = $prevRev;
  249. $vars['prevrevurl'] = $revurl.createRevAndPegString($prevRev, $peg).$revurlSuffix;
  250. }
  251. $bugtraq = new Bugtraq($rep, $svnrep, $ppath);
  252. $vars['action'] = '';
  253. $vars['rev'] = $rev;
  254. $vars['peg'] = $peg;
  255. $vars['path'] = escape($ppath);
  256. $vars['lastchangedrev'] = $lastChangedRev;
  257. if ($logEntry) {
  258. $vars['date'] = $logEntry->date;
  259. $vars['age'] = datetimeFormatDuration(time() - strtotime($logEntry->date));
  260. $vars['author'] = $logEntry->author;
  261. $vars['log'] = nl2br($bugtraq->replaceIDs(create_anchors(xml_entities($logEntry->msg))));
  262. }
  263. $vars['revurl'] = $config->getURL($rep, ($path == '/' ? '' : $path), 'revision').$isDirString.$passRevString;
  264. $vars['revlink'] = '<a href="'.$vars['revurl'].'">'.$lang['LASTMOD'].'</a>';
  265. if ($history && count($history->entries) > 1) {
  266. $vars['compareurl'] = $config->getURL($rep, '', 'comp').'compare[]='.urlencode($history->entries[1]->path).'@'.$history->entries[1]->rev. '&amp;compare[]='.urlencode($history->entries[0]->path).'@'.$history->entries[0]->rev;
  267. $vars['comparelink'] = '<a href="'.$vars['compareurl'].'">'.$lang['DIFFPREV'].'</a>';
  268. }
  269. $vars['logurl'] = $config->getURL($rep, $path, 'log').$isDirString.$passRevString;
  270. $vars['loglink'] = '<a href="'.$vars['logurl'].'">'.$lang['VIEWLOG'].'</a>';
  271. if ($rep->isRssEnabled()) {
  272. $vars['rssurl'] = $config->getURL($rep, $path, 'rss').$isDirString.createRevAndPegString('', $peg);
  273. $vars['rsslink'] = '<a href="'.$vars['rssurl'].'">'.$lang['RSSFEED'].'</a>';
  274. }
  275. // Set up the tarball link
  276. $subs = explode('/', $path);
  277. $level = count($subs) - 2;
  278. if ($rep->isDownloadAllowed($path) && !isset($vars['warning'])) {
  279. $vars['downloadurl'] = $config->getURL($rep, $path, 'dl').$isDirString.$passRevString;
  280. }
  281. $vars['compare_form'] = '<form method="get" action="'.$config->getURL($rep, '', 'comp').'" id="compare">';
  282. if ($config->multiViews) {
  283. $vars['compare_form'] .= '<input type="hidden" name="op" value="comp"/>';
  284. } else {
  285. $vars['compare_form'] .= '<input type="hidden" name="repname" value="'.$repname.'" />';
  286. }
  287. $vars['compare_submit'] = '<input type="submit" value="'.$lang['COMPAREPATHS'].'" />';
  288. $vars['compare_endform'] = '</form>';
  289. $vars['showlastmod'] = $config->showLastModInListing();
  290. $listing = showTreeDir($svnrep, $path, $rev, $peg, array());
  291. if (!$rep->hasReadAccess($path, true)) {
  292. $vars['error'] = $lang['NOACCESS'];
  293. checkSendingAuthHeader($rep);
  294. }
  295. $vars['restricted'] = !$rep->hasReadAccess($path, false);
  296. } else {
  297. header('HTTP/1.x 404 Not Found', true, 404);
  298. }
  299. renderTemplate('directory');