phpdarcsview personal branch

a modified version of phpdarcsview repo

Repository URL: http://sc-gtk-server.sourceforge.net/darcs/phpdarcsview

Darcs command: $ darcs get http://sc-gtk-server.sourceforge.net/darcs/phpdarcsview

Show:


File: /index.php

<?php
/*
 * phpDarcsView
 * Copyright (C) 2005-2007 Sergej Chodarev
 */
 
/*
 *  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 Library 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.
 */
 
define('VERSION', '0.5.2');
define('HOMEPAGE', 'http://sergejx.mysteria.cz/phpdarcsview/');
 
 
//======= Configuration variables (with default configuration) ===============
 
/*
 * If you want to change these settings, modify variables defined below in your
 * config.php file.
 */
 
/** List of repositories
 *
 * Every item of array is array itself and must contain fields with these keys:
 *   - dir - directory where repository is placed, relative to current dir
 *   - title - repository title
 *   - description - repository description
 *   - url - URL of repository
 * For example see file config.php.sample
 */
$repos = array();
 
/** Configuration of syntax highlight
 *
 * This array defines associations between file extensions and language names
 * used by FSHL.
 */
$languages = array(
	'html' => 'HTML', 'htm' => 'HTML', 'php' => 'HTML',
	'css' => 'CSS',
	'js' => 'JS',
	'c' => 'CPP', 'cpp' => 'CPP', 'cc' => 'CPP', 'h' => 'CPP',
	'java' => 'JAVA',
	'sql' => 'SQL',
	'py' => 'PY',
        'ss' => 'scheme', 'scm' => 'scheme', 'sch' => 'scheme', 'sc' => 'scheme'         
);
 
/** Extensions of binary files
 *
 * Files with these extensions will be not displayed as text.
 */
$binaries = array('png', 'jpg', 'jpeg', 'gif', 'pdf');
 
//======= End of configuration variables definitions =========================
 
 
// Now include user configuration file
require('config.php');
 
// Try include FSHL
@include("fshl/fshl.php");
 
// Try include geshi
@include("geshi/geshi.php");
 
// Date formats
define('DATE_NORMAL', 'j M Y, G:i');
if (!defined('DATE_ATOM'))
    define('DATE_ATOM', 'Y-m-d\TH:i:s\Z'); // For PHP4
 
 
//===== Main code ============================================================
 
if (isset($_GET['repo']) && isset($repos[$_GET['repo']])) {
    $id = $_GET['repo'];
    $repo = $GLOBALS['repos'][$id];
 
    if (isset($_GET['inventory']))
        show_patches($id, $repo);
    else if (isset($_GET['patch']))
        show_patch($id, $repo);
    else if (isset($_GET['feed']))
        show_feed($id, $repo);
    else
        show_files($id, $repo);
} else {
    list_repos();
}
 
 
//===== Darcs repository access ==============================================
 
/** Information about patch and optionaly it's contents */
class Patch {
    /** @var string patch name */
    var $name;
    /** @var string patch comment */
    var $comment;
    /** @var string patch author */
    var $author;
    /** @var string date when patch was recorded (format: YYYYMMDDhhmmss) */
    var $date;
    /** @var boolean is this inverted patch? */
    var $inverted;
    /** @var string patch identifier */
    var $hash;
    /** @var string patch contents */
    var $contents;
}
 
/** Get list of changes in repository (or part of the list)
 *
 * Darcs stores list of patches in several inventory files. Last changes are
 * in file _darcs/inventory, older are in directory _darcs/inventories/.
 *
 * If inventory name is invalid, contents of main inventory are returned.
 *
 * @param strint $repo path to repository
 * @param string $inventory name of inventory file to read list from
 *                          (empty for _darcs/inventory)
 * @return array with two items: list of changes and name of inventory file
 *               with older changes (Null if there are no older changes)
 */
