Difference between revisions of "Xmlwiki.php"

From Organic Design wiki
Line 6: Line 6:
 
# Maybe only use XML transforms
 
# Maybe only use XML transforms
 
# - use PHP via processing instructions
 
# - use PHP via processing instructions
 
# AdminPage should be moved out to a separate file again
 
# - so that hack can still be unhooked even if it has fatal errors
 
  
 
# DTD's
 
# DTD's
Line 44: Line 41:
 
# - if saving, do publishing
 
# - if saving, do publishing
 
# - render output & messages
 
# - render output & messages
# TRANSFORM
+
# ARTICLE FUNCTIONS
# - if article is an object,
+
# - get article content
#   - process transforms if doctype is one of ours,
+
# - get article type
#   - validate and apply XSLT if not
+
# - domificate article
# - apply geshi on article
+
# - transform article
 
# TRANSFORMS
 
# TRANSFORMS
 
# - document transforms
 
# - document transforms
Line 55: Line 52:
 
# - database table transforms
 
# - database table transforms
 
# - transform object to php-nav-tree
 
# - transform object to php-nav-tree
 +
# - geshi syntax highlighting
 
# FUNCTIONS
 
# FUNCTIONS
# - get article
 
 
# - etxract hrefs from text
 
# - etxract hrefs from text
 
# - message/log
 
# - message/log
# - geshi syntax highlighting
 
# ADMIN
 
# - enable/disable patch
 
  
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
Line 67: Line 61:
  
 
# If xmlwiki.php requested directly, return admin page
 
# If xmlwiki.php requested directly, return admin page
if (eregi("xmlwiki\\.php", $_SERVER['SCRIPT_NAME'])) xwAdminPage();
+
if (!eregi("index\\.php", $_SERVER['SCRIPT_NAME'])) die('xmlwiki.php must be included from index.php!');
else {
 
  
# Geshi syntax highlighting is called from xmlWiki and should be removed from LocalSettings.php
+
# Geshi syntax highlighting is now called from xmlWiki and should be removed from LocalSettings.php
require_once("extensions/GeshiSyntaxHighlight.php");
+
require_once("extensions/GeshiSyntaxHighlight.php");
  
# Otherwise set up xmlWiki global environment ready for input and output processing
+
# Otherwise set up xmlWiki global environment ready for input and output processing
$xwMessages = array();
+
$xwMessages = array();
$xwArticleCache = array();
+
$xwArticleCache = array();
$xwPostTransforms = array('geshi', 'default');
+
$xwPostTransforms = array('geshi', 'default');
  
$xwArticle = $wgArticle->getContent(false);
+
$xwEdit = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'edit');
$xwArticleTitle = $wgTitle->getPrefixedURL();
+
$xwView = (!isset($_REQUEST['action']) || (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'view')));
 +
$xwRaw = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'raw');
 +
$xwPreview = isset($_REQUEST['wpPreview']);
 +
$xwSave = isset($_REQUEST['wpSave']);
 +
$xwUserLinks = array();
 +
$xwArticleLinks = array();
  
$xwEdit = isset($_REQUEST['action'])&&($_REQUEST['action'] == 'edit');
+
$xwArticleTitle = $wgTitle->getPrefixedURL();
$xwView = (!isset($_REQUEST['action'])||(isset($_REQUEST['action'])&&($_REQUEST['action'] == 'view')));
+
$xwArticle = $wgArticle->getContent(false);
$xwRaw = isset($_REQUEST['action'])&&($_REQUEST['action'] == 'raw');
+
$xwArticleMeta = xwArticleContent("xmlwiki:$wgTitle");
$xwPreview = isset($_REQUEST['wpPreview']);  
 
$xwSave = isset($_REQUEST['wpSave']);
 
$xwUserLinks = array();
 
$xwArticleLinks = array();
 
  
$xwUserName = $wgUser->mName;
+
$xwUserName = $wgUser->mName;
$xwUserArticle = xwArticleContent("User:$xwUserName");
+
$xwUserMeta = xwArticleContent("xmlwiki:$xwUserName");
$xwReadable = true;
 
$xwWritable = true;
 
  
# Try to convert articles from XML to a DOM object
+
$xwSecurity = xwArticleContent("xmlwiki:Security"); # holds group membership info
xwDomificateArticle($xwArticle);
+
$xwReadable = true;
xwDomificateArticle($xwUserArticle);
+
$xwWritable = true;
  
# If successfully converted, get perms (?)
+
# Try to convert articles from XML to a DOM object
if (is_object($xwArticle)) {
+
xwDomificateArticle($xwArticle);
$xwUserGroups[] = $xwUserName;
+
xwDomificateArticle($xwArticleMeta);
$xwReadable = null;
+
xwDomificateArticle($xwUserMeta);
$xwWritable = null;
+
 
}
+
# If successfully converted, get perms (?)
 +
if (is_object($xwArticle)) {
 +
$xwUserGroups[] = $xwUserName;
 +
 +
# can't write to xmlwiki:security unless admin
  
 +
 +
$xwReadable = null;
 +
$xwWritable = null;
 
}
 
}
  
