Difference between revisions of "Xmlwiki.php"

From Organic Design wiki
Line 3: Line 3:
 
# Nad - 2005-05-18
 
# Nad - 2005-05-18
  
# INIT HOOK
+
# INIT
 +
# SECURITY
 
# INPUT HOOK
 
# INPUT HOOK
 
# OUTPUT HOOK
 
# OUTPUT HOOK
Line 9: Line 10:
 
# SKIN HOOK
 
# SKIN HOOK
 
# ARTICLE FUNCTIONS
 
# ARTICLE FUNCTIONS
# TRANSFORMS
 
 
# UTILITY FUNCTIONS
 
# UTILITY FUNCTIONS
  
Line 15: Line 15:
  
 
# TODO:
 
# TODO:
 +
# - Undomificate: validation, xslt
 +
# - Transform: check perms on php-execution
 
# Publish:
 
# Publish:
 
# - xml:article - holds article publish-list
 
# - xml:article - holds article publish-list
Line 21: Line 23:
 
# - xml:article - now transforms are in here (main article is now like <body>)
 
# - xml:article - now transforms are in here (main article is now like <body>)
 
# - so we don't do the "its one of ours" thing anymore
 
# - so we don't do the "its one of ours" thing anymore
# Raw as Default:
 
# - Need to mod the article-link so it has action=view
 
  
 
# LATER:
 
# LATER:
Line 30: Line 30:
  
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
# INIT HOOK
+
# INIT
  
 
# Exit if not included from index.php
 
# Exit if not included from index.php
Line 39: Line 39:
  
 
# 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();
$xwEdit = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'edit');
+
$xwEdit = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'edit');
$xwView = (!isset($_REQUEST['action']) || (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'view')));
+
$xwView = (!isset($_REQUEST['action']) || (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'view')));
$xwRaw = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'raw');
+
$xwRaw = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'raw');
$xwPreview = isset($_REQUEST['wpPreview']);  
+
$xwPreview = isset($_REQUEST['wpPreview']);  
$xwSave = isset($_REQUEST['wpSave']);
+
$xwSave = isset($_REQUEST['wpSave']);
$xwUserName = $wgUser->mName;
+
$xwUserName = $wgUser->mName;
$xwArticle = $xwPreview ? $wgRequest->gettext('wpTextbox1') : $wgArticle->getContent(false);
+
$xwUserGroups = array(strtolower($xwUserName), 'anyone', 'everyone', 'nobody', 'world');
$xwArticleTitle = $wgTitle->getPrefixedURL();
+
$xwArticleReadableBy = array('anyone');
$xwArticleCache = array();
+
$xwArticleWritableBy = array('anyone');
$xwArticleLinks = array();
+
$xwArticle = $xwPreview ? $wgRequest->gettext('wpTextbox1') : $wgArticle->getContent(false);
$xwUserLinks = array();
+
$xwArticleTitle = $wgTitle->getPrefixedURL();
$xwMetaArticle = false;
+
$xwArticleCache = array();
$xwMsgToken = '<!--xwMsg-->';
+
$xwArticleLinks = array();
$xwTemplate = null;
+
$xwUserLinks = array();
$xwParserHookCalled = false;
+
$xwMetaArticle = false;
$xwSkinHookCalled = false;
+
$xwMsgToken = '<!--xwMsg-->';
 +
$xwTemplate = null;
 +
$xwParserHookCalled = false;
 +
$xwSkinHookCalled = false;
  
# R/W by user, not currently used, but for xmlWiki prefs etc
+
# ---------------------------------------------------------------------------------------------------------------------- #
 +
# SECURITY
 +
 
 +
# Not currently used: R/W by user, for xmlWiki prefs etc
 
if ($xwUserXML = xwArticleContent("xml:$xwUserName"))
 
if ($xwUserXML = xwArticleContent("xml:$xwUserName"))
 
xwDomificateArticle($xwUserXML, "xml:$xwUserName");
 
xwDomificateArticle($xwUserXML, "xml:$xwUserName");
  
 
# R/W by admin only, holds users groups and other system info like stats
 
# R/W by admin only, holds users groups and other system info like stats
 +
# - no sys:user means "anyone" in group "everyone" as far as perms goes
 
if ($xwUserSYS = xwArticleContent("sys:$xwUserName"))
 
if ($xwUserSYS = xwArticleContent("sys:$xwUserName"))
 
xwDomificateArticle($xwUserSYS, "sys:$xwUserName");
 
xwDomificateArticle($xwUserSYS, "sys:$xwUserName");
  
# Security
+
# If sys:user exists, get users' group-list
 +
if (is_object($xwUserSYS)) {
 +
$xwRoot = $xwUserSYS->document_element();
 +
if (count($xwList = $xwRoot->get_elements_by_tagname('groups')))
 +
$xwUserGroups = array_merge($xwUserGroups, split(',', strtolower($xwList[0]->get_content())));
 +
}
 +
 
 
# - Security info is: owner, read-groups, write-groups (default values are "world")
 
# - Security info is: owner, read-groups, write-groups (default values are "world")
 
