Difference between revisions of "Extension:RecordAdmin"

From Organic Design wiki
(add reset button and links back to new search)
(add support for multiple input types and add css rules to hide cols)
Line 11: Line 11:
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
  
define('RECORDADMIN_VERSION','0.0.5, 2007-10-19');
+
define('RECORDADMIN_VERSION','0.0.6, 2007-10-20');
  
 
$wgRecordAdminCategory = 'Records';
 
$wgRecordAdminCategory = 'Records';
Line 63: Line 63:
  
 
# Read in the form for this record type if one has been selected
 
# Read in the form for this record type if one has been selected
# - extract only the content from between the form tags and remove the submit input
+
# - we're using the record's own form as a filter for searching for records
 +
# - extract only the content from between the form tags and remove any submit inputs
 
if ($type) {
 
if ($type) {
 
$form = new Article(Title::newFromText("Form:$type"));
 
$form = new Article(Title::newFromText("Form:$type"));
Line 70: Line 71:
 
$form = preg_replace('#^.+?<form.+?>#s', '', $form);
 
$form = preg_replace('#^.+?<form.+?>#s', '', $form);
 
$form = preg_replace('#</form>.+?$#s', '', $form);
 
$form = preg_replace('#</form>.+?$#s', '', $form);
$form = preg_replace('#name=[\'"](.+?)["\']#s', 'name="ra_$1"', $form);
+
$form = preg_replace('#name\s*=\s*[\'"](.+?)["\']#s', 'name="ra_$1"', $form);
 +
$this->form = $form;
 
}
 
}
 +
 +
# Extract the input names and types used in the form
 +
$this->examineForm();
 +
 +
# Clear any default values
 +
$this->populateForm(array());
  
 
# If no type selected, render select list of record types from Category:Records
 
# If no type selected, render select list of record types from Category:Records
Line 77: Line 85:
 
$wgOut->addWikiText("== Select the type of record to search for ==\n");
 
$wgOut->addWikiText("== Select the type of record to search for ==\n");
 
 
 +
# Get titles in Category:Records and build option list
 
$options = '';
 
$options = '';
 
$dbr  = &wfGetDB(DB_SLAVE);
 
$dbr  = &wfGetDB(DB_SLAVE);
Line 84: Line 93:
 
while ($row = $dbr->fetchRow($res)) $options .= '<option>'.Title::newFromID($row[0])->getText().'</option>';
 
while ($row = $dbr->fetchRow($res)) $options .= '<option>'.Title::newFromID($row[0])->getText().'</option>';
  
$wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null));
+
# Render type-selecting form
$wgOut->addHTML("<select name='wpType'>$options</select> ");
+
$wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null)
$wgOut->addHTML(wfElement('input', array('type' => 'submit', 'value' => 'Submit')).'</form>');
+
. "<select name='wpType'>$options</select> "
 +
. wfElement('input', array('type' => 'submit', 'value' => 'Submit'))
 +
. '</form>'
 +
);
 
}
 
}
 
 
 
# Record type known, but no record selected, render form for searching or creating
 
# Record type known, but no record selected, render form for searching or creating
 
elseif (empty($record)) {
 
elseif (empty($record)) {
$wgOut->addWikiText("== Find or Create a $type record ==\n");
+
$wgOut->addWikiText("== Find or Create a \"$type\" record ==\n");
 
 
 
# Process Create submission
 
# Process Create submission
Line 105: Line 117:
 
$text = '';
 
$text = '';
 
foreach ($posted as $k => $v) if ($v) $text .= "| $k = $v\n";
 
foreach ($posted as $k => $v) if ($v) $text .= "| $k = $v\n";
$text = $text ? "{{"."$type\n$text}}" : "{{"."$type}}";
+
$text = $text ? "\{\{$type\n$text}}" : "\{\{$type}}";
 
$success = $article->doEdit($text, $summary, EDIT_NEW);
 
$success = $article->doEdit($text, $summary, EDIT_NEW);
 
 
Line 116: Line 128:
 
}
 
}
 
 
# Populate the form with the posted values
+
# Populate the search form with any posted values
foreach ($posted as $k => $v) $form = str_replace("name=\"ra_$k\"", "name=\"ra_$k\" value='$v'", $form);
+
$this->populateForm($posted);
 
 
 
