Difference between revisions of "Xmlwiki.php"

From Organic Design wiki
Line 15: Line 15:
  
 
# TODO:
 
# TODO:
 
+
# - Must deal with page-move: security and also pseudo-namespace moving (maybe use proper namespace?)
# - STACK: <data> & <view> instead of <transform>
+
# - setProperty needs to create element if not there so <output> not needed in swf properties
# - <onChange> (publish in OUTPUT-HOOK is to be <publish.php/> and in the onChange list
+
# - XPath bug with create element (is it the same setProperty bug?)
 +
# - also language-override in ming-swf.php can then be simpler too
 +
# - move geshi/wiki out to a default language.php (which uses the <language> element)
  
 
# - TransformArticle:
 
# - TransformArticle:
 
#  - VALIDATE: validate article-properties against xmlwiki:properties.dtd (does PHP4 do this?)
 
#  - VALIDATE: validate article-properties against xmlwiki:properties.dtd (does PHP4 do this?)
 
#  - TRAP: fatal errors in php-transform execution
 
#  - TRAP: fatal errors in php-transform execution
#  - 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!)
Line 28: Line 29:
  
 
# BUGS:
 
# BUGS:
 +
# - Rob is admin and can't write to Nad pages
 +
# - Pages can be moved regardless of perms!!!
 +
# - login error goes to old-wiki
 
# - tabReplace doesn't work for /^\t+.+\t.+$/
 
# - tabReplace doesn't work for /^\t+.+\t.+$/
  
Line 41: Line 45:
 
defined('MEDIAWIKI') or die('xmlwiki.php must be included from MediaWiki\'s index.php!');
 
defined('MEDIAWIKI') or die('xmlwiki.php must be included from MediaWiki\'s index.php!');
  
# Allows Geshi to be installed by PARSER-HOOK if not installed th usual way already
+
# Allows Geshi to be installed by PARSER-HOOK if not installed the usual way already
if (!@is_object($wgGeshiSyntaxSettings)) $wgGeshiSyntaxSettings = false;
+
# - still needs to be present in ./extensions though
 +
if (!@is_object($wgGeshiSyntaxSettings)) die('xmlWiki requires the Geshi syntax-highlighting extension');
  
 
# System globals
 
# System globals
Line 48: Line 53:
 
$xwMessages = array();
 
$xwMessages = array();
 
$xwArticleCache = array();
 
$xwArticleCache = array();
 +
$xwStyleSheets = array();
 
$xwTemplate = null;
 
$xwTemplate = null;
 
$xwParserHookCalled = false;
 
$xwParserHookCalled = false;
Line 73: Line 79:
 
$xwArticleReadableBy = array('anyone');
 
$xwArticleReadableBy = array('anyone');
 
$xwArticleWritableBy = array('anyone');
 
$xwArticleWritableBy = array('anyone');
 +
$xwLanguage = '';
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# SECURITY
 
# SECURITY
Line 91: Line 98:
 
$xwArticleReadableBy = xwGetListByTagname($xwArticleProperties, 'read') + array('anyone');
 
$xwArticleReadableBy = xwGetListByTagname($xwArticleProperties, 'read') + array('anyone');
 
$xwArticleWritableBy = xwGetListByTagname($xwArticleProperties, 'write') + array('anyone');
 
$xwArticleWritableBy = xwGetListByTagname($xwArticleProperties, 'write') + array('anyone');
if ($xwIsProperties) unset($xwArticleProperties);
+
if ($xwIsProperties) $xwArticleProperties = false;
 
}
 
}
 +
 +
# Merge this articles properties with default-properties
 +
if (is_object($xwDefaultProperties = xwDomificateArticle(xwArticleContent('default-properties.xml'),'defaults')))
 +
$xwArticleProperties = xwMergeDOM($xwDefaultProperties, $xwArticleProperties);
 +
 +
# Get language from properties, or articleType()
 +
$xwLanguage = count($results = xwXPathQuery($xwArticleProperties, '/properties/language'))
 +
? strtolower($results[0]->get_content())
 +
