Difference between revisions of "Extension:RecordAdmin"
(add support for multiple input types and add css rules to hide cols) |
(make a guid title if none selected when creating from admin) |
||
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. | + | define('RECORDADMIN_VERSION','0.0.7, 2007-10-22'); |
$wgRecordAdminCategory = 'Records'; | $wgRecordAdminCategory = 'Records'; | ||
Line 51: | Line 51: | ||
$type = $wgRequest->getText('wpType') or $type = $param; | $type = $wgRequest->getText('wpType') or $type = $param; | ||
$record = $wgRequest->getText('wpRecord'); | $record = $wgRequest->getText('wpRecord'); | ||
+ | $invert = $wgRequest->getText('wpInvert'); | ||
$title = Title::makeTitle(NS_SPECIAL, 'RecordAdmin'); | $title = Title::makeTitle(NS_SPECIAL, 'RecordAdmin'); | ||
$wpTitle = trim($wgRequest->getText('wpTitle')); | $wpTitle = trim($wgRequest->getText('wpTitle')); | ||
Line 107: | Line 108: | ||
# Process Create submission | # Process Create submission | ||
if (count($posted) && $wgRequest->getText('wpCreate')) { | if (count($posted) && $wgRequest->getText('wpCreate')) { | ||
+ | if (empty($wpTitle)) $wpTitle = strftime('%Y%m%d',time()).'-'.substr(strtoupper(uniqid()),-5); | ||
$t = Title::newFromText($wpTitle); | $t = Title::newFromText($wpTitle); | ||
if (is_object($t)) { | if (is_object($t)) { | ||
Line 116: | Line 118: | ||
$summary = "[[Special:RecordAdmin/$type|RecordAdmin]]: New $type created"; | $summary = "[[Special:RecordAdmin/$type|RecordAdmin]]: New $type created"; | ||
$text = ''; | $text = ''; | ||
− | foreach ($posted as $k => $v) if ($v) $text .= "| $k = $v\n"; | + | foreach ($posted as $k => $v) if ($v) { |
+ | if ($this->types[$k] == 'bool') $v = 'yes'; | ||
+ | $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 134: | Line 139: | ||
$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('<b>Record ID:</b> '.wfElement('input', array('name' => 'wpTitle', 'size' => 30, 'value' => $wpTitle))); | $wgOut->addHTML('<b>Record ID:</b> '.wfElement('input', array('name' => 'wpTitle', 'size' => 30, 'value' => $wpTitle))); | ||
+ | $wgOut->addHTML(' '.wfElement('input', array('name' => 'wpInvert', 'type' => 'checkbox')).' Invert selection'); | ||
$wgOut->addHTML("\n<br><br><hr><br>\n"); | $wgOut->addHTML("\n<br><br><hr><br>\n"); | ||
$wgOut->addHTML($this->form); | $wgOut->addHTML($this->form); | ||
Line 162: | Line 168: | ||
$r = array($t); | $r = array($t); | ||
foreach (array_keys($this->types) as $k) { | foreach (array_keys($this->types) as $k) { | ||
− | $v = isset($posted[$k]) ? $posted[$k] : ''; | + | $v = isset($posted[$k]) ? ($this->types[$k] == 'bool' ? 'yes' : $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; | ||
$r[$k] = isset($m[1]) ? $m[1] : ''; | $r[$k] = isset($m[1]) ? $m[1] : ''; | ||
} | } | ||
+ | if ($invert) $match = !$match; | ||
if ($match) $records[$t->getPrefixedText()] = $r; | if ($match) $records[$t->getPrefixedText()] = $r; | ||
} | } | ||
Line 211: | Line 218: | ||
$summary = "[[Special:RecordAdmin/$type|RecordAdmin]]: $type properties updated"; | $summary = "[[Special:RecordAdmin/$type|RecordAdmin]]: $type properties updated"; | ||
$replace = ''; | $replace = ''; | ||
− | foreach ($posted as $k => $v) if ($v) $replace .= "| $k = $v\n"; | + | foreach ($posted as $k => $v) if ($v) { |
+ | if ($this->types[$k] == 'bool') $v = 'yes'; | ||
+ | $replace .= "| $k = $v\n"; | ||
+ | } | ||
$replace = $replace ? "{{"."$type\n$replace}}" : "{{"."$type}}"; | $replace = $replace ? "{{"."$type\n$replace}}" : "{{"."$type}}"; | ||
$text = substr_replace($text, $replace, $braces['OFFSET'], $braces['LENGTH']); | $text = substr_replace($text, $replace, $braces['OFFSET'], $braces['LENGTH']); |
Revision as of 11:27, 21 October 2008
<?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.7, 2007-10-22');
$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'); $invert = $wgRequest->getText('wpInvert'); $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')) { if (empty($wpTitle)) $wpTitle = strftime('%Y%m%d',time()).'-'.substr(strtoupper(uniqid()),-5); $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) { if ($this->types[$k] == 'bool') $v = 'yes'; $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(' '.wfElement('input', array('name' => 'wpInvert', 'type' => 'checkbox')).' Invert selection');
$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]) ? ($this->types[$k] == 'bool' ? 'yes' : $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 ($invert) $match = !$match; 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) { if ($this->types[$k] == 'bool') $v = 'yes'; $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()); }