Difference between revisions of "Extension:Workflow.php"

From Organic Design wiki
m
(make into a Workflow class now that more complex)
Line 7: Line 7:
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
  
define('WORKFLOW_VERSION','0.0.1, 2007-10-23');
+
define('WORKFLOW_VERSION','0.0.2, 2007-10-24');
  
$wgWorkflowMagic               = "workflow";
+
$wgWorkflowMagic = "workflow";
$wgExtensionFunctions[]        = 'wfSetupWorkflow';
 
$wgHooks['LanguageGetMagic'][] = 'wfWorkflowLanguageGetMagic';
 
  
 
$wgExtensionCredits['parserhook'][] = array(
 
$wgExtensionCredits['parserhook'][] = array(
Line 21: Line 19:
 
);
 
);
  
# If it's a workflow-related ajax call, don't use dispatcher (because we need the catlinks generated by normal page render)
+
class Workflow {
if ($wgUseAjax && $_REQUEST['action'] == 'ajax' && $_REQUEST['rs'] == 'wfWorkflowUpdateState' && is_array($_REQUEST['rsargs'])) {
 
list($_REQUEST['title'],$wgWorkflowName,$wgWorkflowState) = $_REQUEST['rsargs'];
 
$wgHooks['OutputPageBeforeHTML'][] = 'wfWorkflowReturnCatlinks';
 
$wgWorkflowUpdateState = true;
 
$_REQUEST['action'] = 'render';
 
} else $wgWorkflowUpdateState = false;
 
  
# Expand the #tags to reveal the current state and hide the others and add javascript
+
var $magic;
# - note the hidden states mustn't be rendered because they contain categorisation links which shouldn't be processed
+
var $state;
function wfWorkflowExpand() {
+
var $name;
 +
var $pagename;
 +
var $update;
  
# Extend catlinks information to include workflows
+
# Constructor
wfWorkflowExtendCatlinks();
+
function Workflow() {
 +
global $wgHooks,$wgExtensionFunctions,$wgWorkflowMagic;
 +
$wgExtensionFunctions[] = array($this,'init');
 +
$wgHooks['LanguageGetMagic'][] = array($this,'languageGetMagic');
 +
$this->bypassAjaxDispatcher();
 +
$this->magic = $wgWorkflowMagic;
 +
$this->pagename = $_REQUEST['title'];
 +
}
  
# Populate $argv with both named and numeric parameters
+
# Called from $wgExtensionFunctions array when initialising extensions
$argv  = array();
+
function init() {
$items = array();
+
global $wgParser,$wgWorkflowMagic,$wgWorkflowUpdateState;
foreach (func_get_args() as $arg) if (!is_object($arg)) {
+
$wgParser->setFunctionHook($wgWorkflowMagic,array($this,'expandMagic'));
if (preg_match('/^(.+?)\\s*=\\s*(.+)$/',$arg,$match)) $argv[$match[1]] = $match[2]; else $argv[] = $arg;
+
if ($this->update) $this->updateState();
 
}
 
}
  
# Render the visible item
+
# Expand the #tags to reveal the current state and hide the others and add javascript
# - could also render all non-visible items here too, but with a cloned parser object so that the categorisation doesn't occur
+
# - note the hidden states mustn't be rendered because they contain categorisation links which shouldn't be processed
# - that way the JS can instantly cycle through all rendered states, then set new state and get updated catlinks via ajax
+
function expandMagic() {
$text = 'done';
+
 
foreach ($argv as $k => $v) {
+
# Extend catlinks information to include workflows
if ($format == 'tag') {
+
$this->extendCatlinks();
$text = "<div class='$class' id='$id'>\n$text</div>";
+
 
 +
# Populate $argv with both named and numeric parameters
 +
$argv  = array();
 +
$items = array();
 +
foreach (func_get_args() as $arg) if (!is_object($arg)) {
 +
if (preg_match('/^(.+?)\\s*=\\s*(.+)$/',$arg,$match)) $argv[$match[1]] = $match[2]; else $argv[] = $arg;
 
}
 
}
elseif ($format == 'list') {
+
 
$text .= "<div class='$class item' id='$k'>$v</div>\n";
+
# Render the visible item
$text = "<div class='$class' id='$id'>\n$text</div>";
+
# - could also render all non-visible items here too, but with a cloned parser object so that the categorisation doesn't occur
 +
# - that way the JS can instantly cycle through all rendered states, then set new state and get updated catlinks via ajax
 +
$text = 'done';
 +
foreach ($argv as $k => $v) {
 +
if ($format == 'tag') {
 +
$text = "<div class='$class' id='$id'>\n$text</div>";
 +
}
 +
elseif ($format == 'list') {
 +
$text .= "<div class='$class item' id='$k'>$v</div>\n";
 +
$text = "<div class='$class' id='$id'>\n$text</div>";
 +
}
 
}
 
}
 +
 +
return $text;
 
}
 
}
  
return $text;
+
# Update a tag state in an article
}
+
function updateState() {
 +
$title = Title::newFromtext($this->pagename);
 +
if (is_object($title) && $title->exists()) {
 +
$article = new Article($title);
 +
$text = $article->getContent();
 +
preg_replace_callback("/\\{\\{#{$this->magic}:.*?id\\s*=\\s*['\"]?$id\\W.*?\\}\\}/s",array($this,'updateStateCB'),$text,1,$count);
 +
if ($count) $article->updateArticle($text,"summary",false,false);
 +
}
 +
}
  
# Update a tag state in an article
+
# Replacement callback for updating tag state
function wfWorkflowUpdateState($pagename,$name,$state) {
+
function updateStateCB($m) {
global $wgWorkflowMagic,$wgWorkflowState;
+
$m[0] = preg_replace("/(state\\s*=\\s*['\"]?)\\w+/","$1{$this->state}",$m[0],1,$count);
$title = Title::newFromtext($pagename);
+
if ($count < 1) $m[0] = preg_replace("/(\\{\\{#{$this->magic}:/","$0state=${$this->state}|",$m[0]);
if (is_object($title) && $title->exists()) {
+
return $m[0];
$article = new Article($title);
 
$text = $article->getContent();
 
$wgWorkflowState = $state;
 
preg_replace_callback("/\\{\\{#$wgWorkflowMagic:.*?id\\s*=\\s*['\"]?$id\\W.*?\\}\\}/s",'wfWorkflowUpdateStateCB',$text,1,$count);
 
if ($count) $article->updateArticle($text,"summary",false,false);
 
 
}
 
}
}
 
  
# Replacement callback for updating tag state
+
# Extend catlinks information to include workflows
function wfWorkflowUpdateStateCB($m) {
+
function extendCatlinks() {
global $wgWorkflowMagic,$wgWorkflowState;
+
static $done = 0;
$m[0] = preg_replace("/(state\\s*=\\s*['\"]?)\\w+/","$1$wgWorkflowState",$m[0],1,$count);
+
if ($done++) return;
if ($count < 1) $m[0] = preg_replace("/(\\{\\{#$wgWorkflowMagic:/","$0state=$wgWorkflowState|",$m[0]);
+
global $wgUser;
return $m[0];
+
$skin = $wgUser->getSkin();
}
+
 
 +
# Create a new Skin class (WorkflowSkin) by extending the existing one with overridden getCategoryLinks method
 +
$class = get_class($skin);
 +
eval("class WorkflowSkin extends {$class} ".'{
 +
function getCategoryLinks() {
 +
$catlinks = parent::getCategoryLinks();
 +
$catlinks .= "<br>foo";
 +
return $catlinks;
 +
}
 +
}');
 +
 
 +
# Replace user's skin with a WorkflowSkin replica
 +
$wgUser->mSkin = new WorkflowSkin();
 +
foreach (array_keys(get_class_vars($class)) as $k) $wgUser->mSkin->$k = $skin->$k;
 +
}
  
# Extend catlinks information to include workflows
+
# Return just the catlinks to the client after updating a tag state
function wfWorkflowExtendCatlinks() {
+
function returnCatlinks() {
static $done = 0;
+
global $wgUser,$wgOut;
if ($done++) return;
+
$skin = $wgUser->getSkin();
global $wgUser;
+
$catlinks = is_object($skin) ? $skin->getCategoryLinks() : 'Error: no skin!';
$skin = $wgUser->getSkin();
+
$wgOut->disable();
 +
while(@ob_end_clean());
 +
if (in_array('Content-Encoding: gzip',headers_list())) $catlinks = gzencode($catlinks);
 +
echo($catlinks);
 +
return false;
 +
}
  
# Create a new Skin class (WorkflowSkin) by extending the existing one with overridden getCategoryLinks method
+
# If it's a workflow-related ajax call, don't use dispatcher (because we need the catlinks generated by normal page render)
$class = get_class($skin);
+
function bypassAjaxDispatcher() {
eval("class WorkflowSkin extends {$class} ".'{
+
global $wgUseAjax,$wgHooks;
function getCategoryLinks() {
+
if ($wgUseAjax && $_REQUEST['action'] == 'ajax' && $_REQUEST['rs'] == $this->magic && is_array($_REQUEST['rsargs'])) {
$catlinks = parent::getCategoryLinks();
+
list($_REQUEST['title'],$this->name,$this->state) = $_REQUEST['rsargs'];
$catlinks .= "<br>foo";
+
$wgHooks['OutputPageBeforeHTML'][] = array($this,'returnCatlinks');
return $catlinks;
+
$this->update = true;
 +
$_REQUEST['action'] = 'render';
 
}
 
}
}');
+
else $this->update = false;
 +
}
  
# Replace user's skin with a WorkflowSkin replica
+
# Needed in some versions to prevent Special:Version from breaking
$wgUser->mSkin = new WorkflowSkin();
+
function __toString() { return 'Workflow'; }
foreach (array_keys(get_class_vars($class)) as $k) $wgUser->mSkin->$k = $skin->$k;
 
}
 
  
# Return just the catlinks to the client after updating a tag state
+
# Set up the magic words
function wfWorkflowReturnCatlinks() {
+
function languageGetMagic(&$magicWords,$langCode = 0) {
global $wgUser,$wgOut;
+
global $wgWorkflowMagic;
$skin = $wgUser->getSkin();
+
$magicWords[$wgWorkflowMagic] = array(0,$wgWorkflowMagic);
$catlinks = is_object($skin) ? $skin->getCategoryLinks() : 'Error: no skin!';
+
return true;
$wgOut->disable();
+
}
while(@ob_end_clean());
+
if (in_array('Content-Encoding: gzip',headers_list())) $catlinks = gzencode($catlinks);
 
echo($catlinks);
 
return false;
 
 
}
 
}
  