: xwArticleType($xwArticleTitle, $xwArticle);
  
 
# Set perms for this request
 
# Set perms for this request
Line 188: Line 204:
 
if (!$xwParserHookCalled) xwMessage('PARSER-HOOK was not called','green');
 
if (!$xwParserHookCalled) xwMessage('PARSER-HOOK was not called','green');
 
if (!$xwSkinHookCalled) xwMessage('SKIN-HOOK was not called','green');
 
if (!$xwSkinHookCalled) xwMessage('SKIN-HOOK was not called','green');
#if (is_object($xwArticleProperties)) xwMessage($xwArticleProperties->dump_mem(true),'purple');
+
if (is_object($xwArticleProperties)) xwMessage($xwArticleProperties->dump_mem(true),'purple');
 
}
 
}
 
xwAddMessages();
 
xwAddMessages();
Line 199: Line 215:
 
function xwParserHook(&$text) {
 
function xwParserHook(&$text) {
  
global $xwRaw, $xwParserHookCalled, $xwDebug, $xwArticleTitle, $xwArticle, $xwReadable;
+
global $xwArticleTitle, $xwArticle, $xwArticleProperties;
 +
global $xwRaw, $xwParserHookCalled, $xwDebug, $xwReadable, $xwLanguage;
 
if ($xwDebug) xwMessage('PARSER-HOOK:','green');
 
if ($xwDebug) xwMessage('PARSER-HOOK:','green');
 
$xwParserHookCalled = true;
 
$xwParserHookCalled = true;
$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 or strncmp($text, xwArticleContent($xwArticleTitle), 100) == 0) {
 
if ($xwDebug) xwMessage("Matching text-fragment intercepted.");
 
if ($xwDebug) xwMessage("Matching text-fragment intercepted.");
if (!$xwReadable) { $text=''; xwMessage('Sorry, article not readable!','red'); }
+
if (!$xwReadable) { xwMessage('Sorry, article not readable!','red'); return $text = false; }
$xwArticle = $text; # needed because xwTransform works on global article
+
# Apply data-transforms
# If attempted xml encountered, try to domificate and transform
+
# - domificate first if xml
if (preg_match("/^<\\?xml/i", $xwArticle)) {
+
# - undomificate after if still an object
$transformed = true;
+
if (preg_match("/^<\\?xml/i", $text)) xwDomificateArticle($text, 'ParserHook/text');
xwDomificateArticle($xwArticle, 'ParserHook/text');
+
if (is_object($xwArticleProperties)) {
}
+
$docType = $xwArticleProperties->doctype();
xwTransformArticle();
+
$root = $xwArticleProperties->document_element();  
# If not a 'raw' request, try some Geshi on
+
# If xmlwiki:properties doctype, apply data-transforms
if (!$xwRaw) {
+
if (ereg('^xmlwiki:properties', $docType->name)) {
global $wgGeshiSyntaxSettings, $xwArticleProperties;
+
if ($xwDebug) xwMessage('Applying "data" transforms.');
if (!is_object($wgGeshiSyntaxSettings)) require_once('extensions/geshi/geshi.php');
+
while (count($tNodes = $root->get_elements_by_tagname('data'))) {
+
$tName = $tNodes[0]->get_content();
# Get language from properties, or articleType()
+
xwApplyTransform($tName, $text, $xwArticleTitle, $xwArticleProperties, 'data');
$lang = count($results = xwXPathQuery($xwArticleProperties, '/properties/language'))
+
$tNodes[0]->unlink_node();
  ? 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.*?>/", '<div class="xwcode">', $geshi->parse_code());
 
 
}
 
}
 +
else xwMessage('Could not transform: article-properties must be xmlwiki:properties doctype!','red');
 
}
 
}
$text = $xwArticle;
+
if (is_object($text)) xwUndomificateArticle($text, $xwArticleTitle);  
if ($xwDebug) xwMessage('Content language determined as '.($lang?strtoupper($lang):'WIKI'));
 
 
}
 
}
 
elseif ($xwDebug) xwMessage('Parsing other content');
 
