Difference between revisions of "Xmlwiki.php"

From Organic Design wiki
Line 11: Line 11:
 
# Publish:
 
# Publish:
 
# - xml:article - holds article publish-list
 
# - xml:article - holds article publish-list
 +
# - mirror/wiki-sync
 
# Transforms:
 
# Transforms:
 
# - xml:article - now transforms are in here (main article is now like <body>)
 
# - xml:article - now transforms are in here (main article is now like <body>)
Line 27: Line 28:
 
# Exit if not included from index.php
 
# Exit if not included from index.php
 
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!');
 
# TODO?: ensure $_REQUEST['action'] is valid, unset if not
 
#      otherwise, our hooks are bypassed
 
 
# Ensure our xwskin is active
 
#$wgUser->setOption('skin', 'xwskin');
 
  
 
# Otherwise set up xmlWiki global environment ready for input and output processing
 
# Otherwise set up xmlWiki global environment ready for input and output processing
Line 41: Line 36:
 
$xwSave = isset($_REQUEST['wpSave']);
 
$xwSave = isset($_REQUEST['wpSave']);
 
$xwUserName = $wgUser->mName;
 
$xwUserName = $wgUser->mName;
$xwArticle = $wgArticle->getContent(false);
+
#$xwArticle = $xwPreview ? $wgRequest->gettext('wpTextbox1') : $wgArticle->getContent(false);
 
$xwArticleTitle = $wgTitle->getPrefixedURL();
 
$xwArticleTitle = $wgTitle->getPrefixedURL();
 
$xwArticleCache = array();
 
$xwArticleCache = array();
Line 47: Line 42:
 
$xwUserLinks = array();
 
$xwUserLinks = array();
 
$xwMessages = array();
 
$xwMessages = array();
 +
$xwMetaArticle = false;
  
 
# Get associated articles and attempt to Domificate
 
# Get associated articles and attempt to Domificate
Line 55: Line 51:
 
# Security
 
# Security
 
if (ereg('^xml:(.+)$', $xwArticleTitle)) {
 
if (ereg('^xml:(.+)$', $xwArticleTitle)) {
 +
$xwMetaArticle = true;
 
# This is an xml-pseudo-namespace
 
# This is an xml-pseudo-namespace
 
# - get perms from associated article/user
 
# - get perms from associated article/user
 
}
 
}
 