function darcs_get_changes($repo, $inventory=Null) {
    if (strpos($inventory, '/') !== FALSE) // Check for invalid inventory name
        $inventory = Null;
 
    if (!empty($inventory) &&
        file_exists($repo . '/_darcs/inventories/' . $inventory))
        $filename = $repo . '/_darcs/inventories/' . $inventory;
    else
        $filename = $repo . '/_darcs/inventory';
 
    $file = fopen($filename, 'r');
 
    $patches = array();
    $prev_inventory = Null; // name of inventory file with older changes
    while ((list($patch, $prev) = parse_patch_info($file)) != Null) {
        calculate_patch_hash($patch);
        $patches[] = $patch;
        if ($prev)
            $prev_inventory = $patch->hash;
    }
    return array($patches, $prev_inventory);
}
 
/** Get information about patch
 *
 * @param string $repo path to repository
 * @param string $hash patch hash
 * @return object information about patch and it's contents, or Null on error
 */
function darcs_get_patch($repo, $hash) {
    if (strpos($hash, '/') !== FALSE) // Invalid patch hash
        return Null;
 
    $filepath = $repo . '/_darcs/patches/' . $hash;
    if (!file_exists($filepath))
        return Null;
 
    $file = fopen('compress.zlib://' . $filepath, 'r');
    list($patch, $prev) = parse_patch_info($file);
    $patch->hash = $hash;
 
    // Read patch contents
    while (fgetc($file) != "{");
    $patch->contents = "{";
    while (!feof($file)) $patch->contents .= fgets($file);
 
    return $patch;
}
 
/** Parse patch information in Darcs inventory or patch file.
 *
 * Function also checks if there is inventory file with older changes. If so,
 * it has the same name as hash of this patch.
 *
 * @param resource $file opened file pointer
 * @return array with two items: patch information and boolean variable which
 *               informs, if there is inventory file with older changes.
 */
function parse_patch_info($file) {
    // Skip everything before information about patch
    $skiped = '';
    while ((($c = fgetc($file)) != '[') and (!feof($file)))
        $skiped .= $c;
    // Check if there is inventory file with older changes
    if ($skiped == "Starting with tag:\n")
        $prev = True;
    else
        $prev = False;
 
    if (feof($file))
        return Null; // There are no more patches
 
    // Read patch information
    $patch = new Patch();
 
    $patch->name = '';
    while (($c = fgetc($file)) != "\n")
        $patch->name .= $c;
 
    $patch->author = '';
    while (($c = fgetc($file)) != '*')
        $patch->author .= $c;
 
    $patch->inverted = (fgetc($file) == '-');
    $patch->date = fread($file, 14);
 
    $patch->comment = '';
    if (($c = fgetc($file)) == "\n") {
        while (fgetc($file) == ' ') {
            while(($c = fgetc($file)) != "\n")
                $patch->comment .= $c;
            $patch->comment .= "\n";
        }
        $patch->comment = substr($patch->comment, 0, -1); // Remove last LF
    }
 
    return array($patch, $prev);
}
 
/** Calculate hash, which identifies patch
 *
 * @param object $patch
 */
function calculate_patch_hash(&$patch) {
    $author_hash = substr(sha1($patch->author), 0, 5);
    $hash = sha1($patch->name . $patch->author . $patch->date .
        str_replace("\n", '', $patch->comment) . ($patch->inverted?'t':'f'));
    $patch->hash = "$patch->date-$author_hash-$hash.gz";
}
 
 
//===== Application parts -- controllers =====================================
 
function list_repos() {
    global $repos;
    repos_view($repos);
}
 
/** Show file or directory stored in repository */
function show_files($id, $repo) {
    if (isset($_GET['file']))
        $filename = $_GET['file'];
    else
        $filename = '/';
    $real_path = realpath($repo['dir'] . $filename);
    $repo_real_path = realpath($repo['dir']);
 
    // Check if file exists and if it is inside repository
    if (strncmp($real_path, $repo_real_path, strlen($repo_real_path)) != 0
            || !file_exists($real_path)) {
        $filename = '/';
        $real_path = $repo_real_path;
    }
 
    if (is_dir($real_path))
        show_directory($id, $repo, $filename, $real_path);
    else
        show_file($id, $repo, $filename, $real_path);
}
 
