Rev 1189 Rev 4988
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.
11 // 9 //
12 // This program is distributed in the hope that it will be useful, 10 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details. 13 // GNU General Public License for more details.
16 // 14 //
17 // You should have received a copy of the GNU General Public License 15 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software 16 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // 18 //
21 // -- 19 // --
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; -  
46   -  
47 if ($ppath{strlen($ppath)-1} != "/") -  
48 $ppath .= "/"; -  
49 -  
50 if ($file{0} == "/") -  
51 $pfile = substr($file, 1); -  
52 else -  
53 $pfile = $file; -  
54   -  
55 if ($returnjoin) -  
56 return $ppath.$pfile; -  
57   -  
58 $isDir = $pfile{strlen($pfile) - 1} == "/"; -  
59 -  
60 if ($passrev) $passrevstr = "rev=$passrev&amp;"; else $passrevstr = ""; -  
61 if ($showchanged) $showchangedstr = "sc=$showchanged"; else $showchangedstr = ""; -  
62   -  
63 if ($isDir) -  
64 { -  
65 $url = $config->getURL($rep, $ppath.$pfile, "dir"); -  
66   -  
67 // XHTML doesn't allow slashes in IDs ~J -  
68 $id = str_replace('/', '_', $ppath.$pfile); -  
69 $url = "<a id='$id' href=\"${url}$passrevstr$showchangedstr"; -  
70   -  
71 $url = removeURLSeparator($url); -  
72 if ($config->treeView) $url .= "#$id"; -  
73 $url .= "\">$pfile</a>"; -  
74 } -  
75 else -  
76 { -  
77 $url = $config->getURL($rep, $ppath.$pfile, "file"); -  
78 $url .= $passrevstr.$showchangedstr; -  
79 $url = removeURLSeparator($url); -  
80 $url = "<a href=\"${url}\">$pfile</a>"; -  
81 } -  
82   -  
83 return $url; -  
84 } -  
85   -  
86 function fileLinkGetFile($path, $file) -  
87 { -  
88 global $rep, $passrev, $showchanged, $config; -  
89   -  
90 if ($path == "" || $path{0} != "/") -  
91 $ppath = "/".$path; -  
92 else -  
93 $ppath = $path; -  
94   -  
95 if ($ppath{strlen($ppath)-1} != "/") -  
96 $ppath .= "/"; -  
97   -  
98 if ($file{0} == "/") -  
99 $pfile = substr($file, 1); -  
100 else -  
101 $pfile = $file; 38 global $config, $rep;
102   39  
103 $isDir = $pfile{strlen($pfile) - 1} == "/"; 40 $isDir = $fullpath[strlen($fullpath) - 1] == '/';
104   -  
105 if (!$isDir) -  
106 { -  
107 $url = $config->getURL($rep, $ppath.$pfile, "file"); -  
108 $url = removeURLSeparator($url); -  
109 $url = "<a href=\"${url}&getfile\">Get</a>"; -  
110 } -  
111   -  
112 return $url; -  
113 } -  
114   -  
115 function showDirFiles($svnrep, $subs, $level, $limit, $rev, $listing, $index, $treeview = true) -  
116 { -  
117 global $rep, $passrev, $showchanged, $config, $lang; -  
118   -  
119 $path = ""; -  
120   -  
121 // Explicitly requested file as attachment -  
122 if (isset($_REQUEST['getfile'])) -  
123 { -  
124 $base = basename($path); -  
125   -  
126 header("Content-Type: application/octet-stream"); -  
127 header("Content-Length: $size"); -  
128 header("Content-Disposition: inline; filename=".urlencode($base)); -  
129   -  
130 $svnrep->getFileContents($path, "", $rev); -  
131   -  
132 exit; -  
133 } -  
134   -  
135 if (!$treeview) -  
136 $level = $limit; -  
137   -  
138 for ($n = 0; $n <= $level; $n++) -  
139 { -  
140 $path .= $subs[$n]."/"; -  
141 } -  
142   -  
143 $contents = $svnrep->dirContents($path, $rev); -  
144   -  
145 // List each file in the current directory -  
146 $loop = 0; -  
147 $last_index = 0; -  
148 $openDir = false; -  
149 $fullaccess = $rep->hasReadAccess($path, false); -  
150 -  
151 foreach($contents as $file) -  
152 { -  
153 $isDir = ($file{strlen($file) - 1} == "/"?1:0); -  
154 $access = false; -  
155   -  
156 if ($isDir) -  
157 { -  
158 if ($rep->hasReadAccess($path.$file, true)) -  
159 { -  
160 $access = true; -  
161 $openDir = (!strcmp($subs[$level+1]."/", $file) || !strcmp($subs[$level+1], $file)); -  
162   -  
163 if ($openDir) -  
164 $listing[$index]["filetype"] = "diropen"; -  
165 else -  
166 $listing[$index]["filetype"] = "dir"; -  
167   -  
168 if ($rep->isDownloadAllowed($path.$file)) -  
169 { -  
170 $dlurl = $config->getURL($rep, $path.$file, "dl"); -  
171 $listing[$index]["fileviewdllink"] = "<a href=\"${dlurl}rev=$passrev&amp;isdir=1\">${lang["TARBALL"]}</a>"; -  
172 } -  
173 else -  
174 $listing[$index]["fileviewdllink"] = "&nbsp;"; -  
175 } -  
176 } -  
177 else -  
178 { -  
179 if ($fullaccess) -  
180 { -  
181 $access = true; -  
182 if ($level != $limit) -  
183 { -  
184 // List directories only, skip all files -  
185 continue; -  
186 } -  
187 -  
188 $listing[$index]["fileviewdllink"] = "&nbsp;"; -  
189 $listing[$index]["filetype"] = strrchr($file, "."); -  
190 } -  
191 } -  
192   -  
193 if ($access) -  
194 { -  
195 $listing[$index]["rowparity"] = ($index % 2)?"L1":"L0"; -  
196 -  
197 if ($treeview) -  
198 $listing[$index]["compare_box"] = "<input type=\"checkbox\" name=\"compare[]\" value=\"".fileLink($path, $file, true)."@$passrev\" onclick=\"checkCB(this)\" />"; -  
199 else -  
200 $listing[$index]["compare_box"] = ""; -  
201   -  
202 if ($openDir) -  
203 $listing[$index]["filelink"] = "<b>".fileLink($path, $file)."</b>"; -  
204 else -  
205 $listing[$index]["filelink"] = fileLink($path, $file); -  
206   -  
207 if ($isDir) -  
208 $listing[$index]["filelinkgetfile"] = ""; -  
209 else -  
210 $listing[$index]["filelinkgetfile"] = fileLinkGetFile($path, $file); -  
211   -  
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   -  
215 $listing[$index]["isDir"] = $isDir; -  
216   -  
217 if ($treeview) -  
218 $listing[$index]["level"] = $level; -  
219 else -  
220 $listing[$index]["level"] = 0; -  
221   -  
222 $listing[$index]["node"] = 0; // t-node -  
223   -  
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   -  
227 $rssurl = $config->getURL($rep, $path.$file, "rss"); -  
228 if ($rep->getHideRss()) -  
229 { -  
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\">"; -  
232 } -  
233   -  
234 $index++; -  
235 $loop++; -  
236 $last_index = $index; -  
237   -  
238 if (($level != $limit) && ($isDir)) -  
239 { -  
240 if (!strcmp($subs[$level + 1]."/", $file)) -  
241 { -  
242 $listing = showDirFiles($svnrep, $subs, $level + 1, $limit, $rev, $listing, $index); -  
243 $index = count($listing); -  
244 } -  
245 } -  
246   -  
247 } -  
248 } -  
249   -  
250 if ($last_index != 0 && $treeview) -  
251 { -  
252 $listing[$last_index - 1]["node"] = 1; // l-node -  
253 } -  
254   -  
255 return $listing; -  
256 } -  
257   -  
258 function showTreeDir($svnrep, $path, $rev, $listing) -  
259 { -  
260 global $vars, $config; -  
261   -  
262 $subs = explode("/", $path); -  
263   -  
264 // For directory, the last element in the subs is empty. -  
265 // For file, the last element in the subs is the file name. -  
266 // Therefore, it is always count($subs) - 2 -  
267 $limit = count($subs) - 2; -  
268   -  
269 for ($n = 0; $n < $limit; $n++) -  
270 { -  
271 $vars["last_i_node"][$n] = FALSE; -  
272 } -  
273   41  
-   42 if ($isDir)
-   43 {
-   44 if ($config->treeView)
-   45 {
-   46 $url = $config->getURL($rep, $fullpath, 'dir').$passRevString;
-   47 $id = anchorForPath($fullpath);
-   48 $url .= '#'.$id.'" id="'.$id;
-   49 }
-   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 }
-   59 return removeURLSeparator($url);
-   60 }
-   61  
-   62 function showDirFiles($svnrep, $subs, $level, $limit, $rev, $peg, $listing, $index, $treeview = true)
-   63 {
-   64 global $config, $lang, $rep, $passrev, $peg, $passRevString;
-   65  
-   66 $path = '';
-   67  
-   68 if (!$treeview)
-   69 {
-   70 $level = $limit;
-   71 }
-   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
-   75 for ($n = 0; $n <= $level; $n++)
-   76 {
-   77 $path .= $subs[$n].'/';
-   78 }
-   79  
-   80 // List each file in the current directory
-   81 $loop = 0;
-   82 $last_index = 0;
-   83 $accessToThisDir = $rep->hasReadAccess($path, false);
-   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]);
-   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));
-   97 $listvar['filetype'] = 'dir';
-   98 $listvar['filename'] = '..';
-   99 $listvar['fileurl'] = urlForPath($parentPath, $passRevString);
-   100 $listvar['filelink'] = '<a href="'.$listvar['fileurl'].'">'.$listvar['filename'].'</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++;
-   109 }
-   110 }
-   111  
-   112 $openDir = false;
-   113 $logList = $svnrep->getList($path, $rev, $peg);
-   114  
-   115 if (!$logList)
-   116 {
-   117 return $listing;
-   118 }
-   119  
-   120 $downloadRevAndPeg = createRevAndPegString($rev, $peg ? $peg : $rev);
-   121 foreach ($logList->entries as $entry)
-   122 {
-   123 $isDir = $entry->isdir;
-   124 if (!$isDir && $level != $limit)
-   125 {
-   126 continue; // Skip any files outside the current directory
-   127 }
-   128 $file = $entry->file;
-   129 $isDirString = ($isDir) ? 'isdir=1&amp;' : '';
-   130  
-   131 // Only list files/directories that are not designated as off-limits
-   132 $access = ($isDir) ? $rep->hasReadAccess($path.$file, false)
-   133 : $accessToThisDir;
-   134  
-   135 if (!$access)
-   136 {
-   137 continue;
-   138 }
-   139  
-   140 $listvar = &$listing[$index];
-   141 $listvar['rowparity'] = $index % 2;
-   142  
-   143 if ($isDir)
-   144 {
-   145 $openDir = isset($subs[$level + 1]) && (!strcmp($subs[$level + 1].'/', $file) || !strcmp($subs[$level + 1], $file));
-   146 $listvar['filetype'] = ($openDir) ? 'diropen' : 'dir';
-   147 }
-   148 else
-   149 {
-   150 $openDir = false;
-   151 $listvar['filetype'] = strtolower(strrchr($file, '.'));
-   152 }
-   153  
-   154 $listvar['isDir'] = $isDir;
-   155 $listvar['openDir'] = $openDir;
-   156 $listvar['level'] = ($treeview) ? $level : 0;
-   157 $listvar['node'] = 0; // t-node
-   158 $listvar['path'] = str_replace('%2F', '/', rawurlencode($path.$file));
-   159 $listvar['filename'] = escape($file);
-   160  
-   161 if ($isDir)
-   162 {
-   163 $listvar['fileurl'] = urlForPath($path.$file, $passRevString);
-   164 }
-   165 else
-   166 {
-   167 $listvar['fileurl'] = urlForPath($path.$file, createDifferentRevAndPegString($passrev, $peg));
-   168 }
-   169  
-   170 $listvar['filelink'] = '<a href="'.$listvar['fileurl'].'">'.$listvar['filename'].'</a>';
-   171  
-   172 if ($isDir)
-   173 {
-   174 $listvar['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.$passRevString;
-   175 }
-   176 else
-   177 {
-   178 $listvar['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.createDifferentRevAndPegString($passrev, $peg);
-   179 }
-   180  
-   181 if ($treeview)
-   182 {
-   183 $listvar['compare_box'] = '<input type="checkbox" name="compare[]" value="'.escape($path.$file).'@'.$passrev.'" onclick="enforceOnlyTwoChecked(this)" />';
-   184 }
-   185  
-   186 if ($config->showLastModInListing())
-   187 {
-   188 $listvar['committime'] = $entry->committime;
-   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 }
-   195  
-   196 if ($rep->isDownloadAllowed($path.$file))
-   197 {
-   198 $downloadurl = $config->getURL($rep, $path.$file, 'dl').$isDirString.$downloadRevAndPeg;
-   199  
-   200 if ($isDir)
-   201 {
-   202 $listvar['downloadurl'] = $downloadurl;
-   203 $listvar['downloadplainurl'] = '';
-   204 }
-   205 else
-   206 {
-   207 $listvar['downloadplainurl'] = $downloadurl;
-   208 $listvar['downloadurl'] = '';
-   209 }
-   210 }
-   211 else
-   212 {
-   213 $listvar['downloadplainurl'] = '';
-   214 $listvar['downloadurl'] = '';
-   215 }
-   216  
-   217 if ($rep->isRssEnabled())
-   218 {
-   219 // RSS should always point to the latest revision, so don't include rev
-   220 $listvar['rssurl'] = $config->getURL($rep, $path.$file, 'rss').$isDirString.createRevAndPegString('', $peg);
-   221 }
-   222  
-   223 $loop++;
-   224 $index++;
-   225 $last_index = $index;
-   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);
-   234 }
-   235 }
-   236 }
-   237  
-   238 // For an expanded tree, give the last entry an "L" node to close the grouping
-   239 if ($treeview && $last_index != 0)
-   240 {
-   241 $listing[$last_index - 1]['node'] = 1; // l-node
-   242 }
-   243  
-   244 return $listing;
-   245 }
-   246  
-   247 function showAllDirFiles($svnrep, $path, $rev, $peg, $listing, $index, $treeView = true)
-   248 {
-   249 global $config, $lang, $rep, $passrev, $peg, $passRevString;
-   250  
-   251 // List each file in the current directory
-   252 $loop = 0;
-   253 $last_index = 0;
-   254 $accessToThisDir = $rep->hasReadAccess($path, false);
-   255  
-   256 // If using flat view and not at the root, create a '..' entry at the top.
-   257 if (!$treeView && count($subs) > 2)
-   258 {
-   259 $parentPath = $subs;
-   260 unset($parentPath[count($parentPath) - 2]);
-   261 $parentPath = implode('/', $parentPath);
-   262  
-   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';
-   276 global $vars;
-   277 $listvar['date'] = $vars['date'];
-   278 $listvar['age'] = datetimeFormatDuration(time() - strtotime($vars['date']), true, true);
-   279 $index++;
-   280 }
-   281 }
-   282  
-   283 $openDir = false;
-   284 $logList = $svnrep->getList($path, $rev, $peg);
-   285  
-   286 if (!$logList)
-   287 {
-   288 return $listing;
-   289 }
-   290  
-   291 $downloadRevAndPeg = createRevAndPegString($rev, $peg ? $peg : $rev);
-   292  
-   293 foreach ($logList->entries as $entry)
-   294 {
-   295 $isDir = $entry->isdir;
-   296  
-   297 $file = $entry->file;
-   298 $isDirString = ($isDir) ? 'isdir=1&amp;' : '';
-   299  
-   300 // Only list files/directories that are not designated as off-limits
-   301 $access = ($isDir) ? $rep->hasReadAccess($path.$file, false)
-   302 : $accessToThisDir;
-   303  
-   304 if (!$access)
-   305 {
-   306 continue;
-   307 }
-   308  
-   309 $listvar = &$listing[$index];
-   310 $listvar['rowparity'] = $index % 2;
-   311  
-   312 if ($isDir)
-   313 {
-   314 $listvar['filetype'] = 'dir';
-   315 $openDir = true;
-   316 }
-   317 else
-   318 {
-   319 $listvar['filetype'] = strtolower(strrchr($file, '.'));
-   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] === "")
-   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]);
-   334 $listvar['classname'] = '';
-   335  
-   336 for ($n = 0; $n < $lastindexfile; ++$n)
-   337 {
-   338 $listvar['last_i_node'][$n] = false;
-   339 $listvar['classname'] = $listvar['classname'].$tempelements[$n].'/';
-   340 }
-   341  
-   342 $listvar['classname'] = $listvar['classname'].$tempelements[$lastindexfile];
-   343 $listvar['last_i_node'][$lastindexfile] = true;
-   344 }
-   345 else
-   346 {
-   347 $lastindexfile = count($tempelements)-1;
-   348 $listvar['node'] = $lastindexfile; // t-node
-   349 $listvar['level'] = ($treeView) ? $lastindexfile : 0;
-   350 $listvar['filename'] = escape($tempelements[$lastindexfile]);
-   351 $listvar['classname'] = '';
-   352  
-   353 for ($n=0; $n < $lastindexfile; ++$n)
-   354 {
-   355 $listvar['last_i_node'][$n] = false;
-   356 $listvar['classname'] = $listvar['classname'].$tempelements[$n].'/';
-   357 }
-   358  
-   359 $listvar['last_i_node'][$lastindexfile] = true;
-   360 }
-   361  
-   362 if ($isDir)
-   363 {
-   364 $listvar['fileurl'] = urlForPath($path.$file, $passRevString);
-   365 }
-   366 else
-   367 {
-   368 $listvar['fileurl'] = urlForPath($path.$file, createDifferentRevAndPegString($passrev, $peg));
-   369 }
-   370  
-   371 $listvar['filelink'] = '<a href="'.$listvar['fileurl'].'">'.$listvar['filename'].'</a>';
-   372  
-   373 if ($isDir)
-   374 {
-   375 $listvar['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.$passRevString;
-   376 }
-   377 else
-   378 {
-   379 $listvar['logurl'] = $config->getURL($rep, $path.$file, 'log').$isDirString.createDifferentRevAndPegString($passrev, $peg);
-   380 }
-   381  
-   382 if ($treeView)
-   383 {
-   384 $listvar['compare_box'] = '<input type="checkbox" name="compare[]" value="'.escape($path.$file).'@'.$passrev.'" onclick="enforceOnlyTwoChecked(this)" />';
-   385 }
-   386  
-   387 if ($config->showLastModInListing())
-   388 {
-   389 $listvar['committime'] = $entry->committime;
-   390 $listvar['revision'] = $entry->rev;
-   391 $listvar['author'] = $entry->author;
-   392 $listvar['age'] = $entry->age;
-   393 $listvar['date'] = $entry->date;
-   394 $listvar['revurl'] = $config->getURL($rep, $path.$file, 'revision').$isDirString.createRevAndPegString($entry->rev, $peg ? $peg : $rev);
-   395 }
-   396  
-   397 if ($rep->isDownloadAllowed($path.$file))
-   398 {
-   399 $downloadurl = $config->getURL($rep, $path.$file, 'dl').$isDirString.$downloadRevAndPeg;
-   400  
-   401 if ($isDir)
-   402 {
-   403 $listvar['downloadurl'] = $downloadurl;
-   404 $listvar['downloadplainurl'] = '';
-   405 }
-   406 else
-   407 {
-   408 $listvar['downloadplainurl'] = $downloadurl;
-   409 $listvar['downloadurl'] = '';
-   410 }
-   411 }
-   412 else
-   413 {
-   414 $listvar['downloadplainurl'] = '';
-   415 $listvar['downloadurl'] = '';
-   416 }
-   417  
-   418 if ($rep->isRssEnabled())
-   419 {
-   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;
-   427 }
-   428  
-   429 return $listing;
-   430 }
-   431  
-   432 function showTreeDir($svnrep, $path, $rev, $peg, $listing)
-   433 {
-   434 global $vars, $config;
-   435  
-   436 if ($config->showLoadAllRepos())
-   437 {
-   438 $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); 439 return showAllDirFiles($svnrep, $path, $rev, $peg, $listing, 0, $config->treeView);
-   440 }
-   441  
-   442 $subs = explode('/', $path);
-   443  
-   444 // The last element in "$subs" is empty for directories, some file name for files, but ALWAYS
-   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
-   449 // both values needed to be different in some environments in the past for some unkown reason.
-   450 //
-   451 // https://github.com/websvnphp/websvn/issues/146#issuecomment-913353366
-   452 $limit = count($subs) - 2;
-   453 $level = $limit;
-   454 $level = $level <= 0 ? 0 : $level;
-   455  
-   456 for ($n = 0; $n < $limit; $n++)
-   457 {
-   458 $vars['last_i_node'][$n] = false;
-   459 }
275   460  
-   461 $vars['compare_box'] = ''; // Set blank once in case tree view is not enabled.
-   462 return showDirFiles($svnrep, $subs, $level, $limit, $rev, $peg, $listing, 0, $config->treeView);
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  
287 // Revision info to pass along chain 473 if (!empty($rev))
-   474 {
288 $passrev = $rev; 475 $info = $svnrep->getInfo($path, $rev, $peg);
289   476  
-   477 if ($info)
-   478 {
290 // Get the directory contents of the given revision, or HEAD if not defined 479 $path = $info->path;
291 $contents = $svnrep->dirContents($path, @$rev); 480 $peg = (int)$info->rev;
-   481 }
-   482 }
292   483  
293 // If there's no revision info, go to the lastest revision for this path -  
294 $history = $svnrep->getLog($path, "", "", false); 484 $history = $svnrep->getLog($path, 'HEAD', 1, false, 2, ($path == '/') ? '' : $peg);
295   485  
296 if (!empty($history->entries[0])) 486 if (!$history)
-   487 {
297 $youngest = $history->entries[0]->rev; 488 unset($vars['error']);
-   489 $history = $svnrep->getLog($path, '', '', false, 2, ($path == '/') ? '' : $peg);
298 else 490  
299 $youngest = -1; 491 if (!$history)
-   492 {
-   493 renderTemplate404('directory','NOPATH');
-   494 }
-   495 }
-   496  
-   497 $youngest = ($history && isset($history->entries[0])) ? $history->entries[0]->rev : 0;
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  
-   529 createPathLinks($rep, $ppath, $passrev, $peg);
331 $vars["repname"] = $rep->getDisplayName(); 530 $passRevString = createRevAndPegString($passrev, $peg);
-   531 $isDirString = 'isdir=1&amp;';
332   532  
333 $dirurl = $config->getURL($rep, $path, "dir"); 533 $revurl = $config->getURL($rep, $path != '/' ? $path : '', 'dir');
334 $logurl = $config->getURL($rep, $path, "log"); -  
335 $rssurl = $config->getURL($rep, $path, "rss"); 534 $revurlSuffix = $path != '/' ? '#'.anchorForPath($path) : '';
336 $dlurl = $config->getURL($rep, $path, "dl"); -  
337 $compurl = $config->getURL($rep, "/", "comp"); -  
338   535  
339 if ($passrev != 0 && $passrev != $headrev && $youngest != -1) 536 if ($rev < $youngest)
-   537 {
-   538 if ($path == '/')
-   539 {
340 $vars["goyoungestlink"] = "<a href=\"${dirurl}opt=dir&amp;sc=1\">${lang["GOYOUNGEST"]}</a>"; 540 $vars['goyoungesturl'] = $config->getURL($rep, '', 'dir');
-   541 }
341 else 542 else
-   543 {
342 $vars["goyoungestlink"] = ""; 544 $vars['goyoungesturl'] = $config->getURL($rep, $path, 'dir').createRevAndPegString($youngest, $peg ? $peg: $rev).$revurlSuffix;
-   545 }
343   546  
344 $bugtraq = new Bugtraq($rep, $svnrep, $ppath); 547 $vars['goyoungestlink'] = '<a href="'.$vars['goyoungesturl'].'"'.($youngest ? ' title="'.$lang['REV'].' '.$youngest.'"' : '').'>'.$lang['GOYOUNGEST'].'</a>';
345   548  
346 $vars["action"] = ""; -  
347 $vars["rev"] = $rev; -  
348 $vars["path"] = $ppath; -  
349 $vars["lastchangedrev"] = $logrev; -  
350 $vars["date"] = $logEntry->date; -  
351 $vars["author"] = $logEntry->author; -  
352 $vars["log"] = nl2br($bugtraq->replaceIDs(create_anchors($logEntry->msg))); 549 $history2 = $svnrep->getLog($path, $rev, $youngest, true, 2, $peg);
353   550  
354 if (!$showchanged) 551 if (isset($history2->entries[1]))
355 { 552 {
356 $vars["showchangeslink"] = "<a href=\"${dirurl}rev=$passrev&amp;sc=1\">${lang["SHOWCHANGED"]}</a>"; 553 $nextRev = $history2->entries[1]->rev;
-   554 if ($nextRev != $youngest)
-   555 {
357 $vars["hidechangeslink"] = ""; 556 $vars['nextrev'] = $nextRev;
-   557 $vars['nextrevurl'] = $revurl.createRevAndPegString($nextRev, $peg).$revurlSuffix;
-   558 }
-   559 }
358   560  
359 $vars["hidechanges"] = true; 561 unset($vars['error']);
360 $vars["showchanges"] = false; -  
361 } 562 }
362 else 563  
-   564 if (isset($history->entries[1]))
363 { 565 {
364 $vars["showchangeslink"] = ""; -  
365 -  
366 $changes = $logEntry->mods; -  
367 -  
368 $firstAdded = true; -  
369 $firstModded = true; -  
370 $firstDeleted = true; -  
371   -  
372 $vars["newfilesbr"] = ""; -  
373 $vars["newfiles"] = ""; -  
374 $vars["changedfilesbr"] = ""; -  
375 $vars["changedfiles"] = ""; -  
376 $vars["deletedfilesbr"] = ""; -  
377 $vars["deletedfiles"] = ""; -  
378   -  
379 foreach ($changes as $file) -  
380 { -  
381 switch ($file->action) -  
382 { -  
383 case "A": -  
384 if (!$firstAdded) $vars["newfilesbr"] .= "<br />"; -  
385 $firstAdded = false; -  
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 />"; -  
399 $firstDeleted = false; 566 $prevRev = $history->entries[1]->rev;
400 $vars["deletedfilesbr"] .= $file->path; -  
401 $vars["deletedfiles"] .= " ".$file->path; -  
402 break; -  
403 } -  
404 } -  
405   -  
406 $vars["hidechangeslink"] = "<a href=\"${dirurl}rev=$passrev&amp;sc=0\">${lang["HIDECHANGED"]}</a>"; -  
407   -  
408 $vars["hidechanges"] = false; -  
409 $vars["showchanges"] = true; -  
410 } -  
411   -  
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) -  
416 { -  
417 $history = $svnrep->getLog($path, $rev, "", false); -  
418 } -  
419   -  
420 if (isset($history->entries[1]->rev)) 567 $prevPath = $history->entries[1]->path;
421 { -  
422 $vars["curdircomplink"] = "<a href=\"${compurl}compare[]=". 568 $vars['prevrev'] = $prevRev;
423 urlencode($history->entries[1]->path)."@".$history->entries[1]->rev. -  
424 "&amp;compare[]=".urlencode($history->entries[0]->path)."@".$history->entries[0]->rev. 569 $vars['prevrevurl'] = $revurl.createRevAndPegString($prevRev, $peg).$revurlSuffix;
425 "\">${lang["DIFFPREV"]}</a>"; -  
426 } 570 }
-   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));
-   578 $vars['safepath'] = escape($ppath);
-   579 $vars['lastchangedrev'] = $lastChangedRev;
-   580  
-   581 if ($logEntry)
-   582 {
-   583 $vars['date'] = $logEntry->date;
-   584 $vars['age'] = datetimeFormatDuration(time() - strtotime($logEntry->date));
-   585 $vars['author'] = $logEntry->author;
-   586 $vars['log'] = nl2br($bugtraq->replaceIDs(create_anchors(xml_entities($logEntry->msg))));
427 else 587 }
-   588  
-   589 $vars['revurl'] = $config->getURL($rep, ($path == '/' ? '' : $path), 'revision').$isDirString.$passRevString;
-   590 $vars['revlink'] = '<a href="'.$vars['revurl'].'">'.$lang['LASTMOD'].'</a>';
-   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 }
-   597  
-   598 $vars['logurl'] = $config->getURL($rep, $path, 'log').$isDirString.$passRevString;
-   599 $vars['loglink'] = '<a href="'.$vars['logurl'].'">'.$lang['VIEWLOG'].'</a>';
431   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 -  
448 $url = $config->getURL($rep, "/", "comp"); -  
449   610  
450 $vars["compare_form"] = "<form action=\"$url\" method=\"post\" name=\"compareform\">"; 611 if ($rep->isDownloadAllowed($path) && !isset($vars['warning']))
-   612 {
451 $vars["compare_submit"] = "<input name=\"comparesubmit\" type=\"submit\" value=\"${lang["COMPAREPATHS"]}\" />"; 613 $vars['downloadurl'] = $config->getURL($rep, $path, 'dl').$isDirString.$passRevString;
452 $vars["compare_endform"] = "<input type=\"hidden\" name=\"op\" value=\"comp\" /><input type=\"hidden\" name=\"sc\" value=\"$showchanged\" /></form>"; -  
-   614 }
453   615  
454 $listing = array(); -  
455 $listing = showTreeDir($svnrep, $path, $rev, $listing); 616 $vars['compare_form'] = '<form method="get" action="'.$config->getURL($rep, '', 'comp').'" id="compare">';
456   617  
-   618 if ($config->multiViews)
-   619 {
-   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 }
-   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);