elseif ($xwDebug) xwMessage('Parsing other content');
 
 
 +
if ($xwDebug) xwMessage('Content language determined as '.($xwLanguage ? strtoupper($xwLanguage) : 'WIKI'));
 
# 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 $lang == '';
+
return $xwLanguage == '';
 
}
 
}
 
 
# Converts tabs to spaces better than geshi
 
function xwReplaceTabs($m) { return $m[1].str_repeat(' ',strlen($m[2])*4-strlen($m[1])%4); }
 
  
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
Line 270: Line 259:
 
function xwSkinHook(&$tmpl) {
 
function xwSkinHook(&$tmpl) {
  
global $xwArticle, $xwTemplate, $xwDebug, $xwSkinHookCalled, $xwReadable, $xwRaw;
+
global $xwArticle, $xwArticleTitle, $xwArticleProperties;
 +
global $xwTemplate, $xwDebug, $xwSkinHookCalled, $xwReadable, $xwRaw;
  
 +
# Extract article from existing template structure
 
if ($xwDebug) xwMessage('SKIN-HOOK:','green');
 
if ($xwDebug) xwMessage('SKIN-HOOK:','green');
 
$xwSkinHookCalled = true;
 
$xwSkinHookCalled = true;
 
$xwTemplate = $tmpl;
 
$xwTemplate = $tmpl;
 
if ($xwReadable) $xwArticle = $xwTemplate->data['bodytext'];
 
if ($xwReadable) $xwArticle = $xwTemplate->data['bodytext'];
#$xslt = xwArticleContent('xmlwiki.xslt');
 
 
# Do xmlWiki page-layout transform
 
 
xwTransformPageLayout(); # should be: xwApplyXSLT($xwArticle, $xslt, 'xmlwiki.xslt');
 
  
 +
# Apply view-transforms
 +
# - "default-skin.php" builds XML output structure
 +
# - "default-skin.xslt" transforms XML to HTML with table-layout
 +
# - "default-skin.css" transforms HTML with design
 +
if (is_object($xwArticleProperties)) {
 +
$docType = $xwArticleProperties->doctype();
 +
$root = $xwArticleProperties->document_element();
 +
# If xmlwiki:properties doctype, apply view-transforms
 +
if (ereg('^xmlwiki:properties', $docType->name)) {
 +
if ($xwDebug) xwMessage('Applying "view" transforms.');
 +
while (count($tNodes = $root->get_elements_by_tagname('view'))) {
 +
$tName = $tNodes[0]->get_content();
 +
xwApplyTransform($tName, $xwArticle, $xwArticleTitle, $xwArticleProperties, 'view');
 +
$tNodes[0]->unlink_node();
 +
}
 +
}
 +
else xwMessage('Could not transform: article-properties must be xmlwiki:properties doctype!','red');
 +
}
 
}
 
}
 
 
Line 340: Line 344:
 
}
 
}
 
ob_end_clean();
 
ob_end_clean();
 +
return $article;
 
}
 
}
  
Line 353: Line 358:
 
# 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
 
# - 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;
 
return true;
 
}
 
}
Line 388: Line 363:
 
# Apply a transform
 
# Apply a transform
 
function xwApplyTransform($transform, &$article, $title, &$properties, $listName) {
 
function xwApplyTransform($transform, &$article, $title, &$properties, $listName) {
global $xwDebug, $xwRaw;
+
global $xwDebug;
 
if ($transform) {
 
if ($transform) {
if ($xwDebug) xwMessage("Applying \"$listName\" transforms.");
 
 
if ($tText = xwArticleContent($transform)) {
 
if ($tText = xwArticleContent($transform)) {
 
$tType = xwArticleType($transform, $tText);
 
$tType = xwArticleType($transform, $tText);
if ($tType == 'xslt') xwApplyXSLT($article, $tText, $transform);
+
if ($tType == 'xslt') xwApplyXSLT($article, $tText, $transform);
elseif ($tType == 'php') xwApplyPHP($article, $tText, $transform, $title, $properties);
+
elseif ($tType == 'php') xwApplyPHP($article, $tText, $transform, $title, $properties, $listName);
elseif ($tType == 'css') xwMessage("CSS's from transform-list aren't applied yet :-(", 'red');
+
elseif ($tType == 'css') xwApplyCSS($transform);
elseif ($tType == 'xml') xwMessage("XML's from transform-list aren't applied yet :-(", 'red');
+
elseif ($tType == 'xml') xwMergeDOM($properties, xwDomificateArticle($tText, $transform));
 
}
 
}
else xwMessage("Unknown transform \"$tName\" found in \"$listName\" list!", 'red');
+
else xwMessage("Unknown transform \"$transform\" found in \"$listName\" list!", 'red');
 
}
 
}
 
}
 
}
  
 
# Apply PHP-transform if article perms are only writable by admin or dev
 
