<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// --
//
// listing.php
//
// Show the listing for the given repository/path/revision
require_once("include/setup.inc");
require_once("include/svnlook.inc");
require_once("include/utils.inc");
require_once("include/template.inc");
require_once("include/bugtraq.inc");
function removeURLSeparator($url)
{
return preg_replace('#(\?|&(amp;)?)$#', '', $url);
}
function fileLink($path, $file, $returnjoin = false)
{
global $rep, $passrev, $showchanged, $config;
if ($path == "" || $path{0} != "/")
$ppath = "/".$path;
else
$ppath = $path;
if ($ppath{strlen($ppath)-1} != "/")
$ppath .= "/";
if ($file{0} == "/")
$pfile = substr($file, 1);
else
$pfile = $file;
if ($returnjoin)
return $ppath.$pfile;
$isDir = $pfile{strlen($pfile) - 1} == "/";
if ($passrev) $passrevstr = "rev=$passrev&"; else $passrevstr = "";
if ($showchanged) $showchangedstr = "sc=$showchanged"; else $showchangedstr = "";
if ($isDir)
{
$url = $config->getURL($rep, $ppath.$pfile, "dir");
// XHTML doesn't allow slashes in IDs ~J
$id = str_replace('/', '_', $ppath.$pfile);
$url = "<a id='$id' href=\"${url}$passrevstr$showchangedstr";
$url = removeURLSeparator($url);
if ($config->treeView) $url .= "#$id";
$url .= "\">$pfile</a>";
}
else
{
$url = $config->getURL($rep, $ppath.$pfile, "file");
$url .= $passrevstr.$showchangedstr;
$url = removeURLSeparator($url);
$url = "<a href=\"${url}\">$pfile</a>";
}
return $url;
}
function fileLinkGetFile($path, $file)
{
global $rep, $passrev, $showchanged, $config;
if ($path == "" || $path{0} != "/")
$ppath = "/".$path;
else
$ppath = $path;
if ($ppath{strlen($ppath)-1} != "/")
$ppath .= "/";
if ($file{0} == "/")
$pfile = substr($file, 1);
else
$pfile = $file;
$isDir = $pfile{strlen($pfile) - 1} == "/";
if (!$isDir)
{
$url = $config->getURL($rep, $ppath.$pfile, "file");
$url = removeURLSeparator($url);
$url = "<a href=\"${url}&getfile\">Get</a>";
}
return $url;
}
function showDirFiles($svnrep, $subs, $level, $limit, $rev, $listing, $index, $treeview = true)
{
global $rep, $passrev, $showchanged, $config, $lang;
$path = "";
// Explicitly requested file as attachment
if (isset($_REQUEST['getfile']))
{
$base = basename($path);
header("Content-Type: application/octet-stream");
header("Content-Length: $size");
header("Content-Disposition: inline; filename=".urlencode($base));
$svnrep->getFileContents($path, "", $rev);
exit;
}
if (!$treeview)
$level = $limit;
for ($n = 0; $n <= $level; $n++)
{
$path .= $subs[$n]."/";
}
$contents = $svnrep->dirContents($path, $rev);
// List each file in the current directory
$loop = 0;
$last_index = 0;
$openDir = false;
$fullaccess = $rep->hasReadAccess($path, false);
foreach($contents as $file)
{
$isDir = ($file{strlen($file) - 1} == "/"?1:0);
$access = false;
if ($isDir)
{
if ($rep->hasReadAccess($path.$file, true))
{
$access = true;
$openDir = (!strcmp($subs[$level+1]."/", $file) || !strcmp($subs[$level+1], $file));
if ($openDir)
$listing[$index]["filetype"] = "diropen";
else
$listing[$index]["filetype"] = "dir";
if ($rep->isDownloadAllowed($path.$file))
{
$dlurl = $config->getURL($rep, $path.$file, "dl");
$listing[$index]["fileviewdllink"] = "<a href=\"${dlurl}rev=$passrev&isdir=1\">${lang["TARBALL"]}</a>";
}
else
$listing[$index]["fileviewdllink"] = " ";
}
}
else
{
if ($fullaccess)
{
$access = true;
if ($level != $limit)
{
// List directories only, skip all files
continue;
}
$listing[$index]["fileviewdllink"] = " ";
$listing[$index]["filetype"] = strrchr($file, ".");
}
}
if ($access)
{
$listing[$index]["rowparity"] = ($index % 2)?"L1":"L0";
if ($treeview)
$listing[$index]["compare_box"] = "<input type=\"checkbox\" name=\"compare[]\" value=\"".fileLink($path, $file, true)."@$passrev\" onclick=\"checkCB(this)\" />";
else
$listing[$index]["compare_box"] = "";
if ($openDir)
$listing[$index]["filelink"] = "<b>".fileLink($path, $file)."</b>";
else
$listing[$index]["filelink"] = fileLink($path, $file);
if ($isDir)
$listing[$index]["filelinkgetfile"] = "";
else
$listing[$index]["filelinkgetfile"] = fileLinkGetFile($path, $file);
// The history command doesn't return with a trailing slash. We need to remember here if the
// file is a directory or not!
$listing[$index]["isDir"] = $isDir;
if ($treeview)
$listing[$index]["level"] = $level;
else
$listing[$index]["level"] = 0;
$listing[$index]["node"] = 0; // t-node
$fileurl = $config->getURL($rep, $path.$file, "log");
$listing[$index]["fileviewloglink"] = "<a href=\"${fileurl}rev=$passrev&sc=$showchanged&isdir=$isDir\">${lang["VIEWLOG"]}</a>";
$rssurl = $config->getURL($rep, $path.$file, "rss");
if ($rep->getHideRss())
{
$listing[$index]["rsslink"] = "<a href=\"${rssurl}rev=$passrev&sc=$showchanged&isdir=$isDir\">${lang["RSSFEED"]}</a>";
$listing[$index]["rssanchor"] = "<a href=\"${rssurl}rev=$passrev&sc=$showchanged&isdir=$isDir\">";
}
$index++;
$loop++;
$last_index = $index;
if (($level != $limit) && ($isDir))
{
if (!strcmp($subs[$level + 1]."/", $file))
{
$listing = showDirFiles($svnrep, $subs, $level + 1, $limit, $rev, $listing, $index);
$index = count($listing);
}
}
}
}
if ($last_index != 0 && $treeview)
{
$listing[$last_index - 1]["node"] = 1; // l-node
}
return $listing;
}
function showTreeDir($svnrep, $path, $rev, $listing)
{
global $vars, $config;
$subs = explode("/", $path);
// For directory, the last element in the subs is empty.
// For file, the last element in the subs is the file name.
// Therefore, it is always count($subs) - 2
$limit = count($subs) - 2;
for ($n = 0; $n < $limit; $n++)
{
$vars["last_i_node"][$n] = FALSE;
}
return showDirFiles($svnrep, $subs, 0, $limit, $rev, $listing, 0, $config->treeView);
}
// Make sure that we have a repository
if (!isset($rep))
{
echo $lang["NOREP"];
exit;
}
$svnrep = new SVNRepository($rep);
// Revision info to pass along chain
$passrev = $rev;
// Get the directory contents of the given revision, or HEAD if not defined
$contents = $svnrep->dirContents($path, @$rev);
// If there's no revision info, go to the lastest revision for this path
$history = $svnrep->getLog($path, "", "", false);
if (!empty($history->entries[0]))
$youngest = $history->entries[0]->rev;
else
$youngest = -1;
// Unless otherwise specified, we get the log details of the latest change
if (empty($rev))
$logrev = $youngest;
else
$logrev = $rev;
if ($logrev != $youngest)
{
$logEntry = $svnrep->getLog($path, $logrev, $logrev, false);
$logEntry = $logEntry->entries[0];
}
else
$logEntry = $history->entries[0];
$headlog = $svnrep->getLog("/", "", "", true, 1);
$headrev = $headlog->entries[0]->rev;
// If we're not looking at a specific revision, get the HEAD revision number
// (the revision of the rest of the tree display)
if (empty($rev))
{
$rev = $headrev;
}
if ($path == "" || $path{0} != "/")
$ppath = "/".$path;
else
$ppath = $path;
$vars["repname"] = $rep->getDisplayName();
$dirurl = $config->getURL($rep, $path, "dir");
$logurl = $config->getURL($rep, $path, "log");
$rssurl = $config->getURL($rep, $path, "rss");
$dlurl = $config->getURL($rep, $path, "dl");
$compurl = $config->getURL($rep, "/", "comp");
if ($passrev != 0 && $passrev != $headrev && $youngest != -1)
$vars["goyoungestlink"] = "<a href=\"${dirurl}opt=dir&sc=1\">${lang["GOYOUNGEST"]}</a>";
else
$vars["goyoungestlink"] = "";
$bugtraq = new Bugtraq($rep, $svnrep, $ppath);
$vars["action"] = "";
$vars["rev"] = $rev;
$vars["path"] = $ppath;
$vars["lastchangedrev"] = $logrev;
$vars["date"] = $logEntry->date;
$vars["author"] = $logEntry->author;
$vars["log"] = nl2br($bugtraq->replaceIDs(create_anchors($logEntry->msg)));
if (!$showchanged)
{
$vars["showchangeslink"] = "<a href=\"${dirurl}rev=$passrev&sc=1\">${lang["SHOWCHANGED"]}</a>";
$vars["hidechangeslink"] = "";
$vars["hidechanges"] = true;
$vars["showchanges"] = false;
}
else
{
$vars["showchangeslink"] = "";
$changes = $logEntry->mods;
$firstAdded = true;
$firstModded = true;
$firstDeleted = true;
$vars["newfilesbr"] = "";
$vars["newfiles"] = "";
$vars["changedfilesbr"] = "";
$vars["changedfiles"] = "";
$vars["deletedfilesbr"] = "";
$vars["deletedfiles"] = "";
foreach ($changes as $file)
{
switch ($file->action)
{
case "A":
if (!$firstAdded) $vars["newfilesbr"] .= "<br />";
$firstAdded = false;
$vars["newfilesbr"] .= fileLink("", $file->path);
$vars["newfiles"] .= " ".fileLink("", $file->path);
break;
case "M":
if (!$firstModded) $vars["changedfilesbr"] .= "<br />";
$firstModded = false;
$vars["changedfilesbr"] .= fileLink("", $file->path);
$vars["changedfiles"] .= " ".fileLink("", $file->path);
break;
case "D":
if (!$firstDeleted) $vars["deletedfilesbr"] .= "<br />";
$firstDeleted = false;
$vars["deletedfilesbr"] .= $file->path;
$vars["deletedfiles"] .= " ".$file->path;
break;
}
}
$vars["hidechangeslink"] = "<a href=\"${dirurl}rev=$passrev&sc=0\">${lang["HIDECHANGED"]}</a>";
$vars["hidechanges"] = false;
$vars["showchanges"] = true;
}
createDirLinks($rep, $ppath, $passrev, $showchanged);
$vars["curdirloglink"] = "<a href=\"${logurl}rev=$passrev&sc=$showchanged&isdir=1\">${lang["VIEWLOG"]}</a>";
if ($rev != $headrev)
{
$history = $svnrep->getLog($path, $rev, "", false);
}
if (isset($history->entries[1]->rev))
{
$vars["curdircomplink"] = "<a href=\"${compurl}compare[]=".
urlencode($history->entries[1]->path)."@".$history->entries[1]->rev.
"&compare[]=".urlencode($history->entries[0]->path)."@".$history->entries[0]->rev.
"\">${lang["DIFFPREV"]}</a>";
}
else
{
$vars["curdircomplink"] = "";
}
if ($rep->getHideRss())
{
$vars["curdirrsslink"] = "<a href=\"${rssurl}rev=$passrev&sc=$showchanged&isdir=1\">${lang["RSSFEED"]}</a>";
$vars["curdirrsshref"] = "${rssurl}rev=$passrev&sc=$showchanged&isdir=1";
$vars["curdirrssanchor"] = "<a href=\"${rssurl}rev=$passrev&sc=$showchanged&isdir=1\">";
}
// Set up the tarball link
$subs = explode("/", $path);
$level = count($subs) - 2;
if ($rep->isDownloadAllowed($path))
$vars["curdirdllink"] = "<a href=\"${dlurl}rev=$passrev&isdir=1\">${lang["TARBALL"]}</a>";
else
$vars["curdirdllink"] = "";
$url = $config->getURL($rep, "/", "comp");
$vars["compare_form"] = "<form action=\"$url\" method=\"post\" name=\"compareform\">";
$vars["compare_submit"] = "<input name=\"comparesubmit\" type=\"submit\" value=\"${lang["COMPAREPATHS"]}\" />";
$vars["compare_endform"] = "<input type=\"hidden\" name=\"op\" value=\"comp\" /><input type=\"hidden\" name=\"sc\" value=\"$showchanged\" /></form>";
$listing = array();
$listing = showTreeDir($svnrep, $path, $rev, $listing);
$vars["version"] = $version;
if (!$rep->hasReadAccess($path, true))
$vars["noaccess"] = true;
if (!$rep->hasReadAccess($path, false))
$vars["restricted"] = true;
parseTemplate($rep->getTemplatePath()."header.tmpl", $vars, $listing);
parseTemplate($rep->getTemplatePath()."directory.tmpl", $vars, $listing);
parseTemplate($rep->getTemplatePath()."footer.tmpl", $vars, $listing);
?>