# Called from $wgExtensionFunctions array when initialising extensions
+
$wgWorkflow = new Workflow();
function wfSetupWorkflow() {
 
global $wgParser,$wgWorkflowMagic,$wgWorkflowUpdateState;
 
$wgParser->setFunctionHook($wgWorkflowMagic,'wfWorkflowExpand');
 
if ($wgWorkflowUpdateState) wfWorkflowUpdateState($_REQUEST['title'],$wgWorkflowName,$wgWorkflowState);
 
}
 
 
 
# Needed in MediaWiki >1.8.0 for magic word hooks to work properly
 
function wfWorkflowLanguageGetMagic(&$magicWords,$langCode = 0) {
 
global $wgWorkflowMagic;
 
$magicWords[$wgWorkflowMagic] = array(0,$wgWorkflowMagic);
 
return true;
 
}
 
 
?>
 
?>

Revision as of 00:06, 24 October 2007

<?php

  1. Extension:Workflow
Info.svg These are the MediaWiki extensions we're using and/or developing. Please refer to the information on the mediawiki.org wiki for installation and usage details. Extensions here which have no corresponding mediawiki article are either not ready for use or have been superseded. You can also browse our extension code in our local Subversion repository or our GitHub mirror.
Voodoo.svg

In computer programming, "Voodoo", or "Magic" refers to techniques that are secret or not widely known, and may be deliberately kept secret. The Jargon File makes a distinction between "deep magic", which refers to code based on esoteric theoretical knowledge; "black magic" (voodoo), which refers to code based on techniques that appear to work but which lack a theoretical explanation; and "heavy wizardry", which refers to code based on obscure or undocumented intricacies of particular hardware or software.

