00001 <?php 00002 # Copyright (C) 2003 Brion Vibber <brion@pobox.com> 00003 # http://www.mediawiki.org/ 00004 # 00005 # This program is free software; you can redistribute it and/or modify 00006 # it under the terms of the GNU General Public License as published by 00007 # the Free Software Foundation; either version 2 of the License, or 00008 # (at your option) any later version. 00009 # 00010 # This program is distributed in the hope that it will be useful, 00011 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 # GNU General Public License for more details. 00014 # 00015 # You should have received a copy of the GNU General Public License along 00016 # with this program; if not, write to the Free Software Foundation, Inc., 00017 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00018 # http://www.gnu.org/copyleft/gpl.html 00019 00020 function wfSpecialImport( $page = "" ) { 00021 global $wgOut, $wgLang, $wgRequest, $wgTitle; 00022 global $wgImportSources; 00023 00024 ### 00025 $wgOut->addWikiText( "Special:Import is not ready for this beta release, sorry." ); 00026 return; 00027 ### 00028 00029 if( $wgRequest->wasPosted() && $wgRequest->getVal( 'action' ) == 'submit') { 00030 $importer = new WikiImporter(); 00031 00032 switch( $wgRequest->getVal( "source" ) ) { 00033 case "upload": 00034 $ok = $importer->setupFromUpload( "xmlimport" ); 00035 break; 00036 case "interwiki": 00037 $ok = $importer->setupFromInterwiki( 00038 $wgRequest->getVal( "interwiki" ), 00039 $wgRequest->getText( "frompage" ) ); 00040 break; 00041 default: 00042 $ok = false; 00043 } 00044 00045 if( $ok ) { 00046 $importer->setRevisionHandler( "wfImportOldRevision" ); 00047 if( $importer->doImport() ) { 00048 # Success! 00049 $wgOut->addHTML( "<p>" . wfMsg( "importsuccess" ) . "</p>" ); 00050 } else { 00051 $wgOut->addHTML( "<p>" . wfMsg( "importfailed", 00052 htmlspecialchars( $importer->getError() ) ) . "</p>" ); 00053 } 00054 } else { 00055 $wgOut->addWikiText( htmlspecialchars( $importer->getError() ) ); 00056 } 00057 } 00058 00059 $wgOut->addWikiText( "<p>" . wfMsg( "importtext" ) . "</p>" ); 00060 $action = $wgTitle->escapeLocalUrl(); 00061 $wgOut->addHTML( " 00062 <fieldset> 00063 <legend>Upload XML</legend> 00064 <form enctype='multipart/form-data' method='post' action=\"$action\"> 00065 <input type='hidden' name='action' value='submit' /> 00066 <input type='hidden' name='source' value='upload' /> 00067 <input type='hidden' name='MAX_FILE_SIZE' value='200000' /> 00068 <input type='file' name='xmlimport' value='' size='30' /> 00069 <input type='submit' value='" . htmlspecialchars( wfMsg( "uploadbtn" ) ) . "'/> 00070 </form> 00071 </fieldset> 00072 " ); 00073 00074 if( !empty( $wgImportSources ) ) { 00075 $wgOut->addHTML( " 00076 <fieldset> 00077 <legend>Interwiki import</legend> 00078 <form method='post' action=\"$action\"> 00079 <input type='hidden' name='action' value='submit' /> 00080 <input type='hidden' name='source' value='interwiki' /> 00081 <select name='interwiki'> 00082 " ); 00083 foreach( $wgImportSources as $interwiki ) { 00084 $iw = htmlspecialchars( $interwiki ); 00085 $wgOut->addHTML( "<option value=\"$iw\">$iw</option>\n" ); 00086 } 00087 $wgOut->addHTML( " 00088 </select> 00089 <input name='frompage' /> 00090 <input type='submit' /> 00091 </form> 00092 </fieldset> 00093 " ); 00094 } 00095 } 00096 00097 function wfImportOldRevision( &$revision ) { 00098 global $wgOut; 00099 $fname = "wfImportOldRevision"; 00100 00101 # Sneak a single revision into place 00102 $ns = IntVal( $revision->title->getNamespace() ); 00103 $t = wfStrencode( $revision->title->getDBkey() ); 00104 $text = wfStrencode( $revision->getText() ); 00105 $ts = wfStrencode( $revision->timestamp ); 00106 $its = wfStrencode( wfInvertTimestamp( $revision->timestamp ) ) ; 00107 $comment = wfStrencode( $revision->getComment() ); 00108 00109 $user = User::newFromName( $revision->getUser() ); 00110 $user_id = IntVal( $user->getId() ); 00111 $user_text = wfStrencode( $user->getName() ); 00112 00113 $minor = 0; # ?? 00114 $flags = ""; 00115 00116 # Make sure it doesn't already exist 00117 $sql = "SELECT 1 FROM old WHERE old_namespace=$ns AND old_title='$t' AND old_timestamp='$ts'"; 00118 $res = wfQuery( $sql, DB_WRITE, $fname ); 00119 $numrows = wfNumRows( $res ); 00120 wfFreeResult( $res ); 00121 if( $numrows > 0 ) { 00122 return wfMsg( "importhistoryconflict" ); 00123 } 00124 00125 $res = wfQuery( "INSERT INTO old " . 00126 "(old_namespace,old_title,old_text,old_comment,old_user,old_user_text," . 00127 "old_timestamp,inverse_timestamp,old_minor_edit,old_flags) " . 00128 "VALUES ($ns,'$t','$text','$comment',$user_id,'$user_text','$ts','$its',$minor,'$flags')", 00129 DB_WRITE, $fname ); 00130 00131 return wfMsg( "ok" ); 00132 } 00133 00134 class WikiRevision { 00135 var $title = NULL; 00136 var $timestamp = "20010115000000"; 00137 var $user = 0; 00138 var $user_text = ""; 00139 var $text = ""; 00140 var $comment = ""; 00141 00142 function setTitle( $text ) { 00143 $text = $this->fixEncoding( $text ); 00144 $this->title = Title::newFromText( $text ); 00145 } 00146 00147 function setTimestamp( $ts ) { 00148 # 2003-08-05T18:30:02Z 00149 $this->timestamp = preg_replace( '/^(....)-(..)-(..)T(..):(..):(..)Z$/', '$1$2$3$4$5$6', $ts ); 00150 } 00151 00152 function setUsername( $user ) { 00153 $this->user_text = $this->fixEncoding( $user ); 00154 } 00155 00156 function setUserIP( $ip ) { 00157 $this->user_text = $this->fixEncoding( $ip ); 00158 } 00159 00160 function setText( $text ) { 00161 $this->text = $this->fixEncoding( $text ); 00162 } 00163 00164 function setComment( $text ) { 00165 $this->comment = $this->fixEncoding( $text ); 00166 } 00167 00168 function fixEncoding( $data ) { 00169 global $wgLang, $wgInputEncoding; 00170 00171 if( strcasecmp( $wgInputEncoding, "utf-8" ) == 0 ) { 00172 return $data; 00173 } else { 00174 return $wgLang->iconv( "utf-8", $wgInputEncoding, $data ); 00175 } 00176 } 00177 00178 function getTitle() { 00179 return $this->title; 00180 } 00181 00182 function getTimestamp() { 00183 return $this->timestamp; 00184 } 00185 00186 function getUser() { 00187 return $this->user_text; 00188 } 00189 00190 function getText() { 00191 return $this->text; 00192 } 00193 00194 function getComment() { 00195 return $this->comment; 00196 } 00197 } 00198 00199 class WikiImporter { 00200 var $mSource = NULL; 00201 var $mError = ""; 00202 var $mXmlError = XML_ERROR_NONE; 00203 var $mRevisionHandler = NULL; 00204 var $lastfield; 00205 00206 function WikiImporter() { 00207 $this->setRevisionHandler( array( &$this, "defaultRevisionHandler" ) ); 00208 } 00209 00210 function setError( $err ) { 00211 $this->mError = $err; 00212 return false; 00213 } 00214 00215 function getError() { 00216 if( $this->mXmlError == XML_ERROR_NONE ) { 00217 return $this->mError; 00218 } else { 00219 return xml_error_string( $this->mXmlError ); 00220 } 00221 } 00222 00223 function throwXmlError( $err ) { 00224 $this->debug( "FAILURE: $err" ); 00225 } 00226 00227 function setupFromFile( $filename ) { 00228 $this->mSource = file_get_contents( $filename ); 00229 return true; 00230 } 00231 00232 function setupFromUpload( $fieldname = "xmlimport" ) { 00233 global $wgOut; 00234 00235 $upload =& $_FILES[$fieldname]; 00236 00237 if( !isset( $upload ) ) { 00238 return $this->setError( wfMsg( "importnofile" ) ); 00239 } 00240 if( !empty( $upload['error'] ) ) { 00241 return $this->setError( wfMsg( "importuploaderror", $upload['error'] ) ); 00242 } 00243 $fname = $upload['tmp_name']; 00244 if( is_uploaded_file( $fname ) ) { 00245 return $this->setupFromFile( $fname ); 00246 } else { 00247 return $this->setError( wfMsg( "importnofile" ) ); 00248 } 00249 } 00250 00251 function setupFromURL( $url ) { 00252 # fopen-wrappers are normally turned off for security. 00253 ini_set( "allow_url_fopen", true ); 00254 $ret = $this->setupFromFile( $url ); 00255 ini_set( "allow_url_fopen", false ); 00256 return $ret; 00257 } 00258 00259 function setupFromInterwiki( $interwiki, $page ) { 00260 $base = Title::getInterwikiLink( $interwiki ); 00261 if( empty( $base ) ) { 00262 return false; 00263 } else { 00264 $import = wfUrlencode( "Special:Export/$page" ); 00265 $url = str_replace( "$1", $import, $base ); 00266 $this->notice( "Importing from $url" ); 00267 return $this->setupFromURL( $url ); 00268 } 00269 } 00270 00271 # -------------- 00272 00273 function doImport() { 00274 if( empty( $this->mSource ) ) { 00275 return $this->setError( wfMsg( "importnotext" ) ); 00276 } 00277 00278 $parser = xml_parser_create( "UTF-8" ); 00279 00280 # case folding violates XML standard, turn it off 00281 xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); 00282 00283 xml_set_object( $parser, &$this ); 00284 xml_set_element_handler( $parser, "in_start", "" ); 00285 00286 if( !xml_parse( $parser, $this->mSource, true ) ) { 00287 # return error message 00288 $this->mXmlError = xml_get_error_code( $parser ); 00289 xml_parser_free( $parser ); 00290 return false; 00291 } 00292 xml_parser_free( $parser ); 00293 00294 return true; 00295 } 00296 00297 function debug( $data ) { 00298 global $wgOut; 00299 # $this->notice( "DEBUG: $data\n" ); 00300 } 00301 00302 function notice( $data ) { 00303 global $wgCommandLineMode; 00304 if( $wgCommandLineMode ) { 00305 print "$data\n"; 00306 } else { 00307 global $wgOut; 00308 $wgOut->addHTML( "<li>$data</li>\n" ); 00309 } 00310 } 00311 00312 function setRevisionHandler( $functionref ) { 00313 $this->mRevisionHandler = $functionref; 00314 } 00315 00316 function defaultRevisionHandler( &$revision ) { 00317 $this->debug( "Got revision:" ); 00318 if( is_object( $revision->title ) ) { 00319 $this->debug( "-- Title: " . $revision->title->getPrefixedText() ); 00320 } else { 00321 $this->debug( "-- Title: <invalid>" ); 00322 } 00323 $this->debug( "-- User: " . $revision->user_text ); 00324 $this->debug( "-- Timestamp: " . $revision->timestamp ); 00325 $this->debug( "-- Comment: " . $revision->comment ); 00326 $this->debug( "-- Text: " . $revision->text ); 00327 } 00328 00329 00330 00331 # XML parser callbacks from here out -- beware! 00332 function donothing( $parser, $x, $y="" ) { 00333 #$this->debug( "donothing" ); 00334 } 00335 00336 function in_start( $parser, $name, $attribs ) { 00337 $this->debug( "in_start $name" ); 00338 if( $name != "mediawiki" ) { 00339 return $this->throwXMLerror( "Expected <mediawiki>, got <$name>" ); 00340 } 00341 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" ); 00342 } 00343 00344 function in_mediawiki( $parser, $name, $attribs ) { 00345 $this->debug( "in_mediawiki $name" ); 00346 if( $name != "page" ) { 00347 return $this->throwXMLerror( "Expected <page>, got <$name>" ); 00348 } 00349 xml_set_element_handler( $parser, "in_page", "out_page" ); 00350 } 00351 function out_mediawiki( $parser, $name ) { 00352 $this->debug( "out_mediawiki $name" ); 00353 if( $name != "mediawiki" ) { 00354 return $this->throwXMLerror( "Expected </mediawiki>, got </$name>" ); 00355 } 00356 xml_set_element_handler( $parser, "donothing", "donothing" ); 00357 } 00358 00359 function in_page( $parser, $name, $attribs ) { 00360 $this->debug( "in_page $name" ); 00361 switch( $name ) { 00362 case "id": 00363 case "title": 00364 case "restrictions": 00365 $this->appendfield = $name; 00366 $this->appenddata = ""; 00367 $this->parenttag = "page"; 00368 xml_set_element_handler( $parser, "in_nothing", "out_append" ); 00369 xml_set_character_data_handler( $parser, "char_append" ); 00370 break; 00371 case "revision": 00372 $this->workRevision = new WikiRevision; 00373 $this->workRevision->setTitle( $this->workTitle ); 00374 xml_set_element_handler( $parser, "in_revision", "out_revision" ); 00375 break; 00376 default: 00377 return $this->throwXMLerror( "Element <$name> not allowed in a <page>." ); 00378 } 00379 } 00380 00381 function out_page( $parser, $name ) { 00382 $this->debug( "out_page $name" ); 00383 if( $name != "page" ) { 00384 return $this->throwXMLerror( "Expected </page>, got </$name>" ); 00385 } 00386 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" ); 00387 00388 $this->workTitle = NULL; 00389 $this->workRevision = NULL; 00390 } 00391 00392 function in_nothing( $parser, $name, $attribs ) { 00393 $this->debug( "in_nothing $name" ); 00394 return $this->throwXMLerror( "No child elements allowed here; got <$name>" ); 00395 } 00396 function char_append( $parser, $data ) { 00397 $this->debug( "char_append '$data'" ); 00398 $this->appenddata .= $data; 00399 } 00400 function out_append( $parser, $name ) { 00401 $this->debug( "out_append $name" ); 00402 if( $name != $this->appendfield ) { 00403 return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" ); 00404 } 00405 xml_set_element_handler( $parser, "in_$this->parenttag", "out_$this->parenttag" ); 00406 xml_set_character_data_handler( $parser, "donothing" ); 00407 switch( $this->appendfield ) { 00408 case "title": 00409 $this->workTitle = $this->appenddata; 00410 break; 00411 case "text": 00412 $this->workRevision->setText( $this->appenddata ); 00413 break; 00414 case "username": 00415 $this->workRevision->setUsername( $this->appenddata ); 00416 break; 00417 case "ip": 00418 $this->workRevision->setUserIP( $this->appenddata ); 00419 break; 00420 case "timestamp": 00421 $this->workRevision->setTimestamp( $this->appenddata ); 00422 break; 00423 case "comment": 00424 $this->workRevision->setComment( $this->appenddata ); 00425 break; 00426 default; 00427 $this->debug( "Bad append: {$this->appendfield}" ); 00428 } 00429 $this->appendfield = ""; 00430 $this->appenddata = ""; 00431 } 00432 00433 function in_revision( $parser, $name, $attribs ) { 00434 $this->debug( "in_revision $name" ); 00435 switch( $name ) { 00436 case "id": 00437 case "timestamp": 00438 case "comment": 00439 case "text": 00440 $this->parenttag = "revision"; 00441 $this->appendfield = $name; 00442 xml_set_element_handler( $parser, "in_nothing", "out_append" ); 00443 xml_set_character_data_handler( $parser, "char_append" ); 00444 break; 00445 case "contributor": 00446 xml_set_element_handler( $parser, "in_contributor", "out_contributor" ); 00447 break; 00448 default: 00449 return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." ); 00450 } 00451 } 00452 00453 function out_revision( $parser, $name ) { 00454 $this->debug( "out_revision $name" ); 00455 if( $name != "revision" ) { 00456 return $this->throwXMLerror( "Expected </revision>, got </$name>" ); 00457 } 00458 xml_set_element_handler( $parser, "in_page", "out_page" ); 00459 00460 $out = call_user_func( $this->mRevisionHandler, &$this->workRevision, &$this ); 00461 if( !empty( $out ) ) { 00462 global $wgOut; 00463 $wgOut->addHTML( "<li>" . $out . "</li>\n" ); 00464 } 00465 } 00466 00467 function in_contributor( $parser, $name, $attribs ) { 00468 $this->debug( "in_contributor $name" ); 00469 switch( $name ) { 00470 case "username": 00471 case "ip": 00472 $this->parenttag = "contributor"; 00473 $this->appendfield = $name; 00474 xml_set_element_handler( $parser, "in_nothing", "out_append" ); 00475 xml_set_character_data_handler( $parser, "char_append" ); 00476 break; 00477 default: 00478 $this->throwXMLerror( "Invalid tag <$name> in <contributor>" ); 00479 } 00480 } 00481 00482 function out_contributor( $parser, $name ) { 00483 $this->debug( "out_contributor $name" ); 00484 if( $name != "contributor" ) { 00485 return $this->throwXMLerror( "Expected </contributor>, got </$name>" ); 00486 } 00487 xml_set_element_handler( $parser, "in_revision", "out_revision" ); 00488 } 00489 } 00490 00491 00492 ?>