# - Users and groups are interchangeable
 
# - Users and groups are interchangeable
# - Default user is "nobody", default group is "everybody"
+
# - Default user is "anyone", default group is "everyone"
 
if (ereg('^xml:(.+)$', $xwArticleTitle)) {
 
if (ereg('^xml:(.+)$', $xwArticleTitle)) {
 
# This is an xml-pseudo-namespace
 
# This is an xml-pseudo-namespace
# - get perms from its 'parent' article
+
# - its perms apply to its 'parent' and to itself aswell
$xwMetaArticle = true;
+
$xwXML = $xwArticle;
 +
xwDomificateArticle($xwXML, $xwArticleTitle);
 +
if (is_object($xwXML)) {
 +
$xwRoot = $xwXML->document_element();
 +
if (count($xwList = $xwRoot->get_elements_by_tagname('read')))
 +
$xwArticleReadableBy = split(',', strtolower($xwList[0]->get_content()));
 +
if (count($xwList = $xwRoot->get_elements_by_tagname('write')))
 +
$xwArticleWritableBy = split(',', strtolower($xwList[0]->get_content()));
 +
}
 
}
 
}
 
elseif (ereg('^sys:(.+)$', $xwArticleTitle)) {
 
elseif (ereg('^sys:(.+)$', $xwArticleTitle)) {
# This is an sys-pseudo-namespace
+
# This is a sys-pseudo-namespace
 
# - only admin can read/write sys:
 
# - only admin can read/write sys:
$xwMetaArticle = true;
+
$xwArticleReadableBy = $articleWritableBy = array('admin');
 
}
 
}
 
else {
 
else {
# xml:article holds article perms
+
# This is a normal article, get xml:article
# - it doesn't have perms of its own
 
 
if ($xwArticleXML = xwArticleContent("xml:$xwArticleTitle"))
 
if ($xwArticleXML = xwArticleContent("xml:$xwArticleTitle"))
 
xwDomificateArticle($xwArticleXML, "xml:$xwArticleTitle");
 
xwDomificateArticle($xwArticleXML, "xml:$xwArticleTitle");
 +
if (is_object($xwArticleXML)) {
 +
$xwRoot = $xwArticleXML->document_element();
 +
if (count($xwList = $xwRoot->get_elements_by_tagname('read')))
 +
$xwArticleReadableBy = split(',', $xwList = strtolower($xwList[0]->get_content()));
 +
if (count($xwList = $xwRoot->get_elements_by_tagname('write')))
 +
$xwArticleWritableBy = split(',', $xwList = strtolower($xwList[0]->get_content()));
 +
}
 
}
 
}
if (!is_object($xwArticleXML)) {
+
 
$xwReadable = $xwWritable = true;
+
# Set perms for this request
if (strtolower($xwArticleTitle) == 'xmlwiki.php') $xwWritable = strtolower($xwUserName) == 'nad';
+
$xwReadable = 0 < count(array_intersect($xwArticleReadableBy, $xwUserGroups));
 +
$xwWritable = 0 < count(array_intersect($xwArticleWritableBy, $xwUserGroups));
 +
if ($xwDebug) {
 +
xwMessage('Groups: '.join(', ', $xwUserGroups));
 +
xwMessage("Readable ($xwReadable): ".join(', ', $xwArticleReadableBy));
 +
xwMessage("Writable ($xwWritable): ".join(', ', $xwArticleWritableBy));
 
}
 
}
 +
 +
# Divert to access-denied article if not readable
 +
if (!$xwReadable) {
 +
xwMessage('no');
 +
$action = 'view';
 +
$xwSave = $xwEdit = false;
 +
$wgTitle = Title::newFromText('Sorry, the requested article was not readable.');
 +
}
 +
  
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
Line 96: Line 137:
 