elseif (ereg('^sys:(.+)$', $xwArticleTitle)) {
 
elseif (ereg('^sys:(.+)$', $xwArticleTitle)) {
 +
$xwMetaArticle = true;
 
# This is an sys-pseudo-namespace
 
# This is an sys-pseudo-namespace
 
# - only admin can read/write sys:
 
# - only admin can read/write sys:
Line 68: Line 66:
  
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
# INPUT
+
# INPUT HOOK
 
 
# No input processing for now
 
# - used later for custom edit forms (database table article will need it)
 
  
 
# Parse and process input from forms
 
# Parse and process input from forms
function xwProcessInput() {
+
function xwInputHook() {
  
global $xwArticle, $xwArticleTitle, $xwReadable, $xwWritable;
+
global $xwArticle, $xwArticleTitle, $xwMetaArticle, $xwReadable, $xwWritable;
 
global $xwSave;
 
global $xwSave;
  
# If article is being updated,
+
# If an article-change is being attempted,
 
if ($xwSave) {
 
if ($xwSave) {
#global $_POST;
 
 
if (!$xwReadable || !$xwWritable) {
 
if (!$xwReadable || !$xwWritable) {
 
# Not writable, change action to view
 
# Not writable, change action to view
xwMessage("I'm sorry Dave, I'm afraid I can't do that.", 'red');
+
xwMessage('Cannot access article for writing, changing action to "view"', 'red');
 
global $action;
 
global $action;
 
$action='view';
 
$action='view';
 +
}
 +
else {
 +
# Parse POST for xmlWiki form input
 +
global $_REQUEST;
 +
foreach ($_REQUEST as $k => $v) {
 +
 +
if (is_object($xwArticle) && preg_match("/^xw:(@?)(\\w*):(.+)$/", $k, $m)) {
 +
# Form-input-name is "xw:[[@]node]:XPathQuery" which directs the value into xwArticle (if DOM)
 +
# - if "node" exists, then a new element (or att if "@node") is created for the value
 +
# - hidden-inputs can direct input-handlers into the transform list
 +
list(, $att, $node, $xpath) = $m;
 +
foreach (xwXPathQuery($xwArticleXML, $xpath) as $result) {
 +
if ($att && $node) {
 +
# create attribute for value
 +
}
 +
elseif ($node) {
 +
# create element for value
 +
}
 +
else {
 +
# overwrite existing node content
 +
}
 +
}
 +
}
 +
 +
elseif ($xwMetaArticle && ($k == 'wpSave')) {
 +
# if it's a pseudo-namespace, wpSave should be valid XML to be saved
 +
xwDomificate($wpSave);
 +
if (!is_object($wpSave)) {
 +
xwMessage('A meta-article must be valid XML! changing action to "edit"', 'red');
 +
global $action;
 +
$action='edit';
 +
}
 +
}
 +
 +
}
 
}
 
}
 
}
 
}
 
 
}
 
}
  
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
# OUTPUT
+
# OUTPUT HOOK
  
 
# Generate output to send back to client
 
# Generate output to send back to client
 
# - if article is not XML, then wiki's parsed content is used
 
# - if article is not XML, then wiki's parsed content is used
function xwProcessOutput() {
+
function xwOutputHook() {
  
 
global $xwArticle, $xwArticleXML, $xwArticleTitle, $xwReadable, $xwWritable;
 
global $xwArticle, $xwArticleXML, $xwArticleTitle, $xwReadable, $xwWritable;
 
global $xwTemplate, $xwView, $xwPreview, $xwSave, $xwRaw, $xwUserLinks, $xwArticleLinks;
 
global $xwTemplate, $xwView, $xwPreview, $xwSave, $xwRaw, $xwUserLinks, $xwArticleLinks;
 +
global $wgOut, $wgUser;
  
# If no read-access, clear output
+
# If readable, get wiki-environment, else clear content
 
if ($xwReadable) {
 
if ($xwReadable) {
global $wgOut, $wgUser;
 
 
$skin = $wgUser->getSkin();
 
$skin = $wgUser->getSkin();
 
$wikiOut = &$wgOut->mBodytext;
 
$wikiOut = &$wgOut->mBodytext;
Line 110: Line 138:
 
else {
 
else {
 
$xwArticle = $wikiOut = '';
 
$xwArticle = $wikiOut = '';
xwMessage('Unable to comply.', 'red');
+
xwMessage('Cannot access article for reading!', 'red');
 
}
 
}
 +
 
# If no write-access, remove edit link
 
# If no write-access, remove edit link
 
if (!$xwWritable) unset($xwArticleLinks['Edit']);
 
if (!$xwWritable) unset($xwArticleLinks['Edit']);
 
if ($xwPreview) {
 
# If Previewing,
 
# - we can get our raw content to transform from inside the <textarea>
 
xwMessage('Previewing Page...');
 
if (preg_match('/^(.+Remember.+saved!.+?<\\/p>)(.+?)(<br style="clear:both;" \\/>.+<textarea.+?>\\s*(.+)<\\/textarea>.+)$/s', $wikiOut, $m)) {
 
xwDomificateArticle($xwArticle = html_entity_decode($m[4]));
 
if (is_object($xwArticle)) {
 
xwTransformArticle($xwArticle);
 
$xwArticle = $m[1].$xwArticle.$m[3];
 
} else $xwArticle = $wikiOut;
 
} else xwMessage('xmlWiki couldn\'t match preview-output!', 'red');
 
}
 
 
else {
 
# Not previewing
 
xwMessage('Processing Page...');
 
xwDomificateArticle($xwArticle);
 
if (is_object($xwArticle)) {
 
xwTransformArticle($xwArticle);
 
# Here we have to replace the $wikiOut content part with $xwArticle
 
} else $xwArticle = $wikiOut;
 
}
 
  
 
# If saving, check the publishing list
 
# If saving, check the publishing list
Line 147: Line 153:
 
foreach ($xwArticlePublish as $url) {
 
foreach ($xwArticlePublish as $url) {
 
if ($FH = fopen($url, 'a')) {
 
if ($FH = fopen($url, 'a')) {
 +
# ftp example: ftp://user:password@example.com/file.txt
 
fwrite($FH, $xwArticle);
 
fwrite($FH, $xwArticle);
 
fclose($FH);
 
fclose($FH);
 
xwMessage("Article published to $url");
 
xwMessage("Article published to $url");
} else xwMessage("Failed to publish to $url", 'red');
+
}
 +
else xwMessage("Failed to publish to $url", 'red');
 
}
 
}
 
}
 
}
 
 
 
# Check mirror-list and process if any
 
# Check mirror-list and process if any
 +
# - allows articles in separate wiki's to be syncronised
 +
# - does rudimentary security by sending a salt and response being a hash of the password generated from the salt
 +
# - this could be done for login too using ECMA-MD5 on browser
 
if (false) {
 
if (false) {
 
xwMessage('Mirroring...');
 
xwMessage('Mirroring...');
Line 166: Line 177:
 
xwTransformGeshi(); # Does geshi based on article-name and content
 
xwTransformGeshi(); # Does geshi based on article-name and content
 
xwTransformAddMessages(); # Adds raw messages to output
 
xwTransformAddMessages(); # Adds raw messages to output
xwTransformPageLayout(); # Uses xwTemplate to build xmlwiki-site structure around content
+
#xwTransformPageLayout(); # Uses xwTemplate to build xmlwiki-site structure around content
 
xwTransformPageStyle(); # Adds colour/font/proportions etc
 
xwTransformPageStyle(); # Adds colour/font/proportions etc
 
}
 
}
  
 
# And finally, output the sunovabitch
 
# And finally, output the sunovabitch
print $xwArticle;
+
$wgOut->output();
 +
print "<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><table><tr><td>$xwArticle</table>";
 
}
 
}
  
 +
# ---------------------------------------------------------------------------------------------------------------------- #
 +
# PARSER HOOK
 +
 +
# If attempted xml encountered, try to domificate and transform
 +
function xwParserHook(&$text) {
 +
 +
if (preg_match("/^<\\?xml/i", $text)) {
 +
xwDomificateArticle($text, 'ParserHook/text');
 +
xwTransformArticle($text);
 +
}
 +
 +
}
 +
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ---------------------------------------------------------------------------------------------------------------------- #
 
# ARTICLE FUNCTIONS
 
# ARTICLE FUNCTIONS
Line 183: Line 208:
 
if (isset($xwArticleCache[$articleTitle])) return $xwArticleCache[$articleTitle];
 
if (isset($xwArticleCache[$articleTitle])) return $xwArticleCache[$articleTitle];
 
# Get wiki article content (or use global if no article passed)
 
# Get wiki article content (or use global if no article passed)
if ($article = new Article(Title::newFromText($articleTitle))) $article = $article->getContent(false);
+
if ($article = new Article(Title::newFromText($articleTitle)))
 +
$article = $article->getContentWithoutUsingSoManyDamnGlobals();
 
if ($article == '(There is currently no text in this page)') return false;
 
if ($article == '(There is currently no text in this page)') return false;
 
if ($articleTitle) $xwArticleCache[$articleTitle] = $article;
 
if ($articleTitle) $xwArticleCache[$articleTitle] = $article;
Line 213: Line 239:
 
xwMessage("Domificating $id");
 
xwMessage("Domificating $id");
 
ob_start();
 
ob_start();
if ($dom = domxml_open_mem($article)) $article = $dom; else {
+
if ($dom = domxml_open_mem($article)) $article = $dom;
 +
else {
 
# Could not convert, extract error messages from output
 
# Could not convert, extract error messages from output
 
xwMessage("&nbsp;&nbsp;&nbsp;Failed :-(", 'red');
 
xwMessage("&nbsp;&nbsp;&nbsp;Failed :-(", 'red');
$err = preg_replace("/<.+?>/", "", ob_get_contents());
+
xwExtractMessages();
$err = preg_replace('/ in .+? on line.+?[0-9]+/', '', $err);
 
$err = preg_replace('/Warning.+?\\(\\): /', '', $err);
 
foreach (split("\n", $err) as $msg) if ($msg) xwMessage("&nbsp;&nbsp;&nbsp;$msg", 'red');
 
 
}
 
}
 
ob_end_clean();
 
ob_end_clean();
Line 246: Line 270:
 
if (preg_match('/<xsl:output +?method *= *"html"/i', $xslt))
 
if (preg_match('/<xsl:output +?method *= *"html"/i', $xslt))
 
$tResult = $tObject->result_dump_mem($tResult);
 
$tResult = $tObject->result_dump_mem($tResult);
$err = preg_replace("/<.+?>/", "", ob_get_contents());
+
xwExtractMessages();
$err = preg_replace('/ in .+? on line.+?[0-9]+/', '', $err);
 
$err = preg_replace('/Warning.+?\\(\\): /', '', $err);
 
foreach (split("\n", $err) as $msg) if ($msg) xwMessage("&nbsp;&nbsp;&nbsp;$msg", 'red');
 
 
ob_end_clean();
 
ob_end_clean();
 
if ($tResult) $article = $tResult; else return false;
 
if ($tResult) $article = $tResult; else return false;
 
return true;
 
return true;
 
}
 
}
 +
 +
