| Index: trunk/extensions/RecordAdmin/SpecialRecordAdmin.php |
| — | — | @@ -0,0 +1,481 @@ |
| | 2 | +<?php |
| | 3 | +/** |
| | 4 | + * Extension:RecordAdmin - MediaWiki extension |
| | 5 | + *{{Category:Extensions|RecordAdmin}}{{php}}{{Category:Extensions created with Template:SpecialPage}} |
| | 6 | + * @package MediaWiki |
| | 7 | + * @subpackage Extensions |
| | 8 | + * @author Aran Dunkley [http://www.organicdesign.co.nz/nad User:Nad] |
| | 9 | + * @licence GNU General Public Licence 2.0 or later |
| | 10 | + */ |
| | 11 | + |
| | 12 | +if (!defined('MEDIAWIKI')) die('Not an entry point.'); |
| | 13 | + |
| | 14 | +define('RECORDADMIN_VERSION','0.1.4, 2008-10-29'); |
| | 15 | + |
| | 16 | +$wgRecordAdminCategory = 'Records'; # Category which contains the templates used as records and having corresponding forms |
| | 17 | +$wgRecordAdminUseNamespaces = false; # Whether record articles should be in a namespace of the same name as their type |
| | 18 | + |
| | 19 | +$wgExtensionFunctions[] = 'wfSetupRecordAdmin'; |
| | 20 | + |
| | 21 | +$wgExtensionCredits['specialpage'][] = array( |
| | 22 | + 'name' => 'Record administration', |
| | 23 | + 'author' => '[http://www.organicdesign.co.nz/nad User:Nad]', |
| | 24 | + 'description' => 'A special page for finding and editing record articles using a form', |
| | 25 | + 'url' => 'http://www.organicdesign.co.nz/Extension:SpecialExample', |
| | 26 | + 'version' => RECORDADMIN_VERSION |
| | 27 | +); |
| | 28 | + |
| | 29 | +require_once "$IP/includes/SpecialPage.php"; |
| | 30 | + |
| | 31 | +/** |
| | 32 | + * Define a new class based on the SpecialPage class |
| | 33 | + */ |
| | 34 | +class SpecialRecordAdmin extends SpecialPage { |
| | 35 | + |
| | 36 | + var $form = ''; |
| | 37 | + var $types = array(); |
| | 38 | + var $guid = ''; |
| | 39 | + |
| | 40 | + function __construct() { |
| | 41 | + |
| | 42 | + # Name to use for creating a new record either via RecordAdmin or a public form |
| | 43 | + # todo: should add a hook here for custom default-naming |
| | 44 | + $this->guid = strftime('%Y%m%d', time()).'-'.substr(strtoupper(uniqid()), -5); |
| | 45 | + |
| | 46 | + SpecialPage::SpecialPage( |
| | 47 | + 'RecordAdmin', # name as seen in links etc |
| | 48 | + 'sysop', # user rights required |
| | 49 | + true, # listed in special:specialpages |
| | 50 | + false, # function called by execute() - defaults to wfSpecial{$name} |
| | 51 | + false, # file included by execute() - defaults to Special{$name}.php, only used if no function |
| | 52 | + false # includable |
| | 53 | + ); |
| | 54 | + } |
| | 55 | + |
| | 56 | + /** |
| | 57 | + * Override SpecialPage::execute() |
| | 58 | + */ |
| | 59 | + function execute($param) { |
| | 60 | + global $wgOut, $wgRequest, $wgRecordAdminCategory, $wgRecordAdminUseNamespaces; |
| | 61 | + $this->setHeaders(); |
| | 62 | + $type = $wgRequest->getText('wpType') or $type = $param; |
| | 63 | + $record = $wgRequest->getText('wpRecord'); |
| | 64 | + $invert = $wgRequest->getText('wpInvert'); |
| | 65 | + $title = Title::makeTitle(NS_SPECIAL, 'RecordAdmin'); |
| | 66 | + $wpTitle = trim($wgRequest->getText('wpTitle')); |
| | 67 | + |
| | 68 | + if ($type && $wgRecordAdminUseNamespaces) { |
| | 69 | + if ($wpTitle && !ereg("^$type:.+$", $wpTitle)) $wpTitle = "$type:$wpTitle"; |
| | 70 | + } |
| | 71 | + |
| | 72 | + $wgOut->addHTML("<div class='center'><a href='".$title->getLocalURL()."/$type'>New $type search</a> | " |
| | 73 | + . "<a href='".$title->getLocalURL()."'>Select another record type</a></div><br>\n" |
| | 74 | + ); |
| | 75 | + |
| | 76 | + # Get posted form values if any |
| | 77 | + $posted = array(); |
| | 78 | + foreach ($_POST as $k => $v) if (ereg('^ra_(.+)$', $k, $m)) $posted[$m[1]] = $v; |
| | 79 | + |
| | 80 | + # Read in and prepare the form for this record type if one has been selected |
| | 81 | + if ($type) $this->preProcessForm($type); |
| | 82 | + |
| | 83 | + # Extract the input names and types used in the form |
| | 84 | + $this->examineForm(); |
| | 85 | + |
| | 86 | + # Clear any default values |
| | 87 | + $this->populateForm(array()); |
| | 88 | + |
| | 89 | + # If no type selected, render select list of record types from Category:Records |
| | 90 | + if (empty($type)) { |
| | 91 | + $wgOut->addWikiText("== Select the type of record to search for ==\n"); |
| | 92 | + |
| | 93 | + # Get titles in Category:Records and build option list |
| | 94 | + $options = ''; |
| | 95 | + $dbr = &wfGetDB(DB_SLAVE); |
| | 96 | + $cl = $dbr->tableName('categorylinks'); |
| | 97 | + $cat = $dbr->addQuotes($wgRecordAdminCategory); |
| | 98 | + $res = $dbr->select($cl, 'cl_from', "cl_to = $cat", __METHOD__, array('ORDER BY' => 'cl_sortkey')); |
| | 99 | + while ($row = $dbr->fetchRow($res)) $options .= '<option>'.Title::newFromID($row[0])->getText().'</option>'; |
| | 100 | + |
| | 101 | + # Render type-selecting form |
| | 102 | + $wgOut->addHTML(wfElement('form', array('action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null) |
| | 103 | + . "<select name='wpType'>$options</select> " |
| | 104 | + . wfElement('input', array('type' => 'submit', 'value' => 'Submit')) |
| | 105 | + . '</form>' |
| | 106 | + ); |
| | 107 | + } |
| | 108 | + |
| | 109 | + # Record type known, but no record selected, render form for searching or creating |
| | 110 | + elseif (empty($record)) { |
| | 111 | + $wgOut->addWikiText("== Find or Create a \"$type\" record ==\n"); |
| | 112 | + |
| | 113 | + # Process Create submission |
| | 114 | + if (count($posted) && $wgRequest->getText('wpCreate')) { |
| | 115 | + if (empty($wpTitle)) { |
| | 116 | + $wpTitle = $this->guid; |
| | 117 | + if ($wgRecordAdminUseNamespaces) $wpTitle = "$type:$wpTitle"; |
| | 118 | + } |
| | 119 | + $t = Title::newFromText($wpTitle); |
| | 120 | + if (is_object($t)) { |
| | 121 | + if ($t->exists()) $wgOut->addHTML("<div class='errorbox'>Sorry, \"$wpTitle\" already exists!</div>\n"); |
| | 122 | + else { |
| | 123 | + |
| | 124 | + # Attempt to create the article |
| | 125 | + $article = new Article($t); |
| | 126 | + $summary = "[[Special:RecordAdmin/$type|RecordAdmin]]: New $type created"; |
| | 127 | + $text = ''; |
| | 128 | + foreach ($posted as $k => $v) if ($v) { |
| | 129 | + if ($this->types[$k] == 'bool') $v = 'yes'; |
| | 130 | + $text .= "| $k = $v\n"; |
| | 131 | + } |
| | 132 | + $text = $text ? "{{"."$type\n$text}}" : "{{"."$type}}"; |
| | 133 | + $success = $article->doEdit($text, $summary, EDIT_NEW); |
| | 134 | + |
| | 135 | + # Report success or error |
| | 136 | + if ($success) $wgOut->addHTML("<div class='successbox'>\"$wpTitle\" created successfully</div>\n"); |
| | 137 | + else $wgOut->addHTML("<div class='errorbox'>An error occurred while attempting to create the $type!</div>\n"); |
| | 138 | + } |
| | 139 | + } else $wgOut->addHTML("<div class='errorbox'>Bad title!</div>\n"); |
| | 140 | + $wgOut->addHTML("<br><br><br><br>\n"); |
| | 141 | + } |
| | 142 | + |
| | 143 | + # Populate the search form with any posted values |
| | 144 | + $this->populateForm($posted); |
| | 145 | + |
| | 146 | + # Render the form |
| | 147 | + $wgOut->addHTML( |
| | 148 | + wfElement('form', array('class' => 'recordadmin', 'action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null) |
| | 149 | + .'<b>Record ID:</b> '.wfElement('input', array('name' => 'wpTitle', 'size' => 30, 'value' => $wpTitle)) |
| | 150 | + .' '.wfElement('input', array('name' => 'wpInvert', 'type' => 'checkbox')).' Invert selection' |
| | 151 | + ."\n<br><br><hr><br>\n{$this->form}" |
| | 152 | + .wfElement('input', array('type' => 'hidden', 'name' => 'wpType', 'value' => $type)) |
| | 153 | + .'<br><hr><br><table width="100%"><tr>' |
| | 154 | + .'<td>'.wfElement('input', array('type' => 'submit', 'name' => 'wpFind', 'value' => "Search")).'</td>' |
| | 155 | + .'<td>'.wfElement('input', array('type' => 'submit', 'name' => 'wpCreate', 'value' => "Create")).'</td>' |
| | 156 | + .'<td width="100%" align="left">'.wfElement('input', array('type' => 'reset', 'value' => "Reset")).'</td>' |
| | 157 | + .'</tr></table></form>' |
| | 158 | + ); |
| | 159 | + |
| | 160 | + # Process Find submission |
| | 161 | + if (count($posted) && $wgRequest->getText('wpFind')) { |
| | 162 | + $wgOut->addWikiText("<br>\n== Search results ==\n"); |
| | 163 | + |
| | 164 | + # Select records which use the template and exhibit a matching title and other fields |
| | 165 | + $records = array(); |
| | 166 | + $dbr = &wfGetDB(DB_SLAVE); |
| | 167 | + $tbl = $dbr->tableName('templatelinks'); |
| | 168 | + $ty = $dbr->addQuotes($type); |
| | 169 | + $res = $dbr->select($tbl, 'tl_from', "tl_namespace = 10 AND tl_title = $ty", __METHOD__); |
| | 170 | + while ($row = $dbr->fetchRow($res)) { |
| | 171 | + $t = Title::newFromID($row[0]); |
| | 172 | + if (empty($wpTitle) || eregi($wpTitle, $t->getPrefixedText())) { |
| | 173 | + $a = new Article($t); |
| | 174 | + $text = $a->getContent(); |
| | 175 | + $match = true; |
| | 176 | + $r = array($t); |
| | 177 | + foreach (array_keys($this->types) as $k) { |
| | 178 | + $v = isset($posted[$k]) ? ($this->types[$k] == 'bool' ? 'yes' : $posted[$k]) : ''; |
| | 179 | + $i = preg_match("|\s*\|\s*$k\s*=\s*(.*?)\s*(?=[\|\}])|si", $text, $m); |
| | 180 | + if ($v && !($i && eregi($v, $m[1]))) $match = false; |
| | 181 | + $r[$k] = isset($m[1]) ? $m[1] : ''; |
| | 182 | + } |
| | 183 | + if ($invert) $match = !$match; |
| | 184 | + if ($match) $records[$t->getPrefixedText()] = $r; |
| | 185 | + } |
| | 186 | + } |
| | 187 | + $dbr->freeResult($res); |
| | 188 | + |
| | 189 | + # Render search results |
| | 190 | + if (count($records)) { |
| | 191 | + |
| | 192 | + # Pass1, scan the records to find the create date of each and sort by that |
| | 193 | + $sorted = array(); |
| | 194 | + foreach ($records as $k => $r) { |
| | 195 | + $t = $r[0]; |
| | 196 | + $id = $t->getArticleID(); |
| | 197 | + $r[1] = $k; |
| | 198 | + $tbl = $dbr->tableName('revision'); |
| | 199 | + $row = $dbr->selectRow( |
| | 200 | + $tbl, |
| | 201 | + 'rev_timestamp', |
| | 202 | + "rev_page = $id", |
| | 203 | + __METHOD__, |
| | 204 | + array('ORDER BY' => 'rev_timestamp') |
| | 205 | + ); |
| | 206 | + $sorted[$row->rev_timestamp] = $r; |
| | 207 | + } |
| | 208 | + krsort($sorted); |
| | 209 | + |
| | 210 | + $table = "<table class='sortable recordadmin $type-record'>\n<tr> |
| | 211 | + <th class='col1'>$type<br></th><th class='col2'>Created<br></th>"; |
| | 212 | + foreach (array_keys($this->types) as $k) $table .= "<th class='col$k'>$k<br></th>"; |
| | 213 | + $table .= "</tr>\n"; |
| | 214 | + $stripe = ''; |
| | 215 | + foreach ($sorted as $ts => $r) { |
| | 216 | + $ts = preg_replace('|^..(..)(..)(..)(..)(..)..$|', '$3/$2/$1 $4:$5', $ts); |
| | 217 | + $t = $r[0]; |
| | 218 | + $k = $r[1]; |
| | 219 | + $stripe = $stripe ? '' : ' class="stripe"'; |
| | 220 | + $table .= "<tr$stripe><td class='col1'>(<a href='".$t->getLocalURL()."'>view</a>)"; |
| | 221 | + $table .= "(<a href='".$title->getLocalURL("wpType=$type&wpRecord=$k")."'>edit</a>)</td>\n"; |
| | 222 | + $table .= "<td class='col2'>$ts</td>\n"; |
| | 223 | + $i = 0; |
| | 224 | + foreach (array_keys($this->types) as $k) { |
| | 225 | + $v = isset($r[$k]) ? $r[$k] : ' '; |
| | 226 | + $table .= "<td class='col$k'>$v</td>"; |
| | 227 | + } |
| | 228 | + $table .= "</tr>\n"; |
| | 229 | + } |
| | 230 | + $table .= "</table>\n"; |
| | 231 | + $wgOut->addHTML($table); |
| | 232 | + } else $wgOut->addWikiText("''No matching records found!''\n"); |
| | 233 | + } |
| | 234 | + } |
| | 235 | + |
| | 236 | + # A specific record has been selected, render form for updating |
| | 237 | + else { |
| | 238 | + $wgOut->addWikiText("== Editing \"$record\" ==\n"); |
| | 239 | + $article = new Article(Title::newFromText($record)); |
| | 240 | + $text = $article->fetchContent(); |
| | 241 | + |
| | 242 | + # Update article if form posted |
| | 243 | + if (count($posted)) { |
| | 244 | + |
| | 245 | + # Get the location and length of the record braces to replace |
| | 246 | + foreach ($this->examineBraces($text) as $brace) if ($brace['NAME'] == $type) $braces = $brace; |
| | 247 | + |
| | 248 | + # Attempt to save the article |
| | 249 | + $summary = "[[Special:RecordAdmin/$type|RecordAdmin]]: $type properties updated"; |
| | 250 | + $replace = ''; |
| | 251 | + foreach ($posted as $k => $v) if ($v) { |
| | 252 | + if ($this->types[$k] == 'bool') $v = 'yes'; |
| | 253 | + $replace .= "| $k = $v\n"; |
| | 254 | + } |
| | 255 | + $replace = $replace ? "{{"."$type\n$replace}}" : "{{"."$type}}"; |
| | 256 | + $text = substr_replace($text, $replace, $braces['OFFSET'], $braces['LENGTH']); |
| | 257 | + $success = $article->doEdit($text, $summary, EDIT_UPDATE); |
| | 258 | + |
| | 259 | + # Report success or error |
| | 260 | + if ($success) $wgOut->addHTML("<div class='successbox'>$type updated successfully</div>\n"); |
| | 261 | + else $wgOut->addHTML("<div class='errorbox'>An error occurred during update!</div>\n"); |
| | 262 | + $wgOut->addHTML("<br><br><br><br>\n"); |
| | 263 | + } |
| | 264 | + |
| | 265 | + # Populate the form with the current values in the article |
| | 266 | + foreach ($this->examineBraces($text) as $brace) if ($brace['NAME'] == $type) $braces = $brace; |
| | 267 | + $this->populateForm(substr($text, $braces['OFFSET'], $braces['LENGTH'])); |
| | 268 | + |
| | 269 | + # Render the form |
| | 270 | + $wgOut->addHTML(wfElement('form', array('class' => 'recordadmin', 'action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null)); |
| | 271 | + $wgOut->addHTML($this->form); |
| | 272 | + $wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpType', 'value' => $type))); |
| | 273 | + $wgOut->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'wpRecord', 'value' => $record))); |
| | 274 | + $wgOut->addHTML('<br><hr><br><table width="100%"><tr>' |
| | 275 | + .'<td>'.wfElement('input', array('type' => 'submit', 'value' => "Save")).'</td>' |
| | 276 | + .'<td width="100%" align="left">'.wfElement('input', array('type' => 'reset', 'value' => "Reset")).'</td>' |
| | 277 | + .'</tr></table></form>' |
| | 278 | + ); |
| | 279 | + } |
| | 280 | + } |
| | 281 | + |
| | 282 | + /** |
| | 283 | + * Read in and prepare the form (for use as a search filter) for passed record type |
| | 284 | + * - we're using the record's own form as a filter for searching for records |
| | 285 | + * - extract only the content from between the form tags and remove any submit inputs |
| | 286 | + */ |
| | 287 | + function preProcessForm($type) { |
| | 288 | + $title = Title::newFromText($type, NS_FORM); |
| | 289 | + if ($title->exists()) { |
| | 290 | + $form = new Article($title); |
| | 291 | + $form = $form->getContent(); |
| | 292 | + $form = preg_replace('#<input.+?type=[\'"]?submit["\']?.+?/(input| *)>#', '', $form); # remove submits |
| | 293 | + $form = preg_replace('#^.+?<form.+?>#s', '', $form); # remove up to and including form open |
| | 294 | + $form = preg_replace('#</form>.+?$#s', '', $form); # remove form close and everything after |
| | 295 | + $form = preg_replace('#name\s*=\s*([\'"])(.*?)\\1#s', 'name="ra_$2"', $form); # prefix input names with ra_ |
| | 296 | + $form = preg_replace('#(<select.+?>)\s*(?!<option/>)#s', '$1<option selected/>', $form); # ensure all select lists have default blank |
| | 297 | + } |
| | 298 | + |
| | 299 | + # Create a red link to the form if it doesn't exist |
| | 300 | + else { |
| | 301 | + $form = "<b>There is no form associated with \"$type\" records!</b>" |
| | 302 | + ."<br><br>click <a href=\"".$title->getLocalURL('action=edit')."\">here</a> to create one</div>"; |
| | 303 | + } |
| | 304 | + $this->form = $form; |
| | 305 | + } |
| | 306 | + |
| | 307 | + |
| | 308 | + /** |
| | 309 | + * Populates the form values from the passed values |
| | 310 | + * - $form is HTML text |
| | 311 | + * - $values may be a hash or wikitext template syntax |
| | 312 | + */ |
| | 313 | + function populateForm($values) { |
| | 314 | + |
| | 315 | + # If values are wikitext, convert to hash |
| | 316 | + if (!is_array($values)) { |
| | 317 | + $text = $values; |
| | 318 | + $values = array(); |
| | 319 | + preg_match_all("|\|\s*(.+?)\s*=\s*(.*?)\s*(?=[\|\}])|s", $text, $m); |
| | 320 | + foreach ($m[1] as $i => $k) $values[$k] = $m[2][$i]; |
| | 321 | + } |
| | 322 | + |
| | 323 | + # Add the values into the form's HTML depending on their type |
| | 324 | + foreach($this->types as $k => $type) { |
| | 325 | + |
| | 326 | + # Get this input element's html text and position and length |
| | 327 | + preg_match("|<([a-zA-Z]+)[^<]+?name=\"ra_$k\".*?>(.*?</\\1>)?|s", $this->form, $m, PREG_OFFSET_CAPTURE); |
| | 328 | + list($html, $pos) = $m[0]; |
| | 329 | + $len = strlen($html); |
| | 330 | + |
| | 331 | + # Modify the element according to its type |
| | 332 | + # - clears default value, then adds new value |
| | 333 | + $v = isset($values[$k]) ? $values[$k] : ''; |
| | 334 | + switch ($type) { |
| | 335 | + case 'text': |
| | 336 | + $html = preg_replace("|value\s*=\s*\".*?\"|", "", $html); |
| | 337 | + if ($v) $html = preg_replace("|(/?>)$|", " value=\"$v\" $1", $html); |
| | 338 | + break; |
| | 339 | + case 'bool': |
| | 340 | + $html = preg_replace("|checked|", "", $html); |
| | 341 | + if ($v) $html = preg_replace("|(/?>)$|", " checked $1", $html); |
| | 342 | + break; |
| | 343 | + case 'list': |
| | 344 | + $html = preg_replace("|(<option[^<>]*) selected|", "$1", $html); |
| | 345 | + if ($v) $html = preg_replace("|(?<=<option)(?=>$v</option>)|s", " selected", $html); |
| | 346 | + break; |
| | 347 | + case 'blob': |
| | 348 | + $html = preg_replace("|>.*?(?=</textarea>)|s", ">$v", $html); |
| | 349 | + break; |
| | 350 | + } |
| | 351 | + |
| | 352 | + # Replace the element in the form with the modified html |
| | 353 | + $this->form = substr_replace($this->form, $html, $pos, $len); |
| | 354 | + } |
| | 355 | + } |
| | 356 | + |
| | 357 | + /** |
| | 358 | + * Returns an array of types used by the passed HTML text form |
| | 359 | + * - supported types, text, select, checkbox, textarea |
| | 360 | + */ |
| | 361 | + function examineForm() { |
| | 362 | + $this->types = array(); |
| | 363 | + preg_match_all("|<([a-zA-Z]+)[^<]+?name=\"ra_(.+?)\".*?>|", $this->form, $m); |
| | 364 | + foreach ($m[2] as $i => $k) { |
| | 365 | + $tag = $m[1][$i]; |
| | 366 | + $type = preg_match("|type\s*=\s*\"(.+?)\"|", $m[0][$i], $n) ? $n[1] : ''; |
| | 367 | + switch ($tag) { |
| | 368 | + case 'input': |
| | 369 | + switch ($type) { |
| | 370 | + case 'checkbox': |
| | 371 | + $this->types[$k] = 'bool'; |
| | 372 | + break; |
| | 373 | + default: |
| | 374 | + $this->types[$k] = 'text'; |
| | 375 | + break; |
| | 376 | + } |
| | 377 | + break; |
| | 378 | + case 'select': |
| | 379 | + $this->types[$k] = 'list'; |
| | 380 | + break; |
| | 381 | + case 'textarea': |
| | 382 | + $this->types[$k] = 'blob'; |
| | 383 | + break; |
| | 384 | + } |
| | 385 | + } |
| | 386 | + } |
| | 387 | + |
| | 388 | + /** |
| | 389 | + * Return array of braces used and the name, position, length and depth |
| | 390 | + * See http://www.organicdesign.co.nz/MediaWiki_code_snippets |
| | 391 | + */ |
| | 392 | + function examineBraces(&$content) { |
| | 393 | + $braces = array(); |
| | 394 | + $depths = array(); |
| | 395 | + $depth = 1; |
| | 396 | + $index = 0; |
| | 397 | + while (preg_match('/\\{\\{\\s*([#a-z0-9_]*)|\\}\\}/is', $content, $match, PREG_OFFSET_CAPTURE, $index)) { |
| | 398 | + $index = $match[0][1]+2; |
| | 399 | + if ($match[0][0] == '}}') { |
| | 400 | + $brace =& $braces[$depths[$depth-1]]; |
| | 401 | + $brace['LENGTH'] = $match[0][1]-$brace['OFFSET']+2; |
| | 402 | + $brace['DEPTH'] = $depth--; |
| | 403 | + } |
| | 404 | + else { |
| | 405 | + $depths[$depth++] = count($braces); |
| | 406 | + $braces[] = array( |
| | 407 | + 'NAME' => $match[1][0], |
| | 408 | + 'OFFSET' => $match[0][1] |
| | 409 | + ); |
| | 410 | + } |
| | 411 | + } |
| | 412 | + return $braces; |
| | 413 | + } |
| | 414 | + |
| | 415 | + /** |
| | 416 | + * A callback for processing public forms |
| | 417 | + */ |
| | 418 | + function createRecord() { |
| | 419 | + global $wgRequest, $wgRecordAdminUseNamespaces; |
| | 420 | + $type = $wgRequest->getText('wpType'); |
| | 421 | + $title = $wgRequest->getText('wpTitle'); |
| | 422 | + |
| | 423 | + # Get types in this kind of record from form |
| | 424 | + $this->preProcessForm($type); |
| | 425 | + $this->examineForm(); |
| | 426 | + |
| | 427 | + # Use guid if no title specified |
| | 428 | + if (empty($title)) { |
| | 429 | + $title = $this->guid; |
| | 430 | + if ($wgRecordAdminUseNamespaces) $title = "$type:$title"; |
| | 431 | + } |
| | 432 | + |
| | 433 | + # Attempt to create the article |
| | 434 | + $title = Title::newFromText($title); |
| | 435 | + if (is_object($title) && !$title->exists()) { |
| | 436 | + $article = new Article($title); |
| | 437 | + $summary = "New $type created from public form"; |
| | 438 | + $text = ''; |
| | 439 | + foreach ($_POST as $k => $v) if ($v && isset($this->types[$k])) { |
| | 440 | + if ($this->types[$k] == 'bool') $v = 'yes'; |
| | 441 | + $text .= "| $k = $v\n"; |
| | 442 | + } |
| | 443 | + $text = $text ? "{{"."$type\n$text}}" : "{{"."$type}}"; |
| | 444 | + $success = $article->doEdit($text, $summary, EDIT_NEW); |
| | 445 | + } |
| | 446 | + } |
| | 447 | + |
| | 448 | + # If a record was created by a public form, make last 5 digits of ID available via a tag |
| | 449 | + function expandTag($text, $argv, &$parser) { |
| | 450 | + $parser->mOutput->mCacheTime = -1; |
| | 451 | + return $this->guid ? substr($this->guid, -5) : ''; |
| | 452 | + } |
| | 453 | + |
| | 454 | +} |
| | 455 | + |
| | 456 | +/** |
| | 457 | + * Called from $wgExtensionFunctions array when initialising extensions |
| | 458 | + */ |
| | 459 | +function wfSetupRecordAdmin() { |
| | 460 | + global $wgSpecialRecordAdmin, $wgParser, $wgLanguageCode, $wgMessageCache, $wgRequest; |
| | 461 | + |
| | 462 | + # Add the messages used by the specialpage |
| | 463 | + if ($wgLanguageCode == 'en') { |
| | 464 | + $wgMessageCache->addMessages(array( |
| | 465 | + 'recordadmin' => 'Record administration' |
| | 466 | + )); |
| | 467 | + } |
| | 468 | + |
| | 469 | + # Make a global singleton so methods are accessible as callbacks etc |
| | 470 | + $wgSpecialRecordAdmin = new SpecialRecordAdmin(); |
| | 471 | + |
| | 472 | + # Make recordID's of articles created with public forms available via recordid tag |
| | 473 | + $wgParser->setHook('recordid', array($wgSpecialRecordAdmin, 'expandTag')); |
| | 474 | + |
| | 475 | + # Check if posting a public creation form |
| | 476 | + $title = Title::newFromText($wgRequest->getText('title')); |
| | 477 | + if (is_object($title) && $title->getNamespace() != NS_SPECIAL && $wgRequest->getText('wpType') && $wgRequest->getText('wpCreate')) |
| | 478 | + $wgSpecialRecordAdmin->createRecord(); |
| | 479 | + |
| | 480 | + # Add the specialpage to the environment |
| | 481 | + SpecialPage::addPage($wgSpecialRecordAdmin); |
| | 482 | +} |