Rev 1189 Rev 4988
Line 1... Line 1...
1 <?php 1 <?php
2 # vim:et:ts=3:sts=3:sw=3:fdm=marker: -  
3   -  
4 // WebSVN - Subversion repository viewing via the web using PHP 2 // WebSVN - Subversion repository viewing via the web using PHP
5 // Copyright © 2004-2006 Tim Armes, Matt Sicker 3 // Copyright (C) 2004-2006 Tim Armes
6 // 4 //
7 // This program is free software; you can redistribute it and/or modify 5 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by 6 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or 7 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version. 8 // (at your option) any later version.
Line 22... Line 20...
22 // 20 //
23 // listing.php 21 // listing.php
24 // 22 //
25 // Show the listing for the given repository/path/revision 23 // Show the listing for the given repository/path/revision
26   24  
27 require_once("include/setup.inc"); 25 require_once 'include/setup.php';
28 require_once("include/svnlook.inc"); 26 require_once 'include/svnlook.php';
29 require_once("include/utils.inc"); 27 require_once 'include/utils.php';
30 require_once("include/template.inc"); 28 require_once 'include/template.php';
31 require_once("include/bugtraq.inc"); 29 require_once 'include/bugtraq.php';
32   30  
33 function removeURLSeparator($url) 31 function removeURLSeparator($url)
34 { 32 {
35 return preg_replace('#(\?|&(amp;)?)$#', '', $url); 33 return preg_replace('#(\?|&(amp;)?)$#', '', $url);
36 } 34 }
37   35  
38 function fileLink($path, $file, $returnjoin = false) 36 function urlForPath($fullpath, $passRevString)
39 { 37 {
40 global $rep, $passrev, $showchanged, $config; -  
41 -  
42 if ($path == "" || $path{0} != "/") -  
43 $ppath = "/".$path; -  
44 else -  
45 $ppath = $path; 38 global $config, $rep;
46   39  
47 if ($ppath{strlen($ppath)-1} != "/") 40 $isDir = $fullpath[strlen($fullpath) - 1] == '/';
48 $ppath .= "/"; -  
49 41  
-   42 if ($isDir)
-   43 {
50 if ($file{0} == "/") 44 if ($config->treeView)
-   45 {
-   46 $url = $config->getURL($rep, $fullpath, 'dir').$passRevString;
51 $pfile = substr($file, 1); 47 $id = anchorForPath($fullpath);
-   48 $url .= '#'.$id.'" id="'.$id;
-   49 }
52 else 50 else
-   51 {
-   52 $url = $config->getURL($rep, $fullpath, 'dir').$passRevString;
-   53 }
-   54 }
-   55 else
-   56 {
-   57 $url = $config->getURL($rep, $fullpath, 'file').$passRevString;
-   58 }
53 $pfile = $file; 59 return removeURLSeparator($url);
-   60 }
54   61  
55 if ($returnjoin) 62 function showDirFiles($svnrep, $subs, $level, $limit, $rev, $peg, $listing, $index, $treeview = true)
-   63 {
56 return $ppath.$pfile; 64 global $config, $lang, $rep, $passrev, $peg, $passRevString;
57   65  
58 $isDir = $pfile{strlen($pfile) - 1} == "/"; 66 $path = '';
59 67  
60 if ($passrev) $passrevstr = "rev=$passrev&amp;"; else $passrevstr = ""; 68 if (!$treeview)
-   69 {
61 if ($showchanged) $showchangedstr = "sc=$showchanged"; else $showchangedstr = ""; 70 $level = $limit;
-   71 }
62   72  
-   73 // TODO: Fix node links to use the path and number of peg revision (if exists)
-   74 // This applies to file detail, log, and RSS -- leave the download link as-is
63 if ($isDir) 75 for ($n = 0; $n <= $level; $n++)
64 { 76 {
65 $url = $config->getURL($rep, $ppath.$pfile, "dir"); 77 $path .= $subs[$n].'/';
-   78 }
66   79  
67 // XHTML doesn't allow slashes in IDs ~J 80 // List each file in the current directory
-   81 $loop = 0;
68 $id = str_replace('/', '_', $ppath.$pfile); 82 $last_index = 0;
69 $url = "<a id='$id' href=\"${url}$passrevstr$showchangedstr"; 83 $accessToThisDir = $rep->hasReadAccess($path, false);
70   84  
-   85 // If using flat view and not at the root, create a '..' entry at the top.
-   86 if (!$treeview && count($subs) > 2)
-   87 {
-   88 $parentPath = $subs;
-   89 unset($parentPath[count($parentPath) - 2]);
71 $url = removeURLSeparator($url); 90 $parentPath = implode('/', $parentPath);
-   91  
-   92 if ($rep->hasReadAccess($parentPath, false))
-   93 {
-   94 $listvar = &$listing[$index];
-   95 $listvar['rowparity'] = $index % 2;
-   96 $listvar['path'] = str_replace('%2F', '/', rawurlencode($parentPath));
72 if ($config->treeView) $url .= "#$id"; 97 $listvar['filetype'] = 'dir';
-   98 $listvar['filename'] = '..';
-   99 $listvar['fileurl'] = urlForPath($parentPath, $passRevString);
-   100 $listvar['filelink'] = '<a href="'.$listvar['fileurl'].'">'.$listvar['filename'].'</a>';
73 $url .= "\">$pfile</a>"; 101 $listvar['level'] = 0;
-   102 $listvar['node'] = 0; // t-node
-   103 $listvar['revision'] = $rev;
-   104 $listvar['revurl'] = $config->getURL($rep, $parentPath, 'revision').'rev='.$rev.'&amp;isdir=1';
-   105 global $vars;
-   106 $listvar['date'] = $vars['date'];
-   107 $listvar['age'] = datetimeFormatDuration(time() - strtotime($vars['date']), true, true);
-   108 $index++;
74 } 109 }
-   110 }
-   111  
75 else 112 $openDir = false;
-   113 $logList = $svnrep->getList($path, $rev, $peg);
-   114  
-   115 if (!$logList)
76 { 116 {
77 $url = $config->getURL($rep, $ppath.$pfile, "file"); -  
78 $url .= $passrevstr.$showchangedstr; 117 return $listing;
79 $url = removeURLSeparator($url); -  
80 $url = "<a href=\"${url}\">$pfile</a>"; -  
81 } 118 }
82   119  
-   120 $downloadRevAndPeg = createRevAndPegString($rev, $peg ? $peg : $rev);
-   121 foreach ($logList->entries as $entry)
-   122 {
83 return $url; 123 $isDir = $entry->isdir;
-   124 if (!$isDir && $level != $limit)
-   125 {
-   126 continue; // Skip any files outside the current directory
84 } 127 }
-   128 $file = $entry->file;
-   129 $isDirString = ($isDir) ? 'isdir=1&amp;' : '';
85   130  
-   131 // Only list files/directories that are not designated as off-limits
86 function fileLinkGetFile($path, $file) 132 $access = ($isDir) ? $rep->hasReadAccess($path.$file, false)
-   133 : $accessToThisDir;
-   134  
-   135 if (!$access)
87 { 136 {
-   137 continue;
-   138 }
-   139  
-   140 $listvar = &$listing[$index];
88 global $rep, $passrev, $showchanged, $config; 141 $listvar['rowparity'] = $index % 2;
89   142  
90 if ($path == "" || $path{0} != "/") 143 if ($isDir)
-   144 {
-   145 $openDir = isset($subs[$level + 1]) && (!strcmp($subs[$level + 1].'/', $file) || !strcmp($subs[$level + 1], $file));
91 $ppath = "/".$path; 146 $listvar['filetype'] = ($openDir) ? 'diropen' : 'dir';
-   147 }
92 else 148 else
-   149 {
93 $ppath = $path; 150 $openDir = false;
-   151 $listvar['filetype'] = strtolower(strrchr($file, '.'));
-   152 }
94   153  
-   154 $listvar['isDir'] = $isDir;
-   155 $listvar['openDir'] = $openDir;
95 if ($ppath{strlen($ppath)-1} != "/") 156 $listvar['level'] = ($treeview) ? $level : 0;
96 $ppath .= "/"; 157 $listvar['node'] = 0; // t-node
-   158 $listvar['path'] = str_replace('%2F', '/', rawurlencode($path.$file));
-   159 $listvar['filename'] = escape($file);
97   160  
98 if ($file{0} == "/") 161 if ($isDir)
-   162 {
99 $pfile = substr($file, 1); 163 $listvar['fileurl'] = urlForPath($path.$file, $passRevString);
-   164 }
100 else 165 else
-   166 {
101 $pfile = $file; 167 $listvar['fileurl'] = urlForPath($path.$file, createDifferentRevAndPegString($passrev, $peg));
-   168 }
102   169  
103 $isDir = $pfile{strlen($pfile) - 1} == "/"; 170 $listvar['filelink'] = '<a href="'.$listvar['fileurl'].'">'.$listvar['filename'].'</a>';
104   171  
105 if (!$isDir) 172 if ($isDir)
106 { 173 {
107 $url = $config->getURL($rep, $ppath.$pfile, "file"); 174 $listvar['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.$passRevString;
-   175 }
108 $url = removeURLSeparator($url); 176 else
-   177 {
109 $url = "<a href=\"${url}&getfile\">Get</a>"; 178 $listvar['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.createDifferentRevAndPegString($passrev, $peg);
110 } 179 }
111   180  
112 return $url; 181 if ($treeview)
-   182 {
-   183 $listvar['compare_box'] = '<input type="checkbox" name="compare[]" value="'.escape($path.$file).'@'.$passrev.'" onclick="enforceOnlyTwoChecked(this)" />';
113 } 184 }
114   185  
115 function showDirFiles($svnrep, $subs, $level, $limit, $rev, $listing, $index, $treeview = true) 186 if ($config->showLastModInListing())
116 { 187 {
-   188 $listvar['committime'] = $entry->committime;
117 global $rep, $passrev, $showchanged, $config, $lang; 189 $listvar['revision'] = $entry->rev;
-   190 $listvar['author'] = $entry->author;
-   191 $listvar['age'] = $entry->age;
-   192 $listvar['date'] = $entry->date;
-   193 $listvar['revurl'] = $config->getURL($rep, $path.$file, 'revision').$isDirString.createRevAndPegString($entry->rev, $peg ? $peg : $rev);
-   194 }
118   195  
119 $path = ""; 196 if ($rep->isDownloadAllowed($path.$file))
-   197 {
-   198 $downloadurl = $config->getURL($rep, $path.$file, 'dl').$isDirString.$downloadRevAndPeg;
120   199  
-   200 if ($isDir)
-   201 {
121 // Explicitly requested file as attachment 202 $listvar['downloadurl'] = $downloadurl;
122 if (isset($_REQUEST['getfile'])) 203 $listvar['downloadplainurl'] = '';
-   204 }
-   205 else
123 { 206 {
-   207 $listvar['downloadplainurl'] = $downloadurl;
-   208 $listvar['downloadurl'] = '';
-   209 }
-   210 }
-   211 else
-   212 {
-   213 $listvar['downloadplainurl'] = '';
124 $base = basename($path); 214 $listvar['downloadurl'] = '';
-   215 }
125   216  
126 header("Content-Type: application/octet-stream"); 217 if ($rep->isRssEnabled())
-   218 {
127 header("Content-Length: $size"); 219 // RSS should always point to the latest revision, so don't include rev
128 header("Content-Disposition: inline; filename=".urlencode($base)); 220 $listvar['rssurl'] = $config->getURL($rep, $path.$file, 'rss').$isDirString.createRevAndPegString('', $peg);
-   221 }
129   222  
-   223 $loop++;
-   224 $index++;
130 $svnrep->getFileContents($path, "", $rev); 225 $last_index = $index;
131   226  
-   227 if ($isDir && ($level != $limit))
-   228 {
-   229 // @todo remove the alternate check with htmlentities when assured that there are not side effects
-   230 if (isset($subs[$level + 1]) && (!strcmp($subs[$level + 1].'/', $file) || !strcmp(htmlentities($subs[$level + 1], ENT_QUOTES).'/', htmlentities($file))))
-   231 {
-   232 $listing = showDirFiles($svnrep, $subs, $level + 1, $limit, $rev, $peg, $listing, $index);
-   233 $index = count($listing);
132 exit; 234 }
-   235 }
133 } 236 }
134   237  
135 if (!$treeview) -  
136 $level = $limit; 238 // For an expanded tree, give the last entry an "L" node to close the grouping
137   -  
138 for ($n = 0; $n <= $level; $n++) 239 if ($treeview && $last_index != 0)
139 { 240 {
140 $path .= $subs[$n]."/"; 241 $listing[$last_index - 1]['node'] = 1; // l-node
141 } 242 }
142   243  
-   244 return $listing;
-   245 }
-   246  
-   247 function showAllDirFiles($svnrep, $path, $rev, $peg, $listing, $index, $treeView = true)
-   248 {
143 $contents = $svnrep->dirContents($path, $rev); 249 global $config, $lang, $rep, $passrev, $peg, $passRevString;
144   250  
145 // List each file in the current directory 251 // List each file in the current directory
146 $loop = 0; 252 $loop = 0;
147 $last_index = 0; 253 $last_index = 0;
148 $openDir = false; -  
149 $fullaccess = $rep->hasReadAccess($path, false); 254 $accessToThisDir = $rep->hasReadAccess($path, false);
150 255  
-   256 // If using flat view and not at the root, create a '..' entry at the top.
151 foreach($contents as $file) 257 if (!$treeView && count($subs) > 2)
152 { 258 {
-   259 $parentPath = $subs;
-   260 unset($parentPath[count($parentPath) - 2]);
-   261 $parentPath = implode('/', $parentPath);
-   262  
153 $isDir = ($file{strlen($file) - 1} == "/"?1:0); 263 if ($rep->hasReadAccess($parentPath, false))
-   264 {
-   265 $listvar = &$listing[$index];
-   266 $listvar['rowparity'] = $index % 2;
-   267 $listvar['path'] = str_replace('%2F', '/', rawurlencode($parentPath));
-   268 $listvar['filetype'] = 'dir';
-   269 $listvar['filename'] = '..';
-   270 $listvar['fileurl'] = urlForPath($parentPath, $passRevString);
-   271 $listvar['filelink'] = '<a href="'.$listvar['fileurl'].'">'.$listvar['filename'].'</a>';
-   272 $listvar['level'] = 0;
-   273 $listvar['node'] = 0; // t-node
-   274 $listvar['revision'] = $rev;
-   275 $listvar['revurl'] = $config->getURL($rep, $parentPath, 'revision').'rev='.$rev.'&amp;isdir=1';
154 $access = false; 276 global $vars;
-   277 $listvar['date'] = $vars['date'];
-   278 $listvar['age'] = datetimeFormatDuration(time() - strtotime($vars['date']), true, true);
-   279 $index++;
-   280 }
-   281 }
155   282  
-   283 $openDir = false;
-   284 $logList = $svnrep->getList($path, $rev, $peg);
-   285  
156 if ($isDir) 286 if (!$logList)
157 { 287 {
-   288 return $listing;
-   289 }
-   290  
-   291 $downloadRevAndPeg = createRevAndPegString($rev, $peg ? $peg : $rev);
-   292  
158 if ($rep->hasReadAccess($path.$file, true)) 293 foreach ($logList->entries as $entry)
159 { 294 {
160 $access = true; 295 $isDir = $entry->isdir;
161 $openDir = (!strcmp($subs[$level+1]."/", $file) || !strcmp($subs[$level+1], $file)); -  
162   296  
163 if ($openDir) 297 $file = $entry->file;
164 $listing[$index]["filetype"] = "diropen"; -  
165 else -  
166 $listing[$index]["filetype"] = "dir"; 298 $isDirString = ($isDir) ? 'isdir=1&amp;' : '';
167   299  
-   300 // Only list files/directories that are not designated as off-limits
168 if ($rep->isDownloadAllowed($path.$file)) 301 $access = ($isDir) ? $rep->hasReadAccess($path.$file, false)
-   302 : $accessToThisDir;
-   303  
-   304 if (!$access)
169 { 305 {
170 $dlurl = $config->getURL($rep, $path.$file, "dl"); -  
171 $listing[$index]["fileviewdllink"] = "<a href=\"${dlurl}rev=$passrev&amp;isdir=1\">${lang["TARBALL"]}</a>"; -  
172 } 306 continue;
173 else -  
174 $listing[$index]["fileviewdllink"] = "&nbsp;"; -  
175 } 307 }
-   308  
-   309 $listvar = &$listing[$index];
-   310 $listvar['rowparity'] = $index % 2;
-   311  
-   312 if ($isDir)
-   313 {
-   314 $listvar['filetype'] = 'dir';
-   315 $openDir = true;
176 } 316 }
177 else 317 else
178 { 318 {
-   319 $listvar['filetype'] = strtolower(strrchr($file, '.'));
179 if ($fullaccess) 320 $openDir = false;
-   321 }
-   322  
-   323 $listvar['isDir'] = $isDir;
-   324 $listvar['openDir'] = $openDir;
-   325 $listvar['path'] = str_replace('%2F', '/', rawurlencode($path.$file));
-   326 $tempelements = explode('/',$file);
-   327  
-   328 if ($tempelements[count($tempelements)-1] === "")
180 { 329 {
-   330 $lastindexfile = count($tempelements)-1 - 1;
-   331 $listvar['node'] = $lastindexfile; // t-node
-   332 $listvar['level'] = ($treeView) ? $lastindexfile : 0;
-   333 $listvar['filename'] = escape($tempelements[$lastindexfile]);
181 $access = true; 334 $listvar['classname'] = '';
-   335  
182 if ($level != $limit) 336 for ($n = 0; $n < $lastindexfile; ++$n)
183 { 337 {
184 // List directories only, skip all files 338 $listvar['last_i_node'][$n] = false;
185 continue; 339 $listvar['classname'] = $listvar['classname'].$tempelements[$n].'/';
186 } 340 }
187 341  
188 $listing[$index]["fileviewdllink"] = "&nbsp;"; 342 $listvar['classname'] = $listvar['classname'].$tempelements[$lastindexfile];
189 $listing[$index]["filetype"] = strrchr($file, "."); 343 $listvar['last_i_node'][$lastindexfile] = true;
190 } -  
191 } 344 }
192   -  
193 if ($access) 345 else
194 { 346 {
-   347 $lastindexfile = count($tempelements)-1;
-   348 $listvar['node'] = $lastindexfile; // t-node
195 $listing[$index]["rowparity"] = ($index % 2)?"L1":"L0"; 349 $listvar['level'] = ($treeView) ? $lastindexfile : 0;
-   350 $listvar['filename'] = escape($tempelements[$lastindexfile]);
-   351 $listvar['classname'] = '';
196 352  
197 if ($treeview) 353 for ($n=0; $n < $lastindexfile; ++$n)
198 $listing[$index]["compare_box"] = "<input type=\"checkbox\" name=\"compare[]\" value=\"".fileLink($path, $file, true)."@$passrev\" onclick=\"checkCB(this)\" />"; -  
199 else 354 {
200 $listing[$index]["compare_box"] = ""; 355 $listvar['last_i_node'][$n] = false;
-   356 $listvar['classname'] = $listvar['classname'].$tempelements[$n].'/';
-   357 }
201   358  
202 if ($openDir) -  
203 $listing[$index]["filelink"] = "<b>".fileLink($path, $file)."</b>"; 359 $listvar['last_i_node'][$lastindexfile] = true;
204 else 360 }
205 $listing[$index]["filelink"] = fileLink($path, $file); -  
206   361  
207 if ($isDir) 362 if ($isDir)
-   363 {
208 $listing[$index]["filelinkgetfile"] = ""; 364 $listvar['fileurl'] = urlForPath($path.$file, $passRevString);
-   365 }
209 else 366 else
-   367 {
210 $listing[$index]["filelinkgetfile"] = fileLinkGetFile($path, $file); 368 $listvar['fileurl'] = urlForPath($path.$file, createDifferentRevAndPegString($passrev, $peg));
211   369 }
212 // The history command doesn't return with a trailing slash. We need to remember here if the -  
213 // file is a directory or not! -  
214   370  
215 $listing[$index]["isDir"] = $isDir; 371 $listvar['filelink'] = '<a href="'.$listvar['fileurl'].'">'.$listvar['filename'].'</a>';
216   372  
217 if ($treeview) 373 if ($isDir)
-   374 {
218 $listing[$index]["level"] = $level; 375 $listvar['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.$passRevString;
-   376 }
219 else 377 else
220 $listing[$index]["level"] = 0; -  
221   378 {
222 $listing[$index]["node"] = 0; // t-node 379 $listvar['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.createDifferentRevAndPegString($passrev, $peg);
223   380 }
224 $fileurl = $config->getURL($rep, $path.$file, "log"); -  
225 $listing[$index]["fileviewloglink"] = "<a href=\"${fileurl}rev=$passrev&amp;sc=$showchanged&amp;isdir=$isDir\">${lang["VIEWLOG"]}</a>"; -  
226   381  
227 $rssurl = $config->getURL($rep, $path.$file, "rss"); -  
228 if ($rep->getHideRss()) 382 if ($treeView)
229 { 383 {
230 $listing[$index]["rsslink"] = "<a href=\"${rssurl}rev=$passrev&amp;sc=$showchanged&amp;isdir=$isDir\">${lang["RSSFEED"]}</a>"; -  
231 $listing[$index]["rssanchor"] = "<a href=\"${rssurl}rev=$passrev&amp;sc=$showchanged&amp;isdir=$isDir\">"; 384 $listvar['compare_box'] = '<input type="checkbox" name="compare[]" value="'.escape($path.$file).'@'.$passrev.'" onclick="enforceOnlyTwoChecked(this)" />';
232 } 385 }
233   386  
-   387 if ($config->showLastModInListing())
-   388 {
-   389 $listvar['committime'] = $entry->committime;
234 $index++; 390 $listvar['revision'] = $entry->rev;
-   391 $listvar['author'] = $entry->author;
235 $loop++; 392 $listvar['age'] = $entry->age;
236 $last_index = $index; 393 $listvar['date'] = $entry->date;
-   394 $listvar['revurl'] = $config->getURL($rep, $path.$file, 'revision').$isDirString.createRevAndPegString($entry->rev, $peg ? $peg : $rev);
-   395 }
237   396  
238 if (($level != $limit) && ($isDir)) 397 if ($rep->isDownloadAllowed($path.$file))
239 { 398 {
240 if (!strcmp($subs[$level + 1]."/", $file)) 399 $downloadurl = $config->getURL($rep, $path.$file, 'dl').$isDirString.$downloadRevAndPeg;
-   400  
-   401 if ($isDir)
241 { 402 {
242 $listing = showDirFiles($svnrep, $subs, $level + 1, $limit, $rev, $listing, $index); 403 $listvar['downloadurl'] = $downloadurl;
243 $index = count($listing); 404 $listvar['downloadplainurl'] = '';
244 } 405 }
-   406 else
-   407 {
-   408 $listvar['downloadplainurl'] = $downloadurl;
-   409 $listvar['downloadurl'] = '';
245 } 410 }
246   -  
247 } 411 }
-   412 else
-   413 {
-   414 $listvar['downloadplainurl'] = '';
-   415 $listvar['downloadurl'] = '';
248 } 416 }
249   417  
250 if ($last_index != 0 && $treeview) 418 if ($rep->isRssEnabled())
251 { 419 {
252 $listing[$last_index - 1]["node"] = 1; // l-node 420 // RSS should always point to the latest revision, so don't include rev
-   421 $listvar['rssurl'] = $config->getURL($rep, $path.$file, 'rss').$isDirString.createRevAndPegString('', $peg);
-   422 }
-   423  
-   424 $loop++;
-   425 $index++;
-   426 $last_index = $index;
253 } 427 }
254   428  
255 return $listing; 429 return $listing;
256 } 430 }
257   431  
258 function showTreeDir($svnrep, $path, $rev, $listing) 432 function showTreeDir($svnrep, $path, $rev, $peg, $listing)
259 { 433 {
260 global $vars, $config; 434 global $vars, $config;
261   435  
-   436 if ($config->showLoadAllRepos())
-   437 {
-   438 $vars['compare_box'] = ''; // Set blank once in case tree view is not enabled.
-   439 return showAllDirFiles($svnrep, $path, $rev, $peg, $listing, 0, $config->treeView);
-   440 }
-   441  
262 $subs = explode("/", $path); 442 $subs = explode('/', $path);
263   443  
-   444 // The last element in "$subs" is empty for directories, some file name for files, but ALWAYS
264 // For directory, the last element in the subs is empty. 445 // exists as an additional element. Hence, "-2" is necessary.
-   446 //
-   447 // Additionally, level and limit are conceptually different things, first to e.g. start access
-   448 // checks FROM, latter to process UNTIL. So don't merge those values too easily, especially as
265 // For file, the last element in the subs is the file name. 449 // both values needed to be different in some environments in the past for some unkown reason.
-   450 //
266 // Therefore, it is always count($subs) - 2 451 // https://github.com/websvnphp/websvn/issues/146#issuecomment-913353366
267 $limit = count($subs) - 2; 452 $limit = count($subs) - 2;
-   453 $level = $limit;
-   454 $level = $level <= 0 ? 0 : $level;
268   455  
269 for ($n = 0; $n < $limit; $n++) 456 for ($n = 0; $n < $limit; $n++)
270 { 457 {
271 $vars["last_i_node"][$n] = FALSE; 458 $vars['last_i_node'][$n] = false;
272 } 459 }
273   460  
-   461 $vars['compare_box'] = ''; // Set blank once in case tree view is not enabled.
274 return showDirFiles($svnrep, $subs, 0, $limit, $rev, $listing, 0, $config->treeView); 462 return showDirFiles($svnrep, $subs, $level, $limit, $rev, $peg, $listing, 0, $config->treeView);
275   -  
276 } 463 }
277   464  
278 // Make sure that we have a repository 465 // Make sure that we have a repository
279 if (!isset($rep)) 466 if (!$rep)
280 { 467 {
281 echo $lang["NOREP"]; 468 renderTemplate404('directory','NOREP');
282 exit; -  
283 } 469 }
284   470  
285 $svnrep = new SVNRepository($rep); 471 $svnrep = new SVNRepository($rep);
286   472  
-   473 if (!empty($rev))
-   474 {
287 // Revision info to pass along chain 475 $info = $svnrep->getInfo($path, $rev, $peg);
-   476  
-   477 if ($info)
-   478 {
-   479 $path = $info->path;
288 $passrev = $rev; 480 $peg = (int)$info->rev;
-   481 }
-   482 }
-   483  
-   484 $history = $svnrep->getLog($path, 'HEAD', 1, false, 2, ($path == '/') ? '' : $peg);
289   485  
-   486 if (!$history)
-   487 {
290 // Get the directory contents of the given revision, or HEAD if not defined 488 unset($vars['error']);
291 $contents = $svnrep->dirContents($path, @$rev); 489 $history = $svnrep->getLog($path, '', '', false, 2, ($path == '/') ? '' : $peg);
292   490  
293 // If there's no revision info, go to the lastest revision for this path 491 if (!$history)
-   492 {
294 $history = $svnrep->getLog($path, "", "", false); 493 renderTemplate404('directory','NOPATH');
-   494 }
-   495 }
295   496  
296 if (!empty($history->entries[0])) -  
297 $youngest = $history->entries[0]->rev; 497 $youngest = ($history && isset($history->entries[0])) ? $history->entries[0]->rev : 0;
298 else -  
299 $youngest = -1; -  
300   498  
301 // Unless otherwise specified, we get the log details of the latest change 499 // Unless otherwise specified, we get the log details of the latest change
302 if (empty($rev)) -  
303 $logrev = $youngest; 500 $lastChangedRev = ($passrev) ? $passrev : $youngest;
304 else -  
305 $logrev = $rev; -  
306   501  
307 if ($logrev != $youngest) 502 if ($lastChangedRev != $youngest)
308 { 503 {
309 $logEntry = $svnrep->getLog($path, $logrev, $logrev, false); 504 $history = $svnrep->getLog($path, $lastChangedRev, 1, false, 2, $peg);
310 $logEntry = $logEntry->entries[0]; -  
311 } 505 }
312 else -  
313 $logEntry = $history->entries[0]; -  
314   506  
-   507 $logEntry = ($history && isset($history->entries[0])) ? $history->entries[0] : 0;
-   508  
315 $headlog = $svnrep->getLog("/", "", "", true, 1); 509 $headlog = $svnrep->getLog('/', '', '', true, 1);
316 $headrev = $headlog->entries[0]->rev; 510 $headrev = ($headlog && isset($headlog->entries[0])) ? $headlog->entries[0]->rev : 0;
317   511  
318 // If we're not looking at a specific revision, get the HEAD revision number 512 // If we're not looking at a specific revision, get the HEAD revision number
319 // (the revision of the rest of the tree display) 513 // (the revision of the rest of the tree display)
320   514  
321 if (empty($rev)) 515 if (empty($rev))
322 { 516 {
323 $rev = $headrev; 517 $rev = $headrev;
324 } 518 }
325   519  
326 if ($path == "" || $path{0} != "/") 520 if ($path == '' || $path[0] != '/')
-   521 {
327 $ppath = "/".$path; 522 $ppath = '/'.$path;
-   523 }
328 else 524 else
-   525 {
329 $ppath = $path; 526 $ppath = $path;
-   527 }
330   528  
331 $vars["repname"] = $rep->getDisplayName(); -  
332   -  
333 $dirurl = $config->getURL($rep, $path, "dir"); -  
334 $logurl = $config->getURL($rep, $path, "log"); -  
335 $rssurl = $config->getURL($rep, $path, "rss"); 529 createPathLinks($rep, $ppath, $passrev, $peg);
336 $dlurl = $config->getURL($rep, $path, "dl"); -  
337 $compurl = $config->getURL($rep, "/", "comp"); -  
338   -  
339 if ($passrev != 0 && $passrev != $headrev && $youngest != -1) 530 $passRevString = createRevAndPegString($passrev, $peg);
340 $vars["goyoungestlink"] = "<a href=\"${dirurl}opt=dir&amp;sc=1\">${lang["GOYOUNGEST"]}</a>"; -  
341 else -  
342 $vars["goyoungestlink"] = ""; 531 $isDirString = 'isdir=1&amp;';
343   -  
344 $bugtraq = new Bugtraq($rep, $svnrep, $ppath); -  
345   532  
346 $vars["action"] = ""; -  
347 $vars["rev"] = $rev; -  
348 $vars["path"] = $ppath; -  
349 $vars["lastchangedrev"] = $logrev; 533 $revurl = $config->getURL($rep, $path != '/' ? $path : '', 'dir');
350 $vars["date"] = $logEntry->date; -  
351 $vars["author"] = $logEntry->author; -  
352 $vars["log"] = nl2br($bugtraq->replaceIDs(create_anchors($logEntry->msg))); 534 $revurlSuffix = $path != '/' ? '#'.anchorForPath($path) : '';
353   535  
354 if (!$showchanged) 536 if ($rev < $youngest)
355 { 537 {
356 $vars["showchangeslink"] = "<a href=\"${dirurl}rev=$passrev&amp;sc=1\">${lang["SHOWCHANGED"]}</a>"; -  
357 $vars["hidechangeslink"] = ""; 538 if ($path == '/')
358   539 {
359 $vars["hidechanges"] = true; -  
360 $vars["showchanges"] = false; 540 $vars['goyoungesturl'] = $config->getURL($rep, '', 'dir');
361 } 541 }
362 else 542 else
363 { 543 {
364 $vars["showchangeslink"] = ""; 544 $vars['goyoungesturl'] = $config->getURL($rep, $path, 'dir').createRevAndPegString($youngest, $peg ? $peg: $rev).$revurlSuffix;
365 545 }
366 $changes = $logEntry->mods; -  
367 546  
368 $firstAdded = true; -  
369 $firstModded = true; -  
370 $firstDeleted = true; 547 $vars['goyoungestlink'] = '<a href="'.$vars['goyoungesturl'].'"'.($youngest ? ' title="'.$lang['REV'].' '.$youngest.'"' : '').'>'.$lang['GOYOUNGEST'].'</a>';
371   548  
372 $vars["newfilesbr"] = ""; -  
373 $vars["newfiles"] = ""; -  
374 $vars["changedfilesbr"] = ""; -  
375 $vars["changedfiles"] = ""; -  
376 $vars["deletedfilesbr"] = ""; 549 $history2 = $svnrep->getLog($path, $rev, $youngest, true, 2, $peg);
377 $vars["deletedfiles"] = ""; -  
378   550  
379 foreach ($changes as $file) 551 if (isset($history2->entries[1]))
380 { 552 {
-   553 $nextRev = $history2->entries[1]->rev;
381 switch ($file->action) 554 if ($nextRev != $youngest)
382 { 555 {
383 case "A": -  
384 if (!$firstAdded) $vars["newfilesbr"] .= "<br />"; -  
385 $firstAdded = false; 556 $vars['nextrev'] = $nextRev;
386 $vars["newfilesbr"] .= fileLink("", $file->path); -  
387 $vars["newfiles"] .= " ".fileLink("", $file->path); -  
388 break; -  
389   -  
390 case "M": -  
391 if (!$firstModded) $vars["changedfilesbr"] .= "<br />"; -  
392 $firstModded = false; -  
393 $vars["changedfilesbr"] .= fileLink("", $file->path); -  
394 $vars["changedfiles"] .= " ".fileLink("", $file->path); -  
395 break; -  
396   -  
397 case "D": -  
398 if (!$firstDeleted) $vars["deletedfilesbr"] .= "<br />"; 557 $vars['nextrevurl'] = $revurl.createRevAndPegString($nextRev, $peg).$revurlSuffix;
399 $firstDeleted = false; -  
400 $vars["deletedfilesbr"] .= $file->path; -  
401 $vars["deletedfiles"] .= " ".$file->path; -  
402 break; -  
403 } 558 }
404 } 559 }
405   560  
406 $vars["hidechangeslink"] = "<a href=\"${dirurl}rev=$passrev&amp;sc=0\">${lang["HIDECHANGED"]}</a>"; -  
407   -  
408 $vars["hidechanges"] = false; 561 unset($vars['error']);
409 $vars["showchanges"] = true; -  
410 } 562 }
411   563  
412 createDirLinks($rep, $ppath, $passrev, $showchanged); -  
413 $vars["curdirloglink"] = "<a href=\"${logurl}rev=$passrev&amp;sc=$showchanged&amp;isdir=1\">${lang["VIEWLOG"]}</a>"; -  
414   -  
415 if ($rev != $headrev) 564 if (isset($history->entries[1]))
416 { 565 {
417 $history = $svnrep->getLog($path, $rev, "", false); 566 $prevRev = $history->entries[1]->rev;
-   567 $prevPath = $history->entries[1]->path;
-   568 $vars['prevrev'] = $prevRev;
-   569 $vars['prevrevurl'] = $revurl.createRevAndPegString($prevRev, $peg).$revurlSuffix;
418 } 570 }
419   571  
-   572 $bugtraq = new Bugtraq($rep, $svnrep, $ppath);
-   573  
-   574 $vars['action'] = '';
-   575 $vars['rev'] = $rev;
-   576 $vars['peg'] = $peg;
-   577 $vars['path'] = str_replace('%2F', '/', rawurlencode($ppath));
420 if (isset($history->entries[1]->rev)) 578 $vars['safepath'] = escape($ppath);
-   579 $vars['lastchangedrev'] = $lastChangedRev;
-   580  
-   581 if ($logEntry)
421 { 582 {
422 $vars["curdircomplink"] = "<a href=\"${compurl}compare[]=". 583 $vars['date'] = $logEntry->date;
423 urlencode($history->entries[1]->path)."@".$history->entries[1]->rev. 584 $vars['age'] = datetimeFormatDuration(time() - strtotime($logEntry->date));
424 "&amp;compare[]=".urlencode($history->entries[0]->path)."@".$history->entries[0]->rev. 585 $vars['author'] = $logEntry->author;
425 "\">${lang["DIFFPREV"]}</a>"; 586 $vars['log'] = nl2br($bugtraq->replaceIDs(create_anchors(xml_entities($logEntry->msg))));
426 } 587 }
-   588  
-   589 $vars['revurl'] = $config->getURL($rep, ($path == '/' ? '' : $path), 'revision').$isDirString.$passRevString;
-   590 $vars['revlink'] = '<a href="'.$vars['revurl'].'">'.$lang['LASTMOD'].'</a>';
427 else 591  
-   592 if ($history && count($history->entries) > 1)
428 { 593 {
-   594 $vars['compareurl'] = $config->getURL($rep, '', 'comp').'compare[]='.rawurlencode($history->entries[1]->path).'@'.$history->entries[1]->rev. '&amp;compare[]='.rawurlencode($history->entries[0]->path).'@'.$history->entries[0]->rev;
429 $vars["curdircomplink"] = ""; 595 $vars['comparelink'] = '<a href="'.$vars['compareurl'].'">'.$lang['DIFFPREV'].'</a>';
430 } 596 }
431   597  
-   598 $vars['logurl'] = $config->getURL($rep, $path, 'log').$isDirString.$passRevString;
-   599 $vars['loglink'] = '<a href="'.$vars['logurl'].'">'.$lang['VIEWLOG'].'</a>';
-   600  
432 if ($rep->getHideRss()) 601 if ($rep->isRssEnabled())
433 { 602 {
434 $vars["curdirrsslink"] = "<a href=\"${rssurl}rev=$passrev&amp;sc=$showchanged&amp;isdir=1\">${lang["RSSFEED"]}</a>"; 603 $vars['rssurl'] = $config->getURL($rep, $path, 'rss').$isDirString.createRevAndPegString('', $peg);
435 $vars["curdirrsshref"] = "${rssurl}rev=$passrev&amp;sc=$showchanged&amp;isdir=1"; -  
436 $vars["curdirrssanchor"] = "<a href=\"${rssurl}rev=$passrev&amp;sc=$showchanged&amp;isdir=1\">"; 604 $vars['rsslink'] = '<a href="'.$vars['rssurl'].'">'.$lang['RSSFEED'].'</a>';
437 } 605 }
438   606  
439 // Set up the tarball link 607 // Set up the tarball link
440   -  
441 $subs = explode("/", $path); 608 $subs = explode('/', $path);
442 $level = count($subs) - 2; 609 $level = count($subs) - 2;
443 if ($rep->isDownloadAllowed($path)) -  
444 $vars["curdirdllink"] = "<a href=\"${dlurl}rev=$passrev&amp;isdir=1\">${lang["TARBALL"]}</a>"; -  
445 else -  
446 $vars["curdirdllink"] = ""; -  
447 610  
-   611 if ($rep->isDownloadAllowed($path) && !isset($vars['warning']))
-   612 {
448 $url = $config->getURL($rep, "/", "comp"); 613 $vars['downloadurl'] = $config->getURL($rep, $path, 'dl').$isDirString.$passRevString;
-   614 }
449   615  
450 $vars["compare_form"] = "<form action=\"$url\" method=\"post\" name=\"compareform\">"; 616 $vars['compare_form'] = '<form method="get" action="'.$config->getURL($rep, '', 'comp').'" id="compare">';
451 $vars["compare_submit"] = "<input name=\"comparesubmit\" type=\"submit\" value=\"${lang["COMPAREPATHS"]}\" />"; -  
452 $vars["compare_endform"] = "<input type=\"hidden\" name=\"op\" value=\"comp\" /><input type=\"hidden\" name=\"sc\" value=\"$showchanged\" /></form>"; -  
453   617  
454 $listing = array(); 618 if ($config->multiViews)
-   619 {
455 $listing = showTreeDir($svnrep, $path, $rev, $listing); 620 $vars['compare_form'] .= '<input type="hidden" name="op" value="comp"/>';
-   621 }
-   622 else
-   623 {
-   624 $vars['compare_form'] .= '<input type="hidden" name="repname" value="'.$repname.'" />';
-   625 }
456   626  
-   627 $vars['compare_submit'] = '<input type="submit" value="'.$lang['COMPAREPATHS'].'" />';
457 $vars["version"] = $version; 628 $vars['compare_endform'] = '</form>';
458   629  
459 if (!$rep->hasReadAccess($path, true)) 630 $vars['showlastmod'] = $config->showLastModInListing();
460 $vars["noaccess"] = true; -  
461   631  
462 if (!$rep->hasReadAccess($path, false)) 632 $vars['loadalldir'] = $config->showLoadAllRepos();
463 $vars["restricted"] = true; 633 $listing = showTreeDir($svnrep, $path, $rev, $peg, array());
464   634  
465 parseTemplate($rep->getTemplatePath()."header.tmpl", $vars, $listing); 635 if (!$rep->hasReadAccess($path))
-   636 {
466 parseTemplate($rep->getTemplatePath()."directory.tmpl", $vars, $listing); 637 $vars['error'] = $lang['NOACCESS'];
467 parseTemplate($rep->getTemplatePath()."footer.tmpl", $vars, $listing); 638 sendHeaderForbidden();
-   639 }
468   640  
469 ?> -  
-   641 $vars['restricted'] = !$rep->hasReadAccess($path, false);
-   642 renderTemplate('directory', $path);