Difference between revisions of "Xmlwiki.php"

From Organic Design wiki
Line 2: Line 2:
 
# xmlWiki - MediaWiki XML Hack
 
# xmlWiki - MediaWiki XML Hack
 
# Nad - 2005-05-18
 
# Nad - 2005-05-18
$xwDebug=1;
+
 
 
# INIT
 
# INIT
 
# SECURITY
 
# SECURITY
Line 15: Line 15:
  
 
# TODO:
 
# TODO:
 +
 +
# - STACK: <data> & <view> instead of <transform>
 +
# - <onChange> (publish in OUTPUT-HOOK is to be <publish.php/> and in the onChange list
 +
 
# - TransformArticle:
 
# - TransformArticle:
#  - validate article-properties against xmlwiki:properties.dtd (does PHP4 do this?)
+
#  - VALIDATE: validate article-properties against xmlwiki:properties.dtd (does PHP4 do this?)
#  - could make transform-list into a stack (so transforms can 'push' other transforms onto the list)
+
#  - TRAP: fatal errors in php-transform execution
#  - trap fatal errors in php-transform execution
 
 
#  - remove global and file access from php-transforms (?)
 
#  - remove global and file access from php-transforms (?)
 
# - Undomificate: validation, xslt
 
# - Undomificate: validation, xslt
 
# - Check (later validate) doctype of user:sys (change users pages on site first!)
 
# - Check (later validate) doctype of user:sys (change users pages on site first!)
 
# - Check (later validate) doctype of user/properties on save (still save though)
 
# - Check (later validate) doctype of user/properties on save (still save though)
# Publish:
+
 
# - article-properties - holds article publish-list
+
# BUGS:
# - article-source (dynamic, periodic, on-notify)
+
# - tabReplace doesn't work for /^\t+.+\t.+$/
  
 
# LATER:
 
# LATER:
Line 42: Line 45:
  
 
# System globals
 
# System globals
 +
$xwDebug = false;
 
$xwMessages = array();
 
$xwMessages = array();
 
$xwArticleCache = array();
 
$xwArticleCache = array();
Line 69: Line 73:
 
$xwArticleReadableBy = array('anyone');
 
$xwArticleReadableBy = array('anyone');
 
$xwArticleWritableBy = array('anyone');
 
$xwArticleWritableBy = array('anyone');
 
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# SECURITY
 
# SECURITY
  
# Get user-system-article
+
# Get user-system-article and extract users' groups from it if any
 
if ($xwUserSYS = xwArticleContent("sys:$xwUserName")) xwDomificateArticle($xwUserSYS, "sys:$xwUserName");
 
if ($xwUserSYS = xwArticleContent("sys:$xwUserName")) xwDomificateArticle($xwUserSYS, "sys:$xwUserName");
 +
$xwUserGroups = array_merge($xwUserGroups, xwGetListByTagname($xwUserSYS, 'groups'));
  
# If sys:user exists, get users' group-list
+
# Read user-prefs
if (is_object($xwUserSYS)) {
+
$xwDebug = count($results = xwXPathQuery($xwUserSYS, '//debug')) ? $results[0]->get_content() : false;
$xwRoot = $xwUserSYS->document_element();
 
if (count($xwList = $xwRoot->get_elements_by_tagname('groups')))
 
$xwUserGroups = array_merge($xwUserGroups, split(',', strtolower($xwList[0]->get_content())));
 
}
 
  
 
# Get article-meta-file and extract article-perms
 
# Get article-meta-file and extract article-perms
Line 87: Line 87:
 