function xwInputHook() {
 
function xwInputHook() {
  
global $xwArticle, $xwArticleTitle, $xwMetaArticle, $xwReadable, $xwWritable;
+
global $xwArticle, $xwArticleXML, $xwArticleTitle, $xwWritable, $_REQUEST;
global $xwDebug, $xwSave;
+
global $xwDebug, $xwSave, $xwEdit, $action, $xwUserName, $wgArticle;
 
if ($xwDebug) xwMessage('xwInputHook()','green');
 
if ($xwDebug) xwMessage('xwInputHook()','green');
  
# If an article-change is being attempted,
+
# If not writable, change action to view
if ($xwSave) {
+
if (!$xwWritable && ($action != 'view')) {
if (!$xwReadable || !$xwWritable) {
+
xwMessage('Sorry, article not writable, action changed to "view".', 'red');
# Not writable, change action to view
+
$action = 'view';
xwMessage('Cannot access article for writing, changing action to "view"', 'red');
+
$xwSave = $xwEdit = false;
global $action;
+
}
$action='view';
 
}
 
else {
 
# Parse POST for xmlWiki form input
 
global $_REQUEST;
 
foreach ($_REQUEST as $k => $v) {
 
  
if (is_object($xwArticle) && preg_match("/^xw:(@?)(\\w*):(.+)$/", $k, $m)) {
+
# Parse POST for XPath inputs
# Form-input-name is "xw:[[@]node]:XPathQuery" which directs the value into xwArticle (if DOM)
+
# - Form-input-name is "xw:[[@]node]:XPathQuery" which directs the value into xwArticle (if DOM)
# - if "node" exists, then a new element (or att if "@node") is created for the value
+
# - if "node" exists, then a new element (or att if "@node") is created for the value
# - hidden-inputs can direct input-handlers into the transform list
+
# - hidden-inputs can direct input-handlers into the transform list
list(, $att, $node, $xpath) = $m;
+
foreach ($_REQUEST as $k => $v) {
foreach (xwXPathQuery($xwArticleXML, $xpath) as $result) {
+
if (is_object($xwArticleXML) && preg_match("/^xw:(@?)(\\w*):(.+)$/", $k, $m)) {
if ($att && $node) {
+
list(, $att, $node, $xpath) = $m;
# create attribute for value
+
foreach (xwXPathQuery($xwArticleXML, $xpath) as $result)
}
+
if ($node) {
elseif ($node) {
+
# create new element/attribute in result-node
# create element for value
+
if ($att) $node = $xwArticleXML->create_attribute($node, $v);
}
+
else {
else {
+
$node = $xwArticleXML->create_element($node);
# overwrite existing node content
+
$node->set_content($v);
}
 
}
 
}
 
 
 
elseif ($xwMetaArticle && ($k == 'wpSave')) {
 
# if it's a pseudo-namespace, wpSave should be valid XML to be saved
 
xwDomificate($wpSave);
 
if (!is_object($wpSave)) {
 
xwMessage('A meta-article must be valid XML! changing action to "edit"', 'red');
 
global $action;
 
$action='edit';
 
 
}
 
}
 +
$result->append_child($node);
 
}
 
}
 +
else $result->set_content($v); # node not set, just replace current-result-value
 +
}
 +
}
  
 +
# if it's a pseudo-namespace...
 +
if (preg_match('/^(xml)|(sys):/', $xwArticleTitle)) {
 +
 +
# wpSave should be valid XML to be saved
 +
if ($xwSave) {
 +
xwDomificate($wpSave);
 +
if (!is_object($wpSave)) {
 +
xwMessage('A meta-article must be valid XML! changing action to "edit"', 'red');
 +
$action='edit';
 
}
 
}
 
}
 
}
 +
 
}
 
}
 +
 
}
 
}
  
Line 158: Line 197:
 
if ($xwDebug) xwMessage('xwOutputHook()','green');
 
if ($xwDebug) xwMessage('xwOutputHook()','green');
  
# If readable, get wiki-environment, else clear content
+
# Generate output page
if ($xwReadable) {
+
$wgUser->setOption('skin', 'xwskin');
$wgUser->setOption('skin', 'xwskin');
+
$wgOut->output();
$wgOut->output();
 
}
 
else {
 
$xwArticle = '';
 
xwMessage('Cannot access article for reading!', 'red');
 
}
 
 
 
# If no write-access, remove edit link
 
if (!$xwWritable) unset($xwArticleLinks['Edit']);
 
  
# NOTE - Why not move this into INPUT-HOOK????????
+
# NOTE - Why not move this into INPUT-HOOK???????? - or maybe not....?
 
# If saving, check the publishing list
 
# If saving, check the publishing list
 
if ($xwSave) {
 
if ($xwSave) {
Line 201: Line 231:
  
 
# Insert messages into final html and output
 
# Insert messages into final html and output
if ($xwDebug && !$xwParserHookCalled) xwMessage('PARSER-HOOK was not called','green');
+
if ($xwDebug) {
if ($xwDebug && !$xwSkinHookCalled) xwMessage('SKIN-HOOK was not called','green');
+
if (!$xwParserHookCalled) xwMessage('PARSER-HOOK was not called','green');
xwTransformAddMessages();
+
if (!$xwSkinHookCalled) xwMessage('SKIN-HOOK was not called','green');
 +
if (is_object($xwArticleXML)) xwMessage($xwArticleXML->dump_mem(true),'purple');
 +
}
 +
xwAddMessages();
 
print $xwArticle;
 
print $xwArticle;
 
}
 
}
Line 218: Line 251:
  
 
# If this text-fragment is our article, apply transforms
 
# If this text-fragment is our article, apply transforms
#xwMessage($text,'purple');
 
#xwMessage($xwArticle,'purple');
 
 
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('- PARSER-HOOK has recognised the article');
+
$xwArticle = $text; # needed because xwTransform works on global article
 
 
 
# If attempted xml encountered, try to domificate and transform
 
# If attempted xml encountered, try to domificate and transform
if (preg_match("/^<\\?xml/i", $text)) {
+
if (preg_match("/^<\\?xml/i", $xwArticle)) {
 
$transformed = true;
 
$transformed = true;
xwDomificateArticle($text, 'ParserHook/text');
+
xwDomificateArticle($xwArticle, 'ParserHook/text');
xwTransformArticle($text);
+
xwTransformArticle();
 
}
 
}
 
 
# 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;
 