/** Show directory */
function show_directory($id, $repo, $dirname, $real_path) {
    $dir = opendir($real_path);
    $dirs = array();  // List of directories
    $files = array(); // List of files
    while (($file = readdir($dir)) !== FALSE) {
        if (!in_array($file, array('.', '..', '_darcs'))) {
            if ($dirname == '/')
                $path = '/' . $file;
            else
                $path = $dirname . '/' . $file;
            $name = $file;
 
            if (is_dir($real_path . '/' . $file)) {
                $name .= '/';
                $dirs[$name] = $path;
            } else {
                $files[$name] = $path;
            }
        }
    }
    if ($dirname != '/')
        $dirs['..'] = dirname($filename);
 
    asort($dirs);
    asort($files);
    directory_view($id, $repo, $dirname, $dirs + $files);
}
 
/** Show file */
function show_file($id, $repo, $filename, $real_path) {
    global $binaries;
    $extension = strtolower(pathinfo($real_path, PATHINFO_EXTENSION));
 
    if (in_array($extension, $binaries)) {
        binary_file_view($filename, $real_path);
    } else {
        $text = file_get_contents($real_path);
        file_view($id, $repo, $filename, $extension, $text);
    }
}
 
/** Show list of patches */
function show_patches($id, $repo) {
    $inventory = $_GET['inventory'];
    list($patches, $prev_inventory) =
        darcs_get_changes($repo['dir'], $inventory);
    patches_view($id, $repo, $patches, $prev_inventory);
}
 
/** Show contents of patch */
function show_patch($id, $repo) {
    $hash = $_GET['patch'];
    $patch = darcs_get_patch($repo['dir'], $hash);
    if ($patch === Null)
        show_parches($id, $repo);
    patch_view($id, $repo, $patch);
}
 
/** Show Atom feed */
function show_feed($id, $repo) {
    $num = 15; // Number of patches in feed
    $patches = array();
    $prev_inventory = Null;
    do { // We need at least $num patches
        list($p, $prev_inventory) =
                darcs_get_changes($repo['dir'], $prev_inventory);
        $patches = array_merge($patches, array_reverse($p));
    } while ((count($patches) < $num) && !empty($prev_inventory));
    // Strip if there is more than $num patches
    $last_changes = array_slice($patches, 0, $num);
    feed_view($id, $repo, $last_changes);
}
 
 
//===== View =================================================================
 
function format_date($date, $format=DATE_NORMAL) {
    $timestamp = mktime(
        substr($date, 8, 2), substr($date, 10, 2), substr($date, 12, 2),
        substr($date, 4, 2), substr($date, 6, 2), substr($date, 0, 4));
    return date($format, $timestamp);
}
 
/** Print hypertext link (anchor) to part of phpDarcsView
 *
 * @param string $text link text
 * @param string $repo repository identifier
 * @param string $param name of additional GET parameter
 * @param string $value value of additional GET parameter
 * @param string $cl value of link class atribute
 */
function a($text, $repo, $param=Null, $value=Null, $cl=Null) {
    $link = '<a href="?repo=' . $repo;
    if (!empty($param))
        $link .= '&amp;' . $param;
    if (!empty($value))
        $link .= '=' . $value;
    $link .= '"';
    if (!empty($cl))
        $link .= ' class="' . $cl .'"';
    $link .= '>' . $text . '</a>';
    echo $link;
}
 
/** Syntax highlight using FSHL
 *
 * If FSHL is not installed, text is returned unchanged
 *
 * @param string $text text which should be highlighted
 * @param string $extension extension of highlighted file
 */