At Organic Design the most common of these is extending an instance's class at runtime after it has been instantiated, a technique that can be used to provide additional hooks into existing code without requiring modification of code-base files. Another is reading in a class file, declaring it under a different name, then sub-classing that with a new class of the original name - that way the environment uses the new extended class thinking it's the original one.

See also

  1. - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html)
  2. - Author: User:NadCategory:Extensions created with Template:Extension
  3. - Started: 2007-10-06

if (!defined('MEDIAWIKI')) die('Not an entry point.');

define('WORKFLOW_VERSION','0.0.2, 2007-10-24');

$wgWorkflowMagic = "workflow";

$wgExtensionCredits['parserhook'][] = array( 'name' => 'Workflow', 'author' => 'User:Nad', 'description' => 'Adds the ability for articles to be part of workflow sequences and easily moved dynamically between phases in the sequence using AJAX.', 'url' => 'http://www.mediawiki.org/wiki/Extension:Workflow', 'version' => WORKFLOW_VERSION );

class Workflow {

var $magic; var $state; var $name; var $pagename; var $update;

# Constructor function Workflow() { global $wgHooks,$wgExtensionFunctions,$wgWorkflowMagic; $wgExtensionFunctions[] = array($this,'init'); $wgHooks['LanguageGetMagic'][] = array($this,'languageGetMagic'); $this->bypassAjaxDispatcher(); $this->magic = $wgWorkflowMagic; $this->pagename = $_REQUEST['title']; }

# Called from $wgExtensionFunctions array when initialising extensions function init() { global $wgParser,$wgWorkflowMagic,$wgWorkflowUpdateState; $wgParser->setFunctionHook($wgWorkflowMagic,array($this,'expandMagic')); if ($this->update) $this->updateState(); }

# Expand the #tags to reveal the current state and hide the others and add javascript # - note the hidden states mustn't be rendered because they contain categorisation links which shouldn't be processed function expandMagic() {

# Extend catlinks information to include workflows $this->extendCatlinks();

# Populate $argv with both named and numeric parameters $argv = array(); $items = array(); foreach (func_get_args() as $arg) if (!is_object($arg)) { if (preg_match('/^(.+?)\\s*=\\s*(.+)$/',$arg,$match)) $argv[$match[1]] = $match[2]; else $argv[] = $arg; }

# Render the visible item # - could also render all non-visible items here too, but with a cloned parser object so that the categorisation doesn't occur # - that way the JS can instantly cycle through all rendered states, then set new state and get updated catlinks via ajax $text = 'done'; foreach ($argv as $k => $v) { if ($format == 'tag') {

$text = "

\n$text

";

} elseif ($format == 'list') {

$text .= "

$v

\n"; $text = "

\n$text

";

} }