Line 116: Line 115:
 
function xwProcessInput() {
 
function xwProcessInput() {
  
global $xwArticle;
+
global $xwArticle, $xwArticleTitle;
global $xwArticleTitle;
+
global $xwReadable, $xwWritable, $xwMessages;
 
 
 
global $xwSave;
 
global $xwSave;
 
global $xwReadable;
 
global $xwWritable;
 
global $xwMessages;
 
  
 
# If article is being updated,
 
# If article is being updated,
Line 140: Line 134:
  
 
global $wgOut;
 
global $wgOut;
+
global $xwArticle, $xwArticleTitle;
global $xwArticle;
+
global $xwView, $xwPreview, $xwSave, $xwRaw, $xwUserLinks, $xwArticleLinks;
global $xwArticleTitle;
+
global $xwUserName, $xwUserMeta, $xwReadable, $xwWritable, $xwMessages;
 
 
global $xwView;
 
global $xwPreview;
 
global $xwSave;
 
global $xwRaw;
 
global $xwUserLinks;
 
global $xwArticleLinks;
 
 
 
global $xwUserName;
 
global $xwUserArticle;
 
global $xwReadable;
 
global $xwWritable;
 
global $xwMessages;
 
  
 
# First trap Wiki output and extract content and links (should use $ver like Admin Page)
 
# First trap Wiki output and extract content and links (should use $ver like Admin Page)
 
ob_start();
 
ob_start();
 
$wgOut->output();
 
$wgOut->output();
$out = ob_get_clean();
+
$out = ob_get_contents();
$xwUserLinks = preg_match('/id="p-personal"(.+?)<\/div>/s', $out, $m) ? xwExtractHrefs($m[1]) : array();
+
$xwUserLinks = preg_match('/id="p-personal"(.+?)<\/div>/s', $out, $m) ? xwExtractHrefs($m[1]) : array();
$xwArticleLinks = preg_match('/id="p-cactions"(.+?)<\/div>/s', $out, $m) ? xwExtractHrefs($m[1]) : array();
+
$xwArticleLinks = preg_match('/id="p-cactions"(.+?)<\/div>/s', $out, $m) ? xwExtractHrefs($m[1]) : array();
 +
ob_end_clean();
  
 
# Security
 
# Security
Line 218: Line 200:
  
 
# Add messages to output unless 'raw' requested
 
# Add messages to output unless 'raw' requested
if (!$xwRaw) $out = preg_replace('/<!-- start content -->/', '<!-- start content -->'.join("\n",$xwMessages), $out);
+
if (!$xwRaw) $out = preg_replace('/<!-- start content -->/', '<!-- start content -->'.join("\n", $xwMessages), $out);
  
 
# Render final output and messages
 
# Render final output and messages
Line 226: Line 208:
  
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
# TRANSFORM
+
# ARTICLE FUNCTIONS
 +
 
 +
# Retreive wiki-article
 +
function xwArticleContent($articleTitle) {
 +
 
 +
# Return with results if already cached
 +
global $xwArticleCache;
 +
if (isset($xwArticleCache[$articleTitle]))
 +
return $xwArticleCache[$articleTitle];
 +
 
 +
# Get wiki article content (or use global if no article passed)
 +
if ($article = new Article(Title::newFromText($articleTitle)))
 +
$article = $article->getContent(false);
 +
 
 +
if ($article == '(There is currently no text in this page)')
 +
return false; # Return without caching
 +
 
 +
if ($articleTitle) $xwArticleCache[$articleTitle] = $article;
 +
return $article;
 +
}
 +
 
 +
# Decide kind of article from content and title
 +
function xwArticleType($title, $article) {
 +
 
 +
# Name based matches
 +
if (ereg('\\.php$', $title)) return 'php';
 +
if (ereg('\\.css$', $title)) return 'css';
 +
if (ereg('\\.as$', $title)) return 'actionscript';
 +
if (ereg('\\.py$', $title)) return 'python';
 +
if (ereg('\\.java$', $title)) return 'java';
 +
if (ereg('\\.(cpp)|(h)$', $title)) return 'cpp';
 +
if (ereg('\\.js$', $title)) return 'javascript';
 +
if (ereg('\\.css$', $title)) return 'css';
 +
 
 +
# Content based matches
 +
if (preg_match('/^<\\?xml.+?\\?>\\s*<xsl:stylesheet/', $article)) return 'xslt';
 +
if (ereg('^<\\?xml', $article)) return 'xml';
 +
if (ereg('^<\\?html', $article)) return 'html4strict';
 +
if (ereg('^#![/a-zA-Z0-9]+\\/perl', $article)) return 'perl';
 +
if (ereg('^#![/a-zA-Z0-9]+sh', $article)) return 'bash';
 +
}
 +
 
 +
# Convert passed article to a DOM object
 +
# - Article is unchanged if not valid XML
 +
function xwDomificateArticle(&$article) {
 +
ob_start();
 +
if ($dom = domxml_open_mem($article)) $article = $dom; else {
 +
# Could not convert, extract error messages from output
 +
xwMessage("Domification failed:", 'red');
 +
$err = preg_replace("/<.+?>/", "", ob_get_contents());
 +
$err = preg_replace('/ in .+? on line.+?[0-9]+/', '', $err);
 +
$err = preg_replace('/Warning.+?\\(\\): /', '', $err);
 +
foreach (split("\n", $err) as $msg) if ($msg) xwMessage($msg, 'red');
 +
}
 +
ob_end_clean();
 +
}
  
 
# Transform an article object
 
# Transform an article object
Line 233: Line 270:
 
function xwTransformArticle($article = false) {
 
function xwTransformArticle($article = false) {
  
global $xwArticle;
+
global $xwArticle, $xwRaw, $xwPostTransforms;
global $xwRaw;
 
global $xwPostTransforms;
 
  
 
# Use global current article if none passed
 
# Use global current article if none passed
Line 243: Line 278:
 
 
 
# Get doc info
 
# Get doc info
$docType = $article->doctype();
+
$aType = $article->doctype();
$docType = $docType->name;
+
$aType = $aType->name;
$docRoot = $article->document_element();  
+
$aRoot = $article->document_element();  
xwMessage("Transforming doctype \"$docType\"");
+
xwMessage("Transforming doctype \"$aType\"");
  
# If it's one of ours,
+
# If it's one of ours, loop thru transforms applying each
if (ereg('^xmlwiki:', $docType)) {
+
# - the first transforms adjust the document while its still a DOM object
# Get the documents transforms
+
# - but one of them will transform it to text,
# - the first transforms adjust the document while its still a DOM object
+
# - it may then have further text-based transforms
# - but at some point it would normally transform to text,
+
if (ereg('^xmlwiki:', $aType)) {
# - it may then have some further text-based transforms
 
 
xwMessage('Applying object\'s transforms...');
 
xwMessage('Applying object\'s transforms...');
foreach ($docRoot->get_elements_by_tagname('transform') as $tElement) {
+
foreach ($aRoot->get_elements_by_tagname('transform') as $tElement) {
# Get transform name
 
 
$tName = $tElement->get_content();
 
$tName = $tElement->get_content();
# If name(), try to execute from a local function declaration
 
# - probably this is not needed soon, only accept article-transforms
 
 
if (ereg('^(.+)\\(\\)$', $tName, $m)) {
 
if (ereg('^(.+)\\(\\)$', $tName, $m)) {
 +
# Transform is "name()" format, try to execute from a local function declaration
 +
# - probably this is not needed soon, only accept article-transforms
 
$tName = $m[1];
 
$tName = $m[1];
 
if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult;
 
if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult;
Line 266: Line 299:
 
}
 
}
 
else {
 
else {
# Transform is not name(), so try getting transform-article-object
+
# Get transform-article and apply to $article
 
if ($tText = xwArticleContent($tName)) {
 
if ($tText = xwArticleContent($tName)) {
 +
# Get transform type
 
$tType = xwArticleType($tName, $tText);
 
$tType = xwArticleType($tName, $tText);
 
if ($tType == 'php') {
 
if ($tType == 'php') {
 +
# php, check perms and execute (trapping errors for reporting - unless fatal)
 +
if ($perms_check_out_ok) {
 +
ob_start();
 +
eval($tText);
 +
$err = preg_replace("/<.+?>/", "", ob_get_contents());
 +
foreach (split("\n", $err) as $msg) if ($msg) xwMessage($msg, 'red');
 +
ob_end_clean();
 +
}
 
}
 
}
 
elseif ($tType == 'xslt') {
 
elseif ($tType == 'xslt') {
 +
# - should probably trap output same as domify
 +
$tObject = domxml_xslt_stylesheet($tText);
 +
$tResult = $tObject->process($article);
 +
# If output-method is html, use XSLT's dump_mem to create an html-string
 +
if (preg_match('/<xsl:output +?method *= *"html"/i', $tText)) {
 +
xwMessage('htmlifying');
 +
$tResult = $tObject->result_dump_mem($tResult);
 +
}
 +
if ($tResult) $article = $tResult;
 
}
 
}
 
elseif ($tType == 'css') {
 
elseif ($tType == 'css') {
 +
# include CSS ref in content
 
}
 
}
 
}
 
}
Line 295: Line 347:
 
# Apply post transforms unless 'raw' requested
 
# Apply post transforms unless 'raw' requested
 
xwMessage('Applying xmlWiki post-transforms...');
 
xwMessage('Applying xmlWiki post-transforms...');
if (!$xwRaw) foreach ($xwPostTransforms as $tName)
+
if (!$xwRaw) {
if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult;
+
foreach ($xwPostTransforms as $tName)
else xwMessage("Unknown post-transform \"$tName\"!", 'red');
+
if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult;
 +
else xwMessage("Unknown post-transform \"$tName\"!", 'red');
 +
}
  
 
return $article;
 
return $article;
Line 311: Line 365:
 
# - this is a special transform, because it affects all pages, even special pages
 
# - this is a special transform, because it affects all pages, even special pages
 
function xwTransform_default($article) {
 
function xwTransform_default($article) {
 +
 +
global $xwArticle, $xwArticleTitle, $xwUserLinks, $xwArticleLinks;
 +
 
xwMessage("Hi defaultTransform here :-)");
 
xwMessage("Hi defaultTransform here :-)");
global $xwArticle;
 
global $xwArticleTitle;
 
global $xwUserLinks;
 
global $xwArticleLinks;
 
 
return "<html><head><title>xmlWiki - $xwArticleTitle</title></head><body bgcolor=#cccccc>
 
return "<html><head><title>xmlWiki - $xwArticleTitle</title></head><body bgcolor=#cccccc>
 
<table border=1 width=100% cellpadding=0 cellspacing=0>
 
<table border=1 width=100% cellpadding=0 cellspacing=0>
Line 335: Line 388:
 
function xwTransform_document_render() {
 
function xwTransform_document_render() {
 
global $xwArticle;
 
global $xwArticle;
$root = $xwArticle->document_element();  
+
$root = $xwArticle->document_element();
 
$body = $root->get_elements_by_tagname('body');  
 
$body = $root->get_elements_by_tagname('body');  
 
if (count($body)) $xwArticle = $body[0]->dump_mem(true);
 
if (count($body)) $xwArticle = $body[0]->dump_mem(true);
Line 356: Line 409:
 
# Converts the in-memory-working image into article content
 
# Converts the in-memory-working image into article content
 
function xwTransform_image_render() {
 
function xwTransform_image_render() {
global $xwArticle;
+
 
global $xwArticleTitle;
+
global $xwArticle, $xwArticleTitle, $xwArticleImage, $xwRaw;
global $xwArticleImage;
+
 
global $xwRaw;
 
 
if ($xwRaw) {
 
if ($xwRaw) {
 
# render article directly if 'raw' request
 
# render article directly if 'raw' request
Line 368: Line 420:
 
header("Content-type: image/png");
 
header("Content-type: image/png");
 
}
 
}
 +
 
else {
 
else {
 
# the raw image is not being requested, render image in-page with info instead
 
# the raw image is not being requested, render image in-page with info instead
Line 402: Line 455:
 
return $article;
 
return $article;
 
 
global $xwArticle;
+
global $xwArticle, $xwArticleTitle;
global $xwArticleTitle;
 
  
 
# First check for entire article highlighting
 
# First check for entire article highlighting
Line 426: Line 478:
  
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
# FUNCTIONS
+
# UTILITY FUNCTIONS
 
 
# Retreive wiki-article
 
function xwArticleContent($articleTitle) {
 
global $xwArticleCache;
 
# Return with results if already cached
 
if (isset($xwArticleCache[$articleTitle]))
 
return $xwArticleCache[$articleTitle];
 
# Get wiki article content (or use global if no article passed)
 
if ($article = new Article(Title::newFromText($articleTitle)))
 
$article = $article->getContent(false);
 
if ($article == '(There is currently no text in this page)')
 
return false; # Return without caching
 
if ($articleTitle) $xwArticleCache[$articleTitle] = $article;
 
return $article;
 
}
 
 
 
# Decide kind of article from content and title
 
function xwArticleType($title, $article) {
 
# Name based matches
 
if (ereg('\\.php$', $title)) return 'php';
 
if (ereg('\\.css$', $title)) return 'css';
 
if (ereg('\\.as$', $title)) return 'actionscript';
 
if (ereg('\\.py$', $title)) return 'python';
 
if (ereg('\\.java$', $title)) return 'java';
 
if (ereg('\\.(cpp)|(h)$', $title)) return 'cpp';
 
if (ereg('\\.js$', $title)) return 'javascript';
 
if (ereg('\\.css$', $title)) return 'css';
 
# Content based matches
 
if (preg_match('/^<\\?xml.+?\\?>\\s*<xsl:stylesheet/', $article)) return 'xslt';
 
if (ereg('^<\\?xml', $article)) return 'xml';
 
if (ereg('^<\\?html', $article)) return 'html4strict';
 
if (ereg('^#![/a-zA-Z0-9]+\\/perl', $article)) return 'perl';
 
if (ereg('^#![/a-zA-Z0-9]+sh', $article)) return 'bash';
 
}
 
 
 
# Convert passed article to a DOM object
 
# - Article is unchanged if not valid XML
 
function xwDomificateArticle(&$article) {
 
ob_start();
 
if ($dom = domxml_open_mem($article)) $article = $dom else {
 
# Could not convert, extract error messages from output
 
xwMessage("Domification faled:", 'red');
 
$err = preg_replace("/<.+?>/", "", ob_get_contents());
 
$err = preg_replace('/ in .+? on line.+?[0-9]+/', '', $err);
 
$err = preg_replace('/Warning.+?\\(\\): /', '', $err);
 
foreach (split("\n", $err) as $msg) if ($msg) xwMessage($msg, 'red');
 
}
 
ob_end_clean();
 
}
 
  
 
# Return a list of the hrefs found in the passed content
 
# Return a list of the hrefs found in the passed content
Line 486: Line 489:
 
function xwMessage($msg, $col = 'blue') {
 
function xwMessage($msg, $col = 'blue') {
 
global $xwMessages;
 
global $xwMessages;
return $xwMessages[] = "<div class=\"xwMessage\"><font color=\"$col\">$msg</font></div>\n";
+
return $xwMessages[] = "<p class=\"xwMessage\"><font color=\"$col\">$msg</font></p>\n";
}
 
 
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ADMIN
 
function xwAdminPage() {
 
 
 
?><html><head><title>xmlWiki Admin Page</title></head><body bgcolor=#cccccc>
 
<table border=0 width=100% cellpadding=0 cellspacing=0>
 
<tr><td align=center><table border=0 width=30%>
 
<tr><td align=center>
 
<a href="./index.php/xmlWiki"><img src="./xmlwiki.jpg" border=0 alt="xmlWiki"></a>
 
<tr><td align=center bgcolor=#003366>
 
<b><font size=5 color=white face=helvetica>xmlWiki Admin Page</font></b>
 
</table></table><br><br><br><br><?
 
 
 
# back up index.php if doesn't already exist
 
if (!file_exists('./index.bak')) {
 
if (!copy('./index.bak', './index.php')) {
 
print xwMessage('Could not back up index.php!', '#d00000');
 
exit;
 
}
 
}
 
if (array_key_exists('enable', $_GET)) {
 
# Replace from backup before enabling
 
if (!copy('./index.bak', './index.php'))
 
print xwMessage('Could not copy index.bak over index.php', '#d00000');
 
require_once('./Version.php');
 
$ver = $wgSoftwareRevision;
 
# Matches for patch-points/MediaWiki versions
 
$requireMatch['1002'] = "/^wfProfileOut\\( *'main-misc-setup' *\\);$/m";
 
$inputMatch['1002'] = "/^wfProfileIn\\( *'main-action' *\\);$/m";
 
$outputMatch['1002'] = "/\\\$wgOut->output\\(\\);/m";
 
# Apply patches
 
if (preg_match($requireMatch[$ver], $file = join('', file('./index.php')))
 
&&  preg_match($inputMatch[$ver], $file)
 
&&  preg_match($outputMatch[$ver], $file)) {
 
$file = preg_replace($requireMatch[$ver], "wfProfileOut( 'main-misc-setup' );\nrequire_once( './xmlwiki.php' );", $file);
 
$file = preg_replace($inputMatch[$ver], "xwProcessInput();\nwfProfileIn( 'main-action' );", $file);
 
$file = preg_replace($outputMatch[$ver], "xwProcessOutput();", $file);
 
if ($handle = fopen('./index.php', 'w')) {
 
fwrite($handle, $file);
 
fclose($handle);
 
} else print xwMessage('Could not open index.php for writing!', '#d00000');
 
} else print xwMessage('Could not match content of index.php script!', '#d00000');
 
}
 
elseif (array_key_exists('disable', $_GET)) {
 
# Replace from backup
 
if (!copy('./index.bak', './index.php'))
 
print xwMessage('Could not copy index.bak over index.php', '#d00000');
 
}
 
# Print current state
 
if (filesize('./index.php') == filesize('./index.bak'))
 
print xwMessage('xmlWiki is currently disabled. [ <a href="./xmlwiki.php?enable">enable</a> ]', '#003366');
 
else print xwMessage('xmlWiki is currently enabled. [ <a href="./xmlwiki.php?disable">disable</a> ]', '#003366');
 
 
 
 
?><br><br><br><table border=0 width=100% cellpadding=0 cellspacing=0>
 
<tr><td align=center colspan=3><table border=0 width=50%>
 
<td align=right valign=top><a href="http://w3c.org/"><img src="./xhtml.jpg" alt="XHTML1.0" border=0 /></a>
 
<td align=right valign=top><a href="http://www.php.net/"><img src="./php.jpg" alt="PHP" border=0 /></a>
 
<td align=right valign=top><a href="http://www.mediawiki.org/"><img src="./mediawiki.jpg" alt="MediaWiki" border=0 /></a>
 
</table></table><br><br></body></html><?
 
 
}
 
}
  
 
?>
 
?>
 
</php>
 
</php>

Revision as of 11:33, 6 June 2005

<php> <?php

  1. xmlWiki - MediaWiki XML Hack
  2. Nad - 2005-05-18
  1. Maybe only use XML transforms
  2. - use PHP via processing instructions
  1. DTD's
  2. - using xmlwiki:document, xmlwiki:image etc is for validation purposes
  3. - xwTransformArticle is generic and cares only for xmlwiki:*
  1. CSS STYLES
  2. the are accessed via http, so their article url can be referred to locally in the html
  3. - maybe make the default request 'raw' and must specifically request 'view'
  4. - the default CSS's should deal with geshi colour/tab etc
  1. LATER:
  2. allow XIncludes to build large docs from others
  3. allow docBook and DSSSL content
  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. SCRIPT CONTENTS
  1. INIT
  2. - do admin and exit if running directly
  3. - include geshi
  4. - get wiki & request environment
  5. - get article (as an object if possible)
  6. - get user-info
  7. - get object-specific info (perms, doc-root, doc-type)
  8. - build env (if action=raw, env is empty)
  9. INPUT
  10. - deny access if !writable
  11. - currently not doing anything else
  12. OUTPUT
  13. - trap MediaWiki output
  14. - extract links (not used yet)
  15. - apply security
  16. - transform for raw, view, preview
  17. - if saving, do publishing
  18. - render output & messages
  19. ARTICLE FUNCTIONS
  20. - get article content
  21. - get article type
  22. - domificate article
  23. - transform article
  24. TRANSFORMS
  25. - document transforms
  26. - image transforms
  27. - layout transforms
  28. - database table transforms
  29. - transform object to php-nav-tree
  30. - geshi syntax highlighting
  31. FUNCTIONS
  32. - etxract hrefs from text
  33. - message/log
  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. INIT
  1. If xmlwiki.php requested directly, return admin page

if (!eregi("index\\.php", $_SERVER['SCRIPT_NAME'])) die('xmlwiki.php must be included from index.php!');

  1. Geshi syntax highlighting is now called from xmlWiki and should be removed from LocalSettings.php

require_once("extensions/GeshiSyntaxHighlight.php");

  1. Otherwise set up xmlWiki global environment ready for input and output processing

$xwMessages = array(); $xwArticleCache = array(); $xwPostTransforms = array('geshi', 'default');

$xwEdit = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'edit'); $xwView = (!isset($_REQUEST['action']) || (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'view'))); $xwRaw = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'raw'); $xwPreview = isset($_REQUEST['wpPreview']); $xwSave = isset($_REQUEST['wpSave']); $xwUserLinks = array(); $xwArticleLinks = array();

$xwArticleTitle = $wgTitle->getPrefixedURL(); $xwArticle = $wgArticle->getContent(false); $xwArticleMeta = xwArticleContent("xmlwiki:$wgTitle");

$xwUserName = $wgUser->mName; $xwUserMeta = xwArticleContent("xmlwiki:$xwUserName");

$xwSecurity = xwArticleContent("xmlwiki:Security"); # holds group membership info $xwReadable = true; $xwWritable = true;

  1. Try to convert articles from XML to a DOM object

xwDomificateArticle($xwArticle); xwDomificateArticle($xwArticleMeta); xwDomificateArticle($xwUserMeta);

  1. If successfully converted, get perms (?)

if (is_object($xwArticle)) { $xwUserGroups[] = $xwUserName;

# can't write to xmlwiki:security unless admin


$xwReadable = null; $xwWritable = null; }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. INPUT
  1. No input processing for now
  2. - used later for custom edit forms (database table article will need it)
  1. Parse and process input from forms

function xwProcessInput() {

global $xwArticle, $xwArticleTitle; global $xwReadable, $xwWritable, $xwMessages; global $xwSave;

# If article is being updated, if ($xwSave) { #global $_POST; #if (!$xwReadable || !$xwWritable) die("I'm sorry Dave, I'm afraid I can't do that."); }

}

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. OUTPUT
  1. Generate new output to send back to browser

function xwProcessOutput() {

global $wgOut; global $xwArticle, $xwArticleTitle; global $xwView, $xwPreview, $xwSave, $xwRaw, $xwUserLinks, $xwArticleLinks; global $xwUserName, $xwUserMeta, $xwReadable, $xwWritable, $xwMessages;

# First trap Wiki output and extract content and links (should use $ver like Admin Page) ob_start(); $wgOut->output(); $out = ob_get_contents(); $xwUserLinks = preg_match('/id="p-personal"(.+?)<\/div>/s', $out, $m) ? xwExtractHrefs($m[1]) : array(); $xwArticleLinks = preg_match('/id="p-cactions"(.+?)<\/div>/s', $out, $m) ? xwExtractHrefs($m[1]) : array(); ob_end_clean();

# Security if (!$xwReadable) { # change output to access denied message if !readable } if (!$xwWritable) { # remove edit link }

# If raw, output consists only of transformed article if ($xwRaw) $out = xwTransformArticle();

# If normal view, article must appear within wiki content elseif ($xwView) { if (preg_match('/^(.+)(.+)(.+)$/ms', $out, $m)) { $out = xwTransformArticle(); } else xwMessage('xmlWiki couldn\'t match view-output!', 'red'); }

# If preview, article must appear in wiki content preserving wiki editing content elseif ($xwPreview) {

if (preg_match('/^(.+.+Remember.+saved!.+?<\\/p>.+?

)(.+?)(<\\/p>.+.+)$/s', $out, $m)) { # We have to transform the content in wikiOutput not the article itself $xwArticle = html_entity_decode($m[2]); xwDomificateArticle($xwArticle); $out = xwTransformArticle(); } else xwMessage('xmlWiki couldn\'t match preview-output!', 'red'); } # If saving, check the publishing list elseif (0&& $xwSave && is_object($xwArticle)) { $transformedArticle = xwTransformArticle(); # Get publish list $publist = $xwDocRoot->get_elements_by_tagname('publish'); if (count($publist)) $publist = $publist[0]->dump_mem(true); $publist = split(',', $publist); # Publish each item foreach ($publist as $url) { if ($FH = fopen($url, 'a')) { fwrite($FH, $transformedArticle); fclose($FH); xwMessage("Article published to $url"); } else xwMessage("Failed to publish to $url", 'red'); } } else { # This is probably a special page # - we'll just extract the content and do only post-default-transform if (preg_match('/^(.+)(.+)(.+)$/ms', $out, $m)) $out = xwTransform_default($m[2]); else xwMessage('xmlWiki couldn\'t match special-output!', 'red'); } # Add messages to output unless 'raw' requested if (!$xwRaw) $out = preg_replace('//', .join("\n", $xwMessages), $out); # Render final output and messages xwMessage('Rendering output'); print $out; }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. ARTICLE FUNCTIONS
  1. Retreive wiki-article

function xwArticleContent($articleTitle) { # Return with results if already cached global $xwArticleCache; if (isset($xwArticleCache[$articleTitle])) return $xwArticleCache[$articleTitle]; # Get wiki article content (or use global if no article passed) if ($article = new Article(Title::newFromText($articleTitle))) $article = $article->getContent(false); if ($article == '(There is currently no text in this page)') return false; # Return without caching if ($articleTitle) $xwArticleCache[$articleTitle] = $article; return $article; }

  1. Decide kind of article from content and title

function xwArticleType($title, $article) { # Name based matches if (ereg('\\.php$', $title)) return 'php'; if (ereg('\\.css$', $title)) return 'css'; if (ereg('\\.as$', $title)) return 'actionscript'; if (ereg('\\.py$', $title)) return 'python'; if (ereg('\\.java$', $title)) return 'java'; if (ereg('\\.(cpp)|(h)$', $title)) return 'cpp'; if (ereg('\\.js$', $title)) return 'javascript'; if (ereg('\\.css$', $title)) return 'css'; # Content based matches if (preg_match('/^<\\?xml.+?\\?>\\s*<xsl:stylesheet/', $article)) return 'xslt'; if (ereg('^<\\?xml', $article)) return 'xml'; if (ereg('^<\\?html', $article)) return 'html4strict'; if (ereg('^#![/a-zA-Z0-9]+\\/perl', $article)) return 'perl'; if (ereg('^#![/a-zA-Z0-9]+sh', $article)) return 'bash'; }

  1. Convert passed article to a DOM object
  2. - Article is unchanged if not valid XML

function xwDomificateArticle(&$article) { ob_start(); if ($dom = domxml_open_mem($article)) $article = $dom; else { # Could not convert, extract error messages from output xwMessage("Domification failed:", 'red'); $err = preg_replace("/<.+?>/", "", ob_get_contents()); $err = preg_replace('/ in .+? on line.+?[0-9]+/', , $err); $err = preg_replace('/Warning.+?\\(\\): /', , $err); foreach (split("\n", $err) as $msg) if ($msg) xwMessage($msg, 'red'); } ob_end_clean(); }

  1. Transform an article object
  2. - non objects (strings) can't be transformed because they can't store any transform properties
  3. - but the environment post-transforms (incl. geshi) will be applied

function xwTransformArticle($article = false) { global $xwArticle, $xwRaw, $xwPostTransforms; # Use global current article if none passed if ($article === false) $article = $xwArticle; if (is_object($article)) { # Get doc info $aType = $article->doctype(); $aType = $aType->name; $aRoot = $article->document_element(); xwMessage("Transforming doctype \"$aType\""); # If it's one of ours, loop thru transforms applying each # - the first transforms adjust the document while its still a DOM object # - but one of them will transform it to text, # - it may then have further text-based transforms if (ereg('^xmlwiki:', $aType)) { xwMessage('Applying object\'s transforms...'); foreach ($aRoot->get_elements_by_tagname('transform') as $tElement) { $tName = $tElement->get_content(); if (ereg('^(.+)\\(\\)$', $tName, $m)) { # Transform is "name()" format, try to execute from a local function declaration # - probably this is not needed soon, only accept article-transforms $tName = $m[1]; if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult; else xwMessage("Unknown transform \"$tName\"!", 'red'); } else { # Get transform-article and apply to $article if ($tText = xwArticleContent($tName)) { # Get transform type $tType = xwArticleType($tName, $tText); if ($tType == 'php') { # php, check perms and execute (trapping errors for reporting - unless fatal) if ($perms_check_out_ok) { ob_start(); eval($tText); $err = preg_replace("/<.+?>/", "", ob_get_contents()); foreach (split("\n", $err) as $msg) if ($msg) xwMessage($msg, 'red'); ob_end_clean(); } } elseif ($tType == 'xslt') { # - should probably trap output same as domify $tObject = domxml_xslt_stylesheet($tText); $tResult = $tObject->process($article); # If output-method is html, use XSLT's dump_mem to create an html-string if (preg_match('/<xsl:output +?method *= *"html"/i', $tText)) { xwMessage('htmlifying'); $tResult = $tObject->result_dump_mem($tResult); } if ($tResult) $article = $tResult; } elseif ($tType == 'css') { # include CSS ref in content } } else xwMessage("No such article \"$tName\"!", 'red'); } } #xwMessage(join(',',$xwTransforms)); # - add php-transform-articles,CSS's & XSLT's to $xwEnv } # If object hasn't been transformed into a string yet (or it isn't one of our kind)... if (!is_string($article)) { xwMessage('Dumping article-object to string'); # validate, report errors # apply stylesheet/XSLT if xml asks for one, # Convert DOM back to XML $article = $article->dump_mem(true); } } # Apply post transforms unless 'raw' requested xwMessage('Applying xmlWiki post-transforms...'); if (!$xwRaw) { foreach ($xwPostTransforms as $tName) if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult; else xwMessage("Unknown post-transform \"$tName\"!", 'red'); } return $article; }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. TRANSFORMS
  3. - these transforms are only temporarily here - they should be articles
  4. - we should use DTD's for each kind: document, image, layout, table
  1. This is the default postTransform which surrounds the page
  2. - most transforms do not reference the global-current-article like this one
  3. - this is a special transform, because it affects all pages, even special pages

function xwTransform_default($article) { global $xwArticle, $xwArticleTitle, $xwUserLinks, $xwArticleLinks; xwMessage("Hi defaultTransform here :-)"); return "

xmlWiki - $xwArticleTitle
".join('
',$xwArticleLinks)."
\"xmlWiki\"
$xwArticleTitle
$article

"; } function xwTransform_document_xxx() { # documents are built from parts, # the parts are a tree in its body # includes 'edit' link for each (like section, and also like table) }

  1. A document object is finalised by transforming it to its body's text

function xwTransform_document_render() { global $xwArticle; $root = $xwArticle->document_element(); $body = $root->get_elements_by_tagname('body'); if (count($body)) $xwArticle = $body[0]->dump_mem(true); } function xwTransform_layout_xxx() { # for layout container style article } function xwTransform_image_xxx() { # object contains properties for loading and transforming an image # image needs no body # Get the global image on which imageTransforms work global $xwArticleImage; }

  1. Converts the in-memory-working image into article content

function xwTransform_image_render() { global $xwArticle, $xwArticleTitle, $xwArticleImage, $xwRaw; if ($xwRaw) { # render article directly if 'raw' request imagePng($xwArticleImage); # Clear article or it will render as xml $xwArticle = ; # set header to image header("Content-type: image/png"); } else { # the raw image is not being requested, render image in-page with info instead $xwArticle = "<img src=\"$xwArticleTitle?action=raw\" alt=\"$xwArticleTitle\"/>"; } }

  1. A table object is an interface to a database table
  2. - this can't be implimented fully until the 'edit' hook is done
  3. - the query result is a list in document body, so any normal document transforms can follow it

function xwTransform_table_xxx() { # - view lists the table/result with links to edit each (like a normal composition-doc) # - also form for create/edit/edlete # - result rows are in document body (just like a content-tree in a document would be) # NODE-LIKE-DB # - if all tables have GUID for pri-key, # - and all have time/duration (or cycle?), # - and we have master list of cols-to-table # - then we could create a meta-query (resulting in pri-key-list of all matches) }

  1. Transform the body content to a xtml/javascript tree

function xwTransform_tree() { # import php-tree-component }

  1. Include Geshi syntax highlighting
  2. - Remember, Geshi should be removed from LocalSettings.php

function xwTransform_geshi($article) { xwMessage("Hi, Geshi here, sorry I'm not currently functional."); return $article; global $xwArticle, $xwArticleTitle; # First check for entire article highlighting # Highlight entire article if xwLang has been set if ($xwLang) { $xwGeshi = new GeSHi($xwArticle, $xwLang, "extensions/geshi/geshi"); $xwGeshi->set_tab_width(4); $geshi->set_keywords_style(1, 'font-weight: 100;', true); #$geshi->set_comments_style #$geshi->set_escape_char_style #$geshi->set_brackets_style #$geshi->set_strings_style #$geshi->set_numbers_style #$geshi->set_methods_style #$geshi->set_symbols_style #$geshi->set_regexps_style #$geshi->set_script_style #$xwArticle = $xwGeshi->parse_code(); } }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. UTILITY FUNCTIONS
  1. Return a list of the hrefs found in the passed content

function xwExtractHrefs($content) { preg_match_all('/(<a.+?<\/a>)/', $content, $match); return $match[1]; }

  1. Add message to queue

function xwMessage($msg, $col = 'blue') { global $xwMessages; return $xwMessages[] = "

$msg

\n";

}

?> </php>