Difference between revisions of "Extension:SimpleForms.php"

From Organic Design wiki
m (cat)
Line 7: Line 7:
  
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
define('SIMPLEFORMS_VERSION', '0.4.6, 2007-11-08');
+
define('SIMPLEFORMS_VERSION', '0.4.7, 2008-03-08');
  
 
# index.php parameter names
 
# index.php parameter names
Line 21: Line 21:
  
 
# Parser function names
 
# Parser function names
$wgSimpleFormsFormMagic       = "form";   # the parser-function name for form containers
+
$wgSimpleFormsFormMagic       = "form";     # the parser-function name for form containers
$wgSimpleFormsInputMagic       = "input";   # the parser-function name for form inputs
+
$wgSimpleFormsInputMagic     = "input";   # the parser-function name for form inputs
$wgSimpleFormsRequestMagic     = "request"; # the parser-function name for accessing the post/get variables
+
$wgSimpleFormsRequestMagic   = "request";  # the parser-function name for accessing the post/get variables
 +
$wgSimpleFormsParaTypeMagic  = "paratype"; # the parser-function name for accessing the post/get variables
  
 
# Configuration
 
# Configuration
$wgSimpleFormsRequestPrefix   = "";       # restricts #request and GET/POST variable names to their own namespace, set to "" to disable
+
$wgSimpleFormsRequestPrefix   = "";         # restricts #request and GET/POST variable names to their own namespace, set to "" to disable
$wgSimpleFormsServerUser       = "";       # Set this to an existing username so server IP doesn't show up in changes
+
$wgSimpleFormsServerUser     = "";         # Set this to an existing username so server IP doesn't show up in changes
$wgSimpleFormsAllowCreate     = true;     # Allow creating new articles from content query item
+
$wgSimpleFormsAllowCreate     = true;       # Allow creating new articles from content query item
$wgSimpleFormsAllowEdit       = true;     # Allow appending, prepending or replacing of content in existing articles from query item
+
$wgSimpleFormsAllowEdit       = true;       # Allow appending, prepending or replacing of content in existing articles from query item
$wgSimpleFormsAllowRemoteAddr = array($_SERVER['SERVER_ADDR'],'127.0.0.1'); # Allow anonymous edits from these addresses
+
$wgSimpleFormsAllowRemoteAddr = array($_SERVER['SERVER_ADDR'],'127.0.0.1'); # Allow anonymous edits from these addresses
  
 
$wgSimpleFormsEnableCaching = true;
 
$wgSimpleFormsEnableCaching = true;
Line 43: Line 44:
  
 
$wgExtensionCredits['parserhook'][] = array(
 
$wgExtensionCredits['parserhook'][] = array(
'name'        => 'Simple Forms',
+
    'name'        => 'Simple Forms',
'author'      => '[http://www.organicdesign.co.nz/nad User:Nad]',
+
    'author'      => '[http://www.organicdesign.co.nz/nad User:Nad]',
'description' => 'Functions to make and process forms.',
+
    'description' => 'Functions to make and process forms.',
'url'        => 'http://www.mediawiki.org/wiki/Extension:Simple_Forms',
+
    'url'        => 'http://www.mediawiki.org/wiki/Extension:Simple_Forms',
'version'    => SIMPLEFORMS_VERSION
+
    'version'    => SIMPLEFORMS_VERSION
);
+
    );
  
 
# If it's a simple-forms ajax call, don't use dispatcher
 
# If it's a simple-forms ajax call, don't use dispatcher
 
if ($wgUseAjax && isset($_REQUEST['action']) && $_REQUEST['action'] == 'ajax' && $_REQUEST['rs'] == 'wfSimpleFormsAjax') {
 
if ($wgUseAjax && isset($_REQUEST['action']) && $_REQUEST['action'] == 'ajax' && $_REQUEST['rs'] == 'wfSimpleFormsAjax') {
$_REQUEST['action'] = 'render';
+
    $_REQUEST['action'] = 'render';
if (is_array($_REQUEST['rsargs']))
+
    if (is_array($_REQUEST['rsargs']))
foreach ($_REQUEST['rsargs'] as $arg)
+
        foreach ($_REQUEST['rsargs'] as $arg)
if (preg_match('/^(.+?)=(.+)$/',$arg,$m))
+
            if (preg_match('/^(.+?)=(.+)$/',$arg,$m))
$_REQUEST[$m[1]] = $m[2];
+
                $_REQUEST[$m[1]] = $m[2];
}
+
    }
  
 
# todo: handle action=edit by making $_REQUEST['preload']='UNTITLED' and still add the AAFC hook
 
# todo: handle action=edit by making $_REQUEST['preload']='UNTITLED' and still add the AAFC hook
Line 65: Line 66:
 
# - there's probably a better way to do this, but this will do for now
 
# - there's probably a better way to do this, but this will do for now
 
if (isset($_REQUEST[SIMPLEFORMS_CONTENT]) && !isset($_REQUEST['title'])) {
 
if (isset($_REQUEST[SIMPLEFORMS_CONTENT]) && !isset($_REQUEST['title'])) {
$wgHooks['ArticleAfterFetchContent'][] = 'wfSimpleFormsUntitledContent';
+
    $wgHooks['ArticleAfterFetchContent'][] = 'wfSimpleFormsUntitledContent';
$_REQUEST['title'] = SIMPLEFORMS_UNTITLED;
+
    $_REQUEST['title'] = SIMPLEFORMS_UNTITLED;
$wgSimpleFormsEnableCaching = false;
+
    $wgSimpleFormsEnableCaching = false;
}
+
    }
 
function wfSimpleFormsUntitledContent(&$article,&$text) {
 
function wfSimpleFormsUntitledContent(&$article,&$text) {
global $wgOut,$wgRequest;
+
    global $wgOut,$wgRequest;
if ($article->getTitle()->getText() == SIMPLEFORMS_UNTITLED) {
+
    if ($article->getTitle()->getText() == SIMPLEFORMS_UNTITLED) {
$text = $wgRequest->getText(SIMPLEFORMS_CONTENT);
+
        $text = $wgRequest->getText(SIMPLEFORMS_CONTENT);
if ($title = $wgRequest->getText(SIMPLEFORMS_PAGENAME)) $wgOut->setPageTitle($title);
+
        if ($title = $wgRequest->getText(SIMPLEFORMS_PAGENAME)) $wgOut->setPageTitle($title);
else {
+
        else {
$wgOut->setPageTitle(' ');
+
            $wgOut->setPageTitle(' ');
$wgOut->addScript('<style>h1.firstHeading{display:none}</style>');
+
            $wgOut->addScript('<style>h1.firstHeading{display:none}</style>');
}
+
            }
}
+
        }
return true;
+
    return true;
}
+
    }
  
 
# If the request originates locally, auto-authenticate the user to the server-user
 
# If the request originates locally, auto-authenticate the user to the server-user
 
$wgHooks['AutoAuthenticate'][] = 'wfSimpleFormsAutoAuthenticate';
 
$wgHooks['AutoAuthenticate'][] = 'wfSimpleFormsAutoAuthenticate';
 
function wfSimpleFormsAutoAuthenticate(&$user) {
 
function wfSimpleFormsAutoAuthenticate(&$user) {
global $wgRequest,$wgSimpleFormsServerUser,$wgSimpleFormsAllowRemoteAddr;
+
    global $wgRequest,$wgSimpleFormsServerUser,$wgSimpleFormsAllowRemoteAddr;
if ($username = $wgRequest->getText(SIMPLEFORMS_USERNAME)) $wgSimpleFormsServerUser = $username;
+
    if ($username = $wgRequest->getText(SIMPLEFORMS_USERNAME)) $wgSimpleFormsServerUser = $username;
if (!empty($wgSimpleFormsServerUser) && in_array($_SERVER['REMOTE_ADDR'],$wgSimpleFormsAllowRemoteAddr))
+
    if (!empty($wgSimpleFormsServerUser) && in_array($_SERVER['REMOTE_ADDR'],$wgSimpleFormsAllowRemoteAddr))
$user = User::newFromName($wgSimpleFormsServerUser);
+
        $user = User::newFromName($wgSimpleFormsServerUser);
return true;
+
    return true;
}
+
    }
  
 
# Define a singleton for SimpleForms operations
 
# Define a singleton for SimpleForms operations
 
