User:Jeblad/Qualified access/QualifiedAccess.php

From MediaWiki.org
Jump to: navigation, search
<?php
// QualifiedAccess MediaWiki extension.
// Make a packet of information that can be used for qualification of
// an external access.
// Copyright (C) 2008 - John Erling Blad.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
# Alert the user that this is not a valid entry point to MediaWiki if they try to access the skin file directly.
if (!defined('MEDIAWIKI')) {
        echo <<<EOT
To install qualified access extension, put the following line in LocalSettings.php:
require_once( "{$IP}/extensions/QualifiedAccess/QualifiedAccess.php" );
EOT;
        exit( 1 );
}
 
#----------------------------------------------------------------------------
#    Extension initialization
#----------------------------------------------------------------------------
 
// Features are specific keywords that turns on access control on the specific keywords
$wgQualifiedAccessFeature = array();
$wgQualifiedAccessFeature['rights'] = array();          // Distinguish on user rights
$wgQualifiedAccessFeature['rights']['read'] = true;
$wgQualifiedAccessFeature['rights']['edit'] = true;
$wgQualifiedAccessFeature['rights']['delete'] = false;
$wgQualifiedAccessFeature['groups'] = array();          // Distinguish on user groups
$wgQualifiedAccessFeature['groups']['autoconfirmed'] = true;
$wgQualifiedAccessFeature['groups']['user'] = true;
$wgQualifiedAccessFeature['groups']['patroller'] = true;
$wgQualifiedAccessFeature['groups']['sysop'] = true;
$wgQualifiedAccessFeature['username'] = true;           // Report users username
$wgQualifiedAccessFeature['realname'] = true;           // Report users realname
$wgQualifiedAccessFeature['editcount'] = true;          // Report class of user contribution
$wgQualifiedAccessFeature['registration'] = true;       // Report age of user account
$wgQualifiedAccessFeature['test'] = false;              // Use the test attribute
 
//$wgQualifiedAccessTest = true;                                // Enable the test attribute
 
$wgQualifiedAccessMethod = array();                     // Default method for the access to the service
$wgQualifiedAccessMethod['post'] = true;
$wgQualifiedAccessMethod['get'] = true;
 
$wgQualifiedAccessDefault = array();
$wgQualifiedAccessDefault['method'] = 'post';     // Default method for the access to the service
$wgQualifiedAccessDefault['keyserver'] = 'hkp://subkeys.pgp.net';
$wgQualifiedAccessDefault['format'] = '<dl>,<dt>,</dt>,<dd>,</dd>,</dl>';
$wgQualifiedAccessDefault['separator'] = ', ';
$wgQualifiedAccessDefault['test'] = '';
$wgQualifiedAccessDefault['time'] = 'Y-m-d\TH:i:s\Z';
 
$wgQualifiedAccessCommand = '/usr/bin/gpg';             // Command to do the encryption and/or signing operation
 
$wgQualifiedAccessOptions = array();
$wgQualifiedAccessOptions['default-key'] = $wgPasswordSender;
$wgQualifiedAccessOptions['local-user'] = 'webmaster@yellowiki.net';
$wgQualifiedAccessOptions['keyserver'] = array();
$wgQualifiedAccessOptions['keyserver']['auto-key-retrieve'] = true;
$wgQualifiedAccessOptions['keyserver']['honor-keyserver-url'] = true;
$wgQualifiedAccessOptions['keyserver']['honor-pka-record'] = true;
$wgQualifiedAccessOptions['keyserver']['timeout'] = 5;
$wgQualifiedAccessOptions['greeting'] = false;
$wgQualifiedAccessOptions['default-keyring'] = false;
$wgQualifiedAccessOptions['keyring'] = 'pubring.gpg';
$wgQualifiedAccessOptions['secret-keyring'] = 'secring.gpg';
$wgQualifiedAccessOptions['options'] = '/var/gnupg/gpg.conf';
$wgQualifiedAccessOptions['homedir'] = '/var/gnupg';
 