# Apply PHP-transform if article perms are only writable by admin or dev
function xwApplyPHP(&$article, &$code, $tName, $title, &$properties) {
+
function xwApplyPHP(&$article, &$code, $tName, $title, &$properties, $listName) {
 
# Get transform perms
 
# Get transform perms
if ($tProperties = xwArticleContent($id = 'xml:'.ucwords($tName)))
+
if ($tProperties = xwArticleContent($id = 'xml:'.ucwords($tName))) xwDomificateArticle($tProperties, $id);
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 419: Line 392:
 
xwExtractMessages();
 
xwExtractMessages();
 
ob_end_clean();
 
ob_end_clean();
 +
}
 +
 +
# Append CSS stylesheet link to output
 +
function xwApplyCSS($cssName) {
 +
global $xwDebug, $xwStyleSheets;
 +
if ($xwDebug) xwMessage("xwApplyCSS($cssName)",'blue');
 +
$xwStyleSheets[] = $cssName;
 
}
 
}
  
Line 485: Line 465:
 
}
 
}
  
# Surrounds the article with HTML structural page-layout containing a ref to xmlwiki.css
 
# - 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 = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="';
 
$html .= htmlspecialchars($data['lang']).'" lang="'.htmlspecialchars($data['lang']).'" dir="';
 
$html .= htmlspecialchars($data['dir']).'">';
 
 
# 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 = '<head><meta http-equiv="Content-Type" content="'.htmlspecialchars($data['mimetype']);
 
$head .= '; charset='.htmlspecialchars($data['charset']).'" />'.$data['headlinks'];
 
$head .= '<link rel="stylesheet" type="text/css" href="/wiki/index.php?title=xmlwiki.css&action=raw&gen=css" />';
 
 
$head .= '<title>'.htmlspecialchars($data['pagetitle']).'</title>';
 
$head .= '</head>';
 
 
# Body opening tag
 
$body = '<body ';
 
if ($data['body_ondblclick']) $body .= 'ondblclick="'.htmlspecialchars($data['body_ondblclick']).'" ';
 
if ($data['nsclass']) $body .= 'class="'.htmlspecialchars($data['nsclass']).'"';
 
$body .= '>';
 
 
# Content
 
$content = '<div id="column-content">';
 
$content .= '<div id="content"><a name="top" id="contentTop"></a>';
 
if ($data['sitenotice']) $content .= '<div id="siteNotice">'.$data['sitenotice'].'</div>';
 
$content .= '<h1 class="firstHeading">'.htmlspecialchars($data['title']).'</h1>';
 
$content .= '<div id="bodyContent">';
 
$content .= '<h3 id="siteSub">'.htmlspecialchars($tmpl->translator->translate('tagline')).'</h3>';
 
$content .= '<div id="contentSub">'.$data['subtitle'].'</div>';
 
if ($data['undelete']) $content .= '<div id="contentSub">'.$data['undelete'].'</div>';
 
if ($data['newtalk']) $content .= '<div class="usermessage">'.$data['newtalk'].'</div>';
 
$content .= "<!-- start content -->$xwArticle"; # <---- Actual article content inserted here
 
if ($data['catlinks']) $content .= '<div id="catlinks">'.$data['catlinks'].'</div>';
 