# Return result set of an XPath query on passed DOM-object
 +
function xwXPathQuery(&$dom, $query) {
 +
xwMessage("Processing XPath Query &quot;$query&quot;");
 +
ob_start();
 +
$context = $dom->xpath_new_context();
 +
if ($xpath = $context->xpath_eval($query)) $result = $xpath->nodeset;
 +
else {
 +
xwMessage('&nbsp;&nbsp;&nbsp;Query Failed! :-(', 'red');
 +
$result = array();
 +
}
 +
xwExtractMessages();
 +
ob_end_clean();
 +
return $result;
 +
}
  
 
# Transform an article object
 
# Transform an article object
Line 274: Line 310:
 
if (ereg('^(.+)\\(\\)$', $tName, $m)) {
 
if (ereg('^(.+)\\(\\)$', $tName, $m)) {
 
# Transform is "name()" format, try to execute from a local function declaration
 
# Transform is "name()" format, try to execute from a local function declaration
# - probably this is not needed soon, only accept article-transforms
+
# - probably this is not needed soon, as XSLT-PI's may be a better structure
 
$tName = $m[1];
 
$tName = $m[1];
 
if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult;
 
if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult;
Line 280: Line 316:
 
}
 
}
 
else {
 
else {
# Get transform-article and apply to $article
+
# Transform is not local, get transform-article and apply to current-article-object
 
if ($tText = xwArticleContent($tName)) {
 
if ($tText = xwArticleContent($tName)) {
 +
xwMessage($tName,'red');
 +
xwMessage($tText,'orange');
 
# Get transform type
 
# Get transform type
 
$tType = xwArticleType($tName, $tText);
 
$tType = xwArticleType($tName, $tText);
Line 290: Line 328:
 
ob_start();
 
ob_start();
 
eval($tText);
 
eval($tText);
$err = preg_replace("/<.+?>/", "", ob_get_contents());
+
xwExtractMessages();
foreach (split("\n", $err) as $msg) if ($msg) xwMessage($msg, 'red');
 
 
ob_end_clean();
 
ob_end_clean();
 
}
 
}
Line 305: Line 342:
 
# If article is still a DOM object, stringify it once and for all
 
# If article is still a DOM object, stringify it once and for all
 
xwUndomificateArticle($article);  
 
xwUndomificateArticle($article);  
 
 
return true;
 
return true;
 
}
 
}
Line 314: Line 350:
 
