r42950 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r42949‎ | r42950 | r42951 >
Date:22:03, 31 October 2008
Author:siebrand
Status:old
Tags:
Comment:
* rename SpecialRecordAdmin* to RecordAdmin*, update paths
* make the Records category configurable in MediaWiki:Recordadmin-category instead of in LocalSettings.php
* update some messages
* remove trailing spaces
* update Translate config file
Modified paths:
  • /trunk/extensions/RecordAdmin/RecordAdmin.18n.php (added) (history)
  • /trunk/extensions/RecordAdmin/RecordAdmin.php (added) (history)
  • /trunk/extensions/RecordAdmin/SpecialRecordAdmin.18n.php (deleted) (history)
  • /trunk/extensions/RecordAdmin/SpecialRecordAdmin.php (deleted) (history)
  • /trunk/extensions/Translate/groups/mediawiki-defines.txt (modified) (history)

Diff [purge]

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

Status & tagging log