# Render the form
 
# Render the form
 
$wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null));
 
$wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null));
$wgOut->addHTML(
+
$wgOut->addHTML('<b>Record ID:</b> '.wfElement('input', array('name' => 'wpTitle', 'size' => 30, 'value' => $wpTitle)));
'<b>Record Name:</b> '
 
. wfElement('input', array('name' => 'wpTitle', 'size' => 30, 'value' => $wpTitle))
 
);
 
 
$wgOut->addHTML("\n<br><br><hr><br>\n");
 
$wgOut->addHTML("\n<br><br><hr><br>\n");
$wgOut->addHTML($form);
+
$wgOut->addHTML($this->form);
 
$wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpType', 'value' => $type)));
 
$wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpType', 'value' => $type)));
 
$wgOut->addHTML('<br><hr><br><table width="100%"><tr>'
 
$wgOut->addHTML('<br><hr><br><table width="100%"><tr>'
Line 152: Line 161:
 
$match = true;
 
$match = true;
 
$r = array($t);
 
$r = array($t);
foreach ($posted as $k => $v) {
+
foreach (array_keys($this->types) as $k) {
 +
$v = isset($posted[$k]) ? $posted[$k] : '';
 
$i = preg_match("|$k\s*=\s*(.+)\s*$|mi", $text, $m);
 
$i = preg_match("|$k\s*=\s*(.+)\s*$|mi", $text, $m);
 
if ($v && !($i && eregi($v, $m[1]))) $match = false;
 
if ($v && !($i && eregi($v, $m[1]))) $match = false;
Line 161: Line 171:
 
}
 
}
  
# Render a table of the matching records
+
# Render search results
 
if (count($records)) {
 
if (count($records)) {
 
ksort($records);
 
ksort($records);
 
$table = "<table class='recordadmin $type-record'>\n<tr><th colspan='2'>$type</th>";
 
$table = "<table class='recordadmin $type-record'>\n<tr><th colspan='2'>$type</th>";
foreach ($posted as $k => $v) $table .= "<th>$k</th>";
+
$i = 0;
 +
foreach (array_keys($this->types) as $k) $table .= "<th class='col".(++$i)."'>$k</th>";
 
$table .= "</tr>\n";
 
$table .= "</tr>\n";
 
$stripe = '';
 
$stripe = '';
Line 172: Line 183:
 
$table .= "<tr$stripe><td><a href='".$r[0]->getLocalURL()."'>".$r[0]->getText()."</a>";
 
$table .= "<tr$stripe><td><a href='".$r[0]->getLocalURL()."'>".$r[0]->getText()."</a>";
 
$table .= "<td>(<a href='".$title->getLocalURL("wpType=$type&wpRecord=$k")."'>edit</a>)</td>\n";
 
$table .= "<td>(<a href='".$title->getLocalURL("wpType=$type&wpRecord=$k")."'>edit</a>)</td>\n";
foreach ($posted as $k => $v) $table .= "<td>".$r[$k]."</td>";
+
$i = 0;
 +
foreach (array_keys($this->types) as $k) {
 +
$v = isset($r[$k]) ? $r[$k] : '&nbsp;';
 +
$table .= "<td class='col".(++$i)."'>$v</td>";
 +
}
 
$table .= "</tr>\n";
 
$table .= "</tr>\n";
 
}
 
}
Line 208: Line 223:
 
 
 
# Populate the form with the current values in the article
 
# Populate the form with the current values in the article
preg_match_all("|\|\s*(.+?)\s*=\s*(.+?)$|m", $text, $m);
+
foreach ($this->examineBraces($text) as $brace) if ($brace['NAME'] == $type) $braces = $brace;
foreach ($m[1] as $i => $k) $form = eregi_replace("name=\"ra_$k\"", "name=\"ra_$k\" value='{$m[2][$i]}'", $form);
+
$this->populateForm(substr($text, $braces['OFFSET'], $braces['LENGTH']));
 
 
 
# Render the form
 
# Render the form
 
$wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null));
 
$wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null));
$wgOut->addHTML($form);
+
$wgOut->addHTML($this->form);
 
$wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpType', 'value' => $type)));
 
$wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpType', 'value' => $type)));
 
$wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpRecord', 'value' => $record)));
 
$wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpRecord', 'value' => $record)));
Line 222: Line 237:
 
);
 
);
 
}
 
}
 +
}
 +
 +
/**
 +
* Populates the form values from the passed values
 +
* - $form is HTML text
 +
* - $values may be a hash or wikitext template syntax
 +
*/
 +
function populateForm($values) {
 
 
 +
# If values are wikitext, convert to hash
 +
if (!is_array($values)) {
 +
$text = $values;
 +
$values = array();
 +
preg_match_all("|\|\s*(.+?)\s*=\s*(.+?)\s*$|m", $text, $m);
 +
foreach ($m[1] as $i => $k) $values[$k] = $m[2][$i];
 +
}
 +
 +
# Add the values into the form's HTML depending on their type
 +
foreach($this->types as $k => $type) {
 +
 +
# Get this input element's html text and position and length
 +
preg_match("|<([a-zA-Z]+)[^<]+?name=\"ra_$k\".*?>(.*?</\\1>)?|s", $this->form, $m, PREG_OFFSET_CAPTURE);
 +
list($html, $pos) = $m[0];
 +
$len = strlen($html);
 +
 +
# Modify the element according to its type
 +
# - clears default value, then adds new value
 +
$v = isset($values[$k]) ? $values[$k] : '';
 +
switch ($type) {
 +
case 'text':
 +
$html = preg_replace("|value\s*=\s*\".*?\"|", "", $html);
 +
if ($v) $html = preg_replace("|(/?>)$|", " value=\"$v\" $1", $html);
 +
break;
 +
case 'bool':
 +
$html = preg_replace("|checked|", "", $html);
 +
if ($v) $html = preg_replace("|(/?>)$|", " checked $1", $html);
 +
break;
 +
case 'list':
 +
$html = preg_replace("|(<option[^<>]*) selected|", "$1", $html);
 +
if ($v) $html = preg_replace("|(?<=<option)(?=>$v</option>)|s", " selected", $html);
 +
break;
 +
case 'blob':
 +
$html = preg_replace("|>.*?(?=</textarea>)|s", ">$v", $html);
 +
break;
 +
}
 +
 +
# Replace the element in the form with the modified html
 +
$this->form = substr_replace($this->form, $html, $pos, $len);
 +
}
 +
}
 +
 +
/**
 +
* Returns an array of types used by the passed HTML text form
 +
* - supported types, text, select, checkbox, textarea
 +
*/
 +
function examineForm() {
 +
$this->types = array();
 +
preg_match_all("|<([a-zA-Z]+)[^<]+?name=\"ra_(.+?)\".*?>|", $this->form, $m);
 +
foreach ($m[2] as $i => $k) {
 +
$tag = $m[1][$i];
 +
$type = preg_match("|type\s*=\s*\"(.+?)\"|", $m[0][$i], $n) ? $n[1] : '';
 +
switch ($tag) {
 +
case 'input':
 +
switch ($type) {
 +
case 'checkbox':
 +
$this->types[$k] = 'bool';
 +
break;
 +
default:
 +
$this->types[$k] = 'text';
 +
break;
 +
}
 +
break;
 +
case 'select':
 +
$this->types[$k] = 'list';
 +
break;
 +
case 'textarea':
 +
$this->types[$k] = 'blob';
 +
break;
 +
}
 +
}
 
}
 
}
  

Revision as of 04:14, 20 October 2008

<?php /**

* Extension:RecordAdmin - MediaWiki extension
*
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.

Template:PhpCategory:Extensions created with Template:SpecialPage

* @package MediaWiki
* @subpackage Extensions
* @author Aran Dunkley User:Nad
* @licence GNU General Public Licence 2.0 or later
*/

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