$content .= '<!-- end content --><div class="visualClear"></div>';
 
$content .= '</div></div></div>';
 
 
# Action-links (remove edit link if not writable)
 
$actions = '<div id="p-cactions" class="portlet"><h5>Views</h5><ul>';
 
 
foreach ($data['content_actions'] as $key => $action) {
 
$actions .= '<li id="ca-'.htmlspecialchars($key).'"';
 
if (!empty($action['class'])) $actions .= ' class="'.htmlspecialchars($action['class']).'"';
 
$href = '<a href="'.htmlspecialchars($action['href']).'">'.htmlspecialchars($action['text']).'</a>';
 
$actions .= ">$href</li>";
 
}
 
$actions .= '</ul></div>';
 
 
# Personal-links
 
$personal = '<div class="portlet" id="p-personal">';
 
$personal .= '<h5>'.htmlspecialchars($tmpl->translator->translate('personaltools')).'</h5>';
 
$personal .= '<div class="pBody"><ul>';
 
foreach ($data['personal_urls'] as $key => $item) {
 
$personal .= '<li id="pt-'.htmlspecialchars($key).'">';
 
$href = '<a href="'.htmlspecialchars($item['href']).'"';
 
if (!empty($item['class'])) $href .= 'class="'.htmlspecialchars($item['class']).'"';
 
$href .= '>'.htmlspecialchars($item['text']).'</a>';
 
$personal .= "$href</li>";
 
}
 
$personal .= "</ul></div></div>";
 
 
# Logo
 
$logo = '<div class="portlet" id="p-logo">';
 
$logo .= '<a style="background-image: url('.htmlspecialchars($data['logopath']).');" ';
 
$logo .= 'href="'.htmlspecialchars($data['nav_urls']['mainpage']['href']).'" ';
 
$logo .= 'title="'.htmlspecialchars($tmpl->translator->translate('mainpage')).'"></a></div>';
 
$logo .= '<script type="text/javascript"> if (window.isMSIE55) fixalpha(); </script>';
 
 
# Navigation
 
$nav = '<div class="portlet" id="p-nav">';
 
$nav .= '<h5>'.htmlspecialchars($tmpl->translator->translate('navigation')).'</h5>';
 
$nav .= '<div class="pBody"><ul>';
 
foreach ($data['navigation_urls'] as $navlink) {
 
$nav .= "<li id=\"".htmlspecialchars($navlink['id'])."\">";
 
$nav .= '<a href="'.htmlspecialchars($navlink['href']).'">'.htmlspecialchars($navlink['text']).'</a>';
 
$nav .= '</li>';
 
}
 
$nav .= '</ul></div></div>';
 
 
# Search
 
$search = '<div id="p-search" class="portlet">';
 
$search .= '<h5><label for="searchInput">'.htmlspecialchars($tmpl->translator->translate('search')).'</label></h5>';
 
$search .= '<div class="pBody">';
 
$search .= '<form name="searchform" action="'.htmlspecialchars($data['searchaction']).'" id="searchform">';
 
$search .= '<input id="searchInput" name="search" type="text"';
 
if ($tmpl->haveMsg('accesskey-search')) $search .= ' accesskey="';
 
$search .= htmlspecialchars($tmpl->translator->translate('accesskey-search')).'"';
 
if (isset($data['search'])) $search .= ' value="'.htmlspecialchars($data['search']).'"';
 
$search .= ' />';
 
$search .= '<input type="submit" name="go" class="searchButton" id="searchGoButton" value="';
 
$search .= htmlspecialchars($tmpl->translator->translate('go')).'" />';
 
$search .= '&nbsp;<input type="submit" name="fulltext" class="searchButton" value="';
 
$search .= htmlspecialchars($tmpl->translator->translate('search')).'" />';
 
$search .= '</form></div></div>';
 
 
# Toolbox
 
$toolbox = '<div class="portlet" id="p-tb">';
 
$toolbox .= '<h5>'.htmlspecialchars($tmpl->translator->translate('toolbox')).'</h5>';
 
