Xmlwiki.php

From Organic Design wiki
Revision as of 10:02, 19 May 2008 by Nad (talk | contribs) (remove perms)
Legacy.svg Legacy: This article describes a concept that has been superseded in the course of ongoing development on the Organic Design wiki. Please do not develop this any further or base work on this concept, this is only useful for a historic record of work done. You may find a link to the currently used concept or function in this article, if not you can contact the author to find out what has taken the place of this legacy item.

<php>

  1. xmlWiki - MediaWiki XML Hack
  2. Nad - Started: 2005-05-18
  1. Exit if not included from index.php

defined('MEDIAWIKI') or die('xmlwiki.php must be included from MediaWiki\'s index.php!'); if (php(3)) die('xmlWiki cannot run on less than PHP4!');

  1. Add the rest of the hooks

$wgHooks['PreParser'][] = 'xwPreParserHook'; $wgHooks['ParserBeforeStrip'][] = 'xwPreParserHook'; $wgHooks['ArticleSaveComplete'][] = 'xwPostParserHook'; $wgHooks['ParserAfterTidy'][] = 'xwPostParserHook'; $wgHooks['Input'][] = 'xwInputHook'; $wgHooks['Output'][] = 'xwOutputHook'; $wgHooks['ParserFunctions'][] = 'xwTransclusionSecurity'; function xwTransclusionSecurity(&$title,&$text,$args,$argc) { $title = ereg_replace('^:',,$title); if ($title && !xwArticleAccess($title)) { $text = 'Sorry, article not readable!'; $title = false; } return true; }

  1. System globals

$xwDebug = false; $xwMessages = array(); $xwArticleCache = array(); $xwStyleSheets = array(); $xwTemplate = null; $xwParserHookCalled = false; $xwSkinHookCalled = false; $xwMsgToken = ; $xwCssToken = ; $xwScript = $wgScript; $xwTransformID = 1;

  1. User globals

$xwUserName = ucwords( $wgUser->mName ); $xwAnonymous = ereg( "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+", $xwUserName ); $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->getPrefixedText(); $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); $xwIsSpecial = preg_match('/^special:.+$/i', $xwArticleTitle); $xwIsAdmin = false; $xwArticleReadableBy = null; $xwArticleWritableBy = null;

  1. Get default properties article

$xwDefaultProperties = $xwDefaultPropertiesXML = xwArticleContent( 'default-properties.xml', false ); xwDomificateArticle( $xwDefaultProperties, 'defaults' );

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

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

  1. Read user-prefs

xwGetProperty( $xwUserSYS, 'xpath:/user/debug', $xwDebug );

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

if ( $xwIsSystem ) $xwArticleReadableBy = $xwArticleWritableBy = array('admin'); else {

# Get xml:article (self if already xml:*) if ( $p = $xwIsProperties ? xwDomificateArticle( $xwArticle ) : xwArticleProperties( $xwArticleTitle ) )

# Start with default-properties and merge article props (so new transforms on top) $xwArticleProperties = '<?xml version="1.0" standalone="yes"?> <!DOCTYPE xmlwiki:properties SYSTEM "xmlwiki-properties.dtd"> <properties/>'; xwDomificateArticle( $xwArticleProperties, 'CreateProperties' ); xwMergeDOM( $xwArticleProperties, $xwDefaultProperties ); xwMergeDOM( $xwArticleProperties, $p );

$xwArticleReadableBy = xwGetListByTagname( $xwArticleProperties, 'read' ); $xwArticleWritableBy = xwGetListByTagname( $xwArticleProperties, 'write' ); if ( $xwIsProperties ) $xwArticleProperties = false; } if ( !is_object( $xwArticleProperties ) ) $xwArticleProperties = $xwDefaultProperties; if ( $xwIsSpecial || !count( $xwArticleReadableBy ) ) $xwArticleReadableBy = array('anyone'); if ( !count($xwArticleWritableBy) ) $xwArticleWritableBy = array('anyone');

  1. If no language specified in properties, guess from name and content

