<?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 ?  '&amp;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 ? '' : '&amp;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. '&amp;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');