class SimpleForms {
 
class SimpleForms {
  
var $id = 0;
+
    var $id = 0;
  
# Constructor
+
    # Constructor
function SimpleForms() {
+
    function SimpleForms() {
global $wgParser,$wgHooks,$wgTitle,$wgSimpleFormsFormMagic,$wgSimpleFormsInputMagic,$wgSimpleFormsRequestMagic,$wgSimpleFormsEnableCaching;
+
        global $wgParser,$wgHooks,$wgTitle,$wgSimpleFormsFormMagic,$wgSimpleFormsInputMagic,$wgSimpleFormsRequestMagic,$wgSimpleFormsParaTypeMagic,$wgSimpleFormsEnableCaching;
$wgParser->setFunctionHook($wgSimpleFormsFormMagic,     array($this,'formMagic'));
+
        $wgParser->setFunctionHook($wgSimpleFormsFormMagic,     array($this,'formMagic'));
$wgParser->setFunctionHook($wgSimpleFormsInputMagic,     array($this,'inputMagic'));
+
        $wgParser->setFunctionHook($wgSimpleFormsInputMagic,   array($this,'inputMagic'));
$wgParser->setFunctionHook($wgSimpleFormsRequestMagic,   array($this,'requestMagic'));
+
        $wgParser->setFunctionHook($wgSimpleFormsRequestMagic, array($this,'requestMagic'));
$this->createUntitled();
+
        $wgParser->setFunctionHook($wgSimpleFormsParaTypeMagic, array($this,'paramTypeMagic'));
$this->processRequest();
+
        $this->createUntitled();
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'render' && (!is_object($wgTitle) || isset($_REQUEST['content']))) {
+
        $this->processRequest();
$wgHooks['OutputPageBeforeHTML'][] = array($this,'render');
+
        if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'render' && (!is_object($wgTitle) || isset($_REQUEST['content']))) {
$wgSimpleFormsEnableCaching = false;
+
            $wgHooks['OutputPageBeforeHTML'][] = array($this,'render');
}
+
            $wgSimpleFormsEnableCaching = false;
$this->id = uniqid('sf-');
+
            }
}
+
        $this->id = uniqid('sf-');
 +
        }
  
# Renders a form and wraps it in tags for processing by tagHook
+
    # Renders a form and wraps it in tags for processing by tagHook
# - if it's an edit-form it will return empty-string unless $this->edit is true
+
    # - if it's an edit-form it will return empty-string unless $this->edit is true
#  i.e. $this->edit would be set by the edit-hook or create-specialpage parsing it
+
    #  i.e. $this->edit would be set by the edit-hook or create-specialpage parsing it
function formMagic(&$parser) {
+
    function formMagic(&$parser) {
global $wgScript,$wgSimpleFormsEnableCaching;
+
        global $wgScript,$wgSimpleFormsEnableCaching;
if (!$wgSimpleFormsEnableCaching) $parser->disableCache();
+
        if (!$wgSimpleFormsEnableCaching) $parser->disableCache();
$argv = func_get_args();
+
        $argv = func_get_args();
$id = $this->id;
+
        $id = $this->id;
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'render' && isset($_REQUEST['wiklet']))
+
        if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'render' && isset($_REQUEST['wiklet']))
$hidden = '<input type="hidden" name="action" value="render"/>
+
            $hidden = '<input type="hidden" name="action" value="render"/>
<input type="hidden" name="wiklet"/>';
+
            <input type="hidden" name="wiklet"/>';
else $hidden = '';
+
        else $hidden = '';
$action = isset($argv['action']) ? $argv['action'] : $wgScript;
+
        $action = isset($argv['action']) ? $argv['action'] : $wgScript;
unset($argv['action']);
+
        unset($argv['action']);
$form  = '';
+
        $form  = '';
$args  = '';
+
        $args  = '';
foreach ($argv as $arg) if (!is_object($arg)) {
+
        foreach ($argv as $arg) if (!is_object($arg)) {
if (preg_match('/^([a-z0-9_]+?)\\s*=\\s*(.+)$/is',$arg,$match)) $args .= " $match[1]=\"$match[2]\""; else $form = $arg;
+
            if (preg_match('/^([a-z0-9_]+?)\\s*=\\s*(.+)$/is',$arg,$match)) $args .= " $match[1]=\"$match[2]\""; else $form = $arg;
}
+
            }
$form = "<form$args action=\"$action\" id=\"$id\">$hidden$form</form>";
+
        $form = "<form$args action=\"$action\" id=\"$id\">$hidden$form</form>";
$this->id = uniqid('sf-');
+
        $this->id = uniqid('sf-');
$form = preg_replace("/^\\s+/m",'',$form);
+
        $form = preg_replace("/^\\s+/m",'',$form);
return array($form,'noparse' => true);
+
        return array($form,'noparse' => true, 'isHTML' => true);
}
+
        }
  
# Renders a form input
+
    # Renders a form input