define('RECORDADMIN_VERSION','0.0.6, 2007-10-20');

$wgRecordAdminCategory = 'Records';

$wgExtensionFunctions[] = 'wfSetupRecordAdmin';

$wgExtensionCredits['specialpage'][] = array( 'name' => 'Special:RecordAdmin', 'author' => 'User:Nad', 'description' => 'A special page for finding and editing record articles using a form', 'url' => 'http://www.organicdesign.co.nz/Extension:SpecialExample', 'version' => RECORDADMIN_VERSION );

require_once "$IP/includes/SpecialPage.php";

/**

* Define a new class based on the SpecialPage class
*/

class SpecialRecordAdmin extends SpecialPage {

function __construct() { SpecialPage::SpecialPage( 'RecordAdmin', # name as seen in links etc 'sysop', # user rights required true, # listed in special:specialpages false, # function called by execute() - defaults to wfSpecial{$name} false, # file included by execute() - defaults to Special{$name}.php, only used if no function false # includable ); }

/** * Override SpecialPage::execute() */ function execute($param) { global $wgOut, $wgRequest, $wgRecordAdminCategory; $this->setHeaders(); $type = $wgRequest->getText('wpType') or $type = $param; $record = $wgRequest->getText('wpRecord'); $title = Title::makeTitle(NS_SPECIAL, 'RecordAdmin'); $wpTitle = trim($wgRequest->getText('wpTitle'));

$wgOut->addHTML("

<a href='".$title->getLocalURL()."/$type'>New $type search</a> | " . "<a href='".$title->getLocalURL()."'>Select another record type</a>


\n"

);

# Get posted form values if any $posted = array(); foreach ($_POST as $k => $v) if (ereg('^ra_(.+)$', $k, $m)) $posted[$m[1]] = $v;

# Read in the form for this record type if one has been selected # - we're using the record's own form as a filter for searching for records # - extract only the content from between the form tags and remove any submit inputs if ($type) { $form = new Article(Title::newFromText("Form:$type")); $form = $form->getContent(); $form = preg_replace('#<input.+?type=[\'"]?submit["\']?.+?/(input| *)>#', , $form); $form = preg_replace('#^.+?<form.+?>#s', , $form); $form = preg_replace('#</form>.+?$#s', , $form); $form = preg_replace('#name\s*=\s*[\'"](.+?)["\']#s', 'name="ra_$1"', $form); $this->form = $form; }

# Extract the input names and types used in the form $this->examineForm();

# Clear any default values $this->populateForm(array());

# If no type selected, render select list of record types from Category:Records if (empty($type)) { $wgOut->addWikiText("== Select the type of record to search for ==\n");

# Get titles in Category:Records and build option list $options = ; $dbr = &wfGetDB(DB_SLAVE); $cl = $dbr->tableName('categorylinks'); $cat = $dbr->addQuotes($wgRecordAdminCategory); $res = $dbr->select($cl, 'cl_from', "cl_to = $cat", __METHOD__, array('ORDER BY' => 'cl_sortkey')); while ($row = $dbr->fetchRow($res)) $options .= '<option>'.Title::newFromID($row[0])->getText().'</option>';

# Render type-selecting form $wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null) . "<select name='wpType'>$options</select> " . wfElement('input', array('type' => 'submit', 'value' => 'Submit')) . '</form>' ); }

# Record type known, but no record selected, render form for searching or creating elseif (empty($record)) { $wgOut->addWikiText("== Find or Create a \"$type\" record ==\n");

# Process Create submission if (count($posted) && $wgRequest->getText('wpCreate')) { $t = Title::newFromText($wpTitle); if (is_object($t)) {

if ($t->exists()) $wgOut->addHTML("

Sorry, \"$wpTitle\" already exists!

\n");

else {

# Attempt to create the article $article = new Article($t); $summary = "RecordAdmin: New $type created"; $text = ; foreach ($posted as $k => $v) if ($v) $text .= "| $k = $v\n"; $text = $text ? "\{\{$type\n$text}}" : "\{\{$type}}"; $success = $article->doEdit($text, $summary, EDIT_NEW);

# Report success or error

if ($success) $wgOut->addHTML("

\"$wpTitle\" created successfully

\n"); else $wgOut->addHTML("

An error occurred while attempting to create the $type!

\n");

}

} else $wgOut->addHTML("

Bad title!

\n");

$wgOut->addHTML("



\n"); }

# Populate the search form with any posted values $this->populateForm($posted);

# Render the form $wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null)); $wgOut->addHTML('Record ID: '.wfElement('input', array('name' => 'wpTitle', 'size' => 30, 'value' => $wpTitle)));

$wgOut->addHTML("\n



\n");

$wgOut->addHTML($this->form); $wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpType', 'value' => $type)));

$wgOut->addHTML('



' .'' .'' .'' .'
'.wfElement('input', array('type' => 'submit', 'name' => 'wpFind', 'value' => "Search")).''.wfElement('input', array('type' => 'submit', 'name' => 'wpCreate', 'value' => "Create")).''.wfElement('input', array('type' => 'reset', 'value' => "Reset")).'

</form>'

);

# Process Find submission if (count($posted) && $wgRequest->getText('wpFind')) { $wgOut->addWikiText("
\n== Search results ==\n");

# Select records which use the template and exhibit a matching title and other fields $records = array(); $dbr = &wfGetDB(DB_SLAVE); $tl = $dbr->tableName('templatelinks'); $ty = $dbr->addQuotes($type); $res = $dbr->select($tl, 'tl_from', "tl_namespace = 10 AND tl_title = $ty", __METHOD__); while ($row = $dbr->fetchRow($res)) { $t = Title::newFromID($row[0]); if (empty($wpTitle) || eregi($wpTitle, $t)) { $a = new Article($t); $text = $a->getContent(); $match = true; $r = array($t); foreach (array_keys($this->types) as $k) { $v = isset($posted[$k]) ? $posted[$k] : ; $i = preg_match("|$k\s*=\s*(.+)\s*$|mi", $text, $m); if ($v && !($i && eregi($v, $m[1]))) $match = false; $r[$k] = isset($m[1]) ? $m[1] : ; } if ($match) $records[$t->getPrefixedText()] = $r; } }

# Render search results if (count($records)) { ksort($records);

$table = "

\n";

$i = 0;

foreach (array_keys($this->types) as $k) $table .= ""; $table .= "\n"; $stripe = ; foreach ($records as $k => $r) { $stripe = $stripe ?  : ' class="stripe"'; $table .= "<tr$stripe>\n";

$i = 0; foreach (array_keys($this->types) as $k) { $v = isset($r[$k]) ? $r[$k] : ' ';

$table .= "";

}

$table .= "\n"; } $table .= "
$type$k
<a href='".$r[0]->getLocalURL()."'>".$r[0]->getText()."</a>"; $table .= "(<a href='".$title->getLocalURL("wpType=$type&wpRecord=$k")."'>edit</a>)$v

\n";

$wgOut->addHTML($table); } else $wgOut->addWikiText("No matching records found!\n"); } }

# A specific record has been selected, render form for updating else { $wgOut->addWikiText("== Editing \"$record\" ==\n"); $article = new Article(Title::newFromText($record)); $text = $article->fetchContent();

# Update article if form posted if (count($posted)) {

# Get the location and length of the record braces to replace foreach ($this->examineBraces($text) as $brace) if ($brace['NAME'] == $type) $braces = $brace;

# Attempt to save the article $summary = "RecordAdmin: $type properties updated"; $replace = ; foreach ($posted as $k => $v) if ($v) $replace .= "| $k = $v\n"; $replace = $replace ? "Template:"."$type\n$replace" : "Template:"."$type"; $text = substr_replace($text, $replace, $braces['OFFSET'], $braces['LENGTH']); $success = $article->doEdit($text, $summary, EDIT_UPDATE);

# Report success or error

if ($success) $wgOut->addHTML("

$type updated successfully

\n"); else $wgOut->addHTML("

An error occurred during update!

\n");

$wgOut->addHTML("



\n"); }

# Populate the form with the current values in the article foreach ($this->examineBraces($text) as $brace) if ($brace['NAME'] == $type) $braces = $brace; $this->populateForm(substr($text, $braces['OFFSET'], $braces['LENGTH']));

# Render the form $wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null)); $wgOut->addHTML($this->form); $wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpType', 'value' => $type))); $wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpRecord', 'value' => $record)));

$wgOut->addHTML('



' .'' .'' .'
'.wfElement('input', array('type' => 'submit', 'value' => "Save")).''.wfElement('input', array('type' => 'reset', 'value' => "Reset")).'

</form>'

); } }

/** * Populates the form values from the passed values * - $form is HTML text * - $values may be a hash or wikitext template syntax */ function populateForm($values) {

# If values are wikitext, convert to hash if (!is_array($values)) { $text = $values; $values = array(); preg_match_all("|\|\s*(.+?)\s*=\s*(.+?)\s*$|m", $text, $m); foreach ($m[1] as $i => $k) $values[$k] = $m[2][$i]; }

# Add the values into the form's HTML depending on their type foreach($this->types as $k => $type) {

# Get this input element's html text and position and length preg_match("|<([a-zA-Z]+)[^<]+?name=\"ra_$k\".*?>(.*?</\\1>)?|s", $this->form, $m, PREG_OFFSET_CAPTURE); list($html, $pos) = $m[0]; $len = strlen($html);

# Modify the element according to its type # - clears default value, then adds new value $v = isset($values[$k]) ? $values[$k] : ; switch ($type) { case 'text': $html = preg_replace("|value\s*=\s*\".*?\"|", "", $html); if ($v) $html = preg_replace("|(/?>)$|", " value=\"$v\" $1", $html); break; case 'bool': $html = preg_replace("|checked|", "", $html); if ($v) $html = preg_replace("|(/?>)$|", " checked $1", $html); break; case 'list': $html = preg_replace("|(<option[^<>]*) selected|", "$1", $html); if ($v) $html = preg_replace("|(?<=<option)(?=>$v</option>)|s", " selected", $html); break; case 'blob': $html = preg_replace("|>.*?(?=</textarea>)|s", ">$v", $html); break; }

# Replace the element in the form with the modified html $this->form = substr_replace($this->form, $html, $pos, $len); } }

/** * Returns an array of types used by the passed HTML text form * - supported types, text, select, checkbox, textarea */ function examineForm() { $this->types = array(); preg_match_all("|<([a-zA-Z]+)[^<]+?name=\"ra_(.+?)\".*?>|", $this->form, $m); foreach ($m[2] as $i => $k) { $tag = $m[1][$i]; $type = preg_match("|type\s*=\s*\"(.+?)\"|", $m[0][$i], $n) ? $n[1] : ; switch ($tag) { case 'input': switch ($type) { case 'checkbox': $this->types[$k] = 'bool'; break; default: $this->types[$k] = 'text'; break; } break; case 'select': $this->types[$k] = 'list'; break; case 'textarea': $this->types[$k] = 'blob'; break; } } }

/** * Return array of braces used and the name, position, length and depth * See http://www.organicdesign.co.nz/MediaWiki_code_snippets */ 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['LENGTH'] = $match[0][1]-$brace['OFFSET']+2; $brace['DEPTH'] = $depth--; } else { $depths[$depth++] = count($braces); $braces[] = array( 'NAME' => $match[1][0], 'OFFSET' => $match[0][1] ); } } return $braces; }

}

/**

* Called from $wgExtensionFunctions array when initialising extensions
*/

function wfSetupRecordAdmin() { global $wgLanguageCode, $wgMessageCache;

# Add the messages used by the specialpage if ($wgLanguageCode == 'en') { $wgMessageCache->addMessages(array( 'recordadmin' => 'Record administration' )); }

# Add the specialpage to the environment SpecialPage::addPage(new SpecialRecordAdmin()); }