<?php
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright (C) 2004-2006 Tim Armes
//
// 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
//
// --
//
// revision.php
//
// Show the details for a given revision
require_once 'include/setup.php';
require_once 'include/svnlook.php';
require_once 'include/utils.php';
require_once 'include/template.php';
require_once 'include/bugtraq.php';
// Make sure that we have a repository
if (!$rep)
{
renderTemplate404('revision','NOREP');
}
$svnrep = new SVNRepository($rep);
$ppath = ($path == '' || $path[0] != '/') ? '/'.$path : $path;
createPathLinks($rep, $ppath, $rev, $peg);
$passRevString = createRevAndPegString($rev, $peg);
// Find the youngest revision containing changes for the given path
$history = $svnrep->getLog($path, 'HEAD', 1, true, 2, ($path == '/') ? '' : $peg);
if (!$history)
{
unset($vars['error']);
$history = $svnrep->getLog($path, '', '', true, 2, ($path == '/') ? '' : $peg);
if (!$history)
{
renderTemplate404('revision','NOPATH');
}
}
$youngest = ($history && isset($history->entries[0])) ? $history->entries[0]->rev : 0;
$vars['youngestrev'] = $youngest;
// TODO The "youngest" rev is often incorrect when both path and rev are specified.
// If a path was last modified at rev M and the URL contains rev N, it uses rev N.
// Unless otherwise specified, we get the log details of the latest change
$lastChangedRev = ($rev) ? $rev : $youngest;
$history = $svnrep->getLog($path, $lastChangedRev, 1, false, 2, $peg, true);
if (!$history)
{
renderTemplate404('revision','NOPATH');
}
if (empty($rev))
{
$rev = $lastChangedRev;
}
// Generate links to newer and older revisions
$revurl = $config->getURL($rep, $path, 'revision');
if ($rev < $youngest)
{
$vars['goyoungesturl'] = $config->getURL($rep, $path, 'revision');
$vars['goyoungestlink'] = '<a href="'.$vars['goyoungesturl'].'"'.($youngest ? ' title="'.$lang['REV'].' '.$youngest.'"' : '').'>'.$lang['GOYOUNGEST'].'</a>';
$history2 = $svnrep->getLog($path, $rev, $youngest, true, 2, $peg);
if (isset($history2->entries[1]))
{
$nextRev = $history2->entries[1]->rev;
if ($nextRev != $youngest)
{
$vars['nextrev'] = $nextRev;
$vars['nextrevurl'] = $revurl.createRevAndPegString($nextRev, $path != '/' ? $peg ? $peg : $rev : '');
//echo 'NEXT='.$vars['nextrevurl'].'<br/>';
}
}
unset($vars['error']);
}
if (isset($history->entries[1]))
{
$prevRev = $history->entries[1]->rev;
$prevPath = $history->entries[1]->path;
$vars['prevrev'] = $prevRev;
$vars['prevrevurl'] = $revurl.createRevAndPegString($prevRev, $path != '/' ? ($peg ? $peg : $rev) : '');
//echo 'PREV='.$vars['prevrevurl'].'<br/>';
}
// Save the entry from which we pull information for the current revision.
$logEntry = (isset($history->entries[0])) ? $history->entries[0] : null;
$bugtraq = new Bugtraq($rep, $svnrep, $ppath);
$vars['action'] = '';
$vars['rev'] = $rev;
$vars['peg'] = $peg;
$vars['path'] = str_replace('%2F', '/', rawurlencode($ppath));
$vars['safepath'] = escape($ppath);
if ($logEntry)
{
$vars['date'] = $logEntry->date;
$vars['age'] = datetimeFormatDuration(time() - strtotime($logEntry->date));
$vars['author'] = $logEntry->author;
$vars['log'] = nl2br($bugtraq->replaceIDs(create_anchors(xml_entities($logEntry->msg))));
}
$isDir = @$_REQUEST['isdir'] == 1 || $path == '' || $path == '/';
$vars['logurl'] = $config->getURL($rep, $path, 'log').$passRevString.($isDir ? '&isdir=1' : '');
$vars['loglink'] = '<a href="'.$vars['logurl'].'">'.$lang['VIEWLOG'].'</a>';
$dirPath = $isDir ? $path : dirname($path).'/';
$vars['directoryurl'] = $config->getURL($rep, $dirPath, 'dir').$passRevString.'#'.anchorForPath($dirPath);
$vars['directorylink'] = '<a href="'.$vars['directoryurl'].'">'.$lang['LISTING'].'</a>';
if ($path != $dirPath)
{
$vars['filedetailurl'] = $config->getURL($rep, $path, 'file').$passRevString;
$vars['filedetaillink'] = '<a href="'.$vars['filedetailurl'].'">'.$lang['FILEDETAIL'].'</a>';
$vars['blameurl'] = $config->getURL($rep, $path, 'blame').$passRevString;
$vars['blamelink'] = '<a href="'.$vars['blameurl'].'">'.$lang['BLAME'].'</a>';
}
if ($rep->isRssEnabled())
{
$vars['rssurl'] = $config->getURL($rep, $path, 'rss').createRevAndPegString('', $peg);
$vars['rsslink'] = '<a href="'.$vars['rssurl'].'">'.$lang['RSSFEED'].'</a>';
}
$changes = $logEntry ? $logEntry->mods : array();
if (!is_array($changes))
{
$changes = array();
}
usort($changes, 'SVNLogEntry_compare');
$row = 0;
$prevRevString = createRevAndPegString($rev - 1, $rev - 1);
$thisRevString = createRevAndPegString($rev, $rev);
foreach ($changes as $change)
{
$linkRevString = ($change->action == 'D') ? $prevRevString : $thisRevString;
$isDir = $change->isdir;
if ($isDir !== null)
{
$isFile = !$isDir;
}
else
{
// NOTE: This is a hack (runs `svn info` on each path) to see if it's a file.
// `svn log --verbose --xml` should really provide this info, but does only in recent version?
$lastSeenRev = ($change->action == 'D') ? $rev - 1 : $rev;
$isFile = $svnrep->isFile($change->path, $lastSeenRev, $lastSeenRev);
}
if (!$isFile && $change->path != '/')
{
$change->path .= '/';
}
$resourceExisted = $change->action == 'M' || $change->copyfrom;
$listing[] = array(
'path' => str_replace('%2F', '/', rawurlencode($change->path)),
'safepath' => escape($change->path),
'oldpath' => str_replace('%2F', '/', rawurlencode($change->copyfrom)).($change->copyfrom ? '@'.$change->copyrev : ''),
'oldsafepath' => escape($change->copyfrom ? $change->copyfrom.'@'.$change->copyrev : ''),
'action' => $change->action,
'added' => $change->action == 'A',
'deleted' => $change->action == 'D',
'modified' => $change->action == 'M',
'detailurl' => $config->getURL($rep, $change->path, ($isFile ? 'file' : 'dir')).$linkRevString,
// For deleted resources, the log link points to the previous revision.
'logurl' => $config->getURL($rep, $change->path, 'log').$linkRevString.($isFile ? '' : '&isdir=1'),
'diffurl' => $resourceExisted ? $config->getURL($rep, $change->path, 'diff').$linkRevString : '',
'blameurl' => $resourceExisted ? $config->getURL($rep, $change->path, 'blame').$linkRevString : '',
'rowparity' => $row,
'notinpath' => substr($change->path, 0, strlen($path)) != $path,
);
$row = 1 - $row;
}
if (isset($prevRev))
{
$vars['compareurl'] = $config->getURL($rep, '', 'comp').'compare[]='.rawurlencode($prevPath).'@'.$prevRev. '&compare[]='.rawurlencode($path).'@'.$rev;
$vars['comparelink'] = '<a href="'.$vars['compareurl'].'">'.$lang['DIFFPREV'].'</a>';
}
if (!$rep->hasReadAccess($path))
{
$vars['error'] = $lang['NOACCESS'];
sendHeaderForbidden();
}
$vars['restricted'] = !$rep->hasReadAccess($path, false);
renderTemplate('revision');