function inputMagic(&$parser) {
+
    function inputMagic(&$parser) {
global $wgSimpleFormsRequestPrefix,$wgSimpleFormsEnableCaching;
+
        global $wgSimpleFormsRequestPrefix,$wgSimpleFormsEnableCaching;
if (!$wgSimpleFormsEnableCaching) $parser->disableCache();
+
        if (!$wgSimpleFormsEnableCaching) $parser->disableCache();
  
 
                 $content = '';
 
                 $content = '';
Line 148: Line 150:
 
                 $argv    = array();
 
                 $argv    = array();
  
# Process args
+
        # Process args
foreach (func_get_args() as $arg) if (!is_object($arg)) {
+
        foreach (func_get_args() as $arg) if (!is_object($arg)) {
if (preg_match('/^([a-z0-9_]+?)\\s*=\\s*(.+)$/is',$arg,$match)) $argv[trim($match[1])] = trim($match[2]);
+
            if (preg_match('/^([a-z0-9_]+?)\\s*=\\s*(.+)$/is',$arg,$match)) $argv[trim($match[1])] = trim($match[2]);
else $content = trim($arg);
+
            else $content = trim($arg);
}
+
            }
if (isset($argv['type'])) $type = $argv['type']; else $type = '';
+
        if (isset($argv['type'])) $type = $argv['type']; else $type = '';
if (isset($argv['name'])) $argv['name'] = $wgSimpleFormsRequestPrefix.$argv['name'];
+
        if (isset($argv['name'])) $argv['name'] = $wgSimpleFormsRequestPrefix.$argv['name'];
 
# Textarea
 
if ($type == 'textarea') {
 
unset($argv['type']);
 
foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
 
$input = "<textarea$args>$content</textarea>";
 
}
 
  
# Select list
+
        # Textarea
elseif ($type == 'select') {
+
        if ($type == 'textarea') {
                        unset($argv['type']);
+
            unset($argv['type']);
                        if (isset($argv['value'])) {
+
            foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
                                $val = $argv['value'];
+
            $input = "<textarea$args>$content</textarea>";
                                unset($argv['value']);
+
            }
                                } else $val = '';
 
                        foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
 
                        preg_match_all('/^\\*\\s*(.*?)\\s*$/m',$content,$m);
 
                        $input = "<select$args>\n";
 
                        foreach ($m[1] as $opt) {
 
                                $sel = $opt == $val ? ' selected' : '';
 
                                $input .= "<option$sel>$opt</option>\n";
 
                                }
 
                        $input .= "</select>\n";
 
}
 
  
# Ajax link or button
+
        # Select list
elseif ($type == 'ajax') {
+
        elseif ( $type == 'select' )
$update = isset($argv['update']) ? $argv['update'] : $this->id;
+
        {
$format = isset($argv['format']) ? $argv['format'] : 'button';
+
            unset( $argv['type'] );
unset($argv['update']);
+
 
unset($argv['format']);
+
            if ( isset( $argv['multiple'] ) )
 +
            {
 +
                if ( isset( $argv['name'] ) )
 +
                    $argv['name'] .= '[]';
 +
            }
 +
 
 +
            if ( isset( $argv['value'] ) )
 +
            {
 +
                $val = $argv['value'];
 +
                unset( $argv['value'] );
 +
            }
 +
            else
 +
                $val = '';
 +
 
 +
            foreach ( $argv as $k => $v )
 +
                $args .= " $k=\"$v\"";
 +
 
 +
            preg_match_all( '/^\\*\\s*(.*?)\\s*$/m', $content, $m );
 +
            $input = "<select$args>\n";
 +
            foreach ( $m[1] as $opt )
 +
            {
 +
                $sel = $opt == $val ? ' selected' : '';
 +
                $input .= "<option$sel>$opt</option>\n";
 +
            }
 +
 
 +
            $input .= "</select>\n";
 +
        }
 +
 
 +
        # Ajax link or button
 +
        elseif ($type == 'ajax') {
 +
            $update = isset($argv['update']) ? $argv['update'] : $this->id;
 +
            $format = isset($argv['format']) ? $argv['format'] : 'button';
 +
            unset($argv['update']);
 +
            unset($argv['format']);
 
                         if (isset($argv['template'])) {
 
                         if (isset($argv['template'])) {
 
                                 $template = '{'.'{'.$argv['template'];
 
                                 $template = '{'.'{'.$argv['template'];
 
                                 $template = "var t = '$template\\n';
 
                                 $template = "var t = '$template\\n';
inputs = f.getElementsByTagName('select');
+
                    inputs = f.getElementsByTagName('select');
for (i = 0; i < inputs.length; i++)
+
                    for (i = 0; i < inputs.length; i++)
if (n = inputs[i].getAttribute('name'))
+
                        if (n = inputs[i].getAttribute('name'))
t += '|' + n + '=' + inputs[i].getAttribute('selected') + '\\n';
+
                            t += '|' + n + '=' + inputs[i].getAttribute('selected') + '\\n';
 
                                         t = t + '}'+'}\\n';
 
                                         t = t + '}'+'}\\n';
alert(t);/*
+
                    alert(t);/*
 
                                         i = document.createElement('input');
 
                                         i = document.createElement('input');
 
                                         i.setAttribute('type','hidden');
 
                                         i.setAttribute('type','hidden');
Line 207: Line 225:
 
                                 unset($argv['template']);
 
                                 unset($argv['template']);
 
                                 } else $template = '';
 
                                 } else $template = '';
if ($format == 'link') {
+
            if ($format == 'link') {
# Render the Ajax input as a link independent of any form
+
                # Render the Ajax input as a link independent of any form
$element = 'a';
+
                $element = 'a';
$t = isset($argv['title']) ? $argv['title'] : false;
+
                $t = isset($argv['title']) ? $argv['title'] : false;
if ($content == '') $content = $t;
+
                if ($content == '') $content = $t;
if ($t) $t = Title::newFromText($t);
+
                if ($t) $t = Title::newFromText($t);
$argv['class'] = !$t || $t->exists() ? 'ajax' : 'new ajax';
+
                $argv['class'] = !$t || $t->exists() ? 'ajax' : 'new ajax';
unset($argv['type']);
+
                unset($argv['type']);
$params = array();
+
                $params = array();
foreach ($argv as $k => $v) if ($k != 'class') $params[] = "'$k=$v'";
+
                foreach ($argv as $k => $v) if ($k != 'class') $params[] = "'$k=$v'";
$params = join(',',$params);
+
                $params = join(',',$params);
$argv['href'] = "javascript:var x = sajax_do_call('wfSimpleFormsAjax',[$params],document.getElementById('$update'))";
+
                $argv['href'] = "javascript:var x = sajax_do_call('wfSimpleFormsAjax',[$params],document.getElementById('$update'))";
}
+
                }
else {
+
            else {
# Render the Ajax input as a form submit button
+
                # Render the Ajax input as a form submit button
$argv['type'] = 'button';
+
                $argv['type'] = 'button';
$element      = 'input';
+
                $element      = 'input';
if (!isset($argv['onClick'])) $argv['onClick'] = '';
+
                if (!isset($argv['onClick'])) $argv['onClick'] = '';
$argv['onClick'] .= "a = [];
+
                $argv['onClick'] .= "a = [];
f = document.getElementById('{$this->id}');
+
                    f = document.getElementById('{$this->id}');
i = f.getElementsByTagName('*');
+
                    i = f.getElementsByTagName('*');
for (k in i) if (i[k].name && i[k].value) a.push(i[k].name+'='+i[k].value);
+
                    for (k in i) if (i[k].name && i[k].value) a.push(i[k].name+'='+i[k].value);
sajax_request_type = 'POST';
+
                    sajax_request_type = 'POST';
x = sajax_do_call('wfSimpleFormsAjax',a,document.getElementById('$update'))";
+
                    x = sajax_do_call('wfSimpleFormsAjax',a,document.getElementById('$update'))";
}
+
                }
 +
 
 +
            foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
 +
            $input = "<$element$args>$content</$element>\n";
 +
            }
 +
 
 +
        # Default: render as normal input element
 +
        else {
 +
            foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
 +
            $input = "<input$args/>";
 +
            }
 +
 
 +
        $input = preg_replace("/^\\s+/m",'',$input);
 +
        return array($input,'noparse' => true, 'isHTML' => true);
 +
        }
 +
 
 +
    # Return value from the global $_REQUEST array (containing GET/POST variables)
 +
    function requestMagic( &$parser )
 +
    {
 +
        global $wgRequest, $wgSimpleFormsRequestPrefix, $wgContLang;
 +
 
 +
        $args = func_get_args();
 +
 
 +
        // the first arg is the parser.  We already have it (by
 +
        // reference even), so we can remove it from the array
 +
        array_shift( $args );
 +
 
 +
        // get the request parameter name
 +
        $paramName = array_shift( $args );
 +
        // only thing left in $args at this point are the array keys
 +
 
 +
        // If no keys are specified, we just call getText()
 +
        if ( count( $args ) == 0 )
 +
        {
 +
            $paramValue = $wgRequest->getText(
 +
                $wgSimpleFormsRequestPrefix . $paramName );
 +
 
 +
            return $paramValue;
 +
        }
 +
 
 +
        // when the parameter is a scalar calling getArray() puts it in an
 +
        // array and returns the array, so we need to do a scalar check
 +
        if ( !is_null( $wgRequest->getVal(
 +
            $wgSimpleFormsRequestPrefix . $paramName ) ) )
 +
            return '';
 +
 
 +
        // get the array associated with this parameter name
 +
        $paramValue = $wgRequest->getArray( $wgSimpleFormsRequestPrefix .
 +
            $paramName );
 +
 
 +
        // time to descend into the depths of the array associated with the
 +
        // parameter name
 +
        while ( count( $args ) > 0 )
 +
        {
 +
            $key = array_shift( $args );
 +
 
 +
            // do we have more keys than we have array nests?
 +
            if ( !is_array( $paramValue ) )
 +
                return '';
 +
 
 +
            // a little closer to the value we want
 +
            $paramValue = $paramValue[$key];
 +
        }
 +
 
 +
        // do we have more array nests than we have keys, or a null?
 +
        if ( is_array( $paramValue ) || is_null( $paramValue ) )
 +
            return '';
 +
 
 +
        // we've found a param value!
 +
        $paramValue = str_replace( "\r\n", "\n",
 +
            $wgContLang->recodeInput( $paramValue ) );
 +
 
 +
        return $paramValue;
 +
    }
 +
 
 +
    /**
 +
    * requestMagic() returns an empty string under three conditions:
 +
    *  1) no such parameter was passed via the request,
 +
    *  2) the specified parameter is an array, and
 +
    *  3) the specified parameter was set to an empty string.
 +
    * Because of this we need a function to determine which is the case.  This
 +
    * function returns '0' if the parameter doesn't exist, '1' if the parameter
 +
    * is a scalar, and '2' if the parameter is an array.
 +
    */
 +
    function paramTypeMagic( &$parser )
 +
    {
 +
        global $wgRequest, $wgSimpleFormsRequestPrefix;
 +
 
 +
        $args = func_get_args();
 +
 
 +
        // the first arg is the parser, we already have it by
 +
        // reference, so we can remove it from the array
 +
        array_shift( $args );
 +
 
 +
        // get the request parameter name
 +
        $paramName = array_shift( $args );
 +
        // only thing left in $args at this point are the array keys
 +
 
 +
        // If no keys are specified, we just try to get a scalar
 +
        if ( count( $args ) == 0 )
 +
        {
 +
            $paramValue = $wgRequest->getVal(
 +
                $wgSimpleFormsRequestPrefix . $paramName );
 +
 
 +
            if ( is_null( $paramValue ) )
 +
            {
 +
                // getVal() returns null if the reqest parameter is an array, so
 +
                // we need to verify that the parameter was not passed.
 +
                $paramValue = $wgRequest->getArray(
 +
                    $wgSimpleFormsRequestPrefix . $paramName );
 +
 
 +
                return is_null( $paramValue ) ? '0' : '2';
 +
            }
 +
 
 +
            // found a scalar
 +
            return '1';
 +
        }
 +
 
 +
        // when the parameter is a scalar calling getArray() puts it in an
 +
        // array and returns the array, so we need to do a scalar check
 +
        if ( !is_null( $wgRequest->getVal(
 +
            $wgSimpleFormsRequestPrefix . $paramName ) ) )
 +
            return '0';
 +
 
 +
        // get the array associated with this parameter name
 +
        $paramValue = $wgRequest->getArray( $wgSimpleFormsRequestPrefix .
 +
            $paramName );
  
foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
+
        // descend into the depths of the array
$input = "<$element$args>$content</$element>\n";
+
        while ( count( $args ) > 0 )
}
+
        {
 +
            $key = array_shift( $args );
  
# Default: render as normal input element
+
            // do we have more keys than we have array nests?
else {
+
            if ( !is_array( $paramValue ) ||
foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
+
                !array_key_exists( $key, $paramValue ) )
$input = "<input$args/>";
+
                return '0';
}
 
  
$input = preg_replace("/^\\s+/m",'',$input);
+
            // a little closer to the value we want
return array($input,'noparse' => true);
+
            $paramValue = $paramValue[$key];
}
+
        }
  
# Return value from the global $_REQUEST array (containing GET/POST variables)
+
        // do we have more array nests than we have keys?
function requestMagic(&$parser,$key) {
+
        return is_array( $paramValue ) ? '2' : '1';
global $wgRequest,$wgSimpleFormsRequestPrefix;
+
    }
return $wgRequest->getText($wgSimpleFormsRequestPrefix.$key);
 
}
 
  
# Return the raw content
+
    # Return the raw content
function raw($text) {
+
    function raw($text) {
global $wgOut,$wgParser,$wgRequest;
+
        global $wgOut,$wgParser,$wgRequest;
$this->setCaching();
+
        $this->setCaching();
$expand = $wgRequest->getText('templates') == 'expand';
+
        $expand = $wgRequest->getText('templates') == 'expand';
if ($expand) $text = $wgParser->preprocess($text,new Title(),new ParserOptions());
+
        if ($expand) $text = $wgParser->preprocess($text,new Title(),new ParserOptions());
$wgOut->disable();
+
        $wgOut->disable();
wfResetOutputBuffers();
+
        wfResetOutputBuffers();
header('Content-Type: application/octet-stream');
+
        header('Content-Type: application/octet-stream');
echo($text);
+
        echo($text);
return false;
+
        return false;
}
+
        }
  
# Return rendered content of page
+
    # Return rendered content of page
function render(&$out,&$text) {
+
    function render(&$out,&$text) {
$this->setCaching();
+
        $this->setCaching();
$out->disable();
+
        $out->disable();
wfResetOutputBuffers();
+
        wfResetOutputBuffers();
echo($text);
+
        echo($text);
return false;
+
        return false;
}
+
        }
  
# Disable caching if necessary
+
    # Disable caching if necessary
function setCaching() {
+
    function setCaching() {
global $wgOut,$wgEnableParserCache,$wgSimpleFormsEnableCaching;
+
        global $wgOut,$wgEnableParserCache,$wgSimpleFormsEnableCaching;
if ($wgSimpleFormsEnableCaching) return;
+
        if ($wgSimpleFormsEnableCaching) return;
$wgOut->enableClientCache(false);
+
        $wgOut->enableClientCache(false);
header("Cache-Control: no-cache, must-revalidate");
+
        header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
}
+
        }
  
# Processes HTTP requests containing wikitext content
+
    # Processes HTTP requests containing wikitext content
function processRequest() {
+
    function processRequest() {
global $wgOut,$wgRequest,$wgUser,$wgTitle,
+
        global $wgOut,$wgRequest,$wgUser,$wgTitle,
      $wgSimpleFormsAllowRemoteAddr,$wgSimpleFormsAllowCreate,$wgSimpleFormsAllowEdit;
+
              $wgSimpleFormsAllowRemoteAddr,$wgSimpleFormsAllowCreate,$wgSimpleFormsAllowEdit;
  
$content = trim($wgRequest->getText(SIMPLEFORMS_CONTENT));
+
        $content = trim($wgRequest->getText(SIMPLEFORMS_CONTENT));
$action  = $wgRequest->getText('action');
+
        $action  = $wgRequest->getText('action');
$title  = $wgRequest->getText('title');
+
        $title  = $wgRequest->getText('title');
  
# Handle content with action=raw case (allows templates=expand too)
+
        # Handle content with action=raw case (allows templates=expand too)
if ($action == 'raw' && isset($_REQUEST[SIMPLEFORMS_CONTENT])) $this->raw($content);
+
        if ($action == 'raw' && isset($_REQUEST[SIMPLEFORMS_CONTENT])) $this->raw($content);
  
# Handle content and title case (will either update or create an article)
+
        # Handle content and title case (will either update or create an article)
if ($title != SIMPLEFORMS_UNTITLED && isset($_REQUEST[SIMPLEFORMS_CONTENT])) {
+
        if ($title != SIMPLEFORMS_UNTITLED && isset($_REQUEST[SIMPLEFORMS_CONTENT])) {
  
$title  = Title::newFromText($wgRequest->getText('title'));
+
            $title  = Title::newFromText($wgRequest->getText('title'));
if ($title->getNamespace() == NS_SPECIAL) return;
+
            if ($title->getNamespace() == NS_SPECIAL) return;
if (!is_object($wgTitle)) $wgTitle = $title; # hack to stop DPL crashing
+
            if (!is_object($wgTitle)) $wgTitle = $title; # hack to stop DPL crashing
$article = new Article($title);
+
            $article = new Article($title);
$allow  = in_array($_SERVER['REMOTE_ADDR'],$wgSimpleFormsAllowRemoteAddr);
+
            $allow  = in_array($_SERVER['REMOTE_ADDR'],$wgSimpleFormsAllowRemoteAddr);
$summary = $wgRequest->getText(SIMPLEFORMS_SUMMARY);
+
            $summary = $wgRequest->getText(SIMPLEFORMS_SUMMARY);
$minor  = $wgRequest->getText(SIMPLEFORMS_MINOR);
+
            $minor  = $wgRequest->getText(SIMPLEFORMS_MINOR);
$return  = $wgRequest->getText(SIMPLEFORMS_RETURN);
+
            $return  = $wgRequest->getText(SIMPLEFORMS_RETURN);
  
# If title exists and allowed to edit, prepend/append/replace content
+
            # If title exists and allowed to edit, prepend/append/replace content
if ($title->exists()) {
+
            if ($title->exists()) {
if ($wgSimpleFormsAllowEdit && ($allow || $wgUser->isAllowed('edit'))) {
+
                if ($wgSimpleFormsAllowEdit && ($allow || $wgUser->isAllowed('edit'))) {
$update = $this->updateTemplates($article->getContent(),$content);
+
                    $update = $this->updateTemplates($article->getContent(),$content);
$article->updateArticle($update,$summary?$summary:wfMsg('sf_editsummary'),false,false);
+
                    $article->updateArticle($update,$summary?$summary:wfMsg('sf_editsummary'),false,false);
}
+
                    }
else $wgOut->setPageTitle(wfMsg('whitelistedittitle'));
+
                else $wgOut->setPageTitle(wfMsg('whitelistedittitle'));
}
+
                }
  
# No such title, create new article from content if allowed to create
+
            # No such title, create new article from content if allowed to create
else {
+
            else {
if ($wgSimpleFormsAllowCreate && ($allow || $wgUser->isAllowed('edit')))
+
                if ($wgSimpleFormsAllowCreate && ($allow || $wgUser->isAllowed('edit')))
$article->insertNewArticle($content,$summary ? $summary : wfMsg('sf_editsummary','created'),false,false);
+
                    $article->insertNewArticle($content,$summary ? $summary : wfMsg('sf_editsummary','created'),false,false);
else $wgOut->setPageTitle(wfMsg('whitelistedittitle'));
+
                else $wgOut->setPageTitle(wfMsg('whitelistedittitle'));
}
+
                }
  
# If returnto is set, add a redirect header and die
+
            # If returnto is set, add a redirect header and die
if ($return) die(header('Location: '.Title::newFromText($return)->getFullURL()));
+
            if ($return) die(header('Location: '.Title::newFromText($return)->getFullURL()));
}
+
            }
}
+
        }
  
# Create a dummy article for rendering content not associated with any title (unless it already exists)
+
    # Create a dummy article for rendering content not associated with any title (unless it already exists)
# - there's probably a better way to do this
+
    # - there's probably a better way to do this
function createUntitled() {
+
    function createUntitled() {
$title = Title::newFromText(SIMPLEFORMS_UNTITLED);
+
        $title = Title::newFromText(SIMPLEFORMS_UNTITLED);
if (!$title->exists()) {
+
        if (!$title->exists()) {
$article = new Article($title);
+
            $article = new Article($title);
$article->insertNewArticle(
+
            $article->insertNewArticle(
'Dummy article used by [http://www.mediawiki.org/wiki/Extension:Simple_Forms Extension:SimpleForms]',
+
                'Dummy article used by [http://www.mediawiki.org/wiki/Extension:Simple_Forms Extension:SimpleForms]',
'Dummy article created for Simple Forms extension',
+
                'Dummy article created for Simple Forms extension',
true,
+
                true,
false
+
                false
);
+
                );
}
+
            }
}
+
        }
  
# Update templates wikitext content
+
    # Update templates wikitext content
# - $updates must start and end with double-braces
+
    # - $updates must start and end with double-braces
# - $updates may contain multiple template updates
+
    # - $updates may contain multiple template updates
# - each update must only match one template, comparison of args will reduce multiple matches
+
    # - each update must only match one template, comparison of args will reduce multiple matches
function updateTemplates($content,$updates) {
+
    function updateTemplates($content,$updates) {
global $wgRequest;
+
        global $wgRequest;
$caction = $wgRequest->getText(SIMPLEFORMS_CACTION);
+
        $caction = $wgRequest->getText(SIMPLEFORMS_CACTION);
$taction = $wgRequest->getText(SIMPLEFORMS_TACTION);
+
        $taction = $wgRequest->getText(SIMPLEFORMS_TACTION);
$regexp  = $wgRequest->getText(SIMPLEFORMS_REGEXP);
+
        $regexp  = $wgRequest->getText(SIMPLEFORMS_REGEXP);
  
# Resort to normal content-action if $updates is not exclusively template definitions or updating templates disabled
+
        # Resort to normal content-action if $updates is not exclusively template definitions or updating templates disabled
if ($taction == 'update' and preg_match('/^\\{\\{.+\\}\\}$/is',$updates,$match)) {
+
        if ($taction == 'update' and preg_match('/^\\{\\{.+\\}\\}$/is',$updates,$match)) {
  
# pattern to extract the first name and value of the first arg from template definition
+
            # pattern to extract the first name and value of the first arg from template definition
$pattern = '/^.+?[:\\|]\\s*(\\w+)\\s*=\\s*(.*?)\\s*[\\|\\}]/s';
+
            $pattern = '/^.+?[:\\|]\\s*(\\w+)\\s*=\\s*(.*?)\\s*[\\|\\}]/s';
$addtext = '';
+
            $addtext = '';
  
# Get the offsets and lengths of template definitions in content and updates wikitexts
+
            # Get the offsets and lengths of template definitions in content and updates wikitexts
$cbraces = $this->examineBraces($content);
+
            $cbraces = $this->examineBraces($content);
$ubraces = $this->examineBraces($updates);
+
            $ubraces = $this->examineBraces($updates);
  
# Loop through the top-level braces in $updates
+
            # Loop through the top-level braces in $updates
foreach ($ubraces as $ubrace) if ($ubrace[SFEB_DEPTH] == 1) {
+
            foreach ($ubraces as $ubrace) if ($ubrace[SFEB_DEPTH] == 1) {
  
# Get the update text
+
                # Get the update text
$utext = substr($updates,$ubrace[SFEB_OFFSET],$ubrace[SFEB_LENGTH]);
+
                $utext = substr($updates,$ubrace[SFEB_OFFSET],$ubrace[SFEB_LENGTH]);
  
# Get braces in content with the same name as this update
+
                # Get braces in content with the same name as this update
$matches = array();
+
                $matches = array();
$uname  = $ubrace[SFEB_NAME];
+
                $uname  = $ubrace[SFEB_NAME];
foreach ($cbraces as $ci => $cbrace) if ($cbrace[SFEB_NAME] == $uname) $matches[] = $ci;
+
                foreach ($cbraces as $ci => $cbrace) if ($cbrace[SFEB_NAME] == $uname) $matches[] = $ci;
  
# If more than one matches, try to reduce to one by comparing the first arg of each with the updates first arg
+
                # If more than one matches, try to reduce to one by comparing the first arg of each with the updates first arg
if (count($matches) > 1 && preg_match($pattern,$utext,$uarg)) {
+
                if (count($matches) > 1 && preg_match($pattern,$utext,$uarg)) {
$tmp = array();
+
                    $tmp = array();
foreach ($matches as $ci) {
+
                    foreach ($matches as $ci) {
$cbrace = &$cbraces[$ci];
+
                        $cbrace = &$cbraces[$ci];
$cbtext = substr($content,$cbrace[SFEB_OFFSET],$cbrace[SFEB_LENGTH]);
+
                        $cbtext = substr($content,$cbrace[SFEB_OFFSET],$cbrace[SFEB_LENGTH]);
if (preg_match($pattern,$cbtext,$carg) && $carg[1] == $uarg[1] && $carg[2] == $uarg[2])
+
                        if (preg_match($pattern,$cbtext,$carg) && $carg[1] == $uarg[1] && $carg[2] == $uarg[2])
$tmp[] = $ci;
+
                            $tmp[] = $ci;
}
+
                        }
$matches = &$tmp;
+
                    $matches = &$tmp;
}
+
                    }
  
# If matches has been reduced to a single item, update the template in the content
+
                # If matches has been reduced to a single item, update the template in the content
if (count($matches) == 1) {
+
                if (count($matches) == 1) {
$coffset = $cbraces[$matches[0]][SFEB_OFFSET];
+
                    $coffset = $cbraces[$matches[0]][SFEB_OFFSET];
$clength = $cbraces[$matches[0]][SFEB_LENGTH];
+
                    $clength = $cbraces[$matches[0]][SFEB_LENGTH];
$content = substr_replace($content,$utext,$coffset,$clength);
+
                    $content = substr_replace($content,$utext,$coffset,$clength);
}
+
                    }
  
# Otherwise (if no matches, or many matches) do normal content-action on the update
+
                # Otherwise (if no matches, or many matches) do normal content-action on the update
else $addtext .= "$utext\n";
+
                else $addtext .= "$utext\n";
}
+
                }
}
+
            }
  
# Do normal content-action if $updates was not purely templates
+
        # Do normal content-action if $updates was not purely templates
else $addtext = $updates;
+
        else $addtext = $updates;
  
# Do regular expression replacement if regexp parameter set
+
        # Do regular expression replacement if regexp parameter set
$addtext = trim($addtext);
+
        $addtext = trim($addtext);
$content = trim($content);
+
        $content = trim($content);
if ($regexp) {
+
        if ($regexp) {
$content = preg_replace("|$regexp|",$addtext,$content,-1,$count);
+
            $content = preg_replace("|$regexp|",$addtext,$content,-1,$count);
if ($count) $addtext = false;
+
            if ($count) $addtext = false;
}
+
            }
  
# Add any prepend/append updates using the content-action
+
        # Add any prepend/append updates using the content-action
if ($addtext) {
+
        if ($addtext) {
if    ($caction == 'prepend') $content = "$addtext\n$content";
+
            if    ($caction == 'prepend') $content = "$addtext\n$content";
elseif ($caction == 'append')  $content = "$content\n$addtext";
+
            elseif ($caction == 'append')  $content = "$content\n$addtext";
elseif ($caction == 'replace') $content = $addtext;
+
            elseif ($caction == 'replace') $content = $addtext;
}
+
            }
  
return $content;
+
        return $content;
}
+
        }
  
# Return a list of info about each template definition in the passed wikitext content
+
    # Return a list of info about each template definition in the passed wikitext content
# - list item format is NAME,OFFSET,LENGTH,DEPTH
+
    # - list item format is NAME,OFFSET,LENGTH,DEPTH
function examineBraces(&$content) {
+
    function examineBraces(&$content) {
$braces = array();
+
        $braces = array();
$depths = array();
+
        $depths = array();
$depth = 1;
+
        $depth = 1;
$index = 0;
+
        $index = 0;
while (preg_match('/\\{\\{\\s*([#a-z0-9_]*)|\\}\\}/is',$content,$match,PREG_OFFSET_CAPTURE,$index)) {
+
        while (preg_match('/\\{\\{\\s*([#a-z0-9_]*)|\\}\\}/is',$content,$match,PREG_OFFSET_CAPTURE,$index)) {
$index = $match[0][1]+2;
+
            $index = $match[0][1]+2;
if ($match[0][0] == '}}') {
+
            if ($match[0][0] == '}}') {
$brace              = &$braces[$depths[$depth-1]];
+
                $brace              = &$braces[$depths[$depth-1]];
$brace[SFEB_LENGTH] = $match[0][1]-$brace[SFEB_OFFSET]+2;
+
                $brace[SFEB_LENGTH] = $match[0][1]-$brace[SFEB_OFFSET]+2;
$brace[SFEB_DEPTH]  = --$depth;
+
                $brace[SFEB_DEPTH]  = --$depth;
}
+
                }
else {
+
            else {
$depths[$depth++]  = count($braces);
+
                $depths[$depth++]  = count($braces);
$braces[]          = array(SFEB_NAME => $match[1][0],SFEB_OFFSET => $match[0][1]);
+
                $braces[]          = array(SFEB_NAME => $match[1][0],SFEB_OFFSET => $match[0][1]);
}
+
                }
}
+
            }
return $braces;
+
        return $braces;
}
+
        }
  
# Needed in some versions to prevent Special:Version from breaking
+
    # Needed in some versions to prevent Special:Version from breaking
function __toString() { return 'SimpleForms'; }
+
    function __toString() { return 'SimpleForms'; }
  
}
+
    }
  
 
# Called from $wgExtensionFunctions array when initialising extensions
 
# Called from $wgExtensionFunctions array when initialising extensions
 
function wfSetupSimpleForms() {
 
function wfSetupSimpleForms() {
global $wgLanguageCode,$wgMessageCache,$wgHooks,$wgRequest,$wgSimpleForms;
+
    global $wgLanguageCode,$wgMessageCache,$wgHooks,$wgRequest,$wgSimpleForms;
  
# Add messages
+
    # Add messages
if ($wgLanguageCode == 'en') {
+
    if ($wgLanguageCode == 'en') {
$wgMessageCache->addMessages(array(
+
        $wgMessageCache->addMessages(array(
'sf_editsummary' => 'Article updated via HTTP request'
+
            'sf_editsummary' => 'Article updated via HTTP request'
));
+
            ));
}
+
        }
  
# Instantiate a singleton for the extension
+
    # Instantiate a singleton for the extension
$wgSimpleForms = new SimpleForms();
+
    $wgSimpleForms = new SimpleForms();
}
+
    }
  
 
# Needed in MediaWiki >1.8.0 for magic word hooks to work properly
 
# Needed in MediaWiki >1.8.0 for magic word hooks to work properly
 
function wfSimpleFormsLanguageGetMagic(&$magicWords,$langCode = 0) {
 
function wfSimpleFormsLanguageGetMagic(&$magicWords,$langCode = 0) {
global $wgSimpleFormsFormMagic,$wgSimpleFormsInputMagic,$wgSimpleFormsRequestMagic;
+
    global $wgSimpleFormsFormMagic,$wgSimpleFormsInputMagic,$wgSimpleFormsRequestMagic,$wgSimpleFormsParaTypeMagic;
$magicWords[$wgSimpleFormsFormMagic]    = array(0,$wgSimpleFormsFormMagic);
+
    $magicWords[$wgSimpleFormsFormMagic]    = array(0,$wgSimpleFormsFormMagic);
$magicWords[$wgSimpleFormsInputMagic]    = array(0,$wgSimpleFormsInputMagic);
+
    $magicWords[$wgSimpleFormsInputMagic]    = array(0,$wgSimpleFormsInputMagic);
$magicWords[$wgSimpleFormsRequestMagic]  = array(0,$wgSimpleFormsRequestMagic);
+
    $magicWords[$wgSimpleFormsRequestMagic]  = array(0,$wgSimpleFormsRequestMagic);
return true;
+
    $magicWords[$wgSimpleFormsParaTypeMagic] = array(0,$wgSimpleFormsParaTypeMagic);
}
+
    return true;
 +
    }

Revision as of 00:40, 8 March 2008

<?php

  1. MediaWiki SimpleForms ExtensionTemplate:Php
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.

Category:Extensions in progress

  1. - See http://www.mediawiki.org/wiki/Extension:Simple_Forms for installation and usage details
  2. - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html)
  3. - Author: http://www.organicdesign.co.nz/nad
  4. - Started: 2007-04-25, see article history

if (!defined('MEDIAWIKI')) die('Not an entry point.'); define('SIMPLEFORMS_VERSION', '0.4.7, 2008-03-08');

  1. index.php parameter names

define('SIMPLEFORMS_CONTENT', 'content'); # used for parsing wikitext content define('SIMPLEFORMS_CACTION', 'caction'); # specify whether to prepend, append or replace existing content define('SIMPLEFORMS_SUMMARY', 'summary'); # specify an edit summary when updating or creating content define('SIMPLEFORMS_PAGENAME','pagename'); # specify a page heading to use when rendering content with no title define('SIMPLEFORMS_MINOR', 'minor'); # specify that the edit/create be flagged as a minor edit define('SIMPLEFORMS_TACTION', 'templates'); # specify that the edit/create be flagged as a minor edit define('SIMPLEFORMS_USERNAME','username'); # specify a different username to use when the server is editing define('SIMPLEFORMS_RETURN', 'returnto'); # specify a page to return to after processing the request define('SIMPLEFORMS_REGEXP', 'regexp'); # if the content-action is replace, a perl regular expression can be used

  1. Parser function names

$wgSimpleFormsFormMagic = "form"; # the parser-function name for form containers $wgSimpleFormsInputMagic = "input"; # the parser-function name for form inputs $wgSimpleFormsRequestMagic = "request"; # the parser-function name for accessing the post/get variables $wgSimpleFormsParaTypeMagic = "paratype"; # the parser-function name for accessing the post/get variables

  1. Configuration

$wgSimpleFormsRequestPrefix = ""; # restricts #request and GET/POST variable names to their own namespace, set to "" to disable $wgSimpleFormsServerUser = ""; # Set this to an existing username so server IP doesn't show up in changes $wgSimpleFormsAllowCreate = true; # Allow creating new articles from content query item $wgSimpleFormsAllowEdit = true; # Allow appending, prepending or replacing of content in existing articles from query item $wgSimpleFormsAllowRemoteAddr = array($_SERVER['SERVER_ADDR'],'127.0.0.1'); # Allow anonymous edits from these addresses

$wgSimpleFormsEnableCaching = true; define('SIMPLEFORMS_UNTITLED','UNTITLED'); define('SFEB_NAME', 0); define('SFEB_OFFSET',1); define('SFEB_LENGTH',2); define('SFEB_DEPTH', 3);

$wgExtensionFunctions[] = 'wfSetupSimpleForms'; $wgHooks['LanguageGetMagic'][] = 'wfSimpleFormsLanguageGetMagic';

$wgExtensionCredits['parserhook'][] = array(

   'name'        => 'Simple Forms',
   'author'      => 'User:Nad',
   'description' => 'Functions to make and process forms.',
   'url'         => 'http://www.mediawiki.org/wiki/Extension:Simple_Forms',
   'version'     => SIMPLEFORMS_VERSION
   );
  1. If it's a simple-forms ajax call, don't use dispatcher

if ($wgUseAjax && isset($_REQUEST['action']) && $_REQUEST['action'] == 'ajax' && $_REQUEST['rs'] == 'wfSimpleFormsAjax') {

   $_REQUEST['action'] = 'render';
   if (is_array($_REQUEST['rsargs']))
       foreach ($_REQUEST['rsargs'] as $arg)
           if (preg_match('/^(.+?)=(.+)$/',$arg,$m))
               $_REQUEST[$m[1]] = $m[2];
   }
  1. todo: handle action=edit by making $_REQUEST['preload']='UNTITLED' and still add the AAFC hook
  2. handle action=raw by changing action to render and adding SimpleForms::raw to an appropriate hook
  1. If there is content passed in the request but no title, set title to the dummy "UNTITLED" article, and add a hook to replace the content
  2. - there's probably a better way to do this, but this will do for now

if (isset($_REQUEST[SIMPLEFORMS_CONTENT]) && !isset($_REQUEST['title'])) {

   $wgHooks['ArticleAfterFetchContent'][] = 'wfSimpleFormsUntitledContent';
   $_REQUEST['title'] = SIMPLEFORMS_UNTITLED;
   $wgSimpleFormsEnableCaching = false;
   }

function wfSimpleFormsUntitledContent(&$article,&$text) {

   global $wgOut,$wgRequest;
   if ($article->getTitle()->getText() == SIMPLEFORMS_UNTITLED) {
       $text = $wgRequest->getText(SIMPLEFORMS_CONTENT);
       if ($title = $wgRequest->getText(SIMPLEFORMS_PAGENAME)) $wgOut->setPageTitle($title);
       else {
           $wgOut->setPageTitle(' ');
           $wgOut->addScript('<style>h1.firstHeading{display:none}</style>');
           }
       }
    return true;
   }
  1. If the request originates locally, auto-authenticate the user to the server-user

$wgHooks['AutoAuthenticate'][] = 'wfSimpleFormsAutoAuthenticate'; function wfSimpleFormsAutoAuthenticate(&$user) {

   global $wgRequest,$wgSimpleFormsServerUser,$wgSimpleFormsAllowRemoteAddr;
   if ($username = $wgRequest->getText(SIMPLEFORMS_USERNAME)) $wgSimpleFormsServerUser = $username;
   if (!empty($wgSimpleFormsServerUser) && in_array($_SERVER['REMOTE_ADDR'],$wgSimpleFormsAllowRemoteAddr))
       $user = User::newFromName($wgSimpleFormsServerUser);
   return true;
   }
  1. Define a singleton for SimpleForms operations

class SimpleForms {

   var $id = 0;
   # Constructor
   function SimpleForms() {
       global $wgParser,$wgHooks,$wgTitle,$wgSimpleFormsFormMagic,$wgSimpleFormsInputMagic,$wgSimpleFormsRequestMagic,$wgSimpleFormsParaTypeMagic,$wgSimpleFormsEnableCaching;
       $wgParser->setFunctionHook($wgSimpleFormsFormMagic,     array($this,'formMagic'));
       $wgParser->setFunctionHook($wgSimpleFormsInputMagic,    array($this,'inputMagic'));
       $wgParser->setFunctionHook($wgSimpleFormsRequestMagic,  array($this,'requestMagic'));
       $wgParser->setFunctionHook($wgSimpleFormsParaTypeMagic, array($this,'paramTypeMagic'));
       $this->createUntitled();
       $this->processRequest();
       if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'render' && (!is_object($wgTitle) || isset($_REQUEST['content']))) {
           $wgHooks['OutputPageBeforeHTML'][] = array($this,'render');
           $wgSimpleFormsEnableCaching = false;
           }
       $this->id = uniqid('sf-');
       }
   # Renders a form and wraps it in tags for processing by tagHook
   # - if it's an edit-form it will return empty-string unless $this->edit is true
   #   i.e. $this->edit would be set by the edit-hook or create-specialpage parsing it
   function formMagic(&$parser) {
       global $wgScript,$wgSimpleFormsEnableCaching;
       if (!$wgSimpleFormsEnableCaching) $parser->disableCache();
       $argv = func_get_args();
       $id = $this->id;
       if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'render' && isset($_REQUEST['wiklet']))
           $hidden = '<input type="hidden" name="action" value="render"/>
           <input type="hidden" name="wiklet"/>';
       else $hidden = ;
       $action = isset($argv['action']) ? $argv['action'] : $wgScript;
       unset($argv['action']);
       $form   = ;
       $args   = ;
       foreach ($argv as $arg) if (!is_object($arg)) {
           if (preg_match('/^([a-z0-9_]+?)\\s*=\\s*(.+)$/is',$arg,$match)) $args .= " $match[1]=\"$match[2]\""; else $form = $arg;
           }
       $form = "<form$args action=\"$action\" id=\"$id\">$hidden$form</form>";
       $this->id = uniqid('sf-');
       $form = preg_replace("/^\\s+/m",,$form);
       return array($form,'noparse' => true, 'isHTML' => true);
       }
   # Renders a form input
   function inputMagic(&$parser) {
       global $wgSimpleFormsRequestPrefix,$wgSimpleFormsEnableCaching;
       if (!$wgSimpleFormsEnableCaching) $parser->disableCache();
               $content = ;
               $method  = ;
               $type    = ;
               $args    = ;
               $argv    = array();
       # Process args
       foreach (func_get_args() as $arg) if (!is_object($arg)) {
           if (preg_match('/^([a-z0-9_]+?)\\s*=\\s*(.+)$/is',$arg,$match)) $argv[trim($match[1])] = trim($match[2]);
           else $content = trim($arg);
           }
       if (isset($argv['type'])) $type = $argv['type']; else $type = ;
       if (isset($argv['name'])) $argv['name'] = $wgSimpleFormsRequestPrefix.$argv['name'];
       # Textarea
       if ($type == 'textarea') {
           unset($argv['type']);
           foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
           $input = "<textarea$args>$content</textarea>";
           }
       # Select list
       elseif ( $type == 'select' )
       {
           unset( $argv['type'] );
           if ( isset( $argv['multiple'] ) )
           {
               if ( isset( $argv['name'] ) )
                   $argv['name'] .= '[]';
           }
           if ( isset( $argv['value'] ) )
           {
               $val = $argv['value'];
               unset( $argv['value'] );
           }
           else
               $val = ;
           foreach ( $argv as $k => $v )
               $args .= " $k=\"$v\"";
           preg_match_all( '/^\\*\\s*(.*?)\\s*$/m', $content, $m );
           $input = "<select$args>\n";
           foreach ( $m[1] as $opt )
           {
               $sel = $opt == $val ? ' selected' : ;
               $input .= "<option$sel>$opt</option>\n";
           }
           $input .= "</select>\n";
       }
       # Ajax link or button
       elseif ($type == 'ajax') {
           $update = isset($argv['update']) ? $argv['update'] : $this->id;
           $format = isset($argv['format']) ? $argv['format'] : 'button';
           unset($argv['update']);
           unset($argv['format']);
                       if (isset($argv['template'])) {
                               $template = '{'.'{'.$argv['template'];
                               $template = "var t = '$template\\n';
                   inputs = f.getElementsByTagName('select');
                   for (i = 0; i < inputs.length; i++)
                       if (n = inputs[i].getAttribute('name'))
                           t += '|' + n + '=' + inputs[i].getAttribute('selected') + '\\n';
                                       t = t + '}'+'}\\n';
                   alert(t);/*
                                       i = document.createElement('input');
                                       i.setAttribute('type','hidden');
                                       i.setAttribute('name','templates');
                                       i.setAttribute('value','update');
                                       f.appendChild(i);
                                       i = document.createElement('input');
                                       i.setAttribute('type','hidden');
                                       i.setAttribute('name','content');
                                       i.setAttribute('value',t);
                                       f.appendChild(i);*/";
                               unset($argv['template']);
                               } else $template = ;
           if ($format == 'link') {
               # Render the Ajax input as a link independent of any form
               $element = 'a';
               $t = isset($argv['title']) ? $argv['title'] : false;
               if ($content == ) $content = $t;
               if ($t) $t = Title::newFromText($t);
               $argv['class'] = !$t || $t->exists() ? 'ajax' : 'new ajax';
               unset($argv['type']);
               $params = array();
               foreach ($argv as $k => $v) if ($k != 'class') $params[] = "'$k=$v'";
               $params = join(',',$params);
               $argv['href'] = "javascript:var x = sajax_do_call('wfSimpleFormsAjax',[$params],document.getElementById('$update'))";
               }
           else {
               # Render the Ajax input as a form submit button
               $argv['type'] = 'button';
               $element      = 'input';
               if (!isset($argv['onClick'])) $argv['onClick'] = ;
               $argv['onClick'] .= "a = [];
                   f = document.getElementById('{$this->id}');
                   i = f.getElementsByTagName('*');
                   for (k in i) if (i[k].name && i[k].value) a.push(i[k].name+'='+i[k].value);
                   sajax_request_type = 'POST';
                   x = sajax_do_call('wfSimpleFormsAjax',a,document.getElementById('$update'))";
               }
           foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
           $input = "<$element$args>$content</$element>\n";
           }
       # Default: render as normal input element
       else {
           foreach ($argv as $k => $v) $args .= " $k=\"$v\"";
           $input = "<input$args/>";
           }
       $input = preg_replace("/^\\s+/m",,$input);
       return array($input,'noparse' => true, 'isHTML' => true);
       }
   # Return value from the global $_REQUEST array (containing GET/POST variables)
   function requestMagic( &$parser )
   {
       global $wgRequest, $wgSimpleFormsRequestPrefix, $wgContLang;
       $args = func_get_args();
       // the first arg is the parser.  We already have it (by
       // reference even), so we can remove it from the array
       array_shift( $args );
       // get the request parameter name
       $paramName = array_shift( $args );
       // only thing left in $args at this point are the array keys
       // If no keys are specified, we just call getText()
       if ( count( $args ) == 0 )
       {
           $paramValue = $wgRequest->getText(
               $wgSimpleFormsRequestPrefix . $paramName );
           return $paramValue;
       }
       // when the parameter is a scalar calling getArray() puts it in an
       // array and returns the array, so we need to do a scalar check
       if ( !is_null( $wgRequest->getVal(
           $wgSimpleFormsRequestPrefix . $paramName ) ) )
           return ;
       // get the array associated with this parameter name
       $paramValue = $wgRequest->getArray( $wgSimpleFormsRequestPrefix .
           $paramName );
       // time to descend into the depths of the array associated with the
       // parameter name
       while ( count( $args ) > 0 )
       {
           $key = array_shift( $args );
           // do we have more keys than we have array nests?
           if ( !is_array( $paramValue ) )
               return ;
           // a little closer to the value we want
           $paramValue = $paramValue[$key];
       }
       // do we have more array nests than we have keys, or a null?
       if ( is_array( $paramValue ) || is_null( $paramValue ) )
           return ;
       // we've found a param value!
       $paramValue = str_replace( "\r\n", "\n",
           $wgContLang->recodeInput( $paramValue ) );
       return $paramValue;
   }
   /**
    * requestMagic() returns an empty string under three conditions:
    *   1) no such parameter was passed via the request,
    *   2) the specified parameter is an array, and
    *   3) the specified parameter was set to an empty string.
    * Because of this we need a function to determine which is the case.  This
    * function returns '0' if the parameter doesn't exist, '1' if the parameter
    * is a scalar, and '2' if the parameter is an array.
    */
   function paramTypeMagic( &$parser )
   {
       global $wgRequest, $wgSimpleFormsRequestPrefix;
       $args = func_get_args();
       // the first arg is the parser, we already have it by
       // reference, so we can remove it from the array
       array_shift( $args );
       // get the request parameter name
       $paramName = array_shift( $args );
       // only thing left in $args at this point are the array keys
       // If no keys are specified, we just try to get a scalar
       if ( count( $args ) == 0 )
       {
           $paramValue = $wgRequest->getVal(
               $wgSimpleFormsRequestPrefix . $paramName );
           if ( is_null( $paramValue ) )
           {
               // getVal() returns null if the reqest parameter is an array, so
               // we need to verify that the parameter was not passed.
               $paramValue = $wgRequest->getArray(
                   $wgSimpleFormsRequestPrefix . $paramName );
               return is_null( $paramValue ) ? '0' : '2';
           }
           // found a scalar
           return '1';
       }
       // when the parameter is a scalar calling getArray() puts it in an
       // array and returns the array, so we need to do a scalar check
       if ( !is_null( $wgRequest->getVal(
           $wgSimpleFormsRequestPrefix . $paramName ) ) )
           return '0';
       // get the array associated with this parameter name
       $paramValue = $wgRequest->getArray( $wgSimpleFormsRequestPrefix .
           $paramName );
       // descend into the depths of the array
       while ( count( $args ) > 0 )
       {
           $key = array_shift( $args );
           // do we have more keys than we have array nests?
           if ( !is_array( $paramValue ) ||
               !array_key_exists( $key, $paramValue ) )
               return '0';
           // a little closer to the value we want
           $paramValue = $paramValue[$key];
       }
       // do we have more array nests than we have keys?
       return is_array( $paramValue ) ? '2' : '1';
   }
   # Return the raw content
   function raw($text) {
       global $wgOut,$wgParser,$wgRequest;
       $this->setCaching();
       $expand = $wgRequest->getText('templates') == 'expand';
       if ($expand) $text = $wgParser->preprocess($text,new Title(),new ParserOptions());
       $wgOut->disable();
       wfResetOutputBuffers();
       header('Content-Type: application/octet-stream');
       echo($text);
       return false;
       }
   # Return rendered content of page
   function render(&$out,&$text) {
       $this->setCaching();
       $out->disable();
       wfResetOutputBuffers();
       echo($text);
       return false;
       }
   # Disable caching if necessary
   function setCaching() {
       global $wgOut,$wgEnableParserCache,$wgSimpleFormsEnableCaching;
       if ($wgSimpleFormsEnableCaching) return;
       $wgOut->enableClientCache(false);
       header("Cache-Control: no-cache, must-revalidate");
       header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
       }
   # Processes HTTP requests containing wikitext content
   function processRequest() {
       global $wgOut,$wgRequest,$wgUser,$wgTitle,
              $wgSimpleFormsAllowRemoteAddr,$wgSimpleFormsAllowCreate,$wgSimpleFormsAllowEdit;
       $content = trim($wgRequest->getText(SIMPLEFORMS_CONTENT));
       $action  = $wgRequest->getText('action');
       $title   = $wgRequest->getText('title');
       # Handle content with action=raw case (allows templates=expand too)
       if ($action == 'raw' && isset($_REQUEST[SIMPLEFORMS_CONTENT])) $this->raw($content);
       # Handle content and title case (will either update or create an article)
       if ($title != SIMPLEFORMS_UNTITLED && isset($_REQUEST[SIMPLEFORMS_CONTENT])) {
           $title   = Title::newFromText($wgRequest->getText('title'));
           if ($title->getNamespace() == NS_SPECIAL) return;
           if (!is_object($wgTitle)) $wgTitle = $title; # hack to stop DPL crashing
           $article = new Article($title);
           $allow   = in_array($_SERVER['REMOTE_ADDR'],$wgSimpleFormsAllowRemoteAddr);
           $summary = $wgRequest->getText(SIMPLEFORMS_SUMMARY);
           $minor   = $wgRequest->getText(SIMPLEFORMS_MINOR);
           $return  = $wgRequest->getText(SIMPLEFORMS_RETURN);
           # If title exists and allowed to edit, prepend/append/replace content
           if ($title->exists()) {
               if ($wgSimpleFormsAllowEdit && ($allow || $wgUser->isAllowed('edit'))) {
                   $update = $this->updateTemplates($article->getContent(),$content);
                   $article->updateArticle($update,$summary?$summary:wfMsg('sf_editsummary'),false,false);
                   }
               else $wgOut->setPageTitle(wfMsg('whitelistedittitle'));
               }
           # No such title, create new article from content if allowed to create
           else {
               if ($wgSimpleFormsAllowCreate && ($allow || $wgUser->isAllowed('edit')))
                   $article->insertNewArticle($content,$summary ? $summary : wfMsg('sf_editsummary','created'),false,false);
               else $wgOut->setPageTitle(wfMsg('whitelistedittitle'));
               }
           # If returnto is set, add a redirect header and die
           if ($return) die(header('Location: '.Title::newFromText($return)->getFullURL()));
           }
       }
   # Create a dummy article for rendering content not associated with any title (unless it already exists)
   # - there's probably a better way to do this
   function createUntitled() {
       $title = Title::newFromText(SIMPLEFORMS_UNTITLED);
       if (!$title->exists()) {
           $article = new Article($title);
           $article->insertNewArticle(
               'Dummy article used by Extension:SimpleForms',
               'Dummy article created for Simple Forms extension',
               true,
               false
               );
           }
       }
   # Update templates wikitext content
   # - $updates must start and end with double-braces
   # - $updates may contain multiple template updates
   # - each update must only match one template, comparison of args will reduce multiple matches
   function updateTemplates($content,$updates) {
       global $wgRequest;
       $caction = $wgRequest->getText(SIMPLEFORMS_CACTION);
       $taction = $wgRequest->getText(SIMPLEFORMS_TACTION);
       $regexp  = $wgRequest->getText(SIMPLEFORMS_REGEXP);
       # Resort to normal content-action if $updates is not exclusively template definitions or updating templates disabled
       if ($taction == 'update' and preg_match('/^\\{\\{.+\\}\\}$/is',$updates,$match)) {
           # pattern to extract the first name and value of the first arg from template definition
           $pattern = '/^.+?[:\\|]\\s*(\\w+)\\s*=\\s*(.*?)\\s*[\\|\\}]/s';
           $addtext = ;
           # Get the offsets and lengths of template definitions in content and updates wikitexts
           $cbraces = $this->examineBraces($content);
           $ubraces = $this->examineBraces($updates);
           # Loop through the top-level braces in $updates
           foreach ($ubraces as $ubrace) if ($ubrace[SFEB_DEPTH] == 1) {
               # Get the update text
               $utext = substr($updates,$ubrace[SFEB_OFFSET],$ubrace[SFEB_LENGTH]);
               # Get braces in content with the same name as this update
               $matches = array();
               $uname   = $ubrace[SFEB_NAME];
               foreach ($cbraces as $ci => $cbrace) if ($cbrace[SFEB_NAME] == $uname) $matches[] = $ci;
               # If more than one matches, try to reduce to one by comparing the first arg of each with the updates first arg
               if (count($matches) > 1 && preg_match($pattern,$utext,$uarg)) {
                   $tmp = array();
                   foreach ($matches as $ci) {
                       $cbrace = &$cbraces[$ci];
                       $cbtext = substr($content,$cbrace[SFEB_OFFSET],$cbrace[SFEB_LENGTH]);
                       if (preg_match($pattern,$cbtext,$carg) && $carg[1] == $uarg[1] && $carg[2] == $uarg[2])
                           $tmp[] = $ci;
                       }
                   $matches = &$tmp;
                   }
               # If matches has been reduced to a single item, update the template in the content
               if (count($matches) == 1) {
                   $coffset = $cbraces[$matches[0]][SFEB_OFFSET];
                   $clength = $cbraces[$matches[0]][SFEB_LENGTH];
                   $content = substr_replace($content,$utext,$coffset,$clength);
                   }
               # Otherwise (if no matches, or many matches) do normal content-action on the update
               else $addtext .= "$utext\n";
               }
           }
       # Do normal content-action if $updates was not purely templates
       else $addtext = $updates;
       # Do regular expression replacement if regexp parameter set
       $addtext = trim($addtext);
       $content = trim($content);
       if ($regexp) {
           $content = preg_replace("|$regexp|",$addtext,$content,-1,$count);
           if ($count) $addtext = false;
           }
       # Add any prepend/append updates using the content-action
       if ($addtext) {
           if     ($caction == 'prepend') $content = "$addtext\n$content";
           elseif ($caction == 'append')  $content = "$content\n$addtext";
           elseif ($caction == 'replace') $content = $addtext;
           }
       return $content;
       }
   # Return a list of info about each template definition in the passed wikitext content
   # - list item format is NAME,OFFSET,LENGTH,DEPTH
   function examineBraces(&$content) {
       $braces = array();
       $depths = array();
       $depth = 1;
       $index = 0;
       while (preg_match('/\\{\\{\\s*([#a-z0-9_]*)|\\}\\}/is',$content,$match,PREG_OFFSET_CAPTURE,$index)) {
           $index = $match[0][1]+2;
           if ($match[0][0] == '}}') {
               $brace              = &$braces[$depths[$depth-1]];
               $brace[SFEB_LENGTH] = $match[0][1]-$brace[SFEB_OFFSET]+2;
               $brace[SFEB_DEPTH]  = --$depth;
               }
           else {
               $depths[$depth++]   = count($braces);
               $braces[]           = array(SFEB_NAME => $match[1][0],SFEB_OFFSET => $match[0][1]);
               }
           }
       return $braces;
       }
   # Needed in some versions to prevent Special:Version from breaking
   function __toString() { return 'SimpleForms'; }
   }
  1. Called from $wgExtensionFunctions array when initialising extensions