function highlight($text, $extension) {
    global $languages;
    if (defined('FSHL_PARSER_VERSION')) { // If FSHL is installed
        if (isset($languages) && array_key_exists($extension, $languages))
            $lang = $languages[$extension];
        else
            $lang = 'SAFE';
 
        $parser = new fshlParser('HTML_UTF8', P_TAB_INDENT | P_LINE_COUNTER);
        return "<pre>" . $parser->highlightString($lang, $text) . "</pre>";
    }
    else if (defined('GESHI_VERSION')) { // If FSHL is installed
        global $languages;
        $lang = $languages[$extension];
        $parser =& new GeSHi($text, $lang);
        return $parser->parse_code();
    }
    else
      return "<pre>" . htmlspecialchars($text) . "</pre>";
}
 
/** Header */
function header_element($id=Null, $repo=Null) {
?>
<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link href="phpdarcsview.css" type="text/css" rel="stylesheet" />
    <?php if ($id !== Null): ?>
        <link rel="alternate" type="application/atom+xml"
            title="<?php echo $repo['title'] ?> repository Atom feed"
            href="?repo=<?php echo $id ?>&amp;feed" />
    <?php endif; ?>
    <meta name="generator" content="phpDarcsView" />
    <?php
    if ($repo != NULL)
      echo "<title>" . $repo['title'] . "</title>";
    else
      echo "<title>phpDarcsView</title>";
    ?>
</head>
<body>
<?php
}
 
/** Footer */
function footer_element() {
?>
<hr />
<p id="footer">
	Powered by:
        <a href="<?php echo HOMEPAGE ?>">phpDarcsView</a> v<?php echo VERSION ?>,
        <a href="http://www.darcs.net/">Darcs</a>,
        <a href="http://www.sourceforge.net/">SourceForge</a>
</p>
</body>
</html>
<?php
}
 
/** Repository header */
function repository_element($id, $repo, $view) {
?>
<div id="repos-link"><a href=".">&laquo; List of repositories</a></div>
<h1><?php echo $repo['title'] ?></h1>
<p id="repo-description"><?php echo $repo['description'] ?></p>
<p id="repo-url">Repository URL: <em><a href='<?php echo $repo['url'] ?>' title='Go to repository'><?php echo $repo['url'] ?></a></em></p>
<p id="repo-url">Darcs command: <span style='font-family: monospace;'>$ darcs get <?php echo $repo['url'] ?></span></p>
<div id="views">
<h3>Show:</h3>
<ul>
    <li><?php a('Files', $id, '', '', ($view=='files')?'active':'') ?></li>
    <li><?php a('Patches', $id, 'inventory', '',
                ($view=='patches')?'active':'') ?></li>
</ul>
</div>
<hr />
<?php
}
 
/** View for list of repositories */
function repos_view($repos) {
    header_element();
?>
<h1>Repositories</h1>
<ul>
    <?php foreach ($repos as $id => $repo): ?>
        <li><?php a($repo['title'], $id) ?> &ndash;
            <?php echo $repo['description'] ?>
        </li>
    <?php endforeach ?>
</ul>
<?php
    footer_element();
}
 
/** Directory view */
function directory_view($id, $repo, $dirname, $files) {
    header_element($id, $repo);
    repository_element($id, $repo, 'files');
?>
<h2>Directory: <?php echo $dirname ?></h2>
<ul class="directory">
    <?php if (is_array($files)) foreach ($files as $name => $path):?>
        <li><?php a($name, $id, 'file', $path);?></li>
    <?php endforeach;?>
</ul>
<?php
    footer_element();
}
 
/** File view */
function file_view($id, $repo, $filename, $extension, $contents) {
    header_element($id, $repo);
    repository_element($id, $repo, 'files');
?>
<h2>File: <?php echo $filename ?></h2>
<?php echo highlight($contents, $extension) ?>
<?php
    footer_element();
}
 
/** Binary file view */
function binary_file_view($filename, $real_path) {
    $name = basename($filename);
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $name . '"');
    readfile($real_path);
}
 
