|
|
(36 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.5, 2007-10-24');
| |
− | | |
− | $wgWorkflowMagic = "workflow";
| |
− | | |
− | $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;
| |
− | var $state;
| |
− | var $name;
| |
− | var $pagename;
| |
− | var $workflows;
| |
− | var $update;
| |
− | var $wf;
| |
− | | |
− | # Constructor
| |
− | function Workflow() {
| |
− | global $wgHooks,$wgExtensionFunctions,$wgWorkflowMagic,$wgExtraNamespaces;
| |
− | $wgExtensionFunctions[] = array($this,'init');
| |
− | $wgHooks['LanguageGetMagic'][] = array($this,'languageGetMagic');
| |
− | $this->bypassAjaxDispatcher();
| |
− | $this->magic = $wgWorkflowMagic;
| |
− | $this->pagename = $_REQUEST['title'];
| |
− | $this->wf = $wgExtraNamespaces[NS_WORKFLOW];
| |
− | return true;
| |
− | }
| |
− | | |
− | # Called from $wgExtensionFunctions array when initialising extensions
| |
− | function init() {
| |
− | global $wgParser,$wgLanguageCode,$wgMessageCache,$wgSiteNotice;
| |
− | | |
− | # Require NS_WORKFLOW to be defined before installing parser-function or javascript etc
| |
− | if (!defined('NS_WORKFLOW')) {
| |
− | $wgSiteNotice .= '<div class="usermessage">'.wfMsg('workflowMissingNamespace').'</div>';
| |
− | return false;
| |
− | }
| |
− |
| |
− | # Add the messages used (todo: move into i18n)
| |
− | if ($wgLanguageCode == 'en') {
| |
− | $wgMessageCache->addMessages(array(
| |
− | 'workflow' => $this->wf,
| |
− | '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();
| |
− | 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)
| |
− | 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) {
| |
− | $magicWords[$this->magic] = array(0,$this->magic);
| |
− | return true;
| |
− | }
| |
− |
| |
− | }
| |
− | | |
− | $wgWorkflow = new Workflow();
| |
− | ?>
| |