$toolbox .= '<div class="pBody"><ul>';
 
if ($data['notspecialpage']) {
 
foreach (array('whatlinkshere', 'recentchangeslinked') as $special ) {
 
$href = '<a href="'.htmlspecialchars($data['nav_urls'][$special]['href']).'">';
 
$href .= htmlspecialchars($tmpl->translator->translate($special)).'</a>';
 
$toolbox .= "<li id=\"t-'$special'\">$href</li>";
 
}
 
}
 
if ($data['feeds']) {
 
$toolbox .= '<li id="feedlinks">';
 
foreach ($data['feeds'] as $key => $feed) {
 
$toolbox .= '<span id="feed-'.htmlspecialchars($key).'">';
 
$toolbox .= '<a href="'.htmlspecialchars($feed['href']).'">'.htmlspecialchars($feed['text']).'</a>';
 
$toolbox .= '&nbsp;</span>';
 
}
 
$toolbox .= '</li>';
 
}
 
foreach (array('contributions', 'emailuser', 'upload', 'specialpages') as $special ) {
 
if ($data['nav_urls'][$special]) {
 
$href = '<a href="'.htmlspecialchars($data['nav_urls'][$special]['href']).'">';
 
$href .= htmlspecialchars($tmpl->translator->translate($special)).'</a>';
 
$toolbox .= "<li id=\"t-$special\">$href</li>";
 
}
 
}
 
$toolbox .= '</ul></div></div>';
 
 
# Language
 
$language = '';
 
if ($data['language_urls']) {
 
$language .= '<div id="p-lang" class="portlet">';
 
$language .= '<h5>'.htmlspecialchars($tmpl->translator->translate('otherlanguages')).'</h5>';
 
$language .= '<div class="pBody"><ul>';
 
foreach ($data['language_urls'] as $langlink) {
 
$href = '<a href="'.htmlspecialchars($langlink['href']).'">'.$langlink['text'].'</a>';
 
$language .= "<li>$href</li>";
 
}
 
$language .= '</ul></div></div>';
 
}
 
 
# Footer
 
$footer = '<div id="footer"><ul id="f-list">';
 
if ($data['lastmod']) $footer .= '<li id="f-lastmod">'.$data['lastmod'].'</li>';
 
if ($data['viewcount']) $footer .= '<li id="f-viewcount">'.$data['viewcount'].'</li>';
 
if ($data['credits']) $footer .= '<li id="f-credits">'.$data['credits'].'</li>';
 
if ($data['copyright']) $footer .= '<li id="f-copyright">'.$data['copyright'].'</li>';
 
if ($data['about']) $footer .= '<li id="f-about">'.$data['about'].'</li>';
 
if ($data['disclaimer']) $footer .= '<li id="f-disclaimer">'.$data['disclaimer'].'</li>';
 
$footer .= '</ul></div>';
 
 
# Return assembled parts
 
$xwArticle = " $doctype
 
$html
 
$head
 
$body
 
<table border=\"0\" cellpadding=\"10\" cellspacing=\"1\">
 
<tr>
 
<td valign=\"top\" bgcolor=\"#cccccc\">
 
<img src=\"/wiki/xmlwiki.jpg\" />
 
$search
 
$nav
 
$toolbox
 
</td>
 
<td width=\"100%\" valign=\"top\" bgcolor=\"#cccccc\">
 
$personal$actions$xwMsgToken$content
 
</td>
 
</tr>
 
<td colspan=\"2\" bgcolor=\"#cccccc\">$footer</td>
 
<tr>
 
</table>
 
</body>
 
</html>
 
";
 
}
 
 
 
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
Line 691: Line 479:
 
}
 
}
 
return $list;
 
return $list;
 +
}
 +
 +
# Merge the two passed DOM objects
 +
# - appends, does not overwrite
 +
