Difference between revisions of "Extension:Workflow.php"

From Organic Design wiki
(0.0.6 - use NS word for special, magic and pfunc)
(in OD repo now)
 
(35 intermediate revisions by 3 users not shown)
Line 1: Line 1:
<?php
+
{{svn|extensions|Workflow/Workflow.php}}
# Extension:Workflow{{Category:Extensions|Workflow}}{{Category:Code that uses voodoo|Workflow}}{{php}}
 
# - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html)
 
# - Author: [http://www.organicdesign.co.nz/nad User:Nad]{{Category:Extensions created with Template:Extension}}
 
# - Started: 2007-10-06
 
 
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
 
 
 
define('WORKFLOW_VERSION','0.0.6, 2007-10-24');
 
 
 
$wgExtensionCredits['parserhook'][] = $wgExtensionCredits['specialpage'][] = array(
 
'name'        => 'Workflow',
 
'author'      => '[http://www.organicdesign.co.nz/nad 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
 
);
 
 
 
# Define a new specialpage for displaying a list of workflows
 
require_once "$IP/includes/SpecialPage.php";
 
class SpecialWorkflow extends SpecialPage {
 
 
 
function SpecialWorkflow() {
 
SpecialPage::SpecialPage(wfMsg('workflow'),'',true,false,false,false);
 
}
 
 
# Render list
 
function execute($param) {
 
global $wgParser,$wgOut;
 
$wgParser->disableCache();
 
$this->setHeaders();
 
$wgOut->addWikiText('Not done yet...',true);
 
}
 
}
 
 
# Define main workflow class containing all the functionality
 
class Workflow {
 
 
 
var $magic; # magic word used for the workflow parser-function (set from messages)
 
var $state; # state to update a workflow to in an ajax update request
 
var $name; # name of the workflow to update
 
var $pagename; # the name of the current page (in which the workflows reside)
 
var $update; # whether or not a state is being updated
 
 
 
# Constructor
 
function Workflow() {
 
global $wgHooks,$wgSiteNotice,$wgExtensionFunctions,$wgWorkflowMagic,$wgExtraNamespaces;
 
 
 
# Require NS_WORKFLOW to be defined before installing parser-function or javascript etc
 
if (!defined('NS_WORKFLOW')) {
 
$wgSiteNotice .= '<div class="usermessage">'.wfMsg('workflowMissingNamespace').'</div>';
 
}
 
 
 
# The parser-function name, special-page name and links all use the word defined in $wgExtraNamespaces[NS_WORKFLOW]
 
$this->magic = $wgExtraNamespaces[NS_WORKFLOW];
 
 
# Reserve the magic word for use as a parser-function
 
$wgHooks['LanguageGetMagic'][] = array($this,'languageGetMagic');
 
 
# If it's a workflow-related ajax call, don't use dispatcher (because we need the catlinks generated by normal page render)
 
# - this sets the state, name, pagename and update properties
 
$this->bypassAjaxDispatcher();
 
 
# Add the Workflow::init method to the list of functions called after the environment is set up
 
$wgExtensionFunctions[] = array($this,'init');
 
}
 
 
 
# Called from $wgExtensionFunctions array when initialising extensions
 
function init() {
 
global $wgParser,$wgLanguageCode,$wgMessageCache;
 
 
 
# Add the messages used (todo: move into i18n)
 
if ($wgLanguageCode == 'en') {
 
$wgMessageCache->addMessages(array(
 
'workflow' => $this->magic, # NOTE: the parser-function, special-page and link-rendering all use this word
 
'workflowMissingNamespace' => 'The NS_WORKFLOW namespace must be defined. The workflow extension is disabled.'
 
));
 
}
 
 
# Add the specialpage to the environment
 
SpecialPage::addPage(new SpecialWorkflow());
 
 
# Add the parser-function hook
 
$wgParser->setFunctionHook($this->magic,array($this,'expandMagic'));
 
 
# Update a workflow state if flag set by Ajax processing
 
if ($this->update) $this->updateState();
 
 
# Add the client-side scripts for changing states
 
$this->addJS();
 
}
 
 
 
# 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(&$parser) {
 
global $wgUser,$namespaceNames,$wgJsMimeType;
 
$tmpl = $namespaceNames[NS_TEMPLATE];
 
$cat = $namespaceNames[NS_CATEGORY];
 
 
 
# Extend catlinks information to include workflows
 
$this->extendCatlinks();
 
 
 
# Populate $argv with both named and numeric parameters
 
$argv  = array();
 
$items = array();
 
$name = 'Untitled';
 
foreach (func_get_args() as $arg) if (!is_object($arg)) {
 
if (preg_match('/^(.+?)\\s*=\\s*(.+)$/',$arg,$match)) $argv[$match[1]] = $match[2]; else $name = $arg;
 
}
 
 
 
# Make a clone of the parser for parsing without affecting LinkHolder or catlinks arrays
 
$psr = new parser;
 
$opt = ParserOptions::newFromUser($wgUser);
 
 
 
# Get the list of workflow states from the Workflow article
 
$states = array();
 
$text = "[[{$this->wf}:$name]]";
 
$title = Title::newFromtext($name,NS_WORKFLOW);
 
if (is_object($title) && $title->exists()) {
 
$article = new Article($title);
 
$links = $psr->parse($article->getContent(),$title,$opt,true,true)->getLinks();
 
if (is_array($links[NS_MAIN])) $states = $links[NS_MAIN];
 
else $text = "[[{$this->wf}:$name|{$this->wf}:$name (".wfMsg('workflowMissingNamespace').")]]";
 
}
 
 
 
# Transclude each (use a parser clone for the ones which aren't current to avoid categorisation)
 
if (count($states)) {
 
$text = "<div class='workflow' id='workflow-$name' onClick='workflowSwitchState(\"$name\")'>";
 
$this->workflows[$name] = array();
 
$data = '0';
 
foreach ($states as $dbk => $id) {
 
$title = Title::newFromText($dbk,NS_TEMPLATE);
 
$text .= $title->exists() ? "<br>($name)" : "<br>[[$template:$dbk]]";
 
$data .= ",'$dbk'";
 
#$text .= "[[$category:$name]]"; # if state is current
 
}
 
$text .= "</div><script type='$wgJsMimeType'>workflowData['$name']=[$data];</script>\n";
 
}
 
 
# Surround the content in a clickable container which updates the displayed item and persistent state
 
# - container must call workflowSwitchState when clicked
 
# - container must update workflowStates[] and workflowLengths[]
 
 
 
return array($text,'noparse' => true);
 
}
 
 
 