else {
 
else {
 
# Get xml:article (self if already xml:*)
 
# Get xml:article (self if already xml:*)
if ($xwIsProperties) $xwArticleProperties = $xwArticle;
+
if ($xwArticleProperties = $xwIsProperties ? $xwArticle : xwArticleContent($id = 'xml:'.ucwords($xwArticleTitle)))
else $xwArticleProperties = xwArticleContent('xml:'.ucwords($xwArticleTitle));
+
xwDomificateArticle($xwArticleProperties, $xwIsProperties ? $xwArticleTitle : $id);
xwDomificateArticle($xwArticleProperties, 'Article-Properties');
 
 
$xwArticleReadableBy = xwGetListByTagname($xwArticleProperties, 'read') + array('anyone');
 
$xwArticleReadableBy = xwGetListByTagname($xwArticleProperties, 'read') + array('anyone');
 
$xwArticleWritableBy = xwGetListByTagname($xwArticleProperties, 'write') + array('anyone');
 
$xwArticleWritableBy = xwGetListByTagname($xwArticleProperties, 'write') + array('anyone');
Line 96: Line 95:
  
 
# Set perms for this request
 
# Set perms for this request
 +
if ($xwDebug) xwMessage('PERMISSIONS:','green');
 
if (in_array('admin', $xwUserGroups)) $xwIsAdmin = $xwReadable = $xwWritable = true;
 
if (in_array('admin', $xwUserGroups)) $xwIsAdmin = $xwReadable = $xwWritable = true;
 
else {
 
else {
Line 103: Line 103:
 
if ($xwDebug) {
 
if ($xwDebug) {
 
xwMessage('Groups: '.join(', ', $xwUserGroups));
 
xwMessage('Groups: '.join(', ', $xwUserGroups));
xwMessage("Readable ($xwReadable): ".join(', ', $xwArticleReadableBy));
+
xwMessage('Readable ('.($xwReadable?'yes':'no').'): '.join(', ', $xwArticleReadableBy));
xwMessage("Writable ($xwWritable): ".join(', ', $xwArticleWritableBy));
+
xwMessage('Writable ('.($xwWritable?'yes':'no').'): '.join(', ', $xwArticleWritableBy));
 
}
 
}
  
Line 122: Line 122:
 
global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwWritable, $_REQUEST;
 
global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwWritable, $_REQUEST;
 
global $xwDebug, $xwSave, $xwEdit, $action, $xwUserName, $wgArticle, $xwIsProperties, $xwIsSystem;
 
global $xwDebug, $xwSave, $xwEdit, $action, $xwUserName, $wgArticle, $xwIsProperties, $xwIsSystem;
if ($xwDebug) xwMessage('xwInputHook()','green');
+
if ($xwDebug) xwMessage('INPUT-HOOK:','green');
  
 
# If not writable, change action to view
 
# If not writable, change action to view
Line 131: Line 131:
 
}
 
}
  
# Parse POST for XPath inputs
+
# Scan POST and apply any XPath inputs
# - Syntax:  xpath:XPathQuery:[[@]node][+] = value
+
foreach ($_REQUEST as $query => $value) xwSetProperty($xwArticleProperties, $query, $value);
# - if "node" exists, then a new element (or att if "@node") is created for the value
 
# - if "+" exists, content is appended, else replaced
 
# - hidden-inputs can direct input-handlers into the transform list
 
foreach ($_REQUEST as $k => $v) {
 
if (is_object($xwArticleProperties) && preg_match("/^xpath:(.+):(@?)(\\w*)(\\+?)$/", $k, $m)) {
 
list(, $xpath, $att, $node, $append) = $m;
 
foreach (xwXPathQuery($xwArticleProperties, $xpath) as $result) {
 
if ($node) {
 
# create new element/attribute in result-node
 
if ($att) $node = $xwArticleProperties->create_attribute($node, $v);
 
else {
 
$node = $xwArticleProperties->create_element($node);
 
$node->set_content($v);
 
}
 
$result->append_child($node);
 
}
 
else {
 
# node not set, replace or append current-result-value
 
if ($append) $result->set_content($v);
 
else {
 
$newnode = $xwArticleProperties->create_element($result->tagname);
 
$newnode->set_content($v);
 
$result->replace_node($newnode);
 
}
 
}
 
}
 
}
 
}
 
  
 
# if it's a pseudo-namespace...
 
# if it's a pseudo-namespace...
Line 185: Line 157:
 
global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwUserName;
 
global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwUserName;
 
global $xwView, $xwPreview, $xwSave, $xwEdit, $xwRaw, $xwIsProperties, $xwIsSystem;
 
global $xwView, $xwPreview, $xwSave, $xwEdit, $xwRaw, $xwIsProperties, $xwIsSystem;
if ($xwDebug) xwMessage('xwOutputHook()','green');
+
if ($xwDebug) xwMessage('OUTPUT-HOOK:','green');
  
 
# Activate the skin-hook and generate wiki's output
 
# Activate the skin-hook and generate wiki's output
Line 191: Line 163:
 
$wgOut->output();
 
$wgOut->output();
  
# If saving, check the publishing list
+
# If saving, execute the transforms in the onChange list
if (0 && $xwSave) {
+
if ($xwSave) {
xwMessage('Publishing...');
+
foreach (xwGetListByTagname($xwArticleProperties, $tListName = 'onChange') as $tName)
# Publish each item
+
xwApplyTransform($tName, $xwArticle, $xwArticleTitle, $xwArticleProperties, $tListName);
foreach ($xwArticlePublish as $url) {
 
if ($FH = fopen($url, 'a')) {
 
# ftp example: ftp://user:password@example.com/file.txt
 
fwrite($FH, $xwArticle);
 
fclose($FH);
 
xwMessage("Article published to $url");
 
}
 
else xwMessage("Failed to publish to $url", 'red');
 
}
 
 
}
 
}
  
Line 217: Line 180:
 
$xwArticle .= "<!DOCTYPE xmlwiki:properties SYSTEM \"xmlwiki-properties.dtd\">\n";
 
$xwArticle .= "<!DOCTYPE xmlwiki:properties SYSTEM \"xmlwiki-properties.dtd\">\n";
 
$xwArticle .= "<properties>\n\t<read>anyone</read>\n\t<write>$xwUserName</write>\n";
 
$xwArticle .= "<properties>\n\t<read>anyone</read>\n\t<write>$xwUserName</write>\n";
$xwArticle .= "\t<transform></transform>\n\t<publish></publish>\n</properties>\n".$m[2];
+
$xwArticle .= "\t<data></data>\n\t<view></view>\n\t<onChange></onChange>\n</properties>\n".$m[2];
 
}
 
}
 
}
 
}
Line 237: Line 200:
  
 
global $xwRaw, $xwParserHookCalled, $xwDebug, $xwArticleTitle, $xwArticle, $xwReadable;
 
global $xwRaw, $xwParserHookCalled, $xwDebug, $xwArticleTitle, $xwArticle, $xwReadable;
if ($xwDebug) xwMessage('xwParserHook(TEXT)','green');
+
if ($xwDebug) xwMessage('PARSER-HOOK:','green');
 
$xwParserHookCalled = true;
 
$xwParserHookCalled = true;
$transformed = false;
+
$lang = false;
  
 
# If this text-fragment is our article, apply transforms
 
# If this text-fragment is our article, apply transforms
 
if ((strncmp($text, $xwArticle, 100) == 0) || (strncmp($text, xwArticleContent($xwArticleTitle), 100) == 0)) {
 
if ((strncmp($text, $xwArticle, 100) == 0) || (strncmp($text, xwArticleContent($xwArticleTitle), 100) == 0)) {
if ($xwDebug) xwMessage("PARSER-HOOK: \"$xwArticleTitle\" text-fragment recognised and intercepted.");
+
if ($xwDebug) xwMessage("Matching text-fragment intercepted.");
 
if (!$xwReadable) { $text=''; xwMessage('Sorry, article not readable!','red'); }
 
if (!$xwReadable) { $text=''; xwMessage('Sorry, article not readable!','red'); }
 
$xwArticle = $text; # needed because xwTransform works on global article
 
$xwArticle = $text; # needed because xwTransform works on global article
Line 254: Line 217:
 
# If not a 'raw' request, try some Geshi on
 
# If not a 'raw' request, try some Geshi on
 
if (!$xwRaw) {
 
if (!$xwRaw) {
global $wgGeshiSyntaxSettings;
+
global $wgGeshiSyntaxSettings, $xwArticleProperties;
 
if (!is_object($wgGeshiSyntaxSettings)) require_once('extensions/geshi/geshi.php');
 
if (!is_object($wgGeshiSyntaxSettings)) require_once('extensions/geshi/geshi.php');
if ($lang = xwArticleType($xwArticleTitle, $xwArticle)) {
+
if ($xwDebug) xwMessage("Geshi: This is \"$lang\".");
+
# Get language from properties, or articleType()
 +
$lang = count($results = xwXPathQuery($xwArticleProperties, '/properties/language'))
 +
  ? strtolower($results[0]->get_content())
 +
  : xwArticleType($xwArticleTitle, $xwArticle);
 +
 +
if ($lang != 'raw' and $lang != '') {
 
# Replace tabs with spaces (better than geshi)
 
# Replace tabs with spaces (better than geshi)
$xwArticle = preg_replace_callback("/^(.*?)(\\t+)/m", 'xwReplaceTabs', $xwArticle);
+
$xwArticle = preg_replace_callback("/^(.*?)(\\t+)/Sm", 'xwReplaceTabs', $xwArticle);
 
# Set up a geshi-parser
 
# Set up a geshi-parser
 
$geshi = new GeSHi($xwArticle, $lang == 'xslt' ? 'xml' : $lang, 'extensions/geshi/geshi');
 
$geshi = new GeSHi($xwArticle, $lang == 'xslt' ? 'xml' : $lang, 'extensions/geshi/geshi');
Line 281: Line 249:
 
# Do the parse
 
# Do the parse
 
$xwArticle = preg_replace("/^<div.*?>/", '<div class="xwcode">', $geshi->parse_code());
 
$xwArticle = preg_replace("/^<div.*?>/", '<div class="xwcode">', $geshi->parse_code());
$transformed = true;
 
 
}
 
}
elseif ($xwDebug) xwMessage('Geshi: Not my problem.');
 
 
}
 
}
 
$text = $xwArticle;
 
$text = $xwArticle;
 +
if ($xwDebug) xwMessage('Content language determined as '.($lang?strtoupper($lang):'WIKI'));
 
}
 
}
 
