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 | +} |