function wfSetupSimpleForms() {

   global $wgLanguageCode,$wgMessageCache,$wgHooks,$wgRequest,$wgSimpleForms;
   # Add messages
   if ($wgLanguageCode == 'en') {
       $wgMessageCache->addMessages(array(
           'sf_editsummary' => 'Article updated via HTTP request'
           ));
       }
   # Instantiate a singleton for the extension
   $wgSimpleForms = new SimpleForms();
   }
  1. Needed in MediaWiki >1.8.0 for magic word hooks to work properly

function wfSimpleFormsLanguageGetMagic(&$magicWords,$langCode = 0) {

   global $wgSimpleFormsFormMagic,$wgSimpleFormsInputMagic,$wgSimpleFormsRequestMagic,$wgSimpleFormsParaTypeMagic;
   $magicWords[$wgSimpleFormsFormMagic]     = array(0,$wgSimpleFormsFormMagic);
   $magicWords[$wgSimpleFormsInputMagic]    = array(0,$wgSimpleFormsInputMagic);
   $magicWords[$wgSimpleFormsRequestMagic]  = array(0,$wgSimpleFormsRequestMagic);
   $magicWords[$wgSimpleFormsParaTypeMagic] = array(0,$wgSimpleFormsParaTypeMagic);
   return true;
   }