elseif ($xwDebug) xwMessage('Parsing other content');
 
elseif ($xwDebug) xwMessage('Parsing other content');
 
 
 
# Return this back to wiki-parser so it can do wiki-markup if it wasn't one of ours
 
# Return this back to wiki-parser so it can do wiki-markup if it wasn't one of ours
return $transformed;
+
return $lang == '';
 
}
 
}
 
 
 
# Converts tabs to spaces better than geshi
 
# Converts tabs to spaces better than geshi
 
function xwReplaceTabs($m) { return $m[1].str_repeat(' ',strlen($m[2])*4-strlen($m[1])%4); }
 
function xwReplaceTabs($m) { return $m[1].str_repeat(' ',strlen($m[2])*4-strlen($m[1])%4); }
+
 
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# SKIN HOOK
 
# SKIN HOOK
Line 303: Line 270:
 
function xwSkinHook(&$tmpl) {
 
function xwSkinHook(&$tmpl) {
  
global $xwArticle, $xwTemplate, $xwDebug, $xwSkinHookCalled;
+
global $xwArticle, $xwTemplate, $xwDebug, $xwSkinHookCalled, $xwReadable, $xwRaw;
if ($xwDebug) xwMessage('xwSkinHook(TEMPLATE)','green');
+
 
 +
if ($xwDebug) xwMessage('SKIN-HOOK:','green');
 
$xwSkinHookCalled = true;
 
$xwSkinHookCalled = true;
 
$xwTemplate = $tmpl;
 
$xwTemplate = $tmpl;
$xwArticle = $xwTemplate->data['bodytext'];
+
if ($xwReadable) $xwArticle = $xwTemplate->data['bodytext'];
 
#$xslt = xwArticleContent('xmlwiki.xslt');
 
#$xslt = xwArticleContent('xmlwiki.xslt');
 
 
Line 329: Line 297:
 
if ($article = new Article(Title::newFromText($articleTitle)))
 
if ($article = new Article(Title::newFromText($articleTitle)))
 
$article = $article->getContentWithoutUsingSoManyDamnGlobals();
 
$article = $article->getContentWithoutUsingSoManyDamnGlobals();
if ($article == '(There is currently no text in this page)') {
+
if ($article == '') {
if ($xwDebug) xwMessage("\"$articleTitle\" not found!",'red');
+
if ($xwDebug) xwMessage("Article \"$articleTitle\" not found.");
 +
return false;
 +
}
 +
elseif ($article == '(There is currently no text in this page)') {
 +
if ($xwDebug) xwMessage("Article \"$articleTitle\" not found.");
 
return false;
 
return false;
 
}
 
}
Line 359: Line 331:
 
function xwDomificateArticle(&$article, $id = '') {
 
function xwDomificateArticle(&$article, $id = '') {
 
global $xwDebug;
 
global $xwDebug;
if ($xwDebug) xwMessage("xwDomificateArticle(XML,$id)",'blue');
+
if ($xwDebug) xwMessage("xwDomificateArticle(\"$id\")",'blue');
 
ob_start();
 
ob_start();
 
if ($dom = domxml_open_mem($article)) $article = $dom;
 
if ($dom = domxml_open_mem($article)) $article = $dom;
Line 375: Line 347:
 
if (!is_object($article)) return false;
 
if (!is_object($article)) return false;
 
global $xwDebug;
 
global $xwDebug;
if ($xwDebug) xwMessage("xwUndomificate(DOM,$id)",'blue');
+
if ($xwDebug) xwMessage("xwUndomificate(\"$id\")",'blue');
 
# TODO:
 
# TODO:
 
# Validate, report errors
 
# Validate, report errors
Line 394: Line 366:
 
$docType = $xwArticleProperties->doctype();
 
$docType = $xwArticleProperties->doctype();
 
$root = $xwArticleProperties->document_element();  
 
$root = $xwArticleProperties->document_element();  
# ...and must be xmlwiki:properties doctype
+
# If xmlwiki:properties doctype, apply all 'view' and 'data' transforms
 
if (ereg('^xmlwiki:properties', $docType->name)) {
 
if (ereg('^xmlwiki:properties', $docType->name)) {
if ($xwDebug) xwMessage("xwTransformArticle()",'blue');
+
foreach (array('data', 'view') as $tListName) {
# Loop thru transforms applying each
+
# Apply each kind as a reducing stack
foreach ($root->get_elements_by_tagname('transform') as $elem) if ($tName = $elem->get_content()) {
+
while (count($tNodes = $root->get_elements_by_tagname($tListName))) {
if ($tText = xwArticleContent($tName)) {
+
$tName = $tNodes[0]->get_content();
$tType = xwArticleType($tName, $tText);
+
xwApplyTransform($tName, $xwArticle, $xwArticleTitle, $xwArticleProperties, $tListName);
if ($tType == 'xslt') xwApplyXSLT($xwArticle, $tText, $tName);
+
$tNodes[0]->unlink_node();
elseif ($tType == 'php') xwApplyPHP($xwArticle, $tText, $tName, $xwArticleTitle, $xwArticleProperties);
 
elseif ($tType == 'css') xwMessage("CSS's from transform-list aren't applied yet :-(", 'red');
 
 
}
 
}
else xwMessage("No such transform-article \"$tName\"!", 'red');
 
 
}
 
}
 
}
 
}
 
else xwMessage('Could not transform: article-properties must be xmlwiki:properties doctype!','red');
 
else xwMessage('Could not transform: article-properties must be xmlwiki:properties doctype!','red');
 
}
 
}
else if ($xwDebug) xwMessage('Article properties not found.','red');
+
else if ($xwDebug) xwMessage('Article has no properties.');
 
}
 
}
 
# If article is still a DOM object, stringify it once and for all
 
# If article is still a DOM object, stringify it once and for all
 
if (is_object($xwArticle)) xwUndomificateArticle($xwArticle, $xwArticleTitle);  
 
if (is_object($xwArticle)) xwUndomificateArticle($xwArticle, $xwArticleTitle);  
 
return true;
 
return true;
 +
}
 +
 +
# Apply a transform
 +