function xwMergeDOM($dom1, $dom2) {
 +
global $xwDebug;
 +
if ($xwDebug) xwMessage('xwMergeDOM()','blue');
 +
# Return if first DOM not a valid properties article
 +
if (!is_object($dom1)) return false;
 +
$docType = $dom1->doctype();
 +
if (!ereg('^xmlwiki:properties', $docType->name)) {
 +
if ($xwDebug) xwMessage('xwMergeDOM: First parameter is not an xmlwiki:properties object!','red');
 +
return false;
 +
}
 +
# Return DOM1 if DOM2 not valid properties
 +
if (!is_object($dom2)) return $dom1;
 +
$docType = $dom2->doctype();
 +
if (!ereg('^xmlwiki:properties', $docType->name)) return $dom1;
 +
# Both are ok, perform the merge
 +
if ($xwDebug) xwMessage('xwMergeDOM()','blue');
 +
$root1 = $dom1->document_element();
 +
$root2 = $dom2->document_element();
 +
foreach ($root2->child_nodes() as $node) {
 +
$root1->append_child($node->clone_node(true));
 +
}
 +
return $dom1;
 
}
 
}
  

Revision as of 09:19, 23 July 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. - Must deal with page-move: security and also pseudo-namespace moving (maybe use proper namespace?)
  3. - setProperty needs to create element if not there so <output> not needed in swf properties
  4. - XPath bug with create element (is it the same setProperty bug?)
  5. - also language-override in ming-swf.php can then be simpler too
  6. - move geshi/wiki out to a default language.php (which uses the <language> element)
  1. - TransformArticle:
  2. - VALIDATE: validate article-properties against xmlwiki:properties.dtd (does PHP4 do this?)
  3. - TRAP: fatal errors in php-transform execution
  4. - Undomificate: validation, xslt
  5. - Check (later validate) doctype of user:sys (change users pages on site first!)
  6. - Check (later validate) doctype of user/properties on save (still save though)
  1. BUGS:
  2. - Rob is admin and can't write to Nad pages
  3. - Pages can be moved regardless of perms!!!
  4. - login error goes to old-wiki
  5. - 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 the usual way already
  2. - still needs to be present in ./extensions though

if (!@is_object($wgGeshiSyntaxSettings)) die('xmlWiki requires the Geshi syntax-highlighting extension');

  1. System globals

$xwDebug = false; $xwMessages = array(); $xwArticleCache = array(); $xwStyleSheets = 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'); $xwLanguage = ;

  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) $xwArticleProperties = false; }

  1. Merge this articles properties with default-properties

if (is_object($xwDefaultProperties = xwDomificateArticle(xwArticleContent('default-properties.xml'),'defaults'))) $xwArticleProperties = xwMergeDOM($xwDefaultProperties, $xwArticleProperties);

  1. Get language from properties, or articleType()

$xwLanguage = count($results = xwXPathQuery($xwArticleProperties, '/properties/language')) ? strtolower($results[0]->get_content()) : xwArticleType($xwArticleTitle, $xwArticle);

  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 $xwArticleTitle, $xwArticle, $xwArticleProperties; global $xwRaw, $xwParserHookCalled, $xwDebug, $xwReadable, $xwLanguage; if ($xwDebug) xwMessage('PARSER-HOOK:','green'); $xwParserHookCalled = true;

# If this text-fragment is our article, apply transforms if (strncmp($text, $xwArticle, 100) == 0 or strncmp($text, xwArticleContent($xwArticleTitle), 100) == 0) { if ($xwDebug) xwMessage("Matching text-fragment intercepted."); if (!$xwReadable) { xwMessage('Sorry, article not readable!','red'); return $text = false; } # Apply data-transforms # - domificate first if xml # - undomificate after if still an object if (preg_match("/^<\\?xml/i", $text)) xwDomificateArticle($text, 'ParserHook/text'); if (is_object($xwArticleProperties)) { $docType = $xwArticleProperties->doctype(); $root = $xwArticleProperties->document_element(); # If xmlwiki:properties doctype, apply data-transforms if (ereg('^xmlwiki:properties', $docType->name)) { if ($xwDebug) xwMessage('Applying "data" transforms.'); while (count($tNodes = $root->get_elements_by_tagname('data'))) { $tName = $tNodes[0]->get_content(); xwApplyTransform($tName, $text, $xwArticleTitle, $xwArticleProperties, 'data'); $tNodes[0]->unlink_node(); } } else xwMessage('Could not transform: article-properties must be xmlwiki:properties doctype!','red'); } if (is_object($text)) xwUndomificateArticle($text, $xwArticleTitle); } elseif ($xwDebug) xwMessage('Parsing other content');