# - we should use DTD's for each kind: document, image, layout, table
 
# - we should use DTD's for each kind: document, image, layout, table
  
# Do wiki markup on content
+
# Do wiki markup on was-object content
 
function xwTransformWikiMarkup() {
 
function xwTransformWikiMarkup() {
 
global $wgOut, $wgUser;
 
global $wgOut, $wgUser;
Line 453: Line 489:
 
# Return a list of the hrefs found in the passed content
 
# Return a list of the hrefs found in the passed content
 
function xwExtractHrefs($content) {
 
function xwExtractHrefs($content) {
preg_match_all('/(<a.+?>(.+)<\/a>)/', $content, $match);
+
preg_match_all('/(<a.+?>(.+)<\/a>)/', $content, $m);
 
$hrefs = array();
 
$hrefs = array();
 
+
for ($i = 0; $i < count($m[0]); $i++) $hrefs[$m[2][$i]] = $m[1][$i];
for ($i = 0; $i < count($match[0]); $i++) $hrefs[$match[2][$i]] = $match[1][$i];
 
 
return $hrefs;
 
return $hrefs;
 
}
 
}
  
 +
# Extract error-messages from captured output and put in proper message-queue
 +
function xwExtractMessages() {
 +
$err = preg_replace("/<.+?>/", "", ob_get_contents());
 +
$err = preg_replace('/ in .+? on line.+?[0-9]+/', '', $err);
 +
$err = preg_replace('/Warning.+?\\(\\): /', '', $err);
 +
foreach (split("\n", $err) as $msg) if ($msg) xwMessage("&nbsp;&nbsp;&nbsp;$msg", 'red');
 +
}
 +
 
# Add message to queue
 
# Add message to queue
 
function xwMessage($msg, $col = 'blue') {
 
function xwMessage($msg, $col = 'blue') {
 
global $xwMessages;
 
global $xwMessages;
 +
$msg = htmlentities("====>$msg<====");
 
return $xwMessages[] = "<p class=\"xwMessage\"><font color=\"$col\">$msg</font></p>\n";
 
return $xwMessages[] = "<p class=\"xwMessage\"><font color=\"$col\">$msg</font></p>\n";
 
}
 
}
  
 +
# Print list of passed object's methods and properties
 +
function xwCheckoutObject($obj) {
 +
global $$obj;
 +
$obj = $$obj->mOutput;
 +
print strtoupper("<br>$obj properties:<br>");
 +
foreach (get_object_vars($obj) as $k=>$v) print htmlentities("$k => $v")."<br>";
 +
print strtoupper("<br>$obj methods:<br>");
 +
foreach (get_class_methods($obj) as $k=>$v) print "$v<br>";
 +
die;
 +
}
  
 
function xwDomificateWiki() {
 
function xwDomificateWiki() {
Line 476: Line 530:
 
}
 
}
 
 
# This returns the monobook template built from string parts
 
function xwMonoBook() {
 
 
global $xwTemplate;
 
$data = &$xwTemplate->data;
 
$doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
 
$html = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="'.htmlspecialchars($data['lang']).'" lang="'.htmlspecialchars($data['lang']).'" dir="'.htmlspecialchars($data['dir']).'">';
 
 
# Head
 
$head = '<head><meta http-equiv="Content-Type" content="'.htmlspecialchars($data['mimetype']).'; charset='.htmlspecialchars($data['charset']).'" />';
 
$head .= $data['headlinks'];
 
$head .= '<title>'.htmlspecialchars($data['pagetitle']).'</title>';
 
$head .= '<style type="text/css" media="screen,projection">/*<![CDATA[*/ @import '.htmlspecialchars($data['stylepath']).'/'.htmlspecialchars($data['stylename']).'/main.css"; /*]]>*/</style>';
 
$head .= '<link rel="stylesheet" type="text/css" media="print" href="'.htmlspecialchars($data['stylepath']).'/common/commonPrint.css" />';
 
$head .= '<!--[if lt IE 5.5000]><style type="text/css">@import "'.htmlspecialchars($data['stylepath']).'/'.htmlspecialchars($data['stylename']).'/IE50Fixes.css";</style><![endif]-->';
 
$head .= '<!--[if IE 5.5000]><style type="text/css">@import "'.htmlspecialchars($data['stylepath']).'/'.htmlspecialchars($data['stylename']).'/IE55Fixes.css";</style><![endif]-->';
 
$head .= '<!--[if IE 6]><style type="text/css">@import "'.htmlspecialchars($data['stylepath']).'/'.htmlspecialchars($data['stylename']).'/IE60Fixes.css";</style><![endif]-->';
 
$head .= '<!--[if IE]><script type="text/javascript" src="'.htmlspecialchars($data['stylepath']).'/common/IEFixes.js"></script>';
 
$head .= '<meta http-equiv="imagetoolbar" content="no" /><![endif]-->';
 
if ($data['jsvarurl']) $head .= '<script type="text/javascript" src="'.htmlspecialchars($data['jsvarurl']).'"></script>';
 
$head .= '<script type="text/javascript" src="'.htmlspecialchars($data['stylepath']).'/common/wikibits.js"></script>';
 
if ($data['usercss']) $head .= '<style type="text/css">'.$data['usercss'].'</style>';
 
if ($data['userjs']) $head .= '<script type="text/javascript" src="'.htmlspecialchars($data['userjs']).'</script>';
 
if ($data['userjsprev']) $head .= '<script type="text/javascript">'.$data['userjsprev'].'</script>';
 
$head .= '</head>';
 
 
# Body 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"><font color="red">'.htmlspecialchars($data['title']).'</font></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 -->'.$data['bodytext'];
 
if ($data['catlinks']) $content .= '<div id="catlinks">'.$data['catlinks'].'</div>';
 
$content .= '<!-- end content --><div class="visualClear"></div>';
 
$content .= '</div></div></div>';
 
 
# Action-links
 
$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 ($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="'.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="'.htmlspecialchars($tmpl->translator->translate('go')).'" />';
 
$search .= '&nbsp;<input type="submit" name="fulltext" class="searchButton" value="'.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']).'">'.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']).'">'.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">';
 
if ($data['poweredbyico']) $footer .= '<div id="f-poweredbyico">'.$data['poweredbyico'].'</div>';
 
if ($data['copyrightico']) $footer .= '<div id="f-copyrightico">'.$data['copyrightico'].'</div>';
 
$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>';
 
 
# Build parts into a whole page and return
 
return " $doctype
 
$html
 
$head
 
$body
 
<div id=\"globalWrapper\">
 
$content
 
<div id=\"column-one\">
 
$actions
 
$personal
 
$logo
 
$nav
 
$search
 
$toolbox
 
$language
 
</div>
 
<div class=\"visualClear\"></div>
 
$footer
 
</div>
 
</body>
 
</html>
 
";
 
}
 
 
 
?>
 
?>
 
</php>
 
</php>

Revision as of 04:39, 13 June 2005

<php> <?php

  1. xmlWiki - MediaWiki XML Hack
  2. Nad - 2005-05-18
  1. TODO:
  2. Security:
  3. - xml:article - holds article perms, it doesn't have perms of its own (owner, read-groups, write-groups)
  4. - xml:user - r/w by user, not currently used, but for xmlWiki prefs etc
  5. - sys:user - r/w by admin only, holds users groups and other system info like stats
  6. Publish:
  7. - xml:article - holds article publish-list
  8. - mirror/wiki-sync
  9. Transforms:
  10. - xml:article - now transforms are in here (main article is now like <body>)
  11. - so we don't do the "its one of ours" thing anymore
  12. Raw as Default:
  13. - Need to mod the article-link so it has action=view
  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. Otherwise set up xmlWiki global environment ready for input and output processing

$xwEdit = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'edit'); $xwView = (!isset($_REQUEST['action']) || (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'view'))); $xwRaw = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'raw'); $xwPreview = isset($_REQUEST['wpPreview']); $xwSave = isset($_REQUEST['wpSave']); $xwUserName = $wgUser->mName;

  1. $xwArticle = $xwPreview ? $wgRequest->gettext('wpTextbox1') : $wgArticle->getContent(false);