if (!is_object($wgGeshiSyntaxSettings)) require_once('extensions/geshi/geshi.php');
 
if (!is_object($wgGeshiSyntaxSettings)) require_once('extensions/geshi/geshi.php');
if ($lang = xwArticleType($xwArticleTitle, $text)) {
+
if ($lang = xwArticleType($xwArticleTitle, $xwArticle)) {
if ($xwDebug) xwMessage("- Geshi: This is \"$lang\".");
+
if ($xwDebug) xwMessage("Geshi: This is \"$lang\".");
 
# Replace tabs with spaces (better than geshi)
 
# Replace tabs with spaces (better than geshi)
$text = preg_replace_callback("/^(.*?)(\\t+)/m", 'xwReplaceTabs', $text);
+
$xwArticle = preg_replace_callback("/^(.*?)(\\t+)/m", 'xwReplaceTabs', $xwArticle);
 
# Set up a geshi-parser
 
# Set up a geshi-parser
$geshi = new GeSHi($text, $lang == 'xslt' ? 'xml' : $lang, 'extensions/geshi/geshi');
+
$geshi = new GeSHi($xwArticle, $lang == 'xslt' ? 'xml' : $lang, 'extensions/geshi/geshi');
 
$geshi->set_header_type(GESHI_HEADER_DIV);
 
$geshi->set_header_type(GESHI_HEADER_DIV);
 
#$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
 
#$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
Line 259: Line 288:
 
@$geshi->set_regexps_style('color:green',true);
 
@$geshi->set_regexps_style('color:green',true);
 
# Do the parse
 
# Do the parse
$text = preg_replace("/^<div.*?>/", '<div class="xwcode">', $geshi->parse_code());
+
$xwArticle = preg_replace("/^<div.*?>/", '<div class="xwcode">', $geshi->parse_code());
 
$transformed = true;
 
$transformed = true;
 
}
 
}
elseif ($xwDebug) xwMessage('- Geshi: Not my problem.');
+
elseif ($xwDebug) xwMessage('Geshi: Not my problem.');
 
}
 
}
 +
$text = $xwArticle;
 
}
 
}
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 $transformed;
Line 273: Line 302:
 
 
 
# Converts tabs to spaces better than geshi
 
# Converts tabs to spaces better than geshi
function xwReplaceTabs($m) {
+
function xwReplaceTabs($m) { return $m[1].str_repeat(' ',strlen($m[2])*4-strlen($m[1])%4); }
return $m[1].str_repeat(' ',strlen($m[2])*4-strlen($m[1])%4);
 
}
 
 
 
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
Line 338: Line 365:
 
