Extension:RecordAdmin
<?php /**
* Extension:RecordAdmin - MediaWiki extension *
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("
\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("
\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("
\n"); else $wgOut->addHTML("
\n");
}
} else $wgOut->addHTML("
\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("
\n"); else $wgOut->addHTML("
\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()); }