return $text; }

# Update a tag state in an article function updateState() { $title = Title::newFromtext($this->pagename); if (is_object($title) && $title->exists()) { $article = new Article($title); $text = $article->getContent(); preg_replace_callback("/\\{\\{#{$this->magic}:.*?id\\s*=\\s*['\"]?$id\\W.*?\\}\\}/s",array($this,'updateStateCB'),$text,1,$count); if ($count) $article->updateArticle($text,"summary",false,false); } }

# Replacement callback for updating tag state function updateStateCB($m) { $m[0] = preg_replace("/(state\\s*=\\s*['\"]?)\\w+/","$1{$this->state}",$m[0],1,$count); if ($count < 1) $m[0] = preg_replace("/(\\{\\{#{$this->magic}:/","$0state=${$this->state}|",$m[0]); return $m[0]; }

# Extend catlinks information to include workflows function extendCatlinks() { static $done = 0; if ($done++) return; global $wgUser; $skin = $wgUser->getSkin();

# Create a new Skin class (WorkflowSkin) by extending the existing one with overridden getCategoryLinks method $class = get_class($skin); eval("class WorkflowSkin extends {$class} ".'{ function getCategoryLinks() { $catlinks = parent::getCategoryLinks(); $catlinks .= "
foo"; return $catlinks; } }');

# Replace user's skin with a WorkflowSkin replica $wgUser->mSkin = new WorkflowSkin(); foreach (array_keys(get_class_vars($class)) as $k) $wgUser->mSkin->$k = $skin->$k; }

# Return just the catlinks to the client after updating a tag state function returnCatlinks() { global $wgUser,$wgOut; $skin = $wgUser->getSkin(); $catlinks = is_object($skin) ? $skin->getCategoryLinks() : 'Error: no skin!'; $wgOut->disable(); while(@ob_end_clean()); if (in_array('Content-Encoding: gzip',headers_list())) $catlinks = gzencode($catlinks); echo($catlinks); return false; }

# If it's a workflow-related ajax call, don't use dispatcher (because we need the catlinks generated by normal page render) function bypassAjaxDispatcher() { global $wgUseAjax,$wgHooks; if ($wgUseAjax && $_REQUEST['action'] == 'ajax' && $_REQUEST['rs'] == $this->magic && is_array($_REQUEST['rsargs'])) { list($_REQUEST['title'],$this->name,$this->state) = $_REQUEST['rsargs']; $wgHooks['OutputPageBeforeHTML'][] = array($this,'returnCatlinks'); $this->update = true; $_REQUEST['action'] = 'render'; } else $this->update = false; }

# Needed in some versions to prevent Special:Version from breaking function __toString() { return 'Workflow'; }

# Set up the magic words function languageGetMagic(&$magicWords,$langCode = 0) { global $wgWorkflowMagic; $magicWords[$wgWorkflowMagic] = array(0,$wgWorkflowMagic); return true; }

}

$wgWorkflow = new Workflow(); ?>