<?php
/** @file index.php
* The main controller gadget. Reads config, processes user input, acts
* accordingly and finally passes required data to templates.
*/
require('include/config.php');
require('include/libsvn.php');
if ($config['debug']) { error_reporting(E_ALL); }
$page['base_href'] = dirname($_SERVER['SCRIPT_NAME']);
if ($page['base_href'] === '/') { $page['base_href'] = ''; }
function linkauthor($author) {
global $config;
if (isset($config['author_link'])) {
return "<a href=\"". sprintf($config['author_link'], urlencode($author)) ."\">$author</a>";
}
return $author;
}
/**
* Make internal link to an action, heeding URL rewriting.
* @return URL relative to server root, or viewsvn dir
*/
function makelink($dict) {
global $config;
global $page;
$base = '';
// If rewriting is enabled, embed some parameters into the URL
if ($config['rewrite'] == 1 || $config['rewrite'] == 2) {
if (isset($dict['do'], $dict['project'], $dict['path'])) {
// unset the "used" parameters and append rest as parameters
$base = $page['base_href'];
// Internal rewriting requires "index.php" in the path;
// everything after it is passed to PHP in PATH_INFO
if ($config['rewrite'] == 2) {
$base .= '/index.php';
}
$base .= "/$dict[do]/". rawurlencode($dict['project']) .':'. rawurlencode($dict['path']);
if (isset($dict['rev']) && $dict['rev'] != 'HEAD') {
$base .= "@$dict[rev]";
}
$base = str_replace('%2F', '/', $base);
unset($dict['do'], $dict['project'], $dict['path'], $dict['rev']);
}
}
// Rest of the parameters go into query string
$params = array();
foreach ($dict as $k => $v) {
if ($k === 'rev' && $v === 'HEAD') { continue; }
$params[] = rawurlencode($k) .'='. str_replace('%2F', '/', rawurlencode($v));
}
if (count($params) > 0) {
return $base .htmlentities('?'. join('&', $params));
} else {
return $base;
}
}
// ------------------------------------------------------------
// Process parameters
// ------------------------------------------------------------
if (isset($_REQUEST['do'])) { $do = $_REQUEST['do']; } else { $do = 'index'; }
if (isset($_REQUEST['project'])) { $project = $_REQUEST['project']; }
if (isset($_REQUEST['path'])) { $path = $_REQUEST['path']; }
if (isset($_REQUEST['rev'])) { $rev = $_REQUEST['rev']; }
if (isset($_REQUEST['oldrev'])) { $oldrev = $_REQUEST['oldrev']; }
// Internal rewriting ($config['rewrite'] = 2)
// /action/project:/path/@rev
if (isset($_SERVER['PATH_INFO'])) {
$path_info = $_SERVER['PATH_INFO'];
if (preg_match('!/([^/]*)/([^:]*):(.*?)(?:@([^@]*))?$!', $path_info, $matches) > 0) {
$do = $matches[1];
$project = $matches[2];
$path = $matches[3];
if (count($matches) > 4) {
$rev = $matches[4];
}
} else {
die("Invalid PATH_INFO");
}
}
// could validate path (no ..'s = escaping out of svnroot)? SVN does this already.
if (isset($project) && !isset($config['projects'][$project])) { unset($project); }
if (!isset($path)) { $path = '/'; }
if (isset($rev) && $rev != 'HEAD' && !is_numeric($rev)) { unset($rev); }
if (isset($oldrev) && $oldrev != 'HEAD' && !is_numeric($oldrev)) { unset($oldrev); }
if (!isset($rev)) { $rev = 'HEAD'; }
// ------------------------------------------------------------
// Init SVN
// ------------------------------------------------------------
$svn = new SVN();
if (isset($config['svnusername']) && isset($config['svnpassword'])) {
$svn->setAuthentication($config['svnusername'], $config['svnpassword']);
}
if (isset($config['cachedir'])) { $svn->setCacheDir($config['cachedir']); }
if (isset($config['svncommand'])) { $svn->setCommand($config['svncommand']); }
if (isset($config['svn_global_parameters'])) { $svn->setGlobalParameters($config['svn_global_parameters']); }
if (isset($project)) {
$p = $config['projects'][$project];
if (isset($p['root'])) {
$svn->setRoot($p['root']);
}
elseif (isset($config['svnroot'])) {
$svn->setRoot($config['svnroot'] .'/'. $project);
}
else {
die("SVN root not configured for '$project'");
}
if (isset($p['username']) && isset($p['password'])) {
$svn->setAuthentication($p['username'], $p['password']);
}
}
if (isset($path)) {
$svn->setPath($path);
}
// ------------------------------------------------------------
// Handle actions
// ------------------------------------------------------------
// First, we process the request we got, get necessary data etc, then set a
// template name that is included in the end to display the requested data.
// In special cases like rss and download we die() before coming out of the
// ifs.
// Information available for all templates
if (isset($project)) { $page['project'] = $project; }
$page['path'] = $path;
$page['rev'] = $rev;
$page['pathnodes'] = split('/', $path);
if (isset($project) && isset($path)) { $page['title'] = "$project:$path - ViewSVN"; }
array_shift($page['pathnodes']);
array_pop($page['pathnodes']);
// ACTION: browse
// PARAMETERS: PROJECT
// COMMANDS: ls -v (1), info (1), log (N)
if ($do == 'browse' && isset($project)) {
if (!isset($path)) { $path = '/'; }
$page['title'] = "$project:$path - ViewSVN";
$page['nodes'] = $svn->listFiles(isset($rev) ? $rev : 'HEAD', true);
if ($config['enable_proplist']) {
$page['proplist'] = $svn->getProperties($rev);
if (!strlen($page['proplist'])) {
unset($page['proplist']);
}
}
$template = 'browse';
}
// A: diff
// P: PROJECT, PATH, REV, OLDREV, RAW?
// C: diff
elseif ($do == 'diff' && isset($project) && isset($path) && isset($rev) && isset($oldrev)) {
if (!$config['enable_diff']) { die('Diff has not been enabled'); }
if (isset($_REQUEST['raw'])) {
header("Content-type: text/plain");
die($svn->getDiff($oldrev, $rev));
}
// Colorify added/removed/@@ lines, and link "Index:" lines
$page['content'] = preg_replace(array(
'/^(@@.*@@)$/m',
'/^(-.*)$/m',
'/^(\+.*)$/m',
'/^Index: (.+)$/m',
), array(
'<span class="pos">$1</span>',
'<span class="removed">$1</span>',
'<span class="added">$1</span>',
"Index: <a href=\"". str_replace('///', '/$1', makelink(array('do' => 'view', 'project' => $project, 'path' => "$path//", 'rev' => $rev))) ."\" title=\"View this file\">$1</a>",
),
htmlspecialchars($svn->getDiff($oldrev, $rev)));
$page['oldrev'] = $oldrev;
$page['title'] = "Diff $oldrev-$rev - $project:$path - ViewSVN";
$template = 'diff';
}
// A: download
// P: PROJECT, PATH, REV?
// C: cat
elseif ($do == 'download' && isset($project) && isset($path) && isset($rev)) {
if (!$config['enable_download']) { die('Download has not been enabled'); }
header('Content-type: archive/x-tar');
header("Content-disposition: filename=".urlencode($project)."-snapshot.tar.bz2");
echo $svn->downloadArchive($rev);
die();
}
// A: list
// P: PROJECT, PATH, REV?
// C: ls -R
elseif ($do == 'list' && isset($project) && isset($path)) {
if (!$config['enable_list']) { die('List has not been enabled'); }
$page['nodes'] = $svn->listFiles($rev, false, true);
$template = 'list';
}
// A: log
// P: PROJECT, PATH, REV?
// C: log
elseif ($do == 'log' && isset($project) && isset($path)) {
if (!$config['enable_log']) { die('Log viewing has not been enabled'); }
$page['title'] = 'View log';
$page['entries'] = $svn->getLog($rev, 1, $config['log_limit']);
$template = 'log';
}
// A: rss
// P: PROJECT, PATH
// C: log
elseif ($do == 'rss' && isset($project) && isset($path)) {
if (!$config['enable_rss']) { die('RSS feeds have not been enabled'); }
$page['items'] = $svn->getLog('HEAD', '1', $config['rss_limit']);
require('templates/rss.php');
die();
}
// A: view
// P: PROJECT, PATH, REV?, ANNOTATE?, RAW?
// C: cat
elseif ($do == 'view' && isset($project) && isset($path)) {
$page['title'] = "$project:$path ($rev) - ViewSVN";
if (isset($_REQUEST['raw'])) {
header('Content-type: text/plain');
echo $svn->getFileContents($rev);
die();
}
if (isset($_REQUEST['annotate']) && $config['enable_annotate']) {
$content = htmlspecialchars($svn->getAnnotation($rev));
$link1 = makelink(array('do' => 'view', 'project' => $project, 'path' => $path, 'rev' => '\\2'));
$link2 = makelink(array('do' => 'log', 'project' => $project, 'path' => $path, 'rev' => '\\2'));
$content = preg_replace('/^( *)(\d+)/m', "\\1<a href=\"$link1\" title=\"Show file at this revision\">\\2</a> <a href=\"$link2\" title=\"View log entry for this line\">?</a>", $content);
$page['filecontent'] = $content;
}
else {
// Get file extension from current path
$tmp = explode('.', $path);
$ext = array_pop($tmp);
if ($ext == 'cc' || $ext == 'hh') { $ext = 'cpp'; }
if ($config['enable_geshi'] && strlen($ext) > 0) {
include_once($config['geshi_path']);
$geshi =& new GeSHi($svn->getFileContents($rev), GeSHi::get_language_name_from_extension($ext));
$page['filecontent'] = $geshi->parse_code();
} else {
$page['filecontent'] = htmlspecialchars($svn->getFileContents($rev));
}
}
$template = 'view';
}
// A: index
// P: -
// C: -
else {
// if there is only one project, browse it by default
if (count(array_keys($config['projects'])) == 1) {
$target = array_keys($config['projects']);
$target = array_shift($target);
$startpath = '/';
if (isset($config['startpath'])) { $startpath = $config['startpath']; }
if (isset($config['projects'][$target]['startpath'])) { $startpath = $config['projects'][$target]['startpath']; }
header("Location: http://". $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'] . "?do=browse&project=". urlencode($target) ."&path=". urlencode($startpath));
die();
}
$page['title'] = 'ViewSVN - List of projects';
$page['description'] = 'List of projects available for browsing';
$page['projects'] = array();
$page['keywords'] = join(', ', array_keys($config['projects']));
foreach ($config['projects'] as $p => $v) {
$startpath = '/';
if (isset($config['startpath'])) { $startpath = $config['startpath']; }
if (isset($v['startpath'])) { $startpath = $v['startpath']; }
array_push($page['projects'], array(
'name' => $p,
'description' => $v['description'],
'path' => $startpath,
));
}
$template = 'projectlist';
}
// Display the requested template
require("templates/header.php");
require("templates/$template.php");
require("templates/footer.php");