function xwApplyTransform($transform, &$article, $title, &$properties, $listName) {
 +
global $xwDebug, $xwRaw;
 +
if ($transform) {
 +
if ($xwDebug) xwMessage("Applying \"$listName\" transforms.");
 +
if ($tText = xwArticleContent($transform)) {
 +
$tType = xwArticleType($transform, $tText);
 +
if ($tType == 'xslt') xwApplyXSLT($article, $tText, $transform);
 +
elseif ($tType == 'php') xwApplyPHP($article, $tText, $transform, $title, $properties);
 +
elseif ($tType == 'css') xwMessage("CSS's from transform-list aren't applied yet :-(", 'red');
 +
elseif ($tType == 'xml') xwMessage("XML's from transform-list aren't applied yet :-(", 'red');
 +
}
 +
else xwMessage("Unknown transform \"$tName\" found in \"$listName\" list!", 'red');
 +
}
 
}
 
}
  
Line 420: Line 405:
 
function xwApplyPHP(&$article, &$code, $tName, $title, &$properties) {
 
function xwApplyPHP(&$article, &$code, $tName, $title, &$properties) {
 
# Get transform perms
 
# Get transform perms
if ($tProperties = xwArticleContent('xml:'.ucwords($tName)))
+
if ($tProperties = xwArticleContent($id = 'xml:'.ucwords($tName)))
xwDomificateArticle($tProperties, 'Transform-Properties');
+
xwDomificateArticle($tProperties, $id);
 
if (!count(preg_grep('/^(admin)|(dev)$/i', xwGetListByTagname($tProperties, 'write')))) {
 
if (!count(preg_grep('/^(admin)|(dev)$/i', xwGetListByTagname($tProperties, 'write')))) {
 
xwMessage('PHP-Transform will not execute unless it is writable by admin or dev only.','red');
 
xwMessage('PHP-Transform will not execute unless it is writable by admin or dev only.','red');
Line 427: Line 412:
 
}
 
}
 
# Permissions ok, execute the transform code and trap output
 
# Permissions ok, execute the transform code and trap output
 +
global $_SERVER;
 +
$path = '/properties/'.preg_replace("/\\.\\w+$/",'',$tName);
 +
$script = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
 
ob_start();
 
ob_start();
@eval("?>$code<?");
+
eval("?>$code<?");
 
xwExtractMessages();
 
xwExtractMessages();
 
ob_end_clean();
 
ob_end_clean();
Line 453: Line 441:
 
# Return result set of an XPath query on passed DOM-object
 
# Return result set of an XPath query on passed DOM-object
 
function xwXPathQuery(&$dom, $query) {
 
function xwXPathQuery(&$dom, $query) {
global $xwDebug;
+
$result = array();
if ($xwDebug) xwMessage("xwXPathQuery(DOM,\"$query\")",'blue');
+
if (is_object($dom)) {
ob_start();
+
global $xwDebug;
$context = $dom->xpath_new_context();  
+
if ($xwDebug) xwMessage("xwXPathQuery(\"$query\")",'blue');
if ($xpath = $context->xpath_eval($query)) $result = $xpath->nodeset;
+
ob_start();
else {
+
$context = $dom->xpath_new_context();  
xwMessage('Query Failed! :-(', 'red');
+
if ($xpath = $context->xpath_eval($query)) $result = @$xpath->nodeset;
$result = array();
+
elseif ($xwDebug) xwMessage("Query \"$query\" failed :-(", 'red');
 +
xwExtractMessages();
 +
ob_end_clean();
 
}
 
}
xwExtractMessages();
 
ob_end_clean();
 
 
return $result;
 
return $result;
 
}
 
}
 +
 +
# - Syntax:  xpath:XPathQuery:[[@]node][+] = value
 +
# - if "node" exists, then a new element (or att if "@node") is created for the value
 +
# - if "+" exists, content is appended, else replaced
 +
function xwSetProperty(&$properties, $query, $value) {
 +
if (!is_object($properties) or !preg_match("/^xpath:(.+?):(@?)(\\w*?)(\\+?)$/", $query, $match)) return false;
 +
list(, $xpath, $att, $node, $append) = $match;
 +
if (!is_array($results = xwXPathQuery($properties, $xpath))) $results = array();
 +
foreach ($results as $result) {
 +
if ($node) {
 +
# create new element/attribute in result-node
 +
if ($att) $node = $properties->create_attribute($node, $value);
 +
else {
 +
$node = $properties->create_element($node);
 +
$node->set_content($value);
 +
}
 +
$result->append_child($node);
 +
}
 +
else {
 +
# node not set, replace or append current-result-value
 +
if ($append) $result->set_content($value);
 +
else {
 +
$newnode = $properties->create_element($result->tagname);
 +
$newnode->set_content($value);
 +
$result->replace_node($newnode);
 +
}
 +
}
 +
}
 +
return true;
 +
}
  
 
# Surrounds the article with HTML structural page-layout containing a ref to xmlwiki.css
 
# Surrounds the article with HTML structural page-layout containing a ref to xmlwiki.css
Line 486: Line 504:
 
elseif ($xwIsSystem)  $data['content_actions']['nstab-main']['href'] = '/wiki/index.php?title=user:'.preg_replace('/^.+?:/i', '', $xwArticleTitle);
 
elseif ($xwIsSystem)  $data['content_actions']['nstab-main']['href'] = '/wiki/index.php?title=user:'.preg_replace('/^.+?:/i', '', $xwArticleTitle);
 
else $data['content_actions']['xml'] = array('href'=>"/wiki/index.php?title=xml:$xwArticleTitle&action=edit", 'text'=>'Properties');
 
else $data['content_actions']['xml'] = array('href'=>"/wiki/index.php?title=xml:$xwArticleTitle&action=edit", 'text'=>'Properties');
# Add system-action if admin and a user-page
+
# If admin, add system-action to user-page and personal-links
if ($xwIsAdmin && $xwIsUser)
+
if ($xwIsAdmin) {
$data['content_actions']['sys'] = array('href'=>'/wiki/index.php?title=sys:'.preg_replace('/^user:/i','',$xwArticleTitle).'&action=edit', 'text'=>'System');
+
if ($xwIsUser)
 +
$data['content_actions']['sys'] = array('href'=>'/wiki/index.php?title=sys:'.preg_replace('/^user:/i','',$xwArticleTitle).'&action=edit', 'text'=>'System');
 +
else {
 +
$data['personal_urls']['mytalk']['href'] = "/wiki/index.php?title=sys:$xwUserName&action=edit";
 +
$data['personal_urls']['mytalk']['text'] = "System";
 +
}
 +
}
  
 
# Head
 
# Head
Line 523: Line 547:
  
 
foreach ($data['content_actions'] as $key => $action) {
 
foreach ($data['content_actions'] as $key => $action) {
#if ($key == '
+
$actions .= '<li id="ca-'.htmlspecialchars($key).'"';
#if (!(ereg('action=edit$', $action['href'])&&!$xwWritable)) {
+
if (!empty($action['class'])) $actions .= ' class="'.htmlspecialchars($action['class']).'"';
$actions .= '<li id="ca-'.htmlspecialchars($key).'"';
+
$href = '<a href="'.htmlspecialchars($action['href']).'">'.htmlspecialchars($action['text']).'</a>';
if (!empty($action['class'])) $actions .= ' class="'.htmlspecialchars($action['class']).'"';
+
$actions .= ">$href</li>";
$href = '<a href="'.htmlspecialchars($action['href']).'">'.htmlspecialchars($action['text']).'</a>';
 
$actions .= ">$href</li>";
 
# }
 
 
}
 
}
 
$actions .= '</ul></div>';
 
$actions .= '</ul></div>';
Line 661: Line 682:
  
 
# Return array of comma-separated-items in element content
 
# Return array of comma-separated-items in element content
 +
# - if more than one element all are split and appended into the list
 
function xwGetListByTagname(&$xml, $tag) {
 
function xwGetListByTagname(&$xml, $tag) {
 +
$list = array();
 
if (is_object($xml)) {
 
if (is_object($xml)) {
 
$root = $xml->document_element();  
 
$root = $xml->document_element();  
if (count($list = $root->get_elements_by_tagname($tag)))
+
foreach ($root->get_elements_by_tagname($tag) as $element)
return split(',', $list[0]->get_content());
+
if ($csv = $element->get_content()) $list = array_merge($list, split(',', $csv));
 
}
 
}
return array();
+
return $list;
 
}
 
}
  
Line 682: Line 705:
 
# Extract error-messages from captured output and put in proper message-queue
 
# Extract error-messages from captured output and put in proper message-queue
 
function xwExtractMessages() {
 
function xwExtractMessages() {
 +
#if (preg_matcheval()'d code on line 2
 
$err = preg_replace("/<.+?>/", "", ob_get_contents());
 
$err = preg_replace("/<.+?>/", "", ob_get_contents());
$err = preg_replace('/ in .+? on line.+?[0-9]+/', '', $err);
+
#$err = preg_replace('/Warning.+?\\(\\): /', '', $err);
$err = preg_replace('/Warning.+?\\(\\): /', '', $err);
+
foreach (split("\n", $err) as $msg) if (trim($msg)) {
foreach (split("\n", $err) as $msg) if (trim($msg)) xwMessage("$msg", 'red');
+
if (ereg("eval\\(\\)'d code on line ([0-9]+)", $msg, $m)) $line = " (Line $m[1])";
 +
$msg = preg_replace('/ in .+? on line.+?[0-9]+/', '', $msg);
 +
if (isset($line)) $msg .= $line;
 +
xwMessage($msg, 'red');
 +
}
 
}
 
}
 
 

Revision as of 04:10, 25 June 2005

<?php

  1. xmlWiki - MediaWiki XML Hack
  2. Nad - 2005-05-18
  1. INIT
  2. SECURITY
  3. INPUT HOOK
  4. OUTPUT HOOK
  5. PARSER HOOK
  6. SKIN HOOK
  7. ARTICLE FUNCTIONS
  8. UTILITY FUNCTIONS
  1. ---------------------------------------------------------------------------------------------------------------------- #
  1. TODO:
  1. - STACK: & <view> instead of <transform>
  2. - <onChange> (publish in OUTPUT-HOOK is to be <publish.php/> and in the onChange list
  1. - TransformArticle:
  2. - VALIDATE: validate article-properties against xmlwiki:properties.dtd (does PHP4 do this?)
  3. - TRAP: fatal errors in php-transform execution
  4. - remove global and file access from php-transforms (?)
  5. - Undomificate: validation, xslt
  6. - Check (later validate) doctype of user:sys (change users pages on site first!)
  7. - Check (later validate) doctype of user/properties on save (still save though)
  1. BUGS:
  2. - tabReplace doesn't work for /^\t+.+\t.+$/
  1. LATER:
  2. - allow XIncludes to build large docs from others
  3. - allow docBook and DSSSL content
  4. - Maybe only use XML transforms and use PHP via PI's
  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. INIT
  1. Exit if not included from index.php

defined('MEDIAWIKI') or die('xmlwiki.php must be included from MediaWiki\'s index.php!');

  1. Allows Geshi to be installed by PARSER-HOOK if not installed th usual way already

if (!@is_object($wgGeshiSyntaxSettings)) $wgGeshiSyntaxSettings = false;

  1. System globals

$xwDebug = false; $xwMessages = array(); $xwArticleCache = array(); $xwTemplate = null; $xwParserHookCalled = false; $xwSkinHookCalled = false; $xwMsgToken = ;

  1. User globals

$xwUserName = ucwords($wgUser->mName); $xwUserSYS = null; $xwUserGroups = array($xwUserName, 'anyone'); $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']);

  1. Article globals

$xwArticleTitle = $wgTitle->getPrefixedURL(); $xwArticle = $xwPreview ? $wgRequest->gettext('wpTextbox1') : $wgArticle->getContent(false); $xwArticleProperties = null; $xwIsProperties = preg_match('/^xml:.+$/i', $xwArticleTitle); $xwIsSystem = preg_match('/^sys:.+$/i', $xwArticleTitle); $xwIsUser = preg_match('/^user:.+$/i', $xwArticleTitle); $xwIsAdmin = false; $xwArticleReadableBy = array('anyone'); $xwArticleWritableBy = array('anyone');

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. SECURITY
  1. Get user-system-article and extract users' groups from it if any

if ($xwUserSYS = xwArticleContent("sys:$xwUserName")) xwDomificateArticle($xwUserSYS, "sys:$xwUserName"); $xwUserGroups = array_merge($xwUserGroups, xwGetListByTagname($xwUserSYS, 'groups'));

  1. Read user-prefs

$xwDebug = count($results = xwXPathQuery($xwUserSYS, '//debug')) ? $results[0]->get_content() : false;

  1. Get article-meta-file and extract article-perms

if ($xwIsSystem) $xwArticleReadableBy = $xwArticleWritableBy = array('admin'); else { # Get xml:article (self if already xml:*) if ($xwArticleProperties = $xwIsProperties ? $xwArticle : xwArticleContent($id = 'xml:'.ucwords($xwArticleTitle))) xwDomificateArticle($xwArticleProperties, $xwIsProperties ? $xwArticleTitle : $id); $xwArticleReadableBy = xwGetListByTagname($xwArticleProperties, 'read') + array('anyone'); $xwArticleWritableBy = xwGetListByTagname($xwArticleProperties, 'write') + array('anyone'); if ($xwIsProperties) unset($xwArticleProperties); }

  1. Set perms for this request

if ($xwDebug) xwMessage('PERMISSIONS:','green'); if (in_array('admin', $xwUserGroups)) $xwIsAdmin = $xwReadable = $xwWritable = true; else { $xwReadable = 0 < count(array_intersect($xwArticleReadableBy, $xwUserGroups)); $xwWritable = 0 < count(array_intersect($xwArticleWritableBy, $xwUserGroups)); } if ($xwDebug) { xwMessage('Groups: '.join(', ', $xwUserGroups)); xwMessage('Readable ('.($xwReadable?'yes':'no').'): '.join(', ', $xwArticleReadableBy)); xwMessage('Writable ('.($xwWritable?'yes':'no').'): '.join(', ', $xwArticleWritableBy)); }

  1. Divert to access-denied article if not readable

if (!$xwReadable) { $action = 'view'; $xwSave = $xwEdit = false; }


  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. INPUT HOOK
  1. Parse and process input from forms

function xwInputHook() {

global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwWritable, $_REQUEST; global $xwDebug, $xwSave, $xwEdit, $action, $xwUserName, $wgArticle, $xwIsProperties, $xwIsSystem; if ($xwDebug) xwMessage('INPUT-HOOK:','green');

# If not writable, change action to view if (!$xwWritable && ($action != 'view')) { xwMessage('Sorry, article not writable, action changed to "view".', 'red'); $action = 'view'; $xwSave = $xwEdit = false; }

# Scan POST and apply any XPath inputs foreach ($_REQUEST as $query => $value) xwSetProperty($xwArticleProperties, $query, $value);

# if it's a pseudo-namespace... if ($xwIsProperties || $xwIsSystem) { # wpSave should be valid XML to be saved if ($xwSave) { xwDomificateArticle($wpSave, 'POST-DATA'); if (!is_object($wpSave)) { xwMessage('A meta-article must be valid XML! changing action to "edit"', 'red'); $action='edit'; } } }

}

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. OUTPUT HOOK
  1. Post-process and output article

function xwOutputHook() {

global $wgUser, $wgOut, $xwParserHookCalled, $xwSkinHookCalled, $xwDebug; global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwUserName; global $xwView, $xwPreview, $xwSave, $xwEdit, $xwRaw, $xwIsProperties, $xwIsSystem; if ($xwDebug) xwMessage('OUTPUT-HOOK:','green');

# Activate the skin-hook and generate wiki's output $wgUser->setOption('skin', 'xwskin'); $wgOut->output();

# If saving, execute the transforms in the onChange list if ($xwSave) { foreach (xwGetListByTagname($xwArticleProperties, $tListName = 'onChange') as $tName) xwApplyTransform($tName, $xwArticle, $xwArticleTitle, $xwArticleProperties, $tListName); }

# If editing an empty sys:article or xml:article, add default content if ($xwEdit && preg_match("/^(.*<textarea .+?>)\\s*(<\\/textarea>.*)$/s", $xwArticle, $m)) { if ($xwIsSystem) { $xwArticle = $m[1]."<?xml version=\"1.0\" standalone=\"yes\"?>\n"; $xwArticle .= "<!DOCTYPE xmlwiki:user SYSTEM \"xmlwiki-user.dtd\">\n"; $xwArticle .= "<user>\n\t<groups></groups>\n</user>\n".$m[2]; } elseif ($xwIsProperties) { $xwArticle = $m[1]."<?xml version=\"1.0\" standalone=\"yes\"?>\n"; $xwArticle .= "<!DOCTYPE xmlwiki:properties SYSTEM \"xmlwiki-properties.dtd\">\n"; $xwArticle .= "<properties>\n\t<read>anyone</read>\n\t<write>$xwUserName</write>\n"; $xwArticle .= "\t\n\t<view></view>\n\t<onChange></onChange>\n</properties>\n".$m[2]; } }

# Insert messages into final html and output if ($xwDebug) { if (!$xwParserHookCalled) xwMessage('PARSER-HOOK was not called','green'); if (!$xwSkinHookCalled) xwMessage('SKIN-HOOK was not called','green'); #if (is_object($xwArticleProperties)) xwMessage($xwArticleProperties->dump_mem(true),'purple'); } xwAddMessages(); print $xwArticle; }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. PARSER HOOK

function xwParserHook(&$text) {

global $xwRaw, $xwParserHookCalled, $xwDebug, $xwArticleTitle, $xwArticle, $xwReadable; if ($xwDebug) xwMessage('PARSER-HOOK:','green'); $xwParserHookCalled = true; $lang = false;

# If this text-fragment is our article, apply transforms if ((strncmp($text, $xwArticle, 100) == 0) || (strncmp($text, xwArticleContent($xwArticleTitle), 100) == 0)) { if ($xwDebug) xwMessage("Matching text-fragment intercepted."); if (!$xwReadable) { $text=; xwMessage('Sorry, article not readable!','red'); } $xwArticle = $text; # needed because xwTransform works on global article # If attempted xml encountered, try to domificate and transform if (preg_match("/^<\\?xml/i", $xwArticle)) { $transformed = true; xwDomificateArticle($xwArticle, 'ParserHook/text'); } xwTransformArticle(); # If not a 'raw' request, try some Geshi on if (!$xwRaw) { global $wgGeshiSyntaxSettings, $xwArticleProperties; if (!is_object($wgGeshiSyntaxSettings)) require_once('extensions/geshi/geshi.php');

# Get language from properties, or articleType() $lang = count($results = xwXPathQuery($xwArticleProperties, '/properties/language'))  ? strtolower($results[0]->get_content())  : xwArticleType($xwArticleTitle, $xwArticle);

if ($lang != 'raw' and $lang != ) { # Replace tabs with spaces (better than geshi) $xwArticle = preg_replace_callback("/^(.*?)(\\t+)/Sm", 'xwReplaceTabs', $xwArticle); # Set up a geshi-parser $geshi = new GeSHi($xwArticle, $lang == 'xslt' ? 'xml' : $lang, 'extensions/geshi/geshi'); $geshi->set_header_type(GESHI_HEADER_DIV); #$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS); #@$geshi->set_line_style('color:#aaaaaa'); #@$geshi->set_code_style('color:black'); @$geshi->set_keyword_group_style(1, 'color:blue', true); @$geshi->set_keyword_group_style(2, 'color:blue', true); @$geshi->set_keyword_group_style(3, 'color:blue', true); @$geshi->set_comments_style(1,'font-style: italic; color:#d00000;',true); @$geshi->set_comments_style(2,'font-style: italic; color:#d00000;',true); @$geshi->set_comments_style(3,'font-style: italic; color:#d00000;',true); @$geshi->set_escape_characters_style('color:red',true); @$geshi->set_brackets_style('color:black',true); @$geshi->set_strings_style('color:#008080',true); @$geshi->set_numbers_style('color:black',true); @$geshi->set_methods_style('color:black',true); @$geshi->set_symbols_style('color:black',true); @$geshi->set_regexps_style('color:green',true); # Do the parse

$xwArticle = preg_replace("/^<div.*?>/", '

', $geshi->parse_code());

} } $text = $xwArticle; if ($xwDebug) xwMessage('Content language determined as '.($lang?strtoupper($lang):'WIKI')); } elseif ($xwDebug) xwMessage('Parsing other content');

# Return this back to wiki-parser so it can do wiki-markup if it wasn't one of ours return $lang == ; }

  1. Converts tabs to spaces better than geshi

function xwReplaceTabs($m) { return $m[1].str_repeat(' ',strlen($m[2])*4-strlen($m[1])%4); }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. SKIN HOOK
  1. If attempted xml encountered, try to domificate and transform
  2. - Raw requests don't get here

function xwSkinHook(&$tmpl) {

global $xwArticle, $xwTemplate, $xwDebug, $xwSkinHookCalled, $xwReadable, $xwRaw;

if ($xwDebug) xwMessage('SKIN-HOOK:','green'); $xwSkinHookCalled = true; $xwTemplate = $tmpl; if ($xwReadable) $xwArticle = $xwTemplate->data['bodytext']; #$xslt = xwArticleContent('xmlwiki.xslt');

# Do xmlWiki page-layout transform

xwTransformPageLayout(); # should be: xwApplyXSLT($xwArticle, $xslt, 'xmlwiki.xslt');

}

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. ARTICLE FUNCTIONS
  1. Retreive wiki-article as raw text

function xwArticleContent($articleTitle) { # Return with results if already cached global $xwArticleCache, $xwDebug; if ($xwDebug) xwMessage("xwArticleContent(\"$articleTitle\")",'blue'); if (isset($xwArticleCache[$articleTitle])) return $xwArticleCache[$articleTitle]; # Get wiki article content (or use global if no article passed) # - using getContentWithoutUsingSoManyDamnGlobals() because getContent() fucks up nested-article-reads if ($article = new Article(Title::newFromText($articleTitle))) $article = $article->getContentWithoutUsingSoManyDamnGlobals(); if ($article == ) { if ($xwDebug) xwMessage("Article \"$articleTitle\" not found."); return false; } elseif ($article == '(There is currently no text in this page)') { if ($xwDebug) xwMessage("Article \"$articleTitle\" not found."); return false; } if ($articleTitle) $xwArticleCache[$articleTitle] = $article; return $article; }

  1. Decide kind of article from content and title

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

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

function xwDomificateArticle(&$article, $id = ) { global $xwDebug; if ($xwDebug) xwMessage("xwDomificateArticle(\"$id\")",'blue'); ob_start(); if ($dom = domxml_open_mem($article)) $article = $dom; else { # Could not convert, extract error messages from output if ($xwDebug) xwMessage("Failed :-(", 'red'); xwExtractMessages(); } ob_end_clean(); }

  1. Reduce article to a string if it's a DOM object
  2. - ie if all xslt's had xml-output-method

function xwUndomificateArticle(&$article, $id = ) { if (!is_object($article)) return false; global $xwDebug; if ($xwDebug) xwMessage("xwUndomificate(\"$id\")",'blue'); # TODO: # Validate, report errors # xwApplyXSLT($article, $xslt) if xml referrs one # Convert DOM back to XML if still an object if (is_object($article)) $article = $article->dump_mem(true); return true; }

  1. Transform an article
  2. - Article is undomificated on exit

function xwTransformArticle() { global $xwDebug, $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwIsProperties, $xwIsSystem; # Article can not be transformed if properties/system if (!$xwIsProperties && !$xwIsSystem) { # Article-properties must be an object... if (is_object($xwArticleProperties)) { $docType = $xwArticleProperties->doctype(); $root = $xwArticleProperties->document_element(); # If xmlwiki:properties doctype, apply all 'view' and 'data' transforms if (ereg('^xmlwiki:properties', $docType->name)) { foreach (array('data', 'view') as $tListName) { # Apply each kind as a reducing stack while (count($tNodes = $root->get_elements_by_tagname($tListName))) { $tName = $tNodes[0]->get_content(); xwApplyTransform($tName, $xwArticle, $xwArticleTitle, $xwArticleProperties, $tListName); $tNodes[0]->unlink_node(); } } } else xwMessage('Could not transform: article-properties must be xmlwiki:properties doctype!','red'); } else if ($xwDebug) xwMessage('Article has no properties.'); } # If article is still a DOM object, stringify it once and for all if (is_object($xwArticle)) xwUndomificateArticle($xwArticle, $xwArticleTitle); return true; }

  1. Apply a transform

function xwApplyTransform($transform, &$article, $title, &$properties, $listName) { global $xwDebug, $xwRaw; if ($transform) { if ($xwDebug) xwMessage("Applying \"$listName\" transforms."); if ($tText = xwArticleContent($transform)) { $tType = xwArticleType($transform, $tText); if ($tType == 'xslt') xwApplyXSLT($article, $tText, $transform); elseif ($tType == 'php') xwApplyPHP($article, $tText, $transform, $title, $properties); elseif ($tType == 'css') xwMessage("CSS's from transform-list aren't applied yet :-(", 'red'); elseif ($tType == 'xml') xwMessage("XML's from transform-list aren't applied yet :-(", 'red'); } else xwMessage("Unknown transform \"$tName\" found in \"$listName\" list!", 'red'); } }

  1. Apply PHP-transform if article perms are only writable by admin or dev

function xwApplyPHP(&$article, &$code, $tName, $title, &$properties) { # Get transform perms if ($tProperties = xwArticleContent($id = 'xml:'.ucwords($tName))) xwDomificateArticle($tProperties, $id); if (!count(preg_grep('/^(admin)|(dev)$/i', xwGetListByTagname($tProperties, 'write')))) { xwMessage('PHP-Transform will not execute unless it is writable by admin or dev only.','red'); return; } # Permissions ok, execute the transform code and trap output global $_SERVER; $path = '/properties/'.preg_replace("/\\.\\w+$/",,$tName); $script = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']; ob_start(); eval("?>$code<?"); xwExtractMessages(); ob_end_clean(); }

  1. Apply XSLT to article-dom
  2. - if output-method is html, article will get stringified

function xwApplyXSLT(&$article, &$xslt, $id = ) { global $xwDebug; if ($xwDebug) xwMessage("xwApplyXSLT(DOM,$id)",'blue'); ob_start(); if ($tObject = domxml_xslt_stylesheet($xslt)) { $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', $xslt)) $tResult = $tObject->result_dump_mem($tResult); } xwExtractMessages(); ob_end_clean(); if (isset($tResult)) $article = $tResult; else return false; return true; }

  1. Return result set of an XPath query on passed DOM-object

function xwXPathQuery(&$dom, $query) { $result = array(); if (is_object($dom)) { global $xwDebug; if ($xwDebug) xwMessage("xwXPathQuery(\"$query\")",'blue'); ob_start(); $context = $dom->xpath_new_context(); if ($xpath = $context->xpath_eval($query)) $result = @$xpath->nodeset; elseif ($xwDebug) xwMessage("Query \"$query\" failed :-(", 'red'); xwExtractMessages(); ob_end_clean(); } return $result; }

  1. - Syntax: xpath:XPathQuery:[[@]node][+] = value
  2. - if "node" exists, then a new element (or att if "@node") is created for the value
  3. - if "+" exists, content is appended, else replaced

function xwSetProperty(&$properties, $query, $value) { if (!is_object($properties) or !preg_match("/^xpath:(.+?):(@?)(\\w*?)(\\+?)$/", $query, $match)) return false; list(, $xpath, $att, $node, $append) = $match; if (!is_array($results = xwXPathQuery($properties, $xpath))) $results = array(); foreach ($results as $result) { if ($node) { # create new element/attribute in result-node if ($att) $node = $properties->create_attribute($node, $value); else { $node = $properties->create_element($node); $node->set_content($value); } $result->append_child($node); } else { # node not set, replace or append current-result-value if ($append) $result->set_content($value); else { $newnode = $properties->create_element($result->tagname); $newnode->set_content($value); $result->replace_node($newnode); } } } return true; }

  1. Surrounds the article with HTML structural page-layout containing a ref to xmlwiki.css
  2. - TEMP: see skin-hook

function xwTransformPageLayout() {

global $xwMsgToken, $xwArticle, $xwArticleTitle, $xwTemplate, $xwDebug; global $xwWritable, $xwUserName, $xwIsAdmin, $xwIsUser, $xwIsProperties, $xwIsSystem; if ($xwDebug) xwMessage("xwTransformPageLayout()",'blue'); $tmpl = &$xwTemplate; $data = &$tmpl->data; $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '; $doctype .= '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'; $html = ''; # Add properties-action if not already a meta-article, or change article-href if it is if ($xwIsProperties) $data['content_actions']['nstab-main']['href'] = '/wiki/index.php?title='.preg_replace('/^.+?:/i', '', $xwArticleTitle); elseif ($xwIsSystem) $data['content_actions']['nstab-main']['href'] = '/wiki/index.php?title=user:'.preg_replace('/^.+?:/i', '', $xwArticleTitle); else $data['content_actions']['xml'] = array('href'=>"/wiki/index.php?title=xml:$xwArticleTitle&action=edit", 'text'=>'Properties'); # If admin, add system-action to user-page and personal-links if ($xwIsAdmin) { if ($xwIsUser) $data['content_actions']['sys'] = array('href'=>'/wiki/index.php?title=sys:'.preg_replace('/^user:/i','',$xwArticleTitle).'&action=edit', 'text'=>'System'); else { $data['personal_urls']['mytalk']['href'] = "/wiki/index.php?title=sys:$xwUserName&action=edit"; $data['personal_urls']['mytalk']['text'] = "System"; } } # Head $head = ''.$data['headlinks']; $head .= ''; $head .= ''.htmlspecialchars($data['pagetitle']).''; $head .= ''; # Body opening tag $body = ''; $content .= '

'.htmlspecialchars($data['title']).'

'; $content .= '
'; $content .= '

'.htmlspecialchars($tmpl->translator->translate('tagline')).'

'; $content .= '
'.$data['subtitle'].'
'; if ($data['undelete']) $content .= '
'.$data['undelete'].'
'; if ($data['newtalk']) $content .= '
'.$data['newtalk'].'
'; $content .= "$xwArticle"; # <---- Actual article content inserted here if ($data['catlinks']) $content .= ''; $content .= '
'; $content .= '

'; # Action-links (remove edit link if not writable) $actions = '

Views
    '; foreach ($data['content_actions'] as $key => $action) { $actions .= '
  • '.htmlspecialchars($action['text']).''; $actions .= ">$href
  • "; } $actions .= '

'; # Personal-links $personal = '

'; $personal .= '
'.htmlspecialchars($tmpl->translator->translate('personaltools')).'
'; $personal .= '

"; # Logo $logo = '

MastodonRSSCodeEmail

'; $logo .= '

'; # Navigation $nav = '

'; $nav .= '
'.htmlspecialchars($tmpl->translator->translate('navigation')).'
'; $nav .= '

'; # Search $search = '

'; # Toolbox $toolbox = '

'; $toolbox .= '
'.htmlspecialchars($tmpl->translator->translate('toolbox')).'
'; $toolbox .= '

'; # Language $language = ''; if ($data['language_urls']) { $language .= '

'; $language .= '
'.htmlspecialchars($tmpl->translator->translate('otherlanguages')).'
'; $language .= '
    '; foreach ($data['language_urls'] as $langlink) { $href = ''.$langlink['text'].''; $language .= "
  • $href
  • "; } $language .= '

'; } # Footer $footer = '

'; # Return assembled parts $xwArticle = " $doctype $html $head $body

$search $nav $toolbox $personal$actions$xwMsgToken$content
$footer

"; }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. UTILITY FUNCTIONS
  1. Return array of comma-separated-items in element content
  2. - if more than one element all are split and appended into the list

function xwGetListByTagname(&$xml, $tag) { $list = array(); if (is_object($xml)) { $root = $xml->document_element(); foreach ($root->get_elements_by_tagname($tag) as $element) if ($csv = $element->get_content()) $list = array_merge($list, split(',', $csv)); } return $list; }

  1. Append messages to content

function xwAddMessages() { global $xwArticle, $xwMessages, $xwMsgToken; if (count($xwMessages)) {

$msg = '

There are Messages

'; $msg .= '

  • '.join("
  • ", $xwMessages).'

';

$xwArticle = ereg_replace($xwMsgToken, $msg, $xwArticle); } }

  1. Extract error-messages from captured output and put in proper message-queue

function xwExtractMessages() { #if (preg_matcheval()'d code on line 2 $err = preg_replace("/<.+?>/", "", ob_get_contents()); #$err = preg_replace('/Warning.+?\\(\\): /', , $err); foreach (split("\n", $err) as $msg) if (trim($msg)) { if (ereg("eval\\(\\)'d code on line ([0-9]+)", $msg, $m)) $line = " (Line $m[1])"; $msg = preg_replace('/ in .+? on line.+?[0-9]+/', , $msg); if (isset($line)) $msg .= $line; xwMessage($msg, 'red'); } }

  1. Add message to queue

function xwMessage($msg, $col = '#000080') { global $xwMessages; $msg = htmlentities($msg); return $xwMessages[] = "$msg"; }

  1. Print list of passed object's methods and properties

function xwCheckoutObject($obj) { if (is_object($obj)) { print strtoupper("
$obj properties:
"); foreach (get_object_vars($obj) as $k=>$v) print htmlentities("$k => $v")."
"; print strtoupper("
$obj methods:
"); foreach (get_class_methods($obj) as $k=>$v) print "$v
"; } if (is_array($obj)) { print strtoupper("
Array content:
"); foreach ($obj as $k=>$v) print "$k => $v
"; } die; }

?>