if ( !xwGetProperty( $xwArticleProperties, 'language', $xwLanguage ) ) xwSetProperty( $xwArticleProperties, 'language', 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. Handle security for Move

if (!$xwIsAdmin and ($target = $_REQUEST['target']) ) { if ($xwArticleProperties = new Article(Title::newFromText("xml:$target"))) $xwArticleProperties = $xwArticleProperties->getContentWithoutUsingSoManyDamnGlobals(); $writableBy = xwGetListByTagname(xwDomificateArticle($xwArticleProperties), 'write'); if (!count($writableBy)) $writableBy = array('anyone'); if (!count(array_intersect($writableBy, $xwUserGroups))) { $xwArticleTitle = $target; xwMessage("Sorry article \"$xwArticleTitle\" not movable!",'red'); $wgArticle = new Article($wgTitle = Title::newFromText($target)); $xwArticle = $wgArticle->getContent(false); } }

  1. Apply init transforms

xwReduceTransformStack( $xwArticle, $xwArticleProperties, $xwArticleTitle, 'init', 'INIT-HOOK' );


  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, $wgTitle; global $xwUserGroups, $xwIsProperties, $xwIsSystem, $xwIsAdmin; if ($xwDebug) xwMessage('INPUT-HOOK:','green');

# If not writable, change action to view if ( !$xwWritable && !in_array($action, array('view','history','raw')) ) { 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) { if (ereg('^xpath.3A.+.3A', $query)) $query = urldecode( $query ); if (ereg('^xpath:.+:', $query)) xwSetProperty($xwArticleProperties, $query, str_replace('&', '%26', $value)); }

# if saving a pseudo-namespace... if ($xwSave) { if ( $xwIsProperties || $xwIsSystem ) { # wpTextbox1 should be valid XML to be saved $tb = $_REQUEST['wpTextbox1']; xwDomificateArticle( $tb, 'POST-DATA' ); if ( !is_object($tb) ) { xwMessage('A meta-article must be valid XML! article not saved.', 'red'); $action = 'view'; $xwSave = $xwEdit = false; } elseif ( $xwIsProperties && !$xwIsAdmin ) { foreach ( xwGetListByTagname($tb, 'write') as $perm ) { if ( !in_array( $perm, $xwUserGroups ) ) { xwMessage("Only members of \"$perm\" can set permissions for \"$perm\"! article not saved.", 'red'); $action = 'view'; $xwSave = $xwEdit = false; } } } } }

}


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

