Other Interests:
  • Biologically inspired computing (Genetic Programming, Neural Nets, etc.)
  • Astronomy; mostly reading, I'm no amateur astronomer
  • Physics
I've installed Wikka to augment our company intranet, and in the process I've made a number of both superficial and functional changes to the code. Many of the changes I've made work only within my company intranet environment (auto-linking of java class names to our javadocs, for instance), but I've tried to post general changes here -- for instance, the SpellcheckHack.

Lately (as of early 2009) I've done a lot of work with our internal wiki to make it more usable within our company. Some of the changes I've made include page categorization including search, Ajax-y menus (used for categories, among other things), and use of script.aculo.us. Unfortunately our internal version is now significantly different from the main dev branch and we have several tool / library dependencies which make posting the changes back here difficult. I intend to post some of the more straightforward ones in the next few months. (We have a googlebox in-house which we use for search, and I'm using ExtJS for our Ajax stuff.)

I hope that in the next year or two (regardless of today's date!) we'll see functional, robust WYSIWYG editors for wikis which will make them even more accessible to common folk, and that will be a good day -- especially for users supporting wiki use in a corporate environment such as myself.

Thank you for a great wiki engine!

Fortune Action

First off: the latest iteration of my {{fortune}} action:


    $location = "SOME DEFAULT";    // for example: http://www.textfiles.com/humor/TAGLINES/random.txt

    if (!$vars["location"])
            $lines = file ($location);
        $location = $vars["location"];

        if ("http" == substr ($location, 0, 4))
            $lines = file ($location);
        else if ($this->IsWikiName ($location))
            if (!$this->ExistsPage ($location))
                echo "<em>The specified page does not exist.</em>";

            $page = $this->LoadPage ($location);
            if ($page)
                $line = 0;
                $pagelines = explode ("\n", $page["body"]);

                for ($i = 0; $i < count ($pagelines); $i++)
                    if (preg_match ("/[ ~]*-(.*)/",$pagelines[$i],$matches))
                        $lines[$line++] = $matches[1];

    echo $lines[array_rand ($lines)];

To use, save as fortune.php in the actions directory.

Usage: {{fortune location="URL|WikiPage"}}

Grab Table as .csv File

One of the things users in my intranet have wanted to do is export tables to .csv files. We're based on the code line with older simple tables (the version sans formatting) added. Because I've got a personal wiki which uses a build of, I'll try to put this together for the more modern version of SimpleTables as well.

SimpleTables isn't in (looks like it's targetted for 1.1.7), this version will be ported to rev 761 of the wikka formatter.

Here's how it works: Tables are formatted as normal, only we add a "Grab" button to each when we close a table. Each table in a page is assigned a unique number, so when the Grab action submits to the grabtable formatter, we know which table to convert and send as a .csv file. This method is easier than trying to adapt grabcode wholesale, as grabbing each cell's contents and urlencoding them proved... well, very, very difficult.

To start, you need to add a page handler, which I've called grabtable.php:
 * Download a table as a csv file.
 * When called by a grab button, forces the download of the associated table.
 * @package Handlers
 * @name    grabtable
 * @author  SpectreMoo
 * @version 0.1
 * @since

// i18n strings
define('ERROR_NO_CODE', 'Sorry, there is no table to download.');

// defaults
define('DEFAULT_FILENAME', 'table.csv'); # default name for code blocks
define('FILE_EXTENSION', '.csv'); # extension appended to code block name

if (!function_exists("getTableCSV"))
    function getTableCSV ($text, $tablenumber)
        //echo "\ntext: ".$text.", number: ".$tablenumber;
        preg_match_all('/\n(\n\|\|[^\n]+)+/', $text, $tables);
        $table = $tables[0][$tablenumber];
        // Strip out table formating, make it CSV
        $csv = preg_replace ("/^\n*\|\|/", "", $table);
        $csv = preg_replace ("/s*\n\|\|/", "\n", $csv);
        $csv = preg_replace ("/\|\|\s*\n/", "\n", $csv);
        $csv = preg_replace ("/\|\|\s*$/", "", $csv);
        $csv = preg_replace ("/\|\|/", ",", $csv);
        // Remove common wikka formatting
        $csv = preg_replace ("/@@|##|\*\*|__/", "", $csv);      # Add any others your users might use
        return $csv;

// initialize variables
$table = '';
$filename = '';

// check if grabtable is allowed
if ($this->GetConfigValue('grabtable_button') == 1) {

    //get URL parameters
    $table = getTableCSV ($this->page['body'], $_POST['tablenumber']);
    // TODO: use central regex library for filename validation
    $filename = (isset($_POST['filename']) && preg_match('/\w[-.\w]*/', $_POST['filename']))? urldecode($_POST['filename']).FILE_EXTENSION : DEFAULT_FILENAME;

    //set HTTP headers
    header('Content-type: text/plain');
    header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT'); //TODO: check for consistency with server time format
    header('Content-Length: '.strlen($table));
    header('Content-Description: '.$filename.' Download Data');
    header('Pragma: no-cache');
    header('Content-Disposition: attachment; filename="'.$filename.'"');

    //print code block
    echo $table;
} else
    echo ERROR_NO_CODE;

Anyone familiar with grabcode.php will see the obvious similarities to the above code, I adapted that handler for my needs. I didn't break the getTableCSV function apart into a formatter; this inline style works and I'm not sure it makes sense to separate the two functions.

Next, add the following Wakka.class.php after the grabcode block (search for "// grabcode page handler"):

        // grabtable page handler
        elseif ($this->method == "grabtable")

Modifying the wakka page formatter is a bit trickier, add the following:

        static $table_count = 0;   # near line 148 of rev 761 wakka.php, in the static declarations

# This block replaces the existing block in the $trigger_table == 1 section:
            else if ($thing != "")
                $trigger_table = 0;
                $output = '';
                // display grab button if option is set in the config file
                if ($wakka->config['grabtable_button'] == '1')
                    $output .= $wakka->FormOpen("grabtable");
                    // build form
                    $output .= '<input type="submit" class="grabcode" name="save" value="'.GRABCODE_BUTTON_VALUE.'" title="'.rtrim(sprintf(GRABCODE_BUTTON_TITLE, $valid_filename)).'" />';
                    $output .= '<input type="hidden" name="filename" value="'.urlencode($valid_filename).'" />';
                    $output .= '<input type="hidden" name="tablenumber" value="'.($table_count++).'" />';
                    $output .= $wakka->FormClose();
                return "</table>\n".$output.wakka2callback($things);

wikka.config.php gets:

    "grabtable_button" => "1",

The same can be added to wikka.php. Also modify the following line in wikka.php:

if (!preg_match("/(xml|raw|mm|grabcode|grabtable)$/", $method))

...and you should now be able to download table.csv files for tables in the same way you'd grab code to codesnippet.txt.

Consider this beta for now, especially given the fact I'm working on a modified version of Wikka. I'll post this to a separate page once I've ported it to

Suggestions, comments, and questions are appreciated!

Moved spellchecking to SpellcheckHack.

Valid XHTML :: Valid CSS: :: Powered by WikkaWiki