function xwDomificateArticle(&$article, $id = '') {
 
function xwDomificateArticle(&$article, $id = '') {
 
global $xwDebug;
 
global $xwDebug;
if ($xwDebug) xwMessage("xwDomificatwArticle(XML,$id)",'blue');
+
if ($xwDebug) xwMessage("xwDomificateArticle(XML,$id)",'blue');
 
ob_start();
 
ob_start();
 
if ($dom = domxml_open_mem($article)) $article = $dom;
 
if ($dom = domxml_open_mem($article)) $article = $dom;
 
else {
 
else {
 
# Could not convert, extract error messages from output
 
# Could not convert, extract error messages from output
xwMessage("- Failed :-(", 'red');
+
xwMessage("Failed :-(", 'red');
 
xwExtractMessages();
 
xwExtractMessages();
 
}
 
}
Line 354: Line 381:
 
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(DOM,$id)",'blue');
 +
# TODO:
 
# Validate, report errors
 
# Validate, report errors
 
# xwApplyXSLT($article, $xslt) if xml referrs one
 
# xwApplyXSLT($article, $xslt) if xml referrs one
 
# Convert DOM back to XML if still an object
 
# Convert DOM back to XML if still an object
 
if (is_object($article)) $article = $article->dump_mem(true);
 
if (is_object($article)) $article = $article->dump_mem(true);
 +
return true;
 +
}
 +
 +
# Transform an article object
 +
# - Article is undomificated on exit
 +
# - Article remains unchanged if not DOM
 +
function xwTransformArticle() {
 +
global $xwDebug, $xwArticle, $xwArticleXML, $xwArticleTitle;
 +
# xml:article must be an object...
 +
if (is_object($xwArticleXML)) {
 +
$docType = $xwArticleXML->doctype();
 +
# ...and must be xmlwiki:* doctype
 +
if (ereg('^xmlwiki:', $docType->name)) {
 +
$root = $xwArticle->document_element();
 +
if ($xwDebug) xwMessage("xwTransformArticle()",'blue');
 +
# Loop thru transforms applying each
 +
foreach ($root->get_elements_by_tagname('transform') as $tElement) {
 +
$tName = $tElement->get_content();
 +
# Transform is not local, get transform-article and apply to current-article-object
 +
if ($tText = xwArticleContent($tName)) {
 +
# Get transform type
 +
$tType = xwArticleType($tName, $tText);
 +
if ($tType == 'xslt') xwApplyXSLT($xwArticle, $tText, $tName);
 +
elseif ($tType == 'php') {
 +
# TODO: php, check perms and execute (trapping errors for reporting - unless fatal)
 +
if ($perms_check_out_ok = true) {
 +
ob_start();
 +
eval("?>$tText<?");
 +
xwExtractMessages();
 +
ob_end_clean();
 +
}
 +
}
 +
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: xml:article must be doctype:xmlwiki!",'red');
 +
}
 +
else xwMessage("xwTransformArticle(NON-OBJECT)",'red');
 +
# If article is still a DOM object, stringify it once and for all
 +
if (is_object($xwArticle)) xwUndomificateArticle($xwArticle, $xwArticleTitle);
 
return true;
 
return true;
 
}
 
}
Line 369: Line 438:
 
if ($xwDebug) xwMessage("xwApplyXSLT(DOM,$id)",'blue');
 
if ($xwDebug) xwMessage("xwApplyXSLT(DOM,$id)",'blue');
 
ob_start();
 
ob_start();
$tObject = domxml_xslt_stylesheet($xslt);
+
if ($tObject = domxml_xslt_stylesheet($xslt)) {
$tResult = $tObject->process($article);
+
$tResult = $tObject->process($article);
# If output-method is html, use XSLT's dump_mem to create an html-string
+
# If output-method is html, use XSLT's dump_mem to create an html-string
if (preg_match('/<xsl:output +?method *= *"html"/i', $xslt))
+
if (preg_match('/<xsl:output +?method *= *"html"/i', $xslt))
$tResult = $tObject->result_dump_mem($tResult);
+
$tResult = $tObject->result_dump_mem($tResult);
 +
}
 
xwExtractMessages();
 
xwExtractMessages();
 
ob_end_clean();
 
ob_end_clean();
if ($tResult) $article = $tResult; else return false;
+
if (isset($tResult)) $article = $tResult; else return false;
 
return true;
 
return true;
 
}
 
}
Line 388: Line 458:
 
if ($xpath = $context->xpath_eval($query)) $result = $xpath->nodeset;
 
if ($xpath = $context->xpath_eval($query)) $result = $xpath->nodeset;
 
else {
 
else {
xwMessage('- Query Failed! :-(', 'red');
+
xwMessage('Query Failed! :-(', 'red');
 
$result = array();
 
$result = array();
 
}
 
}
Line 395: Line 465:
 
return $result;
 
return $result;
 
}
 
}
 
# Transform an article object
 
# - Article must be an xmlwiki:* DOM object to be transformable
 
# - Article is undomificated on exit
 
# - Article remains unchanged if not DOM
 
function xwTransformArticle(&$article) {
 
 
# Get doc info or exit if not one of ours
 
if (!is_object($article)) return false;
 
$aType = $article->doctype();
 
$aType = $aType->name;
 
$aRoot = $article->document_element();
 
#if (!ereg('^xmlwiki:', $aType)) return false; # this must change to meta article
 
global $xwDebug;
 
if ($xwDebug) xwMessage("xwTransformArticle(xmlwiki:$aType)",'blue');
 
 
# Loop thru transforms applying each
 
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, as XSLT-PI's may be a better structure
 
$tName = $m[1];
 
if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult;
 
else xwMessage("- Unknown transform \"$tName\"!", 'red');
 
}
 
else {
 
# Transform is not local, get transform-article and apply to current-article-object
 
if ($tText = xwArticleContent($tName)) {
 
# Get transform type
 
$tType = xwArticleType($tName, $tText);
 
if ($tType == 'xslt') xwApplyXSLT($article, $tText, $tName);
 
elseif ($tType == 'php') {
 
# php, check perms and execute (trapping errors for reporting - unless fatal)
 
if ($perms_check_out_ok) {
 
ob_start();
 
eval($tText);
 
xwExtractMessages();
 
ob_end_clean();
 
}
 
}
 
elseif ($tType == 'css') {
 
# include CSS ref in content
 
}
 
}
 
else xwMessage("- No such transform-article \"$tName\"!", 'red');
 
}
 
}
 
 
# If article is still a DOM object, stringify it once and for all
 
xwUndomificateArticle($article);
 
return true;
 
}
 
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# TRANSFORMS
 
# - these transforms are only temporarily here - they should be articles
 
# - we should use DTD's for each kind: document, image, layout, table
 
  
 