$xwArticleTitle = $wgTitle->getPrefixedURL(); $xwArticleCache = array(); $xwArticleLinks = array(); $xwUserLinks = array(); $xwMessages = array(); $xwMetaArticle = false;

  1. Get associated articles and attempt to Domificate

if ($xwArticleXML = xwArticleContent("xml:$xwArticleTitle")) xwDomificateArticle($xwArticleXML, "xml:$xwArticleTitle"); if ($xwUserXML = xwArticleContent("xml:$xwUserName")) xwDomificateArticle($xwUserXML, "xml:$xwUserName"); if ($xwUserSYS = xwArticleContent("sys:$xwUserName")) xwDomificateArticle($xwUserSYS, "sys:$xwUserName");

  1. Security

if (ereg('^xml:(.+)$', $xwArticleTitle)) { $xwMetaArticle = true; # This is an xml-pseudo-namespace # - get perms from associated article/user } elseif (ereg('^sys:(.+)$', $xwArticleTitle)) { $xwMetaArticle = true; # This is an sys-pseudo-namespace # - only admin can read/write sys: } elseif (is_object($xwArticleXML)) { # Get perms from xml:article } else $xwReadable = $xwWritable = true;

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

function xwInputHook() {

global $xwArticle, $xwArticleTitle, $xwMetaArticle, $xwReadable, $xwWritable; global $xwSave;

# If an article-change is being attempted, if ($xwSave) { if (!$xwReadable || !$xwWritable) { # Not writable, change action to view xwMessage('Cannot access article for writing, changing action to "view"', 'red'); global $action; $action='view'; } else { # Parse POST for xmlWiki form input global $_REQUEST; foreach ($_REQUEST as $k => $v) {

if (is_object($xwArticle) && preg_match("/^xw:(@?)(\\w*):(.+)$/", $k, $m)) { # Form-input-name is "xw:[[@]node]:XPathQuery" which directs the value into xwArticle (if DOM) # - if "node" exists, then a new element (or att if "@node") is created for the value # - hidden-inputs can direct input-handlers into the transform list list(, $att, $node, $xpath) = $m; foreach (xwXPathQuery($xwArticleXML, $xpath) as $result) { if ($att && $node) { # create attribute for value } elseif ($node) { # create element for value } else { # overwrite existing node content } } }

elseif ($xwMetaArticle && ($k == 'wpSave')) { # if it's a pseudo-namespace, wpSave should be valid XML to be saved xwDomificate($wpSave); if (!is_object($wpSave)) { xwMessage('A meta-article must be valid XML! changing action to "edit"', 'red'); global $action; $action='edit'; } }

} } } }

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

