Wiki.php
<?php

class Wiki
{
    function matchWord($word)
    {
        return preg_match('^[A-Z]+[a-z]+[A-Za-z0-9]*$', $word);
    }

    function formatWord($word)
    {
        $word = preg_replace('/([a-z])([A-Z])/', '\1 \2', $word);
        $word = preg_replace('/([a-zA-Z])(\d)/', '\1 \2', $word);
        return $word;
    }

    function process($contents)
    {
        // First, substitute macros
        if (preg_match_all('/{([a-z-]+)\s*([^}]*?)}/s', $contents, $matches)) {
            list($strings, $funcs, $args) = $matches;
            for ($i = 0; $i < sizeof($strings); $i++) {
                $replace = Wiki::runMacro($funcs[$i], $args[$i]);
                $contents = str_replace($strings[$i], $replace, $contents);
            }
        }
        // Finally, add Wiki-links
        if (preg_match_all('#(.*?)\b([A-Z]+[a-z]+[A-Z\d][A-Za-z\d]*)\b(.*?)#s', $contents, $matches)) {
            list(, $pre_text, $nodes, $post_text) = $matches;
            $self = $_SERVER['SCRIPT_NAME'];
            $tmp = '';
            for ($i = 0; $i < sizeof($nodes); $i++) {
                $spaced = Wiki::formatWord($nodes[$i]);
                $replace = "<a href=\"{$self}/{$nodes[$i]}\">$spaced</a>";
                $tmp .= $pre_text[$i] . $replace . $post_text[$i];
            }
            $contents = $tmp;
        }
        return nl2br($contents);
    }

    function nodeExists($node)
    {
        static $cache = array();
        if (isset($cache[$node])) {
            return $cache[$node];
        }
        global $dbh;
        $id = $dbh->getOne('SELECT id FROM nodes WHERE name = ?', $node);
        if ($id !== null) {
            $cache[$node] = $id;
        }
        return (bool)$id;
    }


    function runMacro($func, $arg)
    {
        global $dbh;
        $ret = '';
        switch ($func) {
            case 'list':
                $arg = str_replace('?', '_', $arg);
                $arg = str_replace('*', '%', $arg);
                $sql = "SELECT name FROM nodes WHERE name LIKE '$arg'";
                // catch errors
                $dbh->pushErrorHandling(PEAR_ERROR_RETURN);
                $nodes = $dbh->getCol($sql, 0);
                $dbh->popErrorHandling();
                if (is_array($nodes)) {
                    $ret = implode("\n", $nodes);
                }
                break;
            case 'phpdoc':
                $url = "http://php.net/$arg";
                $ret = "<a href=\"$url\">$arg (php.net)</a>";
                break;
            case 'pear-listall':
                include_once "PEAR/Config.php";
                include_once "PEAR/Remote.php";
                $config = &PEAR_Config::singleton();
                @$remote =& new PEAR_Remote($config);
                @$packages = $remote->call('package.listAll');
                $ret = implode("\n", array_keys($packages));
                break;
            case 'recent':
                include_once "Date.php";
                $arg = (int)$arg;
                $res = $dbh->limitQuery("SELECT name, mtime FROM nodes ORDER BY mtime DESC", 0, $arg);
                $ret = '';
                while ($res->fetchInto($tmp)) {
                    $date = new Date($tmp['mtime']);
                    $ret .= "$tmp[name] " . $date->getDate() . "\n";
                }
                $res->free();
                break;
        }
        return $ret;
    }

    function displayEditor(&$tpl, $node)
    {
        $tpl->loadTemplatefile('edit.tpl');
        if ($node != '__new__') {
            $contents = Wiki::getNodeContents($node);
        }
        if ($contents === null) {
            // new node
            $tpl->setVariable("NewOrEdit", "New");
            $nodename = "new node";
            if ($node == '__new__') {
                $node = "";
            }
            $new = true;
        } else {
            $tpl->setVariable("NewOrEdit", "Edit");
            $nodename = Wiki::formatWord($node);
            $new = false;
        }
        $tpl->setVariable("NodeName", $nodename);
        $tpl->setVariable("PageTitle", "PEAR Wiki Editor: $nodename");
        $form = &Wiki::buildEditForm($node, $new);
        $tpl->setVariable("EditForm", $form->toHtml());
        $tpl->show();
    }

    function displayNode(&$tpl, $node)
    {
        if ($node == 'RootNode') {
            // hack to display a default Root node
            $contents = Wiki::getNodeContents($node);
            if ($contents === null) {
                $contents = '(Empty Root Node)';
            }
        }
        $nodename = Wiki::formatWord($node);
        if (empty($contents)) {
            $contents = Wiki::getNodeContents($node);
        }
        if ($contents === null) {
            $tpl->loadTemplatefile('404.tpl');
        } else {
            $tpl->loadTemplatefile('node.tpl');
            $tpl->setVariable('Contents', Wiki::process($contents));
        }
        $tpl->setVariable('User', $GLOBALS['a']->getUsername());
        $tpl->setVariable('Self', $_SERVER['SCRIPT_NAME']);
        $tpl->setVariable('Version', PEAR_WIKI_VERSION);
        $tpl->setVariable('Node', $node);
        $tpl->setVariable('NodeName', $nodename);
        $tpl->setVariable('PageTitle', "PEAR Wiki: $nodename");
        $tpl->show();
    }

    function getNodeContents($node)
    {
        global $dbh;
        if (empty($dbh)) {
            return null;
        }
        return $dbh->getOne('SELECT content FROM nodes WHERE name = ?',
                            array($node));
    }

    function &buildEditForm($node, $new)
    {
        include_once 'HTML/QuickForm.php';
        $form =& new HTML_QuickForm('editForm', 'POST');
        $form->clearAllTemplates();
        $constants['node'] = $node;
        $defaults['contents'] = Wiki::getNodeContents($node);
        $form->setConstants($constants);
        $form->setDefaults($defaults);
        $form->addData('<table border="0" cellpadding="0" cellspacing="2" bgcolor="#eeeeee">');
        $form->addData('<tr><td colspan="2" align="left">Node:');
        if ($new) {
            $form->addElement('text', 'node', null, 'size="60"');
            $form->addRule('node', 'Must have a node name', 'required', null, 'client');
        } else {
            $form->addElement('hidden', 'node', $node);
            $form->addData("<tt>$node</tt>");
        }
        $form->addData('</td></tr><tr><td align="center" colspan="2">');
        $form->addElement('textarea', 'contents', null, 'cols="60" rows="20" wrap="virtual"');
        $form->addData('</td></tr><tr><td align="center" colspan="2">');
        $form->addElement('submit', 'mode', 'submit');
        $form->addData('</td></tr></table>');
        return $form;
    }

    function storeNode($node, $contents)
    {
        global $dbh, $logger, $a;
        $now = time();
        $sql = 'UPDATE nodes SET content = ?, mtime = ? WHERE name = ?';
        $dbh->query($sql, array($contents, $now, $node));
        if ($dbh->affectedRows() == 0) {
            $sql = 'INSERT INTO nodes VALUES(?,?,?,?)';
            $id = $dbh->nextId('nodes');
            $ret = $dbh->query($sql, array($node, $id, $contents, $now));
            $logger->log($a->getUsername() . " added $node");
        } else {
            $logger->log($a->getUsername() . " modified $node");
        }
        return $ret;
    }

    function deleteNode($node)
    {
        global $dbh, $logger, $a;
        $sql = 'DELETE FROM nodes WHERE name = ?';
        $logger->log($a->getUsername() . " deleted $node");
        return $dbh->query($sql, array($node));
    }
}

?>