Rev Author Line No. Line
185 miho 1 <?php
2 # vim:et:ts=3:sts=3:sw=3:fdm=marker:
3  
4 // WebSVN - Subversion repository viewing via the web using PHP
5 // Copyright © 2004-2006 Tim Armes, Matt Sicker
6 //
7 // 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
9 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 //
21 // --
22 //
23 // listing.php
24 //
25 // Show the listing for the given repository/path/revision
26  
27 require_once("include/setup.inc");
28 require_once("include/svnlook.inc");
29 require_once("include/utils.inc");
30 require_once("include/template.inc");
31 require_once("include/bugtraq.inc");
32  
33 function removeURLSeparator($url)
34 {
35 return preg_replace('#(\?|&(amp;)?)$#', '', $url);
36 }
37  
38 function fileLink($path, $file, $returnjoin = false)
39 {
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  
1189 miho 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;
102  
103 $isDir = $pfile{strlen($pfile) - 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  
185 miho 115 function showDirFiles($svnrep, $subs, $level, $limit, $rev, $listing, $index, $treeview = true)
116 {
117 global $rep, $passrev, $showchanged, $config, $lang;
118  
119 $path = "";
120  
1189 miho 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  
185 miho 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));
1189 miho 162  
185 miho 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 }
1189 miho 192  
185 miho 193 if ($access)
194 {
1189 miho 195 $listing[$index]["rowparity"] = ($index % 2)?"L1":"L0";
185 miho 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"] = "";
1189 miho 201  
185 miho 202 if ($openDir)
203 $listing[$index]["filelink"] = "<b>".fileLink($path, $file)."</b>";
204 else
205 $listing[$index]["filelink"] = fileLink($path, $file);
1189 miho 206  
207 if ($isDir)
208 $listing[$index]["filelinkgetfile"] = "";
209 else
210 $listing[$index]["filelinkgetfile"] = fileLinkGetFile($path, $file);
211  
185 miho 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!
1189 miho 214  
185 miho 215 $listing[$index]["isDir"] = $isDir;
1189 miho 216  
185 miho 217 if ($treeview)
218 $listing[$index]["level"] = $level;
219 else
220 $listing[$index]["level"] = 0;
221  
222 $listing[$index]["node"] = 0; // t-node
1189 miho 223  
185 miho 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>";
1189 miho 226  
185 miho 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 }
1189 miho 233  
185 miho 234 $index++;
235 $loop++;
236 $last_index = $index;
1189 miho 237  
185 miho 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 }
1189 miho 245 }
185 miho 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  
274 return showDirFiles($svnrep, $subs, 0, $limit, $rev, $listing, 0, $config->treeView);
275  
276 }
277  
278 // Make sure that we have a repository
279 if (!isset($rep))
280 {
281 echo $lang["NOREP"];
282 exit;
283 }
284  
285 $svnrep = new SVNRepository($rep);
286  
287 // Revision info to pass along chain
288 $passrev = $rev;
289  
290 // Get the directory contents of the given revision, or HEAD if not defined
291 $contents = $svnrep->dirContents($path, @$rev);
292  
293 // If there's no revision info, go to the lastest revision for this path
294 $history = $svnrep->getLog($path, "", "", false);
295  
296 if (!empty($history->entries[0]))
297 $youngest = $history->entries[0]->rev;
298 else
299 $youngest = -1;
300  
301 // Unless otherwise specified, we get the log details of the latest change
302 if (empty($rev))
303 $logrev = $youngest;
304 else
305 $logrev = $rev;
306  
307 if ($logrev != $youngest)
308 {
309 $logEntry = $svnrep->getLog($path, $logrev, $logrev, false);
310 $logEntry = $logEntry->entries[0];
311 }
312 else
313 $logEntry = $history->entries[0];
314  
315 $headlog = $svnrep->getLog("/", "", "", true, 1);
316 $headrev = $headlog->entries[0]->rev;
317  
318 // If we're not looking at a specific revision, get the HEAD revision number
319 // (the revision of the rest of the tree display)
320  
321 if (empty($rev))
322 {
323 $rev = $headrev;
324 }
325  
326 if ($path == "" || $path{0} != "/")
327 $ppath = "/".$path;
328 else
329 $ppath = $path;
330  
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");
336 $dlurl = $config->getURL($rep, $path, "dl");
337 $compurl = $config->getURL($rep, "/", "comp");
338  
339 if ($passrev != 0 && $passrev != $headrev && $youngest != -1)
340 $vars["goyoungestlink"] = "<a href=\"${dirurl}opt=dir&amp;sc=1\">${lang["GOYOUNGEST"]}</a>";
341 else
342 $vars["goyoungestlink"] = "";
343  
344 $bugtraq = new Bugtraq($rep, $svnrep, $ppath);
345  
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)));
353  
354 if (!$showchanged)
355 {
356 $vars["showchangeslink"] = "<a href=\"${dirurl}rev=$passrev&amp;sc=1\">${lang["SHOWCHANGED"]}</a>";
357 $vars["hidechangeslink"] = "";
358  
359 $vars["hidechanges"] = true;
360 $vars["showchanges"] = false;
361 }
362 else
363 {
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;
1189 miho 389  
185 miho 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;
400 $vars["deletedfilesbr"] .= $file->path;
401 $vars["deletedfiles"] .= " ".$file->path;
402 break;
403 }
404 }
1189 miho 405  
185 miho 406 $vars["hidechangeslink"] = "<a href=\"${dirurl}rev=$passrev&amp;sc=0\">${lang["HIDECHANGED"]}</a>";
1189 miho 407  
185 miho 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))
421 {
1189 miho 422 $vars["curdircomplink"] = "<a href=\"${compurl}compare[]=".
423 urlencode($history->entries[1]->path)."@".$history->entries[1]->rev.
424 "&amp;compare[]=".urlencode($history->entries[0]->path)."@".$history->entries[0]->rev.
425 "\">${lang["DIFFPREV"]}</a>";
185 miho 426 }
427 else
428 {
1189 miho 429 $vars["curdircomplink"] = "";
185 miho 430 }
431  
432 if ($rep->getHideRss())
433 {
434 $vars["curdirrsslink"] = "<a href=\"${rssurl}rev=$passrev&amp;sc=$showchanged&amp;isdir=1\">${lang["RSSFEED"]}</a>";
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\">";
437 }
438  
439 // Set up the tarball link
440  
441 $subs = explode("/", $path);
442 $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  
450 $vars["compare_form"] = "<form action=\"$url\" method=\"post\" name=\"compareform\">";
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  
454 $listing = array();
455 $listing = showTreeDir($svnrep, $path, $rev, $listing);
456  
457 $vars["version"] = $version;
458  
459 if (!$rep->hasReadAccess($path, true))
460 $vars["noaccess"] = true;
461  
462 if (!$rep->hasReadAccess($path, false))
463 $vars["restricted"] = true;
464  
465 parseTemplate($rep->getTemplatePath()."header.tmpl", $vars, $listing);
466 parseTemplate($rep->getTemplatePath()."directory.tmpl", $vars, $listing);
467 parseTemplate($rep->getTemplatePath()."footer.tmpl", $vars, $listing);
468  
469 ?>