function xwOutputHook() {

global $xwArticle, $xwArticleXML, $xwArticleTitle, $xwReadable, $xwWritable; global $xwTemplate, $xwView, $xwPreview, $xwSave, $xwRaw, $xwUserLinks, $xwArticleLinks; global $wgOut, $wgUser;

# If readable, get wiki-environment, else clear content if ($xwReadable) { $skin = $wgUser->getSkin(); $wikiOut = &$wgOut->mBodytext; } else { $xwArticle = $wikiOut = ; xwMessage('Cannot access article for reading!', 'red'); }

# If no write-access, remove edit link if (!$xwWritable) unset($xwArticleLinks['Edit']);

# If saving, check the publishing list if ($xwSave) {

# Check publish-list and process if any if (false) { xwMessage('Publishing...'); # Publish each item foreach ($xwArticlePublish as $url) { if ($FH = fopen($url, 'a')) { # ftp example: ftp://user:password@example.com/file.txt fwrite($FH, $xwArticle); fclose($FH); xwMessage("Article published to $url"); } else xwMessage("Failed to publish to $url", 'red'); } }

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

# Apply xmlwiki-environment-transforms to output unless 'raw' requested # - Article is always in string form by now if (!$xwRaw) { xwTransformWikiMarkup(); # Adds wiki markup to result xwTransformGeshi(); # Does geshi based on article-name and content xwTransformAddMessages(); # Adds raw messages to output #xwTransformPageLayout(); # Uses xwTemplate to build xmlwiki-site structure around content xwTransformPageStyle(); # Adds colour/font/proportions etc }

# And finally, output the sunovabitch $wgOut->output();

print "














$xwArticle

";

}

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. PARSER HOOK
  1. If attempted xml encountered, try to domificate and transform