// Basic credits
$wgExtensionCredits['specialpage'][] = array(
        'author' => 'John Erling Blad',
        'name' => 'Qualified access',
        'url' => 'http://www.mediawiki.org/wiki/Extension:QualifiedAccess',
        'description' => 'Let users with sufficient qualifications access external services like news archives.',
        'version'=>'0.2'
);
 
// Extend the language
$wgHooks['LanguageGetMagic'][] = 'efQualifiedAccessMagic';
 
// Tidy our protected code
$wgHooks['ParserAfterTidy'][] = 'efQualifiedAccessTidy';
 
// Set up the bulk of the extension
$wgExtensionFunctions[] = 'efQualifiedAccessExtension';
 
// Register the special page
$wgSpecialPages['QualifiedAccess'] = 'QualifiedAccess';
 
// Store for localy generated content
// Not quite sure abot this, and if there are any security implications
$efQualifiedAccessArgs = null;
$efQualifiedAccessMarks = array();
 
// Extend the language as necessary
function efQualifiedAccessMagic( &$magicWords, $langCode ) {
        $magicWords['qaccess'] = array( 0, 'qaccess' );
        return true;
}
 
// Add the extra files, localizations, etc
function efQualifiedAccessExtension() {
        global $wgParser;
        global $wgMessageCache, $wgQualifiedAccessMessages;
        # Internationalization
        require( dirname( __FILE__ ) . '/QualifiedAccess.i18n.php' );
        require( dirname( __FILE__ ) . '/QualifiedAccess_body.php' );
        foreach ( $wgQualifiedAccessMessages as $lang => $langMessages ) {
                $wgMessageCache->addMessages( $langMessages, $lang );
        }
        $wgParser->setHook( "qaccess", "efRenderQualifiedAccess" );
}
 
// Tidy additional text.
function efQualifiedAccessTidy(&$parser, &$text) {
        global $efQualifiedAccessMarks;
        for ($i=0;$i<count($efQualifiedAccessMarks);$i++)
                $text = preg_replace('/xx-qaccess-'.$i.'-xx/', $efQualifiedAccessMarks[$i], $text);
        return true;
}
 