# Update the current state of a workflow item in the requested 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,'updateStateCallback'),$text,1,$count);
 
if ($count) $article->updateArticle($text,"summary",false,false);
 
}
 
}
 
 
 
# Replacement callback for updating tag state
 
function updateStateCallback($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() {
 
global $wgWorkflow;
 
return $wgWorkflow->renderWorkflowInfo(parent::getCategoryLinks());
 
}
 
}');
 
 
 
# 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;
 
}
 
 
# Render the workflow info which appears in the catlinks area
 
function renderWorkflowInfo(&$catlinks) {
 
$title = Title::makeTitle(NS_SPECIAL,wfMsg('workflow'));
 
$catlinks .= "<br /><a href='{$title->getLocalURL()}'>".wfMsg('workflow')."</a>:\n";
 
return $catlinks;
 
}
 
 
 
# 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();
 
wfResetOutputBuffers();
 
#while(@ob_end_clean());
 
#if (in_array('Content-Encoding: gzip',headers_list())) $catlinks = gzencode($catlinks);
 
echo($catlinks);
 
return false;
 
}
 
 
 
# Make necessary Javascript functions available to the page
 
function addJS() {
 
global $wgOut,$wgJsMimeType;
 
$wgOut->addScript("<script type='$wgJsMimeType'>
 
var workflowData = [];
 
function workflowUpdateState(name) {
 
// make an ajax request, return into id=catlinks
 
// disable timer
 
var state = workflowData[name][0];
 
sajax_do_call('{$this->magic}',[{$this->pagename},name,state],document.getElementById('catlinks'));
 
}
 
function workflowSwitchState(name) {
 
// one of the states has display='' the others 'none'
 
// enable and reset timer (if state different than last, otherwise ensure diabled)
 
// timer must call workflowUpdateState when expired
 
var wf = document.getElementById('workflow-'+name);
 
var state = workflowData[name][0] = workflowData[name][0] < workflowData[name].length-1 ? workflowData[name]+1 : 1;
 
alert(state);
 
}
 
</script>");
 
}
 
 
 
# If it's a workflow-related ajax call, don't use dispatcher (because we need the catlinks generated by normal page render)
 
# - this sets the state, name, pagename and update properties
 
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;
 
$this->pagename = $_REQUEST['title'];
 
}
 
 
 
# Needed in some versions to prevent Special:Version from breaking
 
function __toString() { return 'Workflow'; }
 
 
 
# Set up the magic words
 
function languageGetMagic(&$magicWords,$langCode = 0) {
 
$magicWords[$this->magic] = array(0,$this->magic);
 
return true;
 
}
 
 
}
 
 
 
$wgWorkflow = new Workflow();
 
?>
 

Latest revision as of 23:25, 3 August 2010

Info.svg This code is in our Git repository here.

Note: If there is no information in this page about this code and it's a MediaWiki extension, there may be something at mediawiki.org.