function xwParserHook(&$text) {

if (preg_match("/^<\\?xml/i", $text)) { xwDomificateArticle($text, 'ParserHook/text'); xwTransformArticle($text); }

}

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

function xwArticleContent($articleTitle) { # Return with results if already cached global $xwArticleCache; if (isset($xwArticleCache[$articleTitle])) return $xwArticleCache[$articleTitle]; # Get wiki article content (or use global if no article passed) if ($article = new Article(Title::newFromText($articleTitle))) $article = $article->getContentWithoutUsingSoManyDamnGlobals(); if ($article == '(There is currently no text in this page)') return false; if ($articleTitle) $xwArticleCache[$articleTitle] = $article; return $article; }

  1. Decide kind of article from content and title

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

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

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

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

function xwUndomificateArticle(&$article, $id = ) { if (!is_object($article)) return false; xwMessage("Undomificating $id"); # 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 XSLT to article-dom
  2. - if output-method is html, article will get stringified

function xwApplyXSLT(&$article, &$xslt, $id = ) { xwMessage("Processing XSLT $id"); ob_start(); $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 ($tResult) $article = $tResult; else return false; return true; }

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

function xwXPathQuery(&$dom, $query) { xwMessage("Processing XPath Query "$query""); ob_start(); $context = $dom->xpath_new_context(); if ($xpath = $context->xpath_eval($query)) $result = $xpath->nodeset; else { xwMessage('   Query Failed! :-(', 'red'); $result = array(); } xwExtractMessages(); ob_end_clean(); return $result; }

  1. Transform an article object
  2. - Article must be an xmlwiki:* DOM object to be transformable
  3. - Article is undomificated on exit
  4. - Article remains unchanged if not DOM

function xwTransformArticle(&$article) {

# Get doc info or exit if not one of ours if (!is_object($article)) return false; $aType = $article->doctype(); $aType = $aType->name; $aRoot = $article->document_element(); if (!ereg('^xmlwiki:', $aType)) return false; # this must change to meta article xwMessage("Transforming doctype \"$aType\"");

# Loop thru transforms applying each foreach ($aRoot->get_elements_by_tagname('transform') as $tElement) { $tName = $tElement->get_content(); if (ereg('^(.+)\\(\\)$', $tName, $m)) { # Transform is "name()" format, try to execute from a local function declaration # - probably this is not needed soon, as XSLT-PI's may be a better structure $tName = $m[1]; if ($tResult = @call_user_func("xwTransform_$tName", $article)) $article = $tResult; else xwMessage("   Unknown transform \"$tName\"!", 'red'); } else { # Transform is not local, get transform-article and apply to current-article-object if ($tText = xwArticleContent($tName)) { xwMessage($tName,'red'); xwMessage($tText,'orange'); # Get transform type $tType = xwArticleType($tName, $tText); if ($tType == 'xslt') xwApplyXSLT($article, $tText, $tName); elseif ($tType == 'php') { # php, check perms and execute (trapping errors for reporting - unless fatal) if ($perms_check_out_ok) { ob_start(); eval($tText); xwExtractMessages(); ob_end_clean(); } } elseif ($tType == 'css') { # include CSS ref in content } } else xwMessage("   No such transform-article \"$tName\"!", 'red'); } }

# If article is still a DOM object, stringify it once and for all xwUndomificateArticle($article); return true; }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. TRANSFORMS
  3. - these transforms are only temporarily here - they should be articles
  4. - we should use DTD's for each kind: document, image, layout, table
  1. Do wiki markup on was-object content

function xwTransformWikiMarkup() { global $wgOut, $wgUser; $skin = $wgUser->getSkin(); $wikiOut = &$wgOut->mBodytext; }

  1. Include Geshi syntax highlighting
  2. NOTE: Geshi can be installed the normal way too now!
  3. because transforms don't use wikiOut

function xwTransformGeshi() { return;

global $wgOut, $wgUser; global $xwTemplate, $xwArticle, $xwArticleTitle;

$skin = $wgUser->getSkin(); $wikiOut = &$wgOut->mBodytext;

# First check for entire article highlighting # Highlight entire article if xwLang has been set if ($xwLang) { $xwGeshi = new GeSHi($xwArticle, $xwLang, "extensions/geshi/geshi"); $xwGeshi->set_tab_width(4); $geshi->set_keywords_style(1, 'font-weight: 100;', true); #$geshi->set_comments_style #$geshi->set_escape_char_style #$geshi->set_brackets_style #$geshi->set_strings_style #$geshi->set_numbers_style #$geshi->set_methods_style #$geshi->set_symbols_style #$geshi->set_regexps_style #$geshi->set_script_style #$xwArticle = $xwGeshi->parse_code(); } }

  1. Append messages to content

function xwTransformAddMessages() { global $xwArticle, $xwMessages; $xwArticle .= join("\n", $xwMessages); }

  1. This is the default postTransform which surrounds the page
  2. - most transforms do not reference the global-current-article like this one
  3. - this is a special transform, because it affects all pages, even special pages

function xwTransformPageLayout() { global $xwArticle, $xwArticleTitle, $xwUserLinks, $xwArticleLinks; $xwArticle = "xmlWiki - $xwArticleTitle

".join('
',$xwArticleLinks)."
\"xmlWiki\"
$xwArticleTitle
$xwArticle

"; }

  1. Apply styling to page

function xwTransformPageStyle() { global $xwArticle;

$xwArticle = preg_replace("/(

)(.+?)(<\\/p>)/", '

   $2   

', $xwArticle);

}

function xwTransform_document_xxx() { # documents are built from parts, # the parts are a tree in its body # includes 'edit' link for each (like section, and also like table) }

  1. A document object is finalised by transforming it to its body's text

function xwTransform_document_render() { global $xwArticle; $root = $xwArticle->document_element(); $body = $root->get_elements_by_tagname('body'); if (count($body)) $xwArticle = $body[0]->dump_mem(true); }

function xwTransform_layout_xxx() { # for layout container style article }

function xwTransform_image_xxx() { # object contains properties for loading and transforming an image # image needs no body # Get the global image on which imageTransforms work global $xwArticleImage; }

  1. Converts the in-memory-working image into article content

function xwTransform_image_render() { global $xwArticle, $xwArticleTitle, $xwArticleImage, $xwRaw; if ($xwRaw) { # render article directly if 'raw' request imagePng($xwArticleImage); # Clear article or it will render as xml $xwArticle = ; # set header to image header("Content-type: image/png"); }

else { # the raw image is not being requested, render image in-page with info instead $xwArticle = "<img src=\"$xwArticleTitle?action=raw\" alt=\"$xwArticleTitle\"/>"; } }

  1. A table object is an interface to a database table
  2. - this can't be implimented fully until the 'edit' hook is done
  3. - the query result is a list in document body, so any normal document transforms can follow it

function xwTransform_table_xxx() { # - view lists the table/result with links to edit each (like a normal composition-doc) # - also form for create/edit/edlete # - result rows are in document body (just like a content-tree in a document would be)

# NODE-LIKE-DB

# - if all tables have GUID for pri-key, # - and all have time/duration (or cycle?), # - and we have master list of cols-to-table # - then we could create a meta-query (resulting in pri-key-list of all matches)

}

  1. Transform the body content to a xtml/javascript tree

function xwTransform_tree() { # import php-tree-component }

  1. ---------------------------------------------------------------------------------------------------------------------- #
  2. UTILITY FUNCTIONS
  1. Return a list of the hrefs found in the passed content

function xwExtractHrefs($content) { preg_match_all('/(<a.+?>(.+)<\/a>)/', $content, $m); $hrefs = array(); for ($i = 0; $i < count($m[0]); $i++) $hrefs[$m[2][$i]] = $m[1][$i]; return $hrefs; }

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

function xwExtractMessages() { $err = preg_replace("/<.+?>/", "", ob_get_contents()); $err = preg_replace('/ in .+? on line.+?[0-9]+/', , $err); $err = preg_replace('/Warning.+?\\(\\): /', , $err); foreach (split("\n", $err) as $msg) if ($msg) xwMessage("   $msg", 'red'); }

  1. Add message to queue

function xwMessage($msg, $col = 'blue') { global $xwMessages; $msg = htmlentities("====>$msg<====");

return $xwMessages[] = "

$msg

\n";

}

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

function xwCheckoutObject($obj) { global $$obj; $obj = $$obj->mOutput; 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
"; die; }

function xwDomificateWiki() { global $xwTemplate; #$doc = domxml_new_doc("1.0"); #$root = $doc->add_root("xmkwiki"); #$head = $root->new_child("head", ""); #$head->new_child("title", "untitiled"); #htmlentities($doc->dump_mem()); }

?> </php>