/** View for list of patches */
function patches_view($id, $repo, $patches, $prev_inventory) {
    header_element($id, $repo);
    repository_element($id, $repo, 'patches');
?>
<h2>Patches</h2>
<table class="patches">
<tr><th>Name</th><th>Author</th><th>Date</th></tr>
    <?php foreach (array_reverse($patches) as $patch):?><tr>
    <td><?php
        $name = '';
        if ($patch->inverted)
            $name .= '<strong>UNDO:</strong> ';
        if (strpos($patch->name, 'TAG') === 0) {
            $name .= '<strong>TAG</strong>';
            $name .= htmlspecialchars(substr($patch->name, 3));
        } else
            $name .= htmlspecialchars($patch->name);
        a($name, $id, 'patch', $patch->hash);
    ?></td>
    <td><?php echo strip_tags($patch->author);?></td>
    <td><?php echo format_date($patch->date);?></td>
    </tr><?php endforeach;?>
</table>
<?php
    if ($prev_inventory != Null) {
        echo '<p>';
        a('Previous patches &raquo;', $id, 'inventory', $prev_inventory);
        echo '</p>';
    }
    footer_element();
}
 
/** Patch view */
function patch_view($id, $repo, $patch) {
    header_element($id, $repo);
    repository_element($id, $repo, 'patches');
    // Write patch info
    echo '<h2>'.htmlspecialchars($patch->name).'</h2>';
    echo '<div><em>'.format_date($patch->date).'</em> ';
    echo '<strong>'.strip_tags($patch->author).'</strong></div>';
    echo '<p>'.nl2br(htmlspecialchars($patch->comment)).'</p>';
 
    // Write patch contents
    echo '<pre class="patch">';
    foreach (explode("\n", $patch->contents) as $line) {
        if ($line != '') {
            switch ($line[0]) {
                case '{':
                    $class = 'begin';
                    break;
                case '}':
                    $class = 'end';
                    break;
                case '+':
                    $class = 'added';
                    break;
                case '-':
                    $class = 'removed';
                    break;
                default:
                    $class = 'change';
            }
            echo '<span class="'.$class.'">'.htmlspecialchars($line)."\n</span>";
        }
    }
    echo '</pre>';
    footer_element();
}
 
/** Atom feed view */
function feed_view($id, $repo, $patches) {
    // Get script URI
    $request_uri = $_SERVER['REQUEST_URI'];
    $path = substr($request_uri, 0, strpos($request_uri, '?'));
    $uri = 'http://' . $_SERVER['HTTP_HOST'] . $path;
 
    $last = $patches[0];
 
    header('Content-Type: application/atom+xml');
    echo '<?xml version="1.0" encoding="utf-8"?>';
?>
 
<feed xmlns="http://www.w3.org/2005/Atom">
    <generator uri="<?php echo HOMEPAGE ?>" version="<?php echo VERSION ?>">
        phpDarcsView
    </generator>
    <id><?php echo "$uri?repo=$id" ?></id>
    <title><?php echo $repo['title'] ?> darcs repository</title>
    <subtitle><?php echo $repo['description'] ?></subtitle>
    <link rel="self" href="<?php echo "$uri?repo=$id&amp;feed" ?>" />
    <updated><?php echo format_date($last->date, DATE_ATOM) ?></updated>
 
    <?php foreach ($patches as $patch): ?><entry>
        <id><?php echo "$uri?repo=$id&amp;patch={$patch->hash}" ?></id>
        <title><?php echo htmlspecialchars($patch->name) ?></title>
        <summary><?php
            echo htmlentities($patch->name);
            echo "\n\n";
            echo htmlspecialchars($patch->comment);
        ?></summary>
        <link href="<?php echo "$uri?repo=$id&amp;patch={$patch->hash}" ?>" />
        <updated><?php echo format_date($patch->date, DATE_ATOM) ?></updated>
        <author><name><?php echo strip_tags($patch->author) ?></name></author>
    </entry><?php endforeach; ?>
 
</feed>
<?php
}
?>