# 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
# - This is temporary
+
# - TEMP: see skin-hook
# - It should return the page as a DOM object to be transformed by xmlwiki.xslt
 
 
function xwTransformPageLayout() {
 
function xwTransformPageLayout() {
  
global $xwMsgToken, $xwArticle, $xwTemplate, $xwDebug;
+
global $xwMsgToken, $xwArticle, $xwTemplate, $xwDebug, $xwWritable;
 
if ($xwDebug) xwMessage("xwTransformPageLayout()",'blue');
 
if ($xwDebug) xwMessage("xwTransformPageLayout()",'blue');
 
$tmpl = &$xwTemplate;
 
$tmpl = &$xwTemplate;
Line 498: Line 509:
 
$content .= '</div></div></div>';
 
$content .= '</div></div></div>';
  
# Action-links
+
# Action-links (remove edit link if not writable)
 
$actions = '<div id="p-cactions" class="portlet"><h5>Views</h5><ul>';
 
$actions = '<div id="p-cactions" class="portlet"><h5>Views</h5><ul>';
 
foreach ($data['content_actions'] as $key => $action) {
 
foreach ($data['content_actions'] as $key => $action) {
$actions .= '<li id="ca-'.htmlspecialchars($key).'"';
+
#if ($key == '
if ($action['class']) $actions .= ' class="'.htmlspecialchars($action['class']).'"';
+
#if (!(ereg('action=edit$', $action['href'])&&!$xwWritable)) {
$href = '<a href="'.htmlspecialchars($action['href']).'">'.htmlspecialchars($action['text']).'</a>';
+
$actions .= '<li id="ca-'.htmlspecialchars($key).'"';
$actions .= ">$href</li>";
+
if ($action['class']) $actions .= ' class="'.htmlspecialchars($action['class']).'"';
 +
$href = '<a href="'.htmlspecialchars($action['href']).'">'.htmlspecialchars($action['text']).'</a>';
 +
$actions .= ">$href</li>";
 +
# }
 
}
 
}
 
$actions .= '</ul></div>';
 
$actions .= '</ul></div>';
Line 632: Line 646:
 
}
 
}
 
 
 +
# ---------------------------------------------------------------------------------------------------------------------- #
 +
# UTILITY FUNCTIONS
 +
 
# Append messages to content
 
# Append messages to content
function xwTransformAddMessages() {
+
function xwAddMessages() {
 
global $xwArticle, $xwMessages, $xwMsgToken;
 
global $xwArticle, $xwMessages, $xwMsgToken;
 
if (count($xwMessages)) {
 
if (count($xwMessages)) {
 
 
$msg = '<div class="portlet" id="p-xwmessages"><h5>There are Messages</h5></div>';
 
$msg = '<div class="portlet" id="p-xwmessages"><h5>There are Messages</h5></div>';
 
$msg .= '<div class="xwmessage"><ul><li>'.join("</li><li>", $xwMessages).'</li></ul></div>';
 
$msg .= '<div class="xwmessage"><ul><li>'.join("</li><li>", $xwMessages).'</li></ul></div>';
 
$xwArticle = ereg_replace($xwMsgToken, $msg, $xwArticle);
 
$xwArticle = ereg_replace($xwMsgToken, $msg, $xwArticle);
 
}
 
}
}
 
 
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)
 
}
 
 
# 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;
 
}
 
 
# 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\"/>";
 
}
 
}
 
 
# A table object is an interface to a database table
 
# - this can't be implimented fully until the 'edit' hook is done
 
# - 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)
 
 
}
 
 
# Transform the body content to a xtml/javascript tree
 
function xwTransform_tree() {
 
# import php-tree-component
 
}
 
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# UTILITY FUNCTIONS
 
 
# Return a list of the hrefs found in the passed content
 
function xwExtractHrefs($content) {
 
preg_match_all('/(<a.+?>(.+)<\/a>)/', $content, $m);
 
$hrefs = array();
 
for ($i = 0; $i < count($m[0]); $i++) $hrefs[$m[2][$i]] = $m[1][$i];
 
return $hrefs;
 
 
}
 
}
  
Line 724: Line 664:
 
$err = preg_replace('/ in .+? on line.+?[0-9]+/', '', $err);
 
$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 ($msg) xwMessage("- $msg", 'red');
+
foreach (split("\n", $err) as $msg) if ($msg) xwMessage("$msg", 'red');
 
}
 
}
 
 
Line 739: Line 679:
 
print strtoupper("<br>$obj properties:<br>");
 
print strtoupper("<br>$obj properties:<br>");
 
foreach (get_object_vars($obj) as $k=>$v) print htmlentities("$k => $v")."<br>";
 
foreach (get_object_vars($obj) as $k=>$v) print htmlentities("$k => $v")."<br>";
 
 
print strtoupper("<br>$obj methods:<br>");
 
print strtoupper("<br>$obj methods:<br>");
 
foreach (get_class_methods($obj) as $k=>$v) print "$v<br>";
 
foreach (get_class_methods($obj) as $k=>$v) print "$v<br>";

Revision as of 21:39, 18 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:
  2. - Undomificate: validation, xslt
  3. - Transform: check perms on php-execution
  4. Publish:
  5. - xml:article - holds article publish-list
  6. - mirror/wiki-sync
  7. Transforms:
  8. - xml:article - now transforms are in here (main article is now like <body>)
  9. - so we don't do the "its one of ours" thing anymore
  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. Otherwise set up xmlWiki global environment ready for input and output processing