function xwOutputHook() {

global $wgUser, $xwUserName, $wgOut, $xwDebug, $xwIsProperties, $xwIsSystem, $xwIsAdmin; global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwEdit, $action; if ($action == 'raw') return; if ($xwDebug) xwMessage('OUTPUT-HOOK:','green');

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

# Editing article if ($xwEdit && preg_match("/^(.*<textarea .+?>)\\s*(<\\/textarea>.*)$/s", $xwArticle, $m)) { # If editing an empty sys:article or xml:article, add default content 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]; xwMessage('Note: There is currently no content for this system article, default content has been generated','red'); } elseif ($xwIsProperties) { $a = xwArticleContent( str_replace( 'Xml:', , $xwArticleTitle ) ); if ( preg_match("/\\[\\[Category:(.+?)]]/", $a, $n) && $xwArticle = xwArticleProperties('Category:'.$n[1]) ) { xwUndomificateArticle( $xwArticle ); xwMessage('Note: This article has no properties, default content has been inherited from Xml:'.$n[1], 'red'); } else { xwMessage('Note: This article has no properties, a general default has been generated', 'red'); $xwArticle = "<?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<language></language>\n\t<category></category>\n\t\n\t<view></view>\n\t<edit></edit>\n\t<save></save>\n</properties>\n"; } $xwArticle = $m[1].$xwArticle.$m[2]; } } elseif ( $xwEdit ) { # Editing normally, apply transforms in edit-list xwReduceTransformStack( $xwArticle, $xwArticleProperties, $xwArticleTitle, 'edit', 'OUTPUT-HOOK' ); }

if ( $xwDebug ) { global $xwParserHookCalled, $xwSkinHookCalled; if ( !$xwParserHookCalled ) xwMessage( 'PARSER-HOOK was not called', 'green' ); if ( !$xwSkinHookCalled ) xwMessage( 'SKIN-HOOK was not called', 'green' ); }

# Insert stylesheets and messages into output xwReplaceTokens( $xwArticle );

# Permhack: remove private info if search result if ( isset($_REQUEST['search']) && !$xwIsAdmin ) $xwArticle = preg_replace_callback(

'/

  • <a href.+?>(.+?)<\\/a>.+?<\\/li>/is', create_function( '$m', 'return xwArticleAccess($m[1],"read")?$m[0]:"";' ), $xwArticle ); # Output final result print $xwArticle; }
    1. ---------------------------------------------------------------------------------------------------------------------- #
    2. PRE-PARSER HOOK
    function xwPreParserHook( &$text ) { global $xwArticleTitle, $xwArticle, $xwArticleProperties, $xwSave; global $xwParserHookCalled, $xwDebug, $xwReadable, $xwScript; if ( $xwDebug ) xwMessage( 'PREPARSER-HOOK:', 'green' ); $xwParserHookCalled = true; # Save clears all output, but we need to parse it for save-transforms if ( $xwSave ) $text = $_REQUEST['wpTextbox1']; # If this text-fragment is our article, apply transforms if ( $xwSave or is_object($xwArticle) or strncmp( $text, $xwArticle, 100 ) == 0 or strncmp( $text, xwArticleContent( $xwArticleTitle, false ), 100 ) == 0 ) { if ( $xwDebug ) xwMessage( "Matching text-fragment intercepted." ); if ( !$xwReadable ) { xwMessage( 'Sorry, article not readable!', 'red' ); $text = $xwArticle = "<a href=\"$xwScript?title=Special:Userlogin\">Please Login</a>"; return false; } # Apply data-transforms # - domificate first if xml # - undomificate after if still an object if ( !is_object($xwArticle) and preg_match("/^<\\?xml/i", $text) ) xwDomificateArticle( $text, 'PreParserHook/text' ); xwReduceTransformStack( $text, $xwArticleProperties, $xwArticleTitle, 'data', 'PREPARSER-HOOK' ); xwUndomificateArticle( $text, $xwArticleTitle ); } elseif ( $xwDebug ) xwMessage( 'Parsing other content' ); # Get language and return true back to parser if lang wasn't ours xwGetProperty( $xwArticleProperties, 'language', $language ); if ($xwDebug) xwMessage( 'Content language determined as '.($language ? strtoupper($language) : 'WIKI') ); return $language == ; }
    1. ---------------------------------------------------------------------------------------------------------------------- #
    2. POST-PARSER HOOK
    3. - this hook was not implemented or had been removed,
    4. I've put in on the official ArticleSaveComplete hook for now
    5. - was function xwPostParserHook( &$text )
    function xwPostParserHook( &$article, &$user, &$text, &$summary, &$minoredit, &$watchthis, &$sectionanchor ) { global $xwArticleTitle, $xwArticleProperties, $xwSave, $xwDebug; #if ( !$xwSave ) return; if ( $xwDebug ) xwMessage( 'POSTPARSER-HOOK:','green' ); # Apply view transforms first #xwReduceTransformStack( $text, $xwArticleProperties, $xwArticleTitle, 'view', 'POSTPARSER-HOOK' ); # Now apply the save-stack $dummy = ""; xwReduceTransformStack( $dummy, $xwArticleProperties, $xwArticleTitle, 'save', 'POSTPARSER-HOOK' ); # Output should be empty for save #$text = ; }
    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, $xwRaw; 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 xwReduceTransformStack( $xwArticle, $xwArticleProperties, $xwArticleTitle, 'view', 'SKIN-HOOK' ); }
    1. ---------------------------------------------------------------------------------------------------------------------- #
    2. ARTICLE FUNCTIONS
    3. - these all use super-globals since they can be called from within evals
    1. Return true if passed title readable/writable by current user
    function xwArticleAccess( $title, $perm = 'read' ) { global $xwIsAdmin, $xwUserGroups, $xwDefaultProperties; if ( !$access = $xwIsAdmin ) { $properties = xwArticleProperties( $title ); xwMergeDOM( $properties, $xwDefaultProperties ); $access = xwGetListByTagname( $properties, $perm ) + array( 'anyone' ); $access = count( array_intersect( $access, $xwUserGroups ) ); } return $access > 0; }
    1. Retreive wiki-article as raw text
    function xwArticleContent( $articleTitle, $secure = true ) { global $xwArticleCache, $xwDebug, $xwIsAdmin, $xwUserGroups; if ($xwDebug) xwMessage("xwArticleContent(\"$articleTitle\")",'blue'); # Temp: quick fix for slack security model :-( # - should combine article and properties into getContent() if ($secure and !$xwIsAdmin) { if ($properties = new Article(Title::newFromText('xml:'.ucwords($articleTitle)))) $properties = $properties->getContentWithoutUsingSoManyDamnGlobals(); $readableBy = xwGetListByTagname(xwDomificateArticle($properties), 'read') + array('anyone'); if (!count(array_intersect($readableBy, $xwUserGroups))) { xwMessage("Sorry article \"$articleTitle\" not readable!",'red'); return "Please Login"; } } # Return with results if already cached 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. Return properties object from title
    2. - returns self if already a properties article
    function xwArticleProperties( $articleTitle, $xmlself = true ) { global $xwDebug; if ($xwDebug) xwMessage("xwArticleProperties(\"$articleTitle\")",'blue'); $id = ( $xmlself && eregi( '^xml:', $articleTitle ) ) ? $articleTitle : 'xml:'.ucfirst($articleTitle); if ( !$properties = xwArticleContent( $id, false ) ) $properties = '<?xml version="1.0" standalone="yes"?> <!DOCTYPE xmlwiki:properties SYSTEM "xmlwiki-properties.dtd"> <properties/>'; return xwDomificateArticle( $properties, $id ); }
    1. Decide kind of article from content and title
    function xwArticleType( $title, $article ) { if ( is_object($article) ) return 'xml'; if ( eregi('\\.php$', $title) ) return 'php'; if ( eregi('\\.css$', $title) ) return 'css'; if ( eregi('^talk:', $title) ) return ; if ( preg_match("/^<\\?xml.+?\\?>.*<xsl:stylesheet/is", $article) ) return 'xslt'; if ( eregi('^(sys|xml):.+$', $title) || ereg('^<\\?xml', $article) ) return 'xml'; if ( eregi('^<\\?html', $article ) || eregi('\\.html$', $title)) return 'html4strict'; if ( eregi('\\.p[lm]$', $title) ) return 'perl'; if ( ereg('^#![/a-zA-Z0-9]+\\/perl', $article) ) return 'perl'; if ( eregi('\\.as$', $title) ) return 'actionscript'; if ( eregi('\\.dtd$', $title) ) return 'xml'; if ( eregi('\\.tex$', $title) ) return 'tex'; if ( eregi('\\.nt$', $title) ) return 'dna'; if ( eregi('\\.r$', $title) ) return 'r'; if ( eregi('\\.py$', $title) ) return 'python'; if ( eregi('\\.java$', $title) ) return 'java'; if ( eregi('\\.(c\\+\\+|cpp)$', $title)) return 'cpp'; if ( eregi('\\.[ch]$', $title) ) return 'c'; if ( eregi('\\.js$', $title) ) return 'javascript'; if ( ereg('^#![/a-zA-Z0-9]+sh', $article) ) return 'bash'; return ; }
    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 ($article) { if (php(4)) $dom = domxml_open_mem($article); else $dom = DOMDocument::loadXML($article); } if (isset($dom) && is_object($dom)) $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 (php(4)) { # Attempt to fix PHP4's crap dump $xml = "<?xml version=\"1.0\" standalone=\"yes\"?>\n"; $children = $article->child_nodes(); $xml .= trim($article->dump_node($children[0]))."\n"; for ($i = 1; $i < count($children); $i++) { $top = $children[$i]; $name = $top->node_name(); $xml .= "<$name>\n"; foreach ($top->child_nodes() as $child) { $line = trim($article->dump_node($child)); if ($line) $xml .= "\t$line\n"; } $xml .= "</$name>\n"; } $article = $xml; } else $article = $article->saveXML(); return true; }
    1. ---------------------------------------------------------------------------------------------------------------------- #
    2. TRANSFORM FUNCTIONS
    1. Apply all transforms in passed stack
    function xwReduceTransformStack( &$article, &$properties, $title, $event, $id = ) { global $xwDebug; $GLOBALS['xwTransformID']++; if ( $xwDebug ) xwMessage( "Applying \"$event\" transforms to \"$title\" from \"$id\"."); if ( is_object( $properties ) ) { if (php(4)) { $docType = $properties->doctype(); $root = $properties->document_element(); } else { $docType = $properties->doctype; $root = $properties->documentElement; } if ( ereg( '^xmlwiki:properties', $docType->name ) ) { if (php(4)) { while (count($tNodes = $root->get_elements_by_tagname($event))) { $tName = $tNodes[0]->get_content(); xwApplyTransform($tName, $article, $properties, $title, $event); $tNodes[0]->unlink_node(); } } else { while (($tNodes = $root->getElementsByTagname($event)) && $tNodes->length) { $tNode = $tNodes->item(0); xwApplyTransform($tNode->nodeValue, $article, $properties, $title, $event); $tNode->parentNode->removeChild($tNode); } } } else xwMessage('Could not transform: article-properties must be xmlwiki:properties doctype!','red'); } }
    1. Apply a transform
    function xwApplyTransform($transform, &$article, &$properties, &$title, $event) { global $xwDebug; if ( $transform ) { if ( $tText = xwArticleContent($transform) ) { # Get transform language (take a guess if none specified) $tProperties = xwArticleProperties($transform); xwGetProperty( $tProperties, 'language', $tLang ); if ( !$tLang ) $tLang = xwArticleType( $transform, $tText ); if ( $tLang == 'xslt' ) xwApplyXSLT( $article, $title, $properties, $tText, $transform ); elseif ( $tLang == 'php' ) xwApplyPHP( $article, $tText, $transform, $title, $properties, $tProperties, $event ); elseif ( $tLang == 'css' ) xwApplyCSS( $title, $transform ); elseif ( $tLang == 'xml' ) xwMergeDOM( $properties, xwDomificateArticle( $tText, $transform ), $title, $transform ); } else xwMessage( "Unknown transform \"$transform\" attached to \"$event\" event!", 'red' ); } }
    1. Apply PHP-transform if article perms are only writable by admin or dev
    function xwApplyPHP( &$article, &$tCode, $tTitle, &$title, &$properties, &$tProperties, $event ) { global $xwDebug; if ( $xwDebug ) xwMessage( "xwApplyPHP( $title , $tTitle )", 'blue' ); # Get transform perms if ( !count( preg_grep( '/^(admin)|(dev)$/i', xwGetListByTagname( $tProperties, 'write' ) ) ) ) { xwMessage( "Failed to execute \"$tTitle\", must be writable only by dev or admin!", 'red' ); return; } # Permissions ok, execute the transform code and trap output $path = '/properties/'.preg_replace( "/\\.\\w+$/", , $tTitle ); $script = $GLOBALS['xwScript']; ob_start(); eval( "?>$tCode<?" ); xwExtractMessages( $tTitle ); ob_end_clean(); }
    1. Append CSS stylesheet link to output
    function xwApplyCSS($title, $tName) { global $xwDebug, $xwStyleSheets; if ($xwDebug) xwMessage("xwApplyCSS( $title , $tName )",'blue'); $xwStyleSheets[] = $tName; }
    1. Apply XSLT to article-dom
    2. - if article is not and object, the XSLT will be appended to the stylesheet-ref list like a CSS
    3. - if output-method is html, article will get stringified and language updated
    function xwApplyXSLT(&$article, $title, $properties, &$xslt, $tName) { global $xwDebug, $xwStyleSheets; if ($xwDebug) xwMessage("xwApplyXSLT( $title , $tName )",'blue'); if (is_object($article)) { 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); xwSetProperty($properties, 'language', ); } } xwExtractMessages(); ob_end_clean(); if (isset($tResult)) $article = $tResult; } else $xwStyleSheets[] = $tName; }
    1. ---------------------------------------------------------------------------------------------------------------------- #
    2. DOM FUNCTIONS
    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(); if (php(4)) { $context = $dom->xpath_new_context(); if ($xpath = $context->xpath_eval($query)) $result = @$xpath->nodeset; elseif ($xwDebug) xwMessage("Query \"$query\" failed :-(", 'red'); } else if ($xpath = new DOMXPath($dom)) $result = $xpath->query($query); xwExtractMessages(); ob_end_clean(); } if (php(4)) return is_array($result) ? $result : array(); else { $tmp = array(); foreach ($result as $node) $tmp[] = $node; return $tmp; } } function xwGetProperty(&$properties, $query, &$value) { global $xwDebug; if (!preg_match("/^xpath:(.+)$/", $query, $match)) $match = array(, "/properties/$query"); if (count($results = xwXPathQuery($properties, $match[1])) == 0) return false; if (php(4)) $value = $results[count($results)-1]->get_content(); else $value = $results[count($results)-1]->nodeValue; if ($xwDebug) xwMessage("xwGetProperty( $query ) returned \"$value\"", 'purple'); return true; }
    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) { global $xwDebug; if ($xwDebug) xwMessage("xwSetProperty( $query , $value )", 'purple'); if (!is_object($properties)) return false; if (!preg_match("/^xpath:(.+):(@?)(\\w*?)(\\+?)$/", $query, $match)) { # If not xpath, remove property before creating $tNodes = xwXPathQuery($properties, "/properties/$query"); foreach ($tNodes as $tNode) if (php(4)) $tNode->unlink_node(); else $tNode->parentNode->removeChild($tNode); $match = array(, '/properties', , $query, ); } list(, $xpath, $att, $node, $append) = $match; foreach (xwXPathQuery($properties, $xpath) as $result) { if ($node) { # create new element/attribute in result-node if ($att) { if (php(4)) $result->append_child($properties->create_attribute($node, $value)); else $result->setAttribute($node, $value); } else { if (php(4)) { $node = $properties->create_element($node); $node->set_content($value); $result->append_child($node); } else $result->appendChild($properties->createElement($node, $value)); } } else { # node not set, replace or append current-result-value if ($append) { if (php(4)) $result->set_content( ($result->get_content()).$value ); else $result->nodeValue .= $value; } else { if (php(4)) { $newnode = $properties->create_element($result->tagname); $newnode->set_content($value); $result->replace_node($newnode); } else $result->parentNode->replaceChild($properties->createElement($result->nodeName, $value), $result); } } } return true; }
    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)) { if (php(4)) { $root = $xml->document_element(); foreach ($root->get_elements_by_tagname($tag) as $element) if ($csv = $element->get_content()) $list = array_merge($list, split(',', $csv)); } else { foreach ($xml->documentElement->getElementsByTagname($tag) as $element) if ($csv = $element->nodeValue) $list = array_merge($list, split(',', $csv)); } } return $list; }
    1. Merge the two passed DOM objects
    2. - appends, does not overwrite
    function xwMergeDOM(&$dom1, &$dom2, $id1 = 'dom1', $id2 = 'dom2') { global $xwDebug; # Return if first DOM not a valid properties article if (!is_object($dom1)) return false; if (php(4)) $docType = $dom1->doctype(); else $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; if (php(4)) $docType = $dom2->doctype(); else $docType = $dom2->doctype; if (!ereg('^xmlwiki:properties', $docType->name)) return $dom1; # Both are ok, perform the merge if ($xwDebug) xwMessage("xwMergeDOM( &$id1 , $id2 )",'blue'); if (php(4)) { $root1 = $dom1->document_element(); $root2 = $dom2->document_element(); foreach ($root2->child_nodes() as $node) $root1->append_child($node->clone_node(true)); } else $dom1->documentElement->appendChild($dom1->importNode($dom2->documentElement, true)); return true; }
    1. Remove all occourences of a certain element
    function xwRemoveElement( &$properties, $element ) { $root = $properties->document_element(); if (php(4)) { while ( count($tNodes = $root->get_elements_by_tagname($element)) ) $tNodes[0]->unlink_node(); } else { while ( ($tNodes = $root->getElementsByTagname($element)) && $tNodes->length ) { $tNode = $tNodes->item(0); $tNode->parentNode->removeChild( $tNode ); } } }
    1. ---------------------------------------------------------------------------------------------------------------------- #
    2. UTILITY FUNCTIONS
    1. Extract error-messages from captured output and put in proper message-queue
    function xwExtractMessages($where = false) { if ($where) $where = " of \"$where\""; $err = preg_replace("/<.+?>/", "", ob_get_contents()); foreach (split("\n", $err) as $msg) if (trim($msg)) { if (ereg("eval\\(\\)'d code on line ([0-9]+)", $msg, $m)) $line = " (Line $m[1]$where)"; $msg = preg_replace('/ in .+? on line.+?([0-9]+)/', " (Line $1$where)", $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"; } function xwReplaceTokens(&$article, $embed = false) { global $xwStyleSheets, $xwMessages, $xwCssToken, $xwMsgToken, $xwScript; # Stylesheets $stylesheets = ; foreach ($xwStyleSheets as $title) if ($embed) $stylesheets .= '<style type="text/css" media="screen">'.xwArticleContent($title).'</style>'; else $stylesheets = "<link rel=\"stylesheet\" type=\"text/css\" href=\"$xwScript?title=$title&action=raw&ctype=text/css\" />\n$stylesheets"; $article = str_replace($xwCssToken, $stylesheets, $article); # Messages if (count($xwMessages)) { $msg = '
    There are Messages
    '; $msg .= '
    • '.join("
    • ", $xwMessages).'
    ';

    $article = str_replace($xwMsgToken, $msg, $article); } }

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

    1. Return true if PHP major version equals passed int
    2. - needed due to different DOM implementation on 4 and 5 (and none on 3)

    function php($ver) { return substr(phpversion(),0,1) == "$ver"; }

    1. Add a log entry to a log article ([[XmlWiki Log]] is default)

    function xwLog($comment, $title = 'XmlWiki Log') { global $wgLang; $ts = $wgLang->timeanddate(wfTimestampNow(),true); $log = xwArticleContent($title)."\n*$ts : $comment"; $a = new Article(Title::newFromText($title)); $a->quickEdit($log); }

    1. Parse wikitext string and return HTML string

    function xwWikiParse(&$text) { global $wgHooks,$wgTitle,$wgUser; $tmp = $wgHooks['PreParser']; $wgHooks['PreParser'] = array(); $parser = new Parser; $output = $parser->parse($text,$wgTitle,ParserOptions::newFromUser($wgUser),true,false); $wgHooks['PreParser'] = $tmp; return $output->getText(); }

    1. Parse an XmlWiki article and return resulting HTML

    function xwXmlWikiParse($title) { global $wgTitle,$wgHooks,$wgUser,$xwUserName,$xwAnonymous; $bookmarks = $xwAnonymous ? "Bookmarks:Default" : "Bookmarks:$xwUserName"; $text = str_replace('Template:BOOKMARKS',$bookmarks,xwArticleContent($title)); # Hack so that Template:BOOKMARKS can be used in an XmlWiki link $text = preg_replace('//s','@@'.'@@$1@@'.'@@',$text); # Hack so that HTML comments in the wikitext are preserved $properties = xwArticleProperties($title); xwSetProperty($properties,'xpath:/properties:data','document.php'); # If no language specified in properties, guess from name and content

           xwGetProperty($properties,'language',$lang);
           if (!$lang) xwSetProperty($properties,'language',xwArticleType($title,$text));
    

    xwReduceTransformStack($text,$properties,$title,'data','xwXmlWikiParse/preParse/data'); $tmp = $wgHooks['PreParser']; $wgHooks['PreParser'] = array(); $parser = new Parser; $output = $parser->parse($text,$wgTitle,ParserOptions::newFromUser($wgUser),true,false); $wgHooks['PreParser'] = $tmp; $text = $output->getText(); $text = preg_replace('/@{4}([^@]+?)@{4}/s',,$text); # HTML comments hack xwSetProperty($properties,'xpath:/properties:view','tree-view.php'); xwReduceTransformStack($text,$properties,$title,'view','xwXmlWikiParse/preParse/view'); return $text; } </php>