// Processes the <qaccess> extension tag.
function efRenderQualifiedAccess( $input, $argv, $parser ) {
        global $wgUser;
        global $wgTitle;
        global $wgMessageCache;
        global $wgSitename;
        global $wgQualifiedAccessFeature;
        global $wgQualifiedAccessMethod;
        global $wgQualifiedAccessTest;
        global $wgQualifiedAccessHash;
        global $wgQualifiedAccessMode;
        global $efQualifiedAccessArgs;
        global $wgQualifiedAccessOptions;
        global $wgQualifiedAccessDefault;
        global $wgQualifiedAccessCommand;
        global $efQualifiedAccessMarks;
        global $wgPasswordSender;
 
        $out = array();
 
        // Reorganize and clean up the format string
        $format = ($argv['format'] ? $argv['format'] : $wgQualifiedAccessDefault['format']);
        $format = str_replace(array('²{', '}²', '²[', ']²', '¦', '\n', '¶'), array('{{', '}}', '[[', ']]', '|', "\n", "\n"), $format);
        $format = split(',', $format);
        foreach ($format as $item) {
                $item = htmlspecialchars($item);
        }
        if (count($format) != 6)
                return '<div class="qaccess-msg">'
                 . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-format-error' ), array( $title ) ) )
                 . '</div>';
 
        // Reorganize and clean up the separator string
        $separator = $argv['separator'] ? $argv['separator'] : $wgQualifiedAccessDefault['separator'];
        $separator = htmlspecialchars($separator);
 
 
        // Don't have to clean up this as it is used in pattern matches later on
        $mode = $argv['mode'] ? $argv['mode'] : $wgQualifiedAccessDefault['mode'];
 
        // Reorganize and clean up the keyserver string
        $keyserver = $argv['keyserver'] ? $argv['keyserver'] : $wgQualifiedAccessDefault['keyserver'];
        $keyserver = htmlspecialchars(trim($keyserver));
 
        // Not much we can do with this except clean it up
        $kid = htmlspecialchars(trim($argv['key']));
 
        // Not much we can do with this except clean it up
        $editlimit = intval($argv['editlimit']);
 
        // Reorganize and clean it up
        $timeformat = $argv['time'] ? $argv['time'] : $wgQualifiedAccessDefault['time'];
        $now = htmlspecialchars(gmdate( $timeformat, time() ));
 
        // handle title and service, they are one way aliases
        $title = trim($argv['title']);
        $service = trim($argv['service']);
        if (!strlen($title) && strlen($service))
                $title = $service;
        if (strlen($title) && !strlen($service))
                $service = $title;
        $title = htmlspecialchars(ucfirst($title));
        $service = htmlspecialchars(strtolower($service));
 
        // handle features
        $features = array();
        $values = explode(',', $argv['features']);
        foreach ($values as $item) {
                $item = trim($item);
                if (!is_bool($wgQualifiedAccessFeature[$item])
                 || !$wgQualifiedAccessFeature[$item]) continue;
                if (strlen($item)>0) $features[$item] = true;
        }
 
        // handle rights
        $rights = array();
        $values = explode(',', $argv['rights']);
        foreach ($values as $item) {
                $item = trim($item);
                if (!is_bool($wgQualifiedAccessFeature['rights'][$item])
                 || !$wgQualifiedAccessFeature['rights'][$item]) continue;
                if (strlen($item)>0) array_push($rights, $item);
        }
 
        // handle groups
        $groups = array();
        $values = explode(',', $argv['groups']);
        foreach ($values as $item) {
                $item = trim($item);
                if (!is_bool($wgQualifiedAccessFeature['groups'][$item])
                 || !$wgQualifiedAccessFeature['groups'][$item]) continue;
                if (strlen($item)>0) array_push($groups, $item);
        }
 
        // what kind of page is this
        $special = -1 == $wgTitle->getNamespace();
 
        // a few vars
        $packet = '';
        $found = array();
 
        // build regexp for resources
        $resources = array();
        $values = explode(',', $argv['resources']);
        foreach ($values as $item) {
                $item = preg_quote(trim($item));
                if (strlen($item)>0) array_push($resources, $item);
        }
        if (count($resources)) $mres = '[^(' . join($resources, '|') . ')]i';
 
        // process a special page
        if ($special)  {
                // if necessary, run the test
                if ($wgQualifiedAccessFeature['test']) {
                        $test = $argv['test'] ? $argv['test'] : $wgQualifiedAccessDefault['test'];
                        $test = trim($test);
                        if (strlen($test)) {
                                $test = $parser->internalParse($test);
                                if (strlen($test))
                                        return '<div class="qaccess-msg">'
                                         . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-test-result' ), array( $title ) ) )
                                         . '</div>';
                        }
                }
 
                // check blocked status
                if ($wgUser->isBlocked())
                        return '<div class="qaccess-msg">'
                         . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notavailable-blocked' ), array( $title ) ) )
                         . '</div>';
 
                // check blocked status
                if ($editlimit > $wgUser->getEditCount())
                        return '<div class="qaccess-msg">'
                         . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notavailable-editlimit' ), array( $title ) ) )
                         . '</div>';
 
                // get site name
                $packet .= "sitename: $wgSitename";
 
                // report time
                $packet .= "\ntime: $now";
 
                // get ip address
                $packet .= "\nipaddress: " . wfGetIp();
 
                // set the action
                $action = htmlspecialchars(trim($argv['action']));
                if (!strlen($action))
                        return '<div class="qaccess-msg">'
                         . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-action-error' ), array( $title ) ) )
                         . '</div>';
 
                // set the method
                $method = htmlspecialchars(trim(strtolower($method)));
                $method = $wgQualifiedAccessMethod[$method] ? $method : $wgQualifiedAccessDefault['method'];
                if (!strlen($action))
                        return '<div class="qaccess-msg">'
                         . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-method-error' ), array( $title ) ) )
                         . '</div>';
 
                // try to identify resources
                foreach ($efQualifiedAccessArgs as $key => $value) {
                        if (preg_match('/^\d+$/',$key) || $key == 'resource' || $key == 'doi' || $key == 'issn') {
                                if (!isset($value) || $value == '') continue;
                                $found[ $key == 'doi' || $key == 'issn' ? "$key $value" : $value ]++;
                        }
                }
        }
 
        $output = $format[0];
        $n = 0; $m = 0;
        if (!$special) {
                // report key server
                $output .= $format[1] . wfMsg( 'qaccess-key-server' ) . $format[2]
                 . $format[3] . $keyserver . $format[4];
 
                // report key id 
                $output .= $format[1] . wfMsg( 'qaccess-key-id' ) . $format[2]
                 . $format[3] . $kid . $format[4];
 
                // report service
                if ($service)
                        $output .= $format[1] . wfMsg( 'qaccess-service' ) . $format[2]
                         . $format[3] . $service . $format[4];
 
                // report edit limit
                if ($editlimit)
                        $output .= $format[1] . wfMsg( 'qaccess-editlimit' ) . $format[2]
                         . $format[3] . $editlimit . $format[4];
        }
 
 
        if ($special) {
                // check service match
                if ($argv['service']) {
                        foreach ($efQualifiedAccessArgs as $key => $value) {
                                if (preg_match('/^\d+$/',$key) || $key == 'service') {
                                        if (!isset($value) || $value == '') continue;
                                        $m++;
                                        if ($service == strtolower($value)) {
                                                $found[ $key == 'doi' || $key == 'issn' ? "$key $value" : $value ]--;
                                                $n++;
                                                break;
                                        }
                                }
                        }
                }
 
                // check resource match
                if ($argv['resources']) {
                        foreach ($efQualifiedAccessArgs as $key => $value) {
                                if (preg_match('/^\d+$/',$key) || $key == 'resource' || $key == 'doi' || $key == 'issn') {
                                        if (!isset($value) || $value == '') continue;
                                        $m++;
                                        if (preg_match($mres, $value)) {
                                                if (($key == 'doi') || ($key == 'issn')) {
                                                        $output .= $format[1] . wfMsg( 'qaccess-resource' ) . $format[2]
                                                         . $format[3] . "$key $value" . $format[4];
                                                        $packet .= "\nresource: $key $value";
                                                        $found[ "$key $value" ]--;
                                                }
                                                else {
                                                        $output .= $format[1] . wfMsg( 'qaccess-resource' ) . $format[2]
                                                         . $format[3] . $value . $format[4];
                                                        $packet .= "\nresource: $value";
                                                        $found[ $value ]--;
                                                }
                                                $n++;
                                        }
                                }
                        }
                }
 
                // still in sync?
                if (($argv['service'] || $argv['resources']) && $m && !$n)
                 return '<div class="qaccess-msg">'
                 . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notfound-resourceorservice' ), array( $title ) ) )
                 . '</div>';
 
                // additional resources
                foreach ($found as $key => $value)
                        if (0<$found[$key]) {
                                $output .= $format[1] . wfMsg( 'qaccess-resource' ) . $format[2]
                                 . $format[3] . $key . $format[4];
                                $packet .= "\nresource: " . $key;
                        }
        }
 
 
        // process a special page
        if ($special)  {
                // get user name
                if ($features['username']) {
                        $output .= $format[1] . wfMsg( 'qaccess-username' ) . $format[2]
                                 . $format[3] . $wgUser->getName() . $format[4];
                        $packet .= "\nusername: " . $wgUser->getName();
                }
 
                // get real name
                if ($features['realname']) {
                        $output .= $format[1] . wfMsg( 'qaccess-realname' ) . $format[2]
                                 . $format[3] . $wgUser->getRealName() . $format[4];
                        $packet .= "\nrealname: " . $wgUser->getRealName();
                }
 
                // get edit count
                if ($features['editcount']) {
                        $n = $wgUser->getEditCount();
                        if (10<$n) $n = round($n, -intval(log10($n)));
                        $output .= $format[1] . wfMsg( 'qaccess-editcount' ) . $format[2]
                                 . $format[3] . $n . $format[4];
                        $packet .= "\neditcount: " . $n;
                }
 
                // get registration
                if ($features['registration']) {
                        $then = $wgUser->getRegistration();
                        $weeks = round((time() - strtotime($then))/604800);
                        $months = round((time() - strtotime($then))/2629744);
                        if ($months<=3) {
                                $output .= $format[1] . wfMsg( 'qaccess-registration' ) . $format[2]
                                 . $format[3]
                                 . wfMsgReplaceArgs( wfMsg( 'qaccess-registration-weeks' ), array( substr($then, 0, 4), $months, $weeks ) )
                                 . $format[4];
                                $packet .= "\nregistration: " . substr($then, 0, 4) . " ($weeks weeks)";
                        }
                        elseif ($months<=12) {
                                $output .= $format[1] . wfMsg( 'qaccess-registration' ) . $format[2]
                                 . $format[3]
                                 . wfMsgReplaceArgs( wfMsg( 'qaccess-registration-months' ), array( substr($then, 0, 4), $months, $weeks ) )
                                 . $format[4];
                                $packet .= "\nregistration: " . substr($then, 0, 4) . " ($months months)";
                        }
                        else {
                                $output .= $format[1] . wfMsg( 'qaccess-registration' ) . $format[2]
                                 . $format[3]
                                 . wfMsgReplaceArgs( wfMsg( 'qaccess-registration-years' ), array( substr($then, 0, 4), $months, $weeks ) )
                                 . $format[4];
                                $packet .= "\nregistration: " . substr($then, 0, 4);
                        }
                }
        }
 
        // check features
        $out = '';
        if (!$special) {
                foreach ($features as $key => $value) {
                        if (isset($wgQualifiedAccessFeature[$key])) {
                                if (0<strlen($out)) $out .= $separator;
                                $out .= wfMsg( 'qaccess-' . $key );
                        }
                }
                if (0<strlen($out))
                        $output .= $format[1] . wfMsg( 'qaccess-features' ) . $format[2]
                         . $format[3] . $out . $format[4];
        }
 
        // check groups
        $out = '';
        if ($special && $argv['groups']) {
                $usergroups = array();
                foreach ($wgUser->getEffectiveGroups() as $item) {
                        $usergroups[$item] = true;
                }
                foreach ($groups as $item) {
                        if ($usergroups[$item]) {
                                if (0<strlen($out)) $out .= $separator;
                                $out .= wfMsg( 'qaccess-' . $item );
                                $packet .= "\ngroup: " . $item;
                        }
                }
        }
        elseif (!$special && $argv['groups']) {
                foreach ($groups as $item) {
                        if (isset($wgQualifiedAccessFeature['groups'][$item])) {
                                if (0<strlen($out)) $out .= $separator;
                                $out .= wfMsg( 'qaccess-' . $item );
                        }
                }
        }
        if ($argv['groups']) {
                if (0<strlen($out))
                         $output .= $format[1] . wfMsg( 'qaccess-groups' ) . $format[2]
                         . $format[3] . $out . $format[4];
                elseif ($special)
                        return '<div class="qaccess-msg">'
                         . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notavailable-groups' ), array( $title ) ) )
                         . '</div>';
        }
 
        // check rights
        $out = '';
        if ($special && $argv['rights']) {
                $userrights = array();
                foreach ($wgUser->getRights() as $item) {
                        $userrights[$item] = true;
                }
                foreach ($rights as $item) {
                        if ($userrights[$item]) {
                                if (0<strlen($out)) $out .= $separator;
                                $out .= wfMsg( 'qaccess-' . $item );
                                $packet .= "\nright: " . $item;
                        }
                }
        }
        if (!$special && $argv['rights']) {
                foreach ($rights as $item) {
                        if (isset($wgQualifiedAccessFeature['rights'][$item])) {
                                if (0<strlen($out)) $out .= $separator;
                                $out .= wfMsg( 'qaccess-' . $item );
                        }
                }
        }
        if ($argv['rights']) {
                if (0<strlen($out))
                        $output .= $format[1] . wfMsg( 'qaccess-rights' ) . $format[2]
                         . $format[3] . $out . $format[4];
                elseif ($special)
                        return '<div class="qaccess-msg">'
                         . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notavailable-rights' ), array( $title ) ) )
                         . '</div>';
        }
 
        $output .= $format[5];
 
        if ($special) {
                // build the packet
                $cmd = $wgQualifiedAccessCommand . ' ';
                foreach ($wgQualifiedAccessOptions as $key => $value) {
                        if (is_int($key) && is_string($value)) $cmd .= "--$value ";
                        elseif (is_int($key)) continue;
                        elseif (is_null($value)) $cmd .= "--$key ";
                        elseif (is_bool($value)) $cmd .= "--" . ($value ? '' : 'no-') . "$key ";
                        elseif (is_string($value) || is_int($v)) $cmd .= "--$key '$value' ";
                        elseif (is_array($value)) {
                                foreach ($value as $k => $v) {
                                        if (is_int($k) && is_string($v)) $cmd .= "--$key $v ";
                                        elseif (is_int($k)) continue;
                                        elseif (is_null($v)) $cmd .= "--$key $k ";
                                        elseif (is_bool($v)) $cmd .= "--$key " . ($value ? '' : 'no-') . "$k ";
                                        elseif (is_string($v) || is_int($v)) $cmd .= "--$key '$k=$v' ";
                                }
                        }
                }
                if (preg_match('/(encrypt)/i', $mode)) {
                        $cmd .= "--encrypt --sign ";
                        $cmd .= "--recipient " . escapeshellarg($kid) . " ";
                        $cmd .= "--keyserver " . escapeshellarg($keyserver) . " ";
                }
                elseif (preg_match('/(armour)/i', $mode))
                        $cmd .= "--sign --armour ";
                else $cmd .= "--clearsign ";
 
                $desc = array(
                        0 => array("pipe", "r"),  // stdin
                        1 => array("pipe", "w"),  // stdout
                        2 => array("pipe", "w"),  // stderr
                );
                $enc = '';
                $env = array();
                $process = proc_open($cmd, $desc, $pipes, $wgQualifiedAccessOptions['homedir'], $env);
                if (is_resource($process)) {
                        fwrite($pipes[0], $packet); fclose($pipes[0]);
                        $enc = stream_get_contents($pipes[1]); fclose($pipes[1]);
                        $err = stream_get_contents($pipes[2]); fclose($pipes[2]);
                        $ret = proc_close($process);
                        // if we gets anything on the error pipe we assume we have an error and terminates
                        if (strlen($err)) {
                                return '<div class="qaccess-msg">'
                                 . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-gpg-error' ), array( $title, $cmd, $err ) ) )
                                 . '</div>';
                        }
                }
 
                // keep the result for later, we have to bypass tidy
                $marks = count($efQualifiedAccessMarks);
                $efQualifiedAccessMarks[$marks] = $enc;
 
                // make a sufficient large textarea, note that this will typically have display:none
                $lines = substr_count($enc, "\n");
 
                // return our form with the report when we are in a special page
                return '<form action="' . $action . '" method="' . $method . '">'
                 . '<fieldset><legend>' . $title . '</legend>'
                 . '<div class="data" style="float:right;width:35%">'
                 . $output
                 . '<input class="submit" type="submit" value="' . wfMsgReplaceArgs( wfMsg( 'qaccess-submit' ), array( $title ) ) . '" title="' . wfMsg( 'qaccess-submit-warning' ) . '">'
                 . '</div>'
                 . $parser->internalParse("__NOEDITSECTION__" . $input)
                 . '<textarea rows="' . $lines . '" cols="64" readonly>' . 'xx-qaccess-'.$marks.'-xx' . '</textarea>'
                 . '</fieldset></form>';
        }
        else {
                // we are not in a form, return a description
                return '<fieldset><legend>' . $title . '</legend>'
                 . '<div class="data" style="float:right;width:35%">'
                 . $output
                 . '</div>'
                 . $parser->internalParse(" __NOEDITSECTION__" . $input)
                 . '</fieldset>';
        }
 
}
Personal tools
Namespaces

Variants
Actions
Navigation
Support
Download
Development
Communication
Print/export
Toolbox