$xwMessages = array(); $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']); $xwUserName = $wgUser->mName; $xwUserGroups = array(strtolower($xwUserName), 'anyone', 'everyone', 'nobody', 'world'); $xwArticleReadableBy = array('anyone'); $xwArticleWritableBy = array('anyone'); $xwArticle = $xwPreview ? $wgRequest->gettext('wpTextbox1') : $wgArticle->getContent(false); $xwArticleTitle = $wgTitle->getPrefixedURL(); $xwArticleCache = array(); $xwArticleLinks = array(); $xwUserLinks = array(); $xwMetaArticle = false; $xwMsgToken = ; $xwTemplate = null; $xwParserHookCalled = false; $xwSkinHookCalled = false;

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. SECURITY
  1. Not currently used: R/W by user, for xmlWiki prefs etc

if ($xwUserXML = xwArticleContent("xml:$xwUserName")) xwDomificateArticle($xwUserXML, "xml:$xwUserName");

  1. R/W by admin only, holds users groups and other system info like stats
  2. - no sys:user means "anyone" in group "everyone" as far as perms goes

if ($xwUserSYS = xwArticleContent("sys:$xwUserName")) xwDomificateArticle($xwUserSYS, "sys:$xwUserName");

  1. If sys:user exists, get users' group-list

if (is_object($xwUserSYS)) { $xwRoot = $xwUserSYS->document_element(); if (count($xwList = $xwRoot->get_elements_by_tagname('groups'))) $xwUserGroups = array_merge($xwUserGroups, split(',', strtolower($xwList[0]->get_content()))); }

  1. - Security info is: owner, read-groups, write-groups (default values are "world")
  2. - Users and groups are interchangeable
  3. - Default user is "anyone", default group is "everyone"

if (ereg('^xml:(.+)$', $xwArticleTitle)) { # This is an xml-pseudo-namespace # - its perms apply to its 'parent' and to itself aswell $xwXML = $xwArticle; xwDomificateArticle($xwXML, $xwArticleTitle); if (is_object($xwXML)) { $xwRoot = $xwXML->document_element(); if (count($xwList = $xwRoot->get_elements_by_tagname('read'))) $xwArticleReadableBy = split(',', strtolower($xwList[0]->get_content())); if (count($xwList = $xwRoot->get_elements_by_tagname('write'))) $xwArticleWritableBy = split(',', strtolower($xwList[0]->get_content())); } } elseif (ereg('^sys:(.+)$', $xwArticleTitle)) { # This is a sys-pseudo-namespace # - only admin can read/write sys: $xwArticleReadableBy = $articleWritableBy = array('admin'); } else { # This is a normal article, get xml:article if ($xwArticleXML = xwArticleContent("xml:$xwArticleTitle")) xwDomificateArticle($xwArticleXML, "xml:$xwArticleTitle"); if (is_object($xwArticleXML)) { $xwRoot = $xwArticleXML->document_element(); if (count($xwList = $xwRoot->get_elements_by_tagname('read'))) $xwArticleReadableBy = split(',', $xwList = strtolower($xwList[0]->get_content())); if (count($xwList = $xwRoot->get_elements_by_tagname('write'))) $xwArticleWritableBy = split(',', $xwList = strtolower($xwList[0]->get_content())); } }

  1. Set perms for this request

$xwReadable = 0 < count(array_intersect($xwArticleReadableBy, $xwUserGroups)); $xwWritable = 0 < count(array_intersect($xwArticleWritableBy, $xwUserGroups)); if ($xwDebug) { xwMessage('Groups: '.join(', ', $xwUserGroups)); xwMessage("Readable ($xwReadable): ".join(', ', $xwArticleReadableBy)); xwMessage("Writable ($xwWritable): ".join(', ', $xwArticleWritableBy)); }

  1. Divert to access-denied article if not readable

if (!$xwReadable) { xwMessage('no'); $action = 'view'; $xwSave = $xwEdit = false; $wgTitle = Title::newFromText('Sorry, the requested article was not readable.'); }


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

function xwInputHook() {

global $xwArticle, $xwArticleXML, $xwArticleTitle, $xwWritable, $_REQUEST; global $xwDebug, $xwSave, $xwEdit, $action, $xwUserName, $wgArticle; if ($xwDebug) xwMessage('xwInputHook()','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; }

# Parse POST for XPath inputs # - Form-input-name is "xw:[[@]node]:XPathQuery" which directs the value into xwArticle (if DOM) # - if "node" exists, then a new element (or att if "@node") is created for the value # - hidden-inputs can direct input-handlers into the transform list foreach ($_REQUEST as $k => $v) { if (is_object($xwArticleXML) && preg_match("/^xw:(@?)(\\w*):(.+)$/", $k, $m)) { list(, $att, $node, $xpath) = $m; foreach (xwXPathQuery($xwArticleXML, $xpath) as $result) if ($node) { # create new element/attribute in result-node if ($att) $node = $xwArticleXML->create_attribute($node, $v); else { $node = $xwArticleXML->create_element($node); $node->set_content($v); } $result->append_child($node); } else $result->set_content($v); # node not set, just replace current-result-value } }

# if it's a pseudo-namespace... if (preg_match('/^(xml)|(sys):/', $xwArticleTitle)) {

# wpSave should be valid XML to be saved if ($xwSave) { xwDomificate($wpSave); if (!is_object($wpSave)) { xwMessage('A meta-article must be valid XML! changing action to "edit"', 'red'); $action='edit'; } }

}

}

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. OUTPUT HOOK
  1. Generate output to send back to client
  2. - if article is not XML, then wiki's parsed content is used

function xwOutputHook() {

global $wgOut, $wgUser, $xwParserHookCalled, $xwSkinHookCalled, $xwDebug; global $xwArticle, $xwArticleXML, $xwArticleTitle, $xwReadable, $xwWritable; global $xwView, $xwPreview, $xwSave, $xwRaw, $xwUserLinks, $xwArticleLinks; if ($xwDebug) xwMessage('xwOutputHook()','green');

# Generate output page $wgUser->setOption('skin', 'xwskin'); $wgOut->output();

# NOTE - Why not move this into INPUT-HOOK???????? - or maybe not....? # If saving, check the publishing list if ($xwSave) {

# Check publish-list and process if any if (false) { xwMessage('Publishing...'); # Publish each item 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'); } }

# Check mirror-list and process if any # - allows articles in separate wiki's to be syncronised # - does rudimentary security by sending a salt and response being a hash of the password generated from the salt # - this could be done for login too using ECMA-MD5 on browser if (false) { xwMessage('Mirroring...'); } }


# 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($xwArticleXML)) xwMessage($xwArticleXML->dump_mem(true),'purple'); } xwAddMessages(); print $xwArticle; }

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

function xwParserHook(&$text) {

global $xwRaw, $xwParserHookCalled, $xwDebug, $xwArticleTitle, $xwArticle; if ($xwDebug) xwMessage('xwParserHook(TEXT)','green'); $xwParserHookCalled = true; $transformed = 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("PARSER-HOOK: \"$xwArticleTitle\" text-fragment recognised and intercepted."); $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; if (!is_object($wgGeshiSyntaxSettings)) require_once('extensions/geshi/geshi.php'); if ($lang = xwArticleType($xwArticleTitle, $xwArticle)) { if ($xwDebug) xwMessage("Geshi: This is \"$lang\"."); # Replace tabs with spaces (better than geshi) $xwArticle = preg_replace_callback("/^(.*?)(\\t+)/m", '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());

$transformed = true; } elseif ($xwDebug) xwMessage('Geshi: Not my problem.'); } $text = $xwArticle; } 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 $transformed; }

  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; if ($xwDebug) xwMessage('xwSkinHook(TEMPLATE)','green'); $xwSkinHookCalled = true; $xwTemplate = $tmpl; $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 == '(There is currently no text in this page)') return false; 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/i", $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, $id = ) { global $xwDebug; if ($xwDebug) xwMessage("xwDomificateArticle(XML,$id)",'blue'); ob_start(); if ($dom = domxml_open_mem($article)) $article = $dom; else { # Could not convert, extract error messages from output 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(DOM,$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 object
  2. - Article is undomificated on exit
  3. - Article remains unchanged if not DOM

