00001 <?php
00030 class WikiRevision {
00031 var $title = null;
00032 var $id = 0;
00033 var $timestamp = "20010115000000";
00034 var $user = 0;
00035 var $user_text = "";
00036 var $text = "";
00037 var $comment = "";
00038 var $minor = false;
00039 var $type = "";
00040 var $action = "";
00041 var $params = "";
00042
00043 function setTitle( $title ) {
00044 if( is_object( $title ) ) {
00045 $this->title = $title;
00046 } elseif( is_null( $title ) ) {
00047 throw new MWException( "WikiRevision given a null title in import. You may need to adjust \$wgLegalTitleChars." );
00048 } else {
00049 throw new MWException( "WikiRevision given non-object title in import." );
00050 }
00051 }
00052
00053 function setID( $id ) {
00054 $this->id = $id;
00055 }
00056
00057 function setTimestamp( $ts ) {
00058 # 2003-08-05T18:30:02Z
00059 $this->timestamp = wfTimestamp( TS_MW, $ts );
00060 }
00061
00062 function setUsername( $user ) {
00063 $this->user_text = $user;
00064 }
00065
00066 function setUserIP( $ip ) {
00067 $this->user_text = $ip;
00068 }
00069
00070 function setText( $text ) {
00071 $this->text = $text;
00072 }
00073
00074 function setComment( $text ) {
00075 $this->comment = $text;
00076 }
00077
00078 function setMinor( $minor ) {
00079 $this->minor = (bool)$minor;
00080 }
00081
00082 function setSrc( $src ) {
00083 $this->src = $src;
00084 }
00085
00086 function setFilename( $filename ) {
00087 $this->filename = $filename;
00088 }
00089
00090 function setSize( $size ) {
00091 $this->size = intval( $size );
00092 }
00093
00094 function setType( $type ) {
00095 $this->type = $type;
00096 }
00097
00098 function setAction( $action ) {
00099 $this->action = $action;
00100 }
00101
00102 function setParams( $params ) {
00103 $this->params = $params;
00104 }
00105
00106 function getTitle() {
00107 return $this->title;
00108 }
00109
00110 function getID() {
00111 return $this->id;
00112 }
00113
00114 function getTimestamp() {
00115 return $this->timestamp;
00116 }
00117
00118 function getUser() {
00119 return $this->user_text;
00120 }
00121
00122 function getText() {
00123 return $this->text;
00124 }
00125
00126 function getComment() {
00127 return $this->comment;
00128 }
00129
00130 function getMinor() {
00131 return $this->minor;
00132 }
00133
00134 function getSrc() {
00135 return $this->src;
00136 }
00137
00138 function getFilename() {
00139 return $this->filename;
00140 }
00141
00142 function getSize() {
00143 return $this->size;
00144 }
00145
00146 function getType() {
00147 return $this->type;
00148 }
00149
00150 function getAction() {
00151 return $this->action;
00152 }
00153
00154 function getParams() {
00155 return $this->params;
00156 }
00157
00158 function importOldRevision() {
00159 $dbw = wfGetDB( DB_MASTER );
00160
00161 # Sneak a single revision into place
00162 $user = User::newFromName( $this->getUser() );
00163 if( $user ) {
00164 $userId = intval( $user->getId() );
00165 $userText = $user->getName();
00166 } else {
00167 $userId = 0;
00168 $userText = $this->getUser();
00169 }
00170
00171
00172 $linkCache = LinkCache::singleton();
00173 $linkCache->clear();
00174
00175 $article = new Article( $this->title );
00176 $pageId = $article->getId();
00177 if( $pageId == 0 ) {
00178 # must create the page...
00179 $pageId = $article->insertOn( $dbw );
00180 $created = true;
00181 } else {
00182 $created = false;
00183
00184 $prior = $dbw->selectField( 'revision', '1',
00185 array( 'rev_page' => $pageId,
00186 'rev_timestamp' => $dbw->timestamp( $this->timestamp ),
00187 'rev_user_text' => $userText,
00188 'rev_comment' => $this->getComment() ),
00189 __METHOD__
00190 );
00191 if( $prior ) {
00192
00193 wfDebug( __METHOD__ . ": skipping existing revision for [[" .
00194 $this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
00195 return false;
00196 }
00197 }
00198
00199 # FIXME: Use original rev_id optionally (better for backups)
00200 # Insert the row
00201 $revision = new Revision( array(
00202 'page' => $pageId,
00203 'text' => $this->getText(),
00204 'comment' => $this->getComment(),
00205 'user' => $userId,
00206 'user_text' => $userText,
00207 'timestamp' => $this->timestamp,
00208 'minor_edit' => $this->minor,
00209 ) );
00210 $revId = $revision->insertOn( $dbw );
00211 $changed = $article->updateIfNewerOn( $dbw, $revision );
00212
00213 # To be on the safe side...
00214 $tempTitle = $GLOBALS['wgTitle'];
00215 $GLOBALS['wgTitle'] = $this->title;
00216
00217 if( $created ) {
00218 wfDebug( __METHOD__ . ": running onArticleCreate\n" );
00219 Article::onArticleCreate( $this->title );
00220
00221 wfDebug( __METHOD__ . ": running create updates\n" );
00222 $article->createUpdates( $revision );
00223
00224 } elseif( $changed ) {
00225 wfDebug( __METHOD__ . ": running onArticleEdit\n" );
00226 Article::onArticleEdit( $this->title );
00227
00228 wfDebug( __METHOD__ . ": running edit updates\n" );
00229 $article->editUpdates(
00230 $this->getText(),
00231 $this->getComment(),
00232 $this->minor,
00233 $this->timestamp,
00234 $revId );
00235 }
00236 $GLOBALS['wgTitle'] = $tempTitle;
00237
00238 return true;
00239 }
00240
00241 function importLogItem() {
00242 $dbw = wfGetDB( DB_MASTER );
00243 # FIXME: this will not record autoblocks
00244 if( !$this->getTitle() ) {
00245 wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
00246 $this->timestamp . "\n" );
00247 return;
00248 }
00249 # Check if it exists already
00250
00251 $prior = $dbw->selectField( 'logging', '1',
00252 array( 'log_type' => $this->getType(),
00253 'log_action' => $this->getAction(),
00254 'log_timestamp' => $dbw->timestamp( $this->timestamp ),
00255 'log_namespace' => $this->getTitle()->getNamespace(),
00256 'log_title' => $this->getTitle()->getDBkey(),
00257 'log_comment' => $this->getComment(),
00258 #'log_user_text' => $this->user_text,
00259 'log_params' => $this->params ),
00260 __METHOD__
00261 );
00262
00263 if( $prior ) {
00264 wfDebug( __METHOD__ . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp " .
00265 $this->timestamp . "\n" );
00266 return false;
00267 }
00268 $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
00269 $data = array(
00270 'log_id' => $log_id,
00271 'log_type' => $this->type,
00272 'log_action' => $this->action,
00273 'log_timestamp' => $dbw->timestamp( $this->timestamp ),
00274 'log_user' => User::idFromName( $this->user_text ),
00275 #'log_user_text' => $this->user_text,
00276 'log_namespace' => $this->getTitle()->getNamespace(),
00277 'log_title' => $this->getTitle()->getDBkey(),
00278 'log_comment' => $this->getComment(),
00279 'log_params' => $this->params
00280 );
00281 $dbw->insert( 'logging', $data, __METHOD__ );
00282 }
00283
00284 function importUpload() {
00285 wfDebug( __METHOD__ . ": STUB\n" );
00286
00307
00308
00309
00310
00311 $file = wfLocalFile( $this->getTitle() );
00312 if( !$file ) {
00313 var_dump( $file );
00314 wfDebug( "IMPORT: Bad file. :(\n" );
00315 return false;
00316 }
00317
00318 $source = $this->downloadSource();
00319 if( !$source ) {
00320 wfDebug( "IMPORT: Could not fetch remote file. :(\n" );
00321 return false;
00322 }
00323
00324 $status = $file->upload( $source,
00325 $this->getComment(),
00326 $this->getComment(),
00327 File::DELETE_SOURCE,
00328 false,
00329 $this->getTimestamp() );
00330
00331 if( $status->isGood() ) {
00332
00333 wfDebug( "IMPORT: is ok?\n" );
00334 return true;
00335 }
00336
00337 wfDebug( "IMPORT: is bad? " . $status->getXml() . "\n" );
00338 return false;
00339
00340 }
00341
00342 function downloadSource() {
00343 global $wgEnableUploads;
00344 if( !$wgEnableUploads ) {
00345 return false;
00346 }
00347
00348 $tempo = tempnam( wfTempDir(), 'download' );
00349 $f = fopen( $tempo, 'wb' );
00350 if( !$f ) {
00351 wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
00352 return false;
00353 }
00354
00355
00356 $src = $this->getSrc();
00357 $data = Http::get( $src );
00358 if( !$data ) {
00359 wfDebug( "IMPORT: couldn't fetch source $src\n" );
00360 fclose( $f );
00361 unlink( $tempo );
00362 return false;
00363 }
00364
00365 fwrite( $f, $data );
00366 fclose( $f );
00367
00368 return $tempo;
00369 }
00370
00371 }
00372
00377 class WikiImporter {
00378 var $mDebug = false;
00379 var $mSource = null;
00380 var $mPageCallback = null;
00381 var $mPageOutCallback = null;
00382 var $mRevisionCallback = null;
00383 var $mLogItemCallback = null;
00384 var $mUploadCallback = null;
00385 var $mTargetNamespace = null;
00386 var $mXmlNamespace = false;
00387 var $lastfield;
00388 var $tagStack = array();
00389
00390 function __construct( $source ) {
00391 $this->setRevisionCallback( array( $this, "importRevision" ) );
00392 $this->setUploadCallback( array( $this, "importUpload" ) );
00393 $this->setLogItemCallback( array( $this, "importLogItem" ) );
00394 $this->mSource = $source;
00395 }
00396
00397 function throwXmlError( $err ) {
00398 $this->debug( "FAILURE: $err" );
00399 wfDebug( "WikiImporter XML error: $err\n" );
00400 }
00401
00402 function handleXmlNamespace ( $parser, $data, $prefix=false, $uri=false ) {
00403 if( preg_match( '/www.mediawiki.org/',$prefix ) ) {
00404 $prefix = str_replace( '/','\/',$prefix );
00405 $this->mXmlNamespace='/^'.$prefix.':/';
00406 }
00407 }
00408
00409 function stripXmlNamespace($name) {
00410 if( $this->mXmlNamespace ) {
00411 return(preg_replace($this->mXmlNamespace,'',$name,1));
00412 }
00413 else {
00414 return($name);
00415 }
00416 }
00417
00418 # --------------
00419
00420 function doImport() {
00421 if( empty( $this->mSource ) ) {
00422 return new WikiErrorMsg( "importnotext" );
00423 }
00424
00425 $parser = xml_parser_create_ns( "UTF-8" );
00426
00427 # case folding violates XML standard, turn it off
00428 xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
00429
00430 xml_set_object( $parser, $this );
00431 xml_set_element_handler( $parser, "in_start", "" );
00432 xml_set_start_namespace_decl_handler( $parser, "handleXmlNamespace" );
00433
00434 $offset = 0;
00435 do {
00436 $chunk = $this->mSource->readChunk();
00437 if( !xml_parse( $parser, $chunk, $this->mSource->atEnd() ) ) {
00438 wfDebug( "WikiImporter::doImport encountered XML parsing error\n" );
00439 return new WikiXmlError( $parser, wfMsgHtml( 'import-parse-failure' ), $chunk, $offset );
00440 }
00441 $offset += strlen( $chunk );
00442 } while( $chunk !== false && !$this->mSource->atEnd() );
00443 xml_parser_free( $parser );
00444
00445 return true;
00446 }
00447
00448 function debug( $data ) {
00449 if( $this->mDebug ) {
00450 wfDebug( "IMPORT: $data\n" );
00451 }
00452 }
00453
00454 function notice( $data ) {
00455 global $wgCommandLineMode;
00456 if( $wgCommandLineMode ) {
00457 print "$data\n";
00458 } else {
00459 global $wgOut;
00460 $wgOut->addHTML( "<li>" . htmlspecialchars( $data ) . "</li>\n" );
00461 }
00462 }
00463
00467 function setDebug( $debug ) {
00468 $this->mDebug = $debug;
00469 }
00470
00476 function setPageCallback( $callback ) {
00477 $previous = $this->mPageCallback;
00478 $this->mPageCallback = $callback;
00479 return $previous;
00480 }
00481
00491 function setPageOutCallback( $callback ) {
00492 $previous = $this->mPageOutCallback;
00493 $this->mPageOutCallback = $callback;
00494 return $previous;
00495 }
00496
00502 function setRevisionCallback( $callback ) {
00503 $previous = $this->mRevisionCallback;
00504 $this->mRevisionCallback = $callback;
00505 return $previous;
00506 }
00507
00513 function setUploadCallback( $callback ) {
00514 $previous = $this->mUploadCallback;
00515 $this->mUploadCallback = $callback;
00516 return $previous;
00517 }
00518
00524 function setLogItemCallback( $callback ) {
00525 $previous = $this->mLogItemCallback;
00526 $this->mLogItemCallback = $callback;
00527 return $previous;
00528 }
00529
00533 function setTargetNamespace( $namespace ) {
00534 if( is_null( $namespace ) ) {
00535
00536 $this->mTargetNamespace = null;
00537 } elseif( $namespace >= 0 ) {
00538
00539 $this->mTargetNamespace = intval( $namespace );
00540 } else {
00541 return false;
00542 }
00543 }
00544
00550 function importRevision( $revision ) {
00551 $dbw = wfGetDB( DB_MASTER );
00552 return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
00553 }
00554
00560 function importLogItem( $rev ) {
00561 $dbw = wfGetDB( DB_MASTER );
00562 return $dbw->deadlockLoop( array( $rev, 'importLogItem' ) );
00563 }
00564
00568 function importUpload( $revision ) {
00569
00570
00571 return false;
00572 }
00573
00579 function debugRevisionHandler( &$revision ) {
00580 $this->debug( "Got revision:" );
00581 if( is_object( $revision->title ) ) {
00582 $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
00583 } else {
00584 $this->debug( "-- Title: <invalid>" );
00585 }
00586 $this->debug( "-- User: " . $revision->user_text );
00587 $this->debug( "-- Timestamp: " . $revision->timestamp );
00588 $this->debug( "-- Comment: " . $revision->comment );
00589 $this->debug( "-- Text: " . $revision->text );
00590 }
00591
00597 function pageCallback( $title ) {
00598 if( is_callable( $this->mPageCallback ) ) {
00599 call_user_func( $this->mPageCallback, $title );
00600 }
00601 }
00602
00611 function pageOutCallback( $title, $origTitle, $revisionCount, $successCount ) {
00612 if( is_callable( $this->mPageOutCallback ) ) {
00613 call_user_func( $this->mPageOutCallback, $title, $origTitle,
00614 $revisionCount, $successCount );
00615 }
00616 }
00617
00618 # XML parser callbacks from here out -- beware!
00619 function donothing( $parser, $x, $y="" ) {
00620 #$this->debug( "donothing" );
00621 }
00622
00623 function in_start( $parser, $name, $attribs ) {
00624 $name = $this->stripXmlNamespace($name);
00625 $this->debug( "in_start $name" );
00626 if( $name != "mediawiki" ) {
00627 return $this->throwXMLerror( "Expected <mediawiki>, got <$name>" );
00628 }
00629 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
00630 }
00631
00632 function in_mediawiki( $parser, $name, $attribs ) {
00633 $name = $this->stripXmlNamespace($name);
00634 $this->debug( "in_mediawiki $name" );
00635 if( $name == 'siteinfo' ) {
00636 xml_set_element_handler( $parser, "in_siteinfo", "out_siteinfo" );
00637 } elseif( $name == 'page' ) {
00638 $this->push( $name );
00639 $this->workRevisionCount = 0;
00640 $this->workSuccessCount = 0;
00641 $this->uploadCount = 0;
00642 $this->uploadSuccessCount = 0;
00643 xml_set_element_handler( $parser, "in_page", "out_page" );
00644 } elseif( $name == 'logitem' ) {
00645 $this->push( $name );
00646 $this->workRevision = new WikiRevision;
00647 xml_set_element_handler( $parser, "in_logitem", "out_logitem" );
00648 } else {
00649 return $this->throwXMLerror( "Expected <page>, got <$name>" );
00650 }
00651 }
00652 function out_mediawiki( $parser, $name ) {
00653 $name = $this->stripXmlNamespace($name);
00654 $this->debug( "out_mediawiki $name" );
00655 if( $name != "mediawiki" ) {
00656 return $this->throwXMLerror( "Expected </mediawiki>, got </$name>" );
00657 }
00658 xml_set_element_handler( $parser, "donothing", "donothing" );
00659 }
00660
00661
00662 function in_siteinfo( $parser, $name, $attribs ) {
00663
00664 $name = $this->stripXmlNamespace($name);
00665 $this->debug( "in_siteinfo $name" );
00666 switch( $name ) {
00667 case "sitename":
00668 case "base":
00669 case "generator":
00670 case "case":
00671 case "namespaces":
00672 case "namespace":
00673 break;
00674 default:
00675 return $this->throwXMLerror( "Element <$name> not allowed in <siteinfo>." );
00676 }
00677 }
00678
00679 function out_siteinfo( $parser, $name ) {
00680 $name = $this->stripXmlNamespace($name);
00681 if( $name == "siteinfo" ) {
00682 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
00683 }
00684 }
00685
00686
00687 function in_page( $parser, $name, $attribs ) {
00688 $name = $this->stripXmlNamespace($name);
00689 $this->debug( "in_page $name" );
00690 switch( $name ) {
00691 case "id":
00692 case "title":
00693 case "redirect":
00694 case "restrictions":
00695 $this->appendfield = $name;
00696 $this->appenddata = "";
00697 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00698 xml_set_character_data_handler( $parser, "char_append" );
00699 break;
00700 case "revision":
00701 $this->push( "revision" );
00702 if( is_object( $this->pageTitle ) ) {
00703 $this->workRevision = new WikiRevision;
00704 $this->workRevision->setTitle( $this->pageTitle );
00705 $this->workRevisionCount++;
00706 } else {
00707
00708 $this->workRevision = null;
00709 }
00710 xml_set_element_handler( $parser, "in_revision", "out_revision" );
00711 break;
00712 case "upload":
00713 $this->push( "upload" );
00714 if( is_object( $this->pageTitle ) ) {
00715 $this->workRevision = new WikiRevision;
00716 $this->workRevision->setTitle( $this->pageTitle );
00717 $this->uploadCount++;
00718 } else {
00719
00720 $this->workRevision = null;
00721 }
00722 xml_set_element_handler( $parser, "in_upload", "out_upload" );
00723 break;
00724 default:
00725 return $this->throwXMLerror( "Element <$name> not allowed in a <page>." );
00726 }
00727 }
00728
00729 function out_page( $parser, $name ) {
00730 $name = $this->stripXmlNamespace($name);
00731 $this->debug( "out_page $name" );
00732 $this->pop();
00733 if( $name != "page" ) {
00734 return $this->throwXMLerror( "Expected </page>, got </$name>" );
00735 }
00736 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
00737
00738 $this->pageOutCallback( $this->pageTitle, $this->origTitle,
00739 $this->workRevisionCount, $this->workSuccessCount );
00740
00741 $this->workTitle = null;
00742 $this->workRevision = null;
00743 $this->workRevisionCount = 0;
00744 $this->workSuccessCount = 0;
00745 $this->pageTitle = null;
00746 $this->origTitle = null;
00747 }
00748
00749 function in_nothing( $parser, $name, $attribs ) {
00750 $name = $this->stripXmlNamespace($name);
00751 $this->debug( "in_nothing $name" );
00752 return $this->throwXMLerror( "No child elements allowed here; got <$name>" );
00753 }
00754
00755 function char_append( $parser, $data ) {
00756 $this->debug( "char_append '$data'" );
00757 $this->appenddata .= $data;
00758 }
00759
00760 function out_append( $parser, $name ) {
00761 $name = $this->stripXmlNamespace($name);
00762 $this->debug( "out_append $name" );
00763 if( $name != $this->appendfield ) {
00764 return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" );
00765 }
00766
00767 switch( $this->appendfield ) {
00768 case "title":
00769 $this->workTitle = $this->appenddata;
00770 $this->origTitle = Title::newFromText( $this->workTitle );
00771 if( !is_null( $this->mTargetNamespace ) && !is_null( $this->origTitle ) ) {
00772 $this->pageTitle = Title::makeTitle( $this->mTargetNamespace,
00773 $this->origTitle->getDBkey() );
00774 } else {
00775 $this->pageTitle = Title::newFromText( $this->workTitle );
00776 }
00777 if( is_null( $this->pageTitle ) ) {
00778
00779 $this->notice( "Skipping invalid page title '$this->workTitle'" );
00780 } elseif( $this->pageTitle->getInterwiki() != '' ) {
00781 $this->notice( "Skipping interwiki page title '$this->workTitle'" );
00782 $this->pageTitle = null;
00783 } else {
00784 $this->pageCallback( $this->workTitle );
00785 }
00786 break;
00787 case "id":
00788 if ( $this->parentTag() == 'revision' || $this->parentTag() == 'logitem' ) {
00789 if( $this->workRevision )
00790 $this->workRevision->setID( $this->appenddata );
00791 }
00792 break;
00793 case "text":
00794 if( $this->workRevision )
00795 $this->workRevision->setText( $this->appenddata );
00796 break;
00797 case "username":
00798 if( $this->workRevision )
00799 $this->workRevision->setUsername( $this->appenddata );
00800 break;
00801 case "ip":
00802 if( $this->workRevision )
00803 $this->workRevision->setUserIP( $this->appenddata );
00804 break;
00805 case "timestamp":
00806 if( $this->workRevision )
00807 $this->workRevision->setTimestamp( $this->appenddata );
00808 break;
00809 case "comment":
00810 if( $this->workRevision )
00811 $this->workRevision->setComment( $this->appenddata );
00812 break;
00813 case "type":
00814 if( $this->workRevision )
00815 $this->workRevision->setType( $this->appenddata );
00816 break;
00817 case "action":
00818 if( $this->workRevision )
00819 $this->workRevision->setAction( $this->appenddata );
00820 break;
00821 case "logtitle":
00822 if( $this->workRevision )
00823 $this->workRevision->setTitle( Title::newFromText( $this->appenddata ) );
00824 break;
00825 case "params":
00826 if( $this->workRevision )
00827 $this->workRevision->setParams( $this->appenddata );
00828 break;
00829 case "minor":
00830 if( $this->workRevision )
00831 $this->workRevision->setMinor( true );
00832 break;
00833 case "filename":
00834 if( $this->workRevision )
00835 $this->workRevision->setFilename( $this->appenddata );
00836 break;
00837 case "src":
00838 if( $this->workRevision )
00839 $this->workRevision->setSrc( $this->appenddata );
00840 break;
00841 case "size":
00842 if( $this->workRevision )
00843 $this->workRevision->setSize( intval( $this->appenddata ) );
00844 break;
00845 default:
00846 $this->debug( "Bad append: {$this->appendfield}" );
00847 }
00848 $this->appendfield = "";
00849 $this->appenddata = "";
00850
00851 $parent = $this->parentTag();
00852 xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
00853 xml_set_character_data_handler( $parser, "donothing" );
00854 }
00855
00856 function in_revision( $parser, $name, $attribs ) {
00857 $name = $this->stripXmlNamespace($name);
00858 $this->debug( "in_revision $name" );
00859 switch( $name ) {
00860 case "id":
00861 case "timestamp":
00862 case "comment":
00863 case "minor":
00864 case "text":
00865 $this->appendfield = $name;
00866 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00867 xml_set_character_data_handler( $parser, "char_append" );
00868 break;
00869 case "contributor":
00870 $this->push( "contributor" );
00871 xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
00872 break;
00873 default:
00874 return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
00875 }
00876 }
00877
00878 function out_revision( $parser, $name ) {
00879 $name = $this->stripXmlNamespace($name);
00880 $this->debug( "out_revision $name" );
00881 $this->pop();
00882 if( $name != "revision" ) {
00883 return $this->throwXMLerror( "Expected </revision>, got </$name>" );
00884 }
00885 xml_set_element_handler( $parser, "in_page", "out_page" );
00886
00887 if( $this->workRevision ) {
00888 $ok = call_user_func_array( $this->mRevisionCallback,
00889 array( $this->workRevision, $this ) );
00890 if( $ok ) {
00891 $this->workSuccessCount++;
00892 }
00893 }
00894 }
00895
00896 function in_logitem( $parser, $name, $attribs ) {
00897 $name = $this->stripXmlNamespace($name);
00898 $this->debug( "in_logitem $name" );
00899 switch( $name ) {
00900 case "id":
00901 case "timestamp":
00902 case "comment":
00903 case "type":
00904 case "action":
00905 case "logtitle":
00906 case "params":
00907 $this->appendfield = $name;
00908 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00909 xml_set_character_data_handler( $parser, "char_append" );
00910 break;
00911 case "contributor":
00912 $this->push( "contributor" );
00913 xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
00914 break;
00915 default:
00916 return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
00917 }
00918 }
00919
00920 function out_logitem( $parser, $name ) {
00921 $name = $this->stripXmlNamespace($name);
00922 $this->debug( "out_logitem $name" );
00923 $this->pop();
00924 if( $name != "logitem" ) {
00925 return $this->throwXMLerror( "Expected </logitem>, got </$name>" );
00926 }
00927 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
00928
00929 if( $this->workRevision ) {
00930 $ok = call_user_func_array( $this->mLogItemCallback,
00931 array( $this->workRevision, $this ) );
00932 if( $ok ) {
00933 $this->workSuccessCount++;
00934 }
00935 }
00936 }
00937
00938 function in_upload( $parser, $name, $attribs ) {
00939 $name = $this->stripXmlNamespace($name);
00940 $this->debug( "in_upload $name" );
00941 switch( $name ) {
00942 case "timestamp":
00943 case "comment":
00944 case "text":
00945 case "filename":
00946 case "src":
00947 case "size":
00948 $this->appendfield = $name;
00949 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00950 xml_set_character_data_handler( $parser, "char_append" );
00951 break;
00952 case "contributor":
00953 $this->push( "contributor" );
00954 xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
00955 break;
00956 default:
00957 return $this->throwXMLerror( "Element <$name> not allowed in an <upload>." );
00958 }
00959 }
00960
00961 function out_upload( $parser, $name ) {
00962 $name = $this->stripXmlNamespace($name);
00963 $this->debug( "out_revision $name" );
00964 $this->pop();
00965 if( $name != "upload" ) {
00966 return $this->throwXMLerror( "Expected </upload>, got </$name>" );
00967 }
00968 xml_set_element_handler( $parser, "in_page", "out_page" );
00969
00970 if( $this->workRevision ) {
00971 $ok = call_user_func_array( $this->mUploadCallback,
00972 array( $this->workRevision, $this ) );
00973 if( $ok ) {
00974 $this->workUploadSuccessCount++;
00975 }
00976 }
00977 }
00978
00979 function in_contributor( $parser, $name, $attribs ) {
00980 $name = $this->stripXmlNamespace($name);
00981 $this->debug( "in_contributor $name" );
00982 switch( $name ) {
00983 case "username":
00984 case "ip":
00985 case "id":
00986 $this->appendfield = $name;
00987 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00988 xml_set_character_data_handler( $parser, "char_append" );
00989 break;
00990 default:
00991 $this->throwXMLerror( "Invalid tag <$name> in <contributor>" );
00992 }
00993 }
00994
00995 function out_contributor( $parser, $name ) {
00996 $name = $this->stripXmlNamespace($name);
00997 $this->debug( "out_contributor $name" );
00998 $this->pop();
00999 if( $name != "contributor" ) {
01000 return $this->throwXMLerror( "Expected </contributor>, got </$name>" );
01001 }
01002 $parent = $this->parentTag();
01003 xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
01004 }
01005
01006 private function push( $name ) {
01007 array_push( $this->tagStack, $name );
01008 $this->debug( "PUSH $name" );
01009 }
01010
01011 private function pop() {
01012 $name = array_pop( $this->tagStack );
01013 $this->debug( "POP $name" );
01014 return $name;
01015 }
01016
01017 private function parentTag() {
01018 $name = $this->tagStack[count( $this->tagStack ) - 1];
01019 $this->debug( "PARENT $name" );
01020 return $name;
01021 }
01022
01023 }
01024
01029 class ImportStringSource {
01030 function __construct( $string ) {
01031 $this->mString = $string;
01032 $this->mRead = false;
01033 }
01034
01035 function atEnd() {
01036 return $this->mRead;
01037 }
01038
01039 function readChunk() {
01040 if( $this->atEnd() ) {
01041 return false;
01042 } else {
01043 $this->mRead = true;
01044 return $this->mString;
01045 }
01046 }
01047 }
01048
01053 class ImportStreamSource {
01054 function __construct( $handle ) {
01055 $this->mHandle = $handle;
01056 }
01057
01058 function atEnd() {
01059 return feof( $this->mHandle );
01060 }
01061
01062 function readChunk() {
01063 return fread( $this->mHandle, 32768 );
01064 }
01065
01066 static function newFromFile( $filename ) {
01067 $file = @fopen( $filename, 'rt' );
01068 if( !$file ) {
01069 return new WikiErrorMsg( "importcantopen" );
01070 }
01071 return new ImportStreamSource( $file );
01072 }
01073
01074 static function newFromUpload( $fieldname = "xmlimport" ) {
01075 $upload =& $_FILES[$fieldname];
01076
01077 if( !isset( $upload ) || !$upload['name'] ) {
01078 return new WikiErrorMsg( 'importnofile' );
01079 }
01080 if( !empty( $upload['error'] ) ) {
01081 switch($upload['error']){
01082 case 1: # The uploaded file exceeds the upload_max_filesize directive in php.ini.
01083 return new WikiErrorMsg( 'importuploaderrorsize' );
01084 case 2: # The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
01085 return new WikiErrorMsg( 'importuploaderrorsize' );
01086 case 3: # The uploaded file was only partially uploaded
01087 return new WikiErrorMsg( 'importuploaderrorpartial' );
01088 case 6: #Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.
01089 return new WikiErrorMsg( 'importuploaderrortemp' );
01090 # case else: # Currently impossible
01091 }
01092
01093 }
01094 $fname = $upload['tmp_name'];
01095 if( is_uploaded_file( $fname ) ) {
01096 return ImportStreamSource::newFromFile( $fname );
01097 } else {
01098 return new WikiErrorMsg( 'importnofile' );
01099 }
01100 }
01101
01102 static function newFromURL( $url, $method = 'GET' ) {
01103 wfDebug( __METHOD__ . ": opening $url\n" );
01104 # Use the standard HTTP fetch function; it times out
01105 # quicker and sorts out user-agent problems which might
01106 # otherwise prevent importing from large sites, such
01107 # as the Wikimedia cluster, etc.
01108 $data = Http::request( $method, $url );
01109 if( $data !== false ) {
01110 $file = tmpfile();
01111 fwrite( $file, $data );
01112 fflush( $file );
01113 fseek( $file, 0 );
01114 return new ImportStreamSource( $file );
01115 } else {
01116 return new WikiErrorMsg( 'importcantopen' );
01117 }
01118 }
01119
01120 public static function newFromInterwiki( $interwiki, $page, $history = false, $templates = false, $pageLinkDepth = 0 ) {
01121 if( $page == '' ) {
01122 return new WikiErrorMsg( 'import-noarticle' );
01123 }
01124 $link = Title::newFromText( "$interwiki:Special:Export/$page" );
01125 if( is_null( $link ) || $link->getInterwiki() == '' ) {
01126 return new WikiErrorMsg( 'importbadinterwiki' );
01127 } else {
01128 $params = array();
01129 if ( $history ) $params['history'] = 1;
01130 if ( $templates ) $params['templates'] = 1;
01131 if ( $pageLinkDepth ) $params['pagelink-depth'] = $pageLinkDepth;
01132 $url = $link->getFullUrl( $params );
01133 # For interwikis, use POST to avoid redirects.
01134 return ImportStreamSource::newFromURL( $url, "POST" );
01135 }
01136 }
01137 }