Xmlwiki.php
{{#security:edit|dev}}
- xmlWiki - MediaWiki XML Hack
- Nad - Started: 2005-05-18
- 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!');
- 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; }
- System globals
$xwDebug = false; $xwMessages = array(); $xwArticleCache = array(); $xwStyleSheets = array(); $xwTemplate = null; $xwParserHookCalled = false; $xwSkinHookCalled = false; $xwMsgToken = ; $xwCssToken = ; $xwScript = $wgScript; $xwTransformID = 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']);
- 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;
- Get default properties article
$xwDefaultProperties = $xwDefaultPropertiesXML = xwArticleContent( 'default-properties.xml', false ); xwDomificateArticle( $xwDefaultProperties, 'defaults' );
- 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' ) );
- Read user-prefs
xwGetProperty( $xwUserSYS, 'xpath:/user/debug', $xwDebug );
- 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');
- If no language specified in properties, guess from name and content
if ( !xwGetProperty( $xwArticleProperties, 'language', $xwLanguage ) ) xwSetProperty( $xwArticleProperties, 'language', xwArticleType( $xwArticleTitle, $xwArticle ) );
- 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)); }
- Divert to access-denied article if not readable
if (!$xwReadable) { $action = 'view'; $xwSave = $xwEdit = false; }
- 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); } }
- Apply init transforms
xwReduceTransformStack( $xwArticle, $xwArticleProperties, $xwArticleTitle, 'init', 'INIT-HOOK' );
- ---------------------------------------------------------------------------------------------------------------------- #
- INPUT HOOK
- 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; } } } } }
}
- ---------------------------------------------------------------------------------------------------------------------- #
- OUTPUT HOOK
- 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(
'/
- ---------------------------------------------------------------------------------------------------------------------- #
- PRE-PARSER HOOK
- ---------------------------------------------------------------------------------------------------------------------- #
- POST-PARSER HOOK
- - this hook was not implemented or had been removed,
- I've put in on the official ArticleSaveComplete hook for now
- - was function xwPostParserHook( &$text )
- ---------------------------------------------------------------------------------------------------------------------- #
- SKIN HOOK
- If attempted xml encountered, try to domificate and transform
- - Raw requests don't get here
- ---------------------------------------------------------------------------------------------------------------------- #
- ARTICLE FUNCTIONS
- - these all use super-globals since they can be called from within evals
- Return true if passed title readable/writable by current user
- Retreive wiki-article as raw text
- Return properties object from title
- - returns self if already a properties article
- Decide kind of article from content and title
- Convert passed article to a DOM object
- - Article is unchanged if not valid XML
- Reduce article to a string if it's a DOM object
- - ie if all xslt's had xml-output-method
- ---------------------------------------------------------------------------------------------------------------------- #
- TRANSFORM FUNCTIONS
- Apply all transforms in passed stack
- Apply a transform
- Apply PHP-transform if article perms are only writable by admin or dev
- Append CSS stylesheet link to output
- Apply XSLT to article-dom
- - if article is not and object, the XSLT will be appended to the stylesheet-ref list like a CSS
- - if output-method is html, article will get stringified and language updated
- ---------------------------------------------------------------------------------------------------------------------- #
- DOM FUNCTIONS
- Return result set of an XPath query on passed DOM-object
- - Syntax: xpath:XPathQuery:[[@]node][+] = value
- - if "node" exists, then a new element (or att if "@node") is created for the value
- - if "+" exists, content is appended, else replaced
- Return array of comma-separated-items in element content
- - if more than one element all are split and appended into the list
- Merge the two passed DOM objects
- - appends, does not overwrite
- Remove all occourences of a certain element
- ---------------------------------------------------------------------------------------------------------------------- #
- UTILITY FUNCTIONS
- Extract error-messages from captured output and put in proper message-queue
- Add message to queue
There are Messages
$article = str_replace($xwMsgToken, $msg, $article); } }
- 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;
}
- Return true if PHP major version equals passed int
- - needed due to different DOM implementation on 4 and 5 (and none on 3)
function php($ver) { return substr(phpversion(),0,1) == "$ver"; }
- 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); }
- 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(); }
- 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; }