function xwTransformArticle() { global $xwDebug, $xwArticle, $xwArticleXML, $xwArticleTitle; # xml:article must be an object... if (is_object($xwArticleXML)) { $docType = $xwArticleXML->doctype(); # ...and must be xmlwiki:* doctype if (ereg('^xmlwiki:', $docType->name)) { $root = $xwArticle->document_element(); if ($xwDebug) xwMessage("xwTransformArticle()",'blue'); # Loop thru transforms applying each foreach ($root->get_elements_by_tagname('transform') as $tElement) { $tName = $tElement->get_content(); # Transform is not local, get transform-article and apply to current-article-object if ($tText = xwArticleContent($tName)) { # Get transform type $tType = xwArticleType($tName, $tText); if ($tType == 'xslt') xwApplyXSLT($xwArticle, $tText, $tName); elseif ($tType == 'php') { # TODO: php, check perms and execute (trapping errors for reporting - unless fatal) if ($perms_check_out_ok = true) { ob_start(); eval("?>$tText<?"); xwExtractMessages(); ob_end_clean(); } } 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: xml:article must be doctype:xmlwiki!",'red'); } else xwMessage("xwTransformArticle(NON-OBJECT)",'red'); # If article is still a DOM object, stringify it once and for all if (is_object($xwArticle)) xwUndomificateArticle($xwArticle, $xwArticleTitle); return true; }

  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) { global $xwDebug; if ($xwDebug) xwMessage("xwXPathQuery(DOM,\"$query\")",'blue'); ob_start(); $context = $dom->xpath_new_context(); if ($xpath = $context->xpath_eval($query)) $result = $xpath->nodeset; else { xwMessage('Query Failed! :-(', 'red'); $result = array(); } xwExtractMessages(); ob_end_clean(); return $result; }

  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, $xwTemplate, $xwDebug, $xwWritable; 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 = ''; # 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) { #if ($key == ' #if (!(ereg('action=edit$', $action['href'])&&!$xwWritable)) { $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. 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() { $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'); }

  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; }

?>