if ($xwDebug) xwMessage('Content language determined as '.($xwLanguage ? strtoupper($xwLanguage) : 'WIKI')); # Return this back to wiki-parser so it can do wiki-markup if it wasn't one of ours return $xwLanguage == ; }


  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, $xwArticleTitle, $xwArticleProperties; global $xwTemplate, $xwDebug, $xwSkinHookCalled, $xwReadable, $xwRaw;

# Extract article from existing template structure if ($xwDebug) xwMessage('SKIN-HOOK:','green'); $xwSkinHookCalled = true; $xwTemplate = $tmpl; if ($xwReadable) $xwArticle = $xwTemplate->data['bodytext'];

# Apply view-transforms # - "default-skin.php" builds XML output structure # - "default-skin.xslt" transforms XML to HTML with table-layout # - "default-skin.css" transforms HTML with design if (is_object($xwArticleProperties)) { $docType = $xwArticleProperties->doctype(); $root = $xwArticleProperties->document_element(); # If xmlwiki:properties doctype, apply view-transforms if (ereg('^xmlwiki:properties', $docType->name)) { if ($xwDebug) xwMessage('Applying "view" transforms.'); while (count($tNodes = $root->get_elements_by_tagname('view'))) { $tName = $tNodes[0]->get_content(); xwApplyTransform($tName, $xwArticle, $xwArticleTitle, $xwArticleProperties, 'view'); $tNodes[0]->unlink_node(); } } else xwMessage('Could not transform: article-properties must be xmlwiki:properties doctype!','red'); } }

  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(); return $article; }

  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. Apply a transform

function xwApplyTransform($transform, &$article, $title, &$properties, $listName) { global $xwDebug; if ($transform) { if ($tText = xwArticleContent($transform)) { $tType = xwArticleType($transform, $tText); if ($tType == 'xslt') xwApplyXSLT($article, $tText, $transform); elseif ($tType == 'php') xwApplyPHP($article, $tText, $transform, $title, $properties, $listName); elseif ($tType == 'css') xwApplyCSS($transform); elseif ($tType == 'xml') xwMergeDOM($properties, xwDomificateArticle($tText, $transform)); } else xwMessage("Unknown transform \"$transform\" 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, $listName) { # 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. Append CSS stylesheet link to output

function xwApplyCSS($cssName) { global $xwDebug, $xwStyleSheets; if ($xwDebug) xwMessage("xwApplyCSS($cssName)",'blue'); $xwStyleSheets[] = $cssName; }

  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. ---------------------------------------------------------------------------------------------------------------------- #
  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. Merge the two passed DOM objects
  2. - appends, does not overwrite

function xwMergeDOM($dom1, $dom2) { global $xwDebug; if ($xwDebug) xwMessage('xwMergeDOM()','blue'); # Return if first DOM not a valid properties article if (!is_object($dom1)) return false; $docType = $dom1->doctype(); if (!ereg('^xmlwiki:properties', $docType->name)) { if ($xwDebug) xwMessage('xwMergeDOM: First parameter is not an xmlwiki:properties object!','red'); return false; } # Return DOM1 if DOM2 not valid properties if (!is_object($dom2)) return $dom1; $docType = $dom2->doctype(); if (!ereg('^xmlwiki:properties', $docType->name)) return $dom1; # Both are ok, perform the merge if ($xwDebug) xwMessage('xwMergeDOM()','blue'); $root1 = $dom1->document_element(); $root2 = $dom2->document_element(); foreach ($root2->child_nodes() as $node) { $root1->append_child($node->clone_node(true)); } return $dom1; }

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

?>