Fix bug 2642 : watchdetails message using HTML instead of wiki syntax. Patch by zigge...
[lhc/web/wiklou.git] / includes / SpecialImport.php
1 <?php
2 /**
3 * MediaWiki page data importer
4 * Copyright (C) 2003,2005 Brion Vibber <brion@pobox.com>
5 * http://www.mediawiki.org/
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * http://www.gnu.org/copyleft/gpl.html
21 *
22 * @package MediaWiki
23 * @subpackage SpecialPage
24 */
25
26 require_once( 'WikiError.php' );
27
28 /**
29 * Constructor
30 */
31 function wfSpecialImport( $page = '' ) {
32 global $wgUser, $wgOut, $wgLang, $wgRequest, $wgTitle;
33 global $wgImportSources;
34
35 ###
36 # $wgOut->addWikiText( "Special:Import is not ready for this beta release, sorry." );
37 # return;
38 ###
39
40 if( $wgRequest->wasPosted() && $wgRequest->getVal( 'action' ) == 'submit') {
41 $importer = new WikiImporter();
42
43 switch( $wgRequest->getVal( "source" ) ) {
44 case "upload":
45 if( $wgUser->isAllowed( 'importupload' ) ) {
46 $result = $importer->setupFromUpload( "xmlimport" );
47 } else {
48 return $wgOut->permissionRequired( 'importupload' );
49 }
50 break;
51 case "interwiki":
52 $result = $importer->setupFromInterwiki(
53 $wgRequest->getVal( "interwiki" ),
54 $wgRequest->getText( "frompage" ) );
55 break;
56 default:
57 $result = new WikiError( "Unknown import source type" );
58 }
59
60 if( WikiError::isError( $result ) ) {
61 $wgOut->addWikiText( wfEscapeWikiText( $result->getMessage() ) );
62 } else {
63 $importer->setRevisionHandler( "wfImportOldRevision" );
64 $result = $importer->doImport();
65 if( WikiError::isError( $result ) ) {
66 $wgOut->addHTML( "<p>" . wfMsg( "importfailed",
67 htmlspecialchars( $result->getMessage() ) ) . "</p>" );
68 } else {
69 # Success!
70 $wgOut->addHTML( "<p>" . wfMsg( "importsuccess" ) . "</p>" );
71 }
72 }
73 }
74
75 $action = $wgTitle->escapeLocalUrl( 'action=submit' );
76
77 if( $wgUser->isAllowed( 'importupload' ) ) {
78 $wgOut->addWikiText( wfMsg( "importtext" ) );
79 $wgOut->addHTML( "
80 <fieldset>
81 <legend>" . wfMsg('upload') . "</legend>
82 <form enctype='multipart/form-data' method='post' action=\"$action\">
83 <input type='hidden' name='action' value='submit' />
84 <input type='hidden' name='source' value='upload' />
85 <input type='hidden' name='MAX_FILE_SIZE' value='2000000' />
86 <input type='file' name='xmlimport' value='' size='30' />
87 <input type='submit' value='" . htmlspecialchars( wfMsg( "uploadbtn" ) ) . "'/>
88 </form>
89 </fieldset>
90 " );
91 } else {
92 if( empty( $wgImportSources ) ) {
93 $wgOut->addWikiText( wfMsg( 'importnosources' ) );
94 }
95 }
96
97 if( !empty( $wgImportSources ) ) {
98 $wgOut->addHTML( "
99 <fieldset>
100 <legend>" . wfMsg('importinterwiki') . "</legend>
101 <form method='post' action=\"$action\">
102 <input type='hidden' name='action' value='submit' />
103 <input type='hidden' name='source' value='interwiki' />
104 <select name='interwiki'>
105 " );
106 foreach( $wgImportSources as $interwiki ) {
107 $iw = htmlspecialchars( $interwiki );
108 $wgOut->addHTML( "<option value=\"$iw\">$iw</option>\n" );
109 }
110 $wgOut->addHTML( "
111 </select>
112 <input name='frompage' />
113 <input type='submit' />
114 </form>
115 </fieldset>
116 " );
117 }
118 }
119
120 function wfImportOldRevision( &$revision ) {
121 $dbw =& wfGetDB( DB_MASTER );
122 $dbw->deadlockLoop( array( &$revision, 'importOldRevision' ) );
123 }
124
125 /**
126 *
127 * @package MediaWiki
128 * @subpackage SpecialPage
129 */
130 class WikiRevision {
131 var $title = NULL;
132 var $timestamp = "20010115000000";
133 var $user = 0;
134 var $user_text = "";
135 var $text = "";
136 var $comment = "";
137
138 function setTitle( $text ) {
139 $this->title = Title::newFromText( $text );
140 }
141
142 function setTimestamp( $ts ) {
143 # 2003-08-05T18:30:02Z
144 $this->timestamp = preg_replace( '/^(....)-(..)-(..)T(..):(..):(..)Z$/', '$1$2$3$4$5$6', $ts );
145 }
146
147 function setUsername( $user ) {
148 $this->user_text = $user;
149 }
150
151 function setUserIP( $ip ) {
152 $this->user_text = $ip;
153 }
154
155 function setText( $text ) {
156 $this->text = $text;
157 }
158
159 function setComment( $text ) {
160 $this->comment = $text;
161 }
162
163 function getTitle() {
164 return $this->title;
165 }
166
167 function getTimestamp() {
168 return $this->timestamp;
169 }
170
171 function getUser() {
172 return $this->user_text;
173 }
174
175 function getText() {
176 return $this->text;
177 }
178
179 function getComment() {
180 return $this->comment;
181 }
182
183 function importOldRevision() {
184 $fname = "WikiImporter::importOldRevision";
185 $dbw =& wfGetDB( DB_MASTER );
186
187 # Sneak a single revision into place
188 $user = User::newFromName( $this->getUser() );
189
190 $article = new Article( $this->title );
191 $pageId = $article->getId();
192 if( $pageId == 0 ) {
193 # must create the page...
194 $pageId = $article->insertOn( $dbw );
195 }
196
197 # FIXME: Check for exact conflicts
198 # FIXME: Use original rev_id optionally
199 # FIXME: blah blah blah
200
201 #if( $numrows > 0 ) {
202 # return wfMsg( "importhistoryconflict" );
203 #}
204
205 # Insert the row
206 $revision = new Revision( array(
207 'page' => $pageId,
208 'text' => $this->getText(),
209 'comment' => $this->getComment(),
210 'user' => IntVal( $user->getId() ),
211 'user_text' => $user->getName(),
212 'timestamp' => $this->timestamp,
213 'minor_edit' => 0
214 ) );
215 $revId = $revision->insertOn( $dbw );
216 $article->updateIfNewerOn( $dbw, $revision );
217
218 return true;
219 }
220
221 }
222
223 /**
224 *
225 * @package MediaWiki
226 * @subpackage SpecialPage
227 */
228 class WikiImporter {
229 var $mSource = NULL;
230 var $mRevisionHandler = NULL;
231 var $lastfield;
232
233 function WikiImporter() {
234 $this->setRevisionHandler( array( &$this, "defaultRevisionHandler" ) );
235 }
236
237 function throwXmlError( $err ) {
238 $this->debug( "FAILURE: $err" );
239 }
240
241 function setupFromFile( $filename ) {
242 $this->mSource = @file_get_contents( $filename );
243 if( $this->mSource === false ) {
244 return new WikiError( "Couldn't open import file" );
245 }
246 return true;
247 }
248
249 function setupFromUpload( $fieldname = "xmlimport" ) {
250 global $wgOut;
251
252 $upload =& $_FILES[$fieldname];
253
254 if( !isset( $upload ) ) {
255 return new WikiErrorMsg( 'importnofile' );
256 }
257 if( !empty( $upload['error'] ) ) {
258 return new WikiErrorMsg( 'importuploaderror', $upload['error'] );
259 }
260 $fname = $upload['tmp_name'];
261 if( is_uploaded_file( $fname ) ) {
262 return $this->setupFromFile( $fname );
263 } else {
264 return new WikiErrorMsg( 'importnofile' );
265 }
266 }
267
268 function setupFromURL( $url ) {
269 # fopen-wrappers are normally turned off for security.
270 ini_set( "allow_url_fopen", true );
271 $ret = $this->setupFromFile( $url );
272 ini_set( "allow_url_fopen", false );
273 return $ret;
274 }
275
276 function setupFromInterwiki( $interwiki, $page ) {
277 $base = Title::getInterwikiLink( $interwiki );
278 if( empty( $base ) ) {
279 return new WikiError( 'Bad interwiki link' );
280 } else {
281 $import = wfUrlencode( "Special:Export/$page" );
282 $url = str_replace( "$1", $import, $base );
283 $this->notice( "Importing from $url" );
284 return $this->setupFromURL( $url );
285 }
286 }
287
288 # --------------
289
290 function doImport() {
291 if( empty( $this->mSource ) ) {
292 return new WikiErrorMsg( "importnotext" );
293 }
294
295 $parser = xml_parser_create( "UTF-8" );
296
297 # case folding violates XML standard, turn it off
298 xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
299
300 xml_set_object( $parser, &$this );
301 xml_set_element_handler( $parser, "in_start", "" );
302
303 if( !xml_parse( $parser, $this->mSource, true ) ) {
304 return new WikiXmlError( $parser );
305 }
306 xml_parser_free( $parser );
307
308 return true;
309 }
310
311 function debug( $data ) {
312 #$this->notice( "DEBUG: $data\n" );
313 }
314
315 function notice( $data ) {
316 global $wgCommandLineMode;
317 if( $wgCommandLineMode ) {
318 print "$data\n";
319 } else {
320 global $wgOut;
321 $wgOut->addHTML( "<li>$data</li>\n" );
322 }
323 }
324
325 function setRevisionHandler( $functionref ) {
326 $this->mRevisionHandler = $functionref;
327 }
328
329 function defaultRevisionHandler( &$revision ) {
330 $this->debug( "Got revision:" );
331 if( is_object( $revision->title ) ) {
332 $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
333 } else {
334 $this->debug( "-- Title: <invalid>" );
335 }
336 $this->debug( "-- User: " . $revision->user_text );
337 $this->debug( "-- Timestamp: " . $revision->timestamp );
338 $this->debug( "-- Comment: " . $revision->comment );
339 $this->debug( "-- Text: " . $revision->text );
340 }
341
342
343
344 # XML parser callbacks from here out -- beware!
345 function donothing( $parser, $x, $y="" ) {
346 #$this->debug( "donothing" );
347 }
348
349 function in_start( $parser, $name, $attribs ) {
350 $this->debug( "in_start $name" );
351 if( $name != "mediawiki" ) {
352 return $this->throwXMLerror( "Expected <mediawiki>, got <$name>" );
353 }
354 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
355 }
356
357 function in_mediawiki( $parser, $name, $attribs ) {
358 $this->debug( "in_mediawiki $name" );
359 if( $name != "page" ) {
360 return $this->throwXMLerror( "Expected <page>, got <$name>" );
361 }
362 xml_set_element_handler( $parser, "in_page", "out_page" );
363 }
364 function out_mediawiki( $parser, $name ) {
365 $this->debug( "out_mediawiki $name" );
366 if( $name != "mediawiki" ) {
367 return $this->throwXMLerror( "Expected </mediawiki>, got </$name>" );
368 }
369 xml_set_element_handler( $parser, "donothing", "donothing" );
370 }
371
372 function in_page( $parser, $name, $attribs ) {
373 $this->debug( "in_page $name" );
374 switch( $name ) {
375 case "id":
376 case "title":
377 case "restrictions":
378 $this->appendfield = $name;
379 $this->appenddata = "";
380 $this->parenttag = "page";
381 xml_set_element_handler( $parser, "in_nothing", "out_append" );
382 xml_set_character_data_handler( $parser, "char_append" );
383 break;
384 case "revision":
385 $this->workRevision = new WikiRevision;
386 $this->workRevision->setTitle( $this->workTitle );
387 xml_set_element_handler( $parser, "in_revision", "out_revision" );
388 break;
389 default:
390 return $this->throwXMLerror( "Element <$name> not allowed in a <page>." );
391 }
392 }
393
394 function out_page( $parser, $name ) {
395 $this->debug( "out_page $name" );
396 if( $name != "page" ) {
397 return $this->throwXMLerror( "Expected </page>, got </$name>" );
398 }
399 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
400
401 $this->workTitle = NULL;
402 $this->workRevision = NULL;
403 }
404
405 function in_nothing( $parser, $name, $attribs ) {
406 $this->debug( "in_nothing $name" );
407 return $this->throwXMLerror( "No child elements allowed here; got <$name>" );
408 }
409 function char_append( $parser, $data ) {
410 $this->debug( "char_append '$data'" );
411 $this->appenddata .= $data;
412 }
413 function out_append( $parser, $name ) {
414 $this->debug( "out_append $name" );
415 if( $name != $this->appendfield ) {
416 return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" );
417 }
418 xml_set_element_handler( $parser, "in_$this->parenttag", "out_$this->parenttag" );
419 xml_set_character_data_handler( $parser, "donothing" );
420 switch( $this->appendfield ) {
421 case "title":
422 $this->workTitle = $this->appenddata;
423 break;
424 case "text":
425 $this->workRevision->setText( $this->appenddata );
426 break;
427 case "username":
428 $this->workRevision->setUsername( $this->appenddata );
429 break;
430 case "ip":
431 $this->workRevision->setUserIP( $this->appenddata );
432 break;
433 case "timestamp":
434 $this->workRevision->setTimestamp( $this->appenddata );
435 break;
436 case "comment":
437 $this->workRevision->setComment( $this->appenddata );
438 break;
439 default:
440 $this->debug( "Bad append: {$this->appendfield}" );
441 }
442 $this->appendfield = "";
443 $this->appenddata = "";
444 }
445
446 function in_revision( $parser, $name, $attribs ) {
447 $this->debug( "in_revision $name" );
448 switch( $name ) {
449 case "id":
450 case "timestamp":
451 case "comment":
452 case "text":
453 $this->parenttag = "revision";
454 $this->appendfield = $name;
455 xml_set_element_handler( $parser, "in_nothing", "out_append" );
456 xml_set_character_data_handler( $parser, "char_append" );
457 break;
458 case "contributor":
459 xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
460 break;
461 default:
462 return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
463 }
464 }
465
466 function out_revision( $parser, $name ) {
467 $this->debug( "out_revision $name" );
468 if( $name != "revision" ) {
469 return $this->throwXMLerror( "Expected </revision>, got </$name>" );
470 }
471 xml_set_element_handler( $parser, "in_page", "out_page" );
472
473 $out = call_user_func( $this->mRevisionHandler, &$this->workRevision, &$this );
474 if( !empty( $out ) ) {
475 global $wgOut;
476 $wgOut->addHTML( "<li>" . $out . "</li>\n" );
477 }
478 }
479
480 function in_contributor( $parser, $name, $attribs ) {
481 $this->debug( "in_contributor $name" );
482 switch( $name ) {
483 case "username":
484 case "ip":
485 $this->parenttag = "contributor";
486 $this->appendfield = $name;
487 xml_set_element_handler( $parser, "in_nothing", "out_append" );
488 xml_set_character_data_handler( $parser, "char_append" );
489 break;
490 default:
491 $this->throwXMLerror( "Invalid tag <$name> in <contributor>" );
492 }
493 }
494
495 function out_contributor( $parser, $name ) {
496 $this->debug( "out_contributor $name" );
497 if( $name != "contributor" ) {
498 return $this->throwXMLerror( "Expected </contributor>, got </$name>" );
499 }
500 xml_set_element_handler( $parser, "in_revision", "out_revision" );
501 }
502
503 }
504
505
506 ?>