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( 'log_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 "restrictions":
00694 $this->appendfield = $name;
00695 $this->appenddata = "";
00696 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00697 xml_set_character_data_handler( $parser, "char_append" );
00698 break;
00699 case "revision":
00700 $this->push( "revision" );
00701 if( is_object( $this->pageTitle ) ) {
00702 $this->workRevision = new WikiRevision;
00703 $this->workRevision->setTitle( $this->pageTitle );
00704 $this->workRevisionCount++;
00705 } else {
00706
00707 $this->workRevision = null;
00708 }
00709 xml_set_element_handler( $parser, "in_revision", "out_revision" );
00710 break;
00711 case "upload":
00712 $this->push( "upload" );
00713 if( is_object( $this->pageTitle ) ) {
00714 $this->workRevision = new WikiRevision;
00715 $this->workRevision->setTitle( $this->pageTitle );
00716 $this->uploadCount++;
00717 } else {
00718
00719 $this->workRevision = null;
00720 }
00721 xml_set_element_handler( $parser, "in_upload", "out_upload" );
00722 break;
00723 default:
00724 return $this->throwXMLerror( "Element <$name> not allowed in a <page>." );
00725 }
00726 }
00727
00728 function out_page( $parser, $name ) {
00729 $name = $this->stripXmlNamespace($name);
00730 $this->debug( "out_page $name" );
00731 $this->pop();
00732 if( $name != "page" ) {
00733 return $this->throwXMLerror( "Expected </page>, got </$name>" );
00734 }
00735 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
00736
00737 $this->pageOutCallback( $this->pageTitle, $this->origTitle,
00738 $this->workRevisionCount, $this->workSuccessCount );
00739
00740 $this->workTitle = null;
00741 $this->workRevision = null;
00742 $this->workRevisionCount = 0;
00743 $this->workSuccessCount = 0;
00744 $this->pageTitle = null;
00745 $this->origTitle = null;
00746 }
00747
00748 function in_nothing( $parser, $name, $attribs ) {
00749 $name = $this->stripXmlNamespace($name);
00750 $this->debug( "in_nothing $name" );
00751 return $this->throwXMLerror( "No child elements allowed here; got <$name>" );
00752 }
00753
00754 function char_append( $parser, $data ) {
00755 $this->debug( "char_append '$data'" );
00756 $this->appenddata .= $data;
00757 }
00758
00759 function out_append( $parser, $name ) {
00760 $name = $this->stripXmlNamespace($name);
00761 $this->debug( "out_append $name" );
00762 if( $name != $this->appendfield ) {
00763 return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" );
00764 }
00765
00766 switch( $this->appendfield ) {
00767 case "title":
00768 $this->workTitle = $this->appenddata;
00769 $this->origTitle = Title::newFromText( $this->workTitle );
00770 if( !is_null( $this->mTargetNamespace ) && !is_null( $this->origTitle ) ) {
00771 $this->pageTitle = Title::makeTitle( $this->mTargetNamespace,
00772 $this->origTitle->getDBkey() );
00773 } else {
00774 $this->pageTitle = Title::newFromText( $this->workTitle );
00775 }
00776 if( is_null( $this->pageTitle ) ) {
00777
00778 $this->notice( "Skipping invalid page title '$this->workTitle'" );
00779 } elseif( $this->pageTitle->getInterwiki() != '' ) {
00780 $this->notice( "Skipping interwiki page title '$this->workTitle'" );
00781 $this->pageTitle = null;
00782 } else {
00783 $this->pageCallback( $this->workTitle );
00784 }
00785 break;
00786 case "id":
00787 if ( $this->parentTag() == 'revision' || $this->parentTag() == 'logitem' ) {
00788 if( $this->workRevision )
00789 $this->workRevision->setID( $this->appenddata );
00790 }
00791 break;
00792 case "text":
00793 if( $this->workRevision )
00794 $this->workRevision->setText( $this->appenddata );
00795 break;
00796 case "username":
00797 if( $this->workRevision )
00798 $this->workRevision->setUsername( $this->appenddata );
00799 break;
00800 case "ip":
00801 if( $this->workRevision )
00802 $this->workRevision->setUserIP( $this->appenddata );
00803 break;
00804 case "timestamp":
00805 if( $this->workRevision )
00806 $this->workRevision->setTimestamp( $this->appenddata );
00807 break;
00808 case "comment":
00809 if( $this->workRevision )
00810 $this->workRevision->setComment( $this->appenddata );
00811 break;
00812 case "type":
00813 if( $this->workRevision )
00814 $this->workRevision->setType( $this->appenddata );
00815 break;
00816 case "action":
00817 if( $this->workRevision )
00818 $this->workRevision->setAction( $this->appenddata );
00819 break;
00820 case "logtitle":
00821 if( $this->workRevision )
00822 $this->workRevision->setTitle( Title::newFromText( $this->appenddata ) );
00823 break;
00824 case "params":
00825 if( $this->workRevision )
00826 $this->workRevision->setParams( $this->appenddata );
00827 break;
00828 case "minor":
00829 if( $this->workRevision )
00830 $this->workRevision->setMinor( true );
00831 break;
00832 case "filename":
00833 if( $this->workRevision )
00834 $this->workRevision->setFilename( $this->appenddata );
00835 break;
00836 case "src":
00837 if( $this->workRevision )
00838 $this->workRevision->setSrc( $this->appenddata );
00839 break;
00840 case "size":
00841 if( $this->workRevision )
00842 $this->workRevision->setSize( intval( $this->appenddata ) );
00843 break;
00844 default:
00845 $this->debug( "Bad append: {$this->appendfield}" );
00846 }
00847 $this->appendfield = "";
00848 $this->appenddata = "";
00849
00850 $parent = $this->parentTag();
00851 xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
00852 xml_set_character_data_handler( $parser, "donothing" );
00853 }
00854
00855 function in_revision( $parser, $name, $attribs ) {
00856 $name = $this->stripXmlNamespace($name);
00857 $this->debug( "in_revision $name" );
00858 switch( $name ) {
00859 case "id":
00860 case "timestamp":
00861 case "comment":
00862 case "minor":
00863 case "text":
00864 $this->appendfield = $name;
00865 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00866 xml_set_character_data_handler( $parser, "char_append" );
00867 break;
00868 case "contributor":
00869 $this->push( "contributor" );
00870 xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
00871 break;
00872 default:
00873 return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
00874 }
00875 }
00876
00877 function out_revision( $parser, $name ) {
00878 $name = $this->stripXmlNamespace($name);
00879 $this->debug( "out_revision $name" );
00880 $this->pop();
00881 if( $name != "revision" ) {
00882 return $this->throwXMLerror( "Expected </revision>, got </$name>" );
00883 }
00884 xml_set_element_handler( $parser, "in_page", "out_page" );
00885
00886 if( $this->workRevision ) {
00887 $ok = call_user_func_array( $this->mRevisionCallback,
00888 array( $this->workRevision, $this ) );
00889 if( $ok ) {
00890 $this->workSuccessCount++;
00891 }
00892 }
00893 }
00894
00895 function in_logitem( $parser, $name, $attribs ) {
00896 $name = $this->stripXmlNamespace($name);
00897 $this->debug( "in_logitem $name" );
00898 switch( $name ) {
00899 case "id":
00900 case "timestamp":
00901 case "comment":
00902 case "type":
00903 case "action":
00904 case "logtitle":
00905 case "params":
00906 $this->appendfield = $name;
00907 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00908 xml_set_character_data_handler( $parser, "char_append" );
00909 break;
00910 case "contributor":
00911 $this->push( "contributor" );
00912 xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
00913 break;
00914 default:
00915 return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
00916 }
00917 }
00918
00919 function out_logitem( $parser, $name ) {
00920 $name = $this->stripXmlNamespace($name);
00921 $this->debug( "out_logitem $name" );
00922 $this->pop();
00923 if( $name != "logitem" ) {
00924 return $this->throwXMLerror( "Expected </logitem>, got </$name>" );
00925 }
00926 xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
00927
00928 if( $this->workRevision ) {
00929 $ok = call_user_func_array( $this->mLogItemCallback,
00930 array( $this->workRevision, $this ) );
00931 if( $ok ) {
00932 $this->workSuccessCount++;
00933 }
00934 }
00935 }
00936
00937 function in_upload( $parser, $name, $attribs ) {
00938 $name = $this->stripXmlNamespace($name);
00939 $this->debug( "in_upload $name" );
00940 switch( $name ) {
00941 case "timestamp":
00942 case "comment":
00943 case "text":
00944 case "filename":
00945 case "src":
00946 case "size":
00947 $this->appendfield = $name;
00948 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00949 xml_set_character_data_handler( $parser, "char_append" );
00950 break;
00951 case "contributor":
00952 $this->push( "contributor" );
00953 xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
00954 break;
00955 default:
00956 return $this->throwXMLerror( "Element <$name> not allowed in an <upload>." );
00957 }
00958 }
00959
00960 function out_upload( $parser, $name ) {
00961 $name = $this->stripXmlNamespace($name);
00962 $this->debug( "out_revision $name" );
00963 $this->pop();
00964 if( $name != "upload" ) {
00965 return $this->throwXMLerror( "Expected </upload>, got </$name>" );
00966 }
00967 xml_set_element_handler( $parser, "in_page", "out_page" );
00968
00969 if( $this->workRevision ) {
00970 $ok = call_user_func_array( $this->mUploadCallback,
00971 array( $this->workRevision, $this ) );
00972 if( $ok ) {
00973 $this->workUploadSuccessCount++;
00974 }
00975 }
00976 }
00977
00978 function in_contributor( $parser, $name, $attribs ) {
00979 $name = $this->stripXmlNamespace($name);
00980 $this->debug( "in_contributor $name" );
00981 switch( $name ) {
00982 case "username":
00983 case "ip":
00984 case "id":
00985 $this->appendfield = $name;
00986 xml_set_element_handler( $parser, "in_nothing", "out_append" );
00987 xml_set_character_data_handler( $parser, "char_append" );
00988 break;
00989 default:
00990 $this->throwXMLerror( "Invalid tag <$name> in <contributor>" );
00991 }
00992 }
00993
00994 function out_contributor( $parser, $name ) {
00995 $name = $this->stripXmlNamespace($name);
00996 $this->debug( "out_contributor $name" );
00997 $this->pop();
00998 if( $name != "contributor" ) {
00999 return $this->throwXMLerror( "Expected </contributor>, got </$name>" );
01000 }
01001 $parent = $this->parentTag();
01002 xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
01003 }
01004
01005 private function push( $name ) {
01006 array_push( $this->tagStack, $name );
01007 $this->debug( "PUSH $name" );
01008 }
01009
01010 private function pop() {
01011 $name = array_pop( $this->tagStack );
01012 $this->debug( "POP $name" );
01013 return $name;
01014 }
01015
01016 private function parentTag() {
01017 $name = $this->tagStack[count( $this->tagStack ) - 1];
01018 $this->debug( "PARENT $name" );
01019 return $name;
01020 }
01021
01022 }
01023
01028 class ImportStringSource {
01029 function __construct( $string ) {
01030 $this->mString = $string;
01031 $this->mRead = false;
01032 }
01033
01034 function atEnd() {
01035 return $this->mRead;
01036 }
01037
01038 function readChunk() {
01039 if( $this->atEnd() ) {
01040 return false;
01041 } else {
01042 $this->mRead = true;
01043 return $this->mString;
01044 }
01045 }
01046 }
01047
01052 class ImportStreamSource {
01053 function __construct( $handle ) {
01054 $this->mHandle = $handle;
01055 }
01056
01057 function atEnd() {
01058 return feof( $this->mHandle );
01059 }
01060
01061 function readChunk() {
01062 return fread( $this->mHandle, 32768 );
01063 }
01064
01065 static function newFromFile( $filename ) {
01066 $file = @fopen( $filename, 'rt' );
01067 if( !$file ) {
01068 return new WikiErrorMsg( "importcantopen" );
01069 }
01070 return new ImportStreamSource( $file );
01071 }
01072
01073 static function newFromUpload( $fieldname = "xmlimport" ) {
01074 $upload =& $_FILES[$fieldname];
01075
01076 if( !isset( $upload ) || !$upload['name'] ) {
01077 return new WikiErrorMsg( 'importnofile' );
01078 }
01079 if( !empty( $upload['error'] ) ) {
01080 switch($upload['error']){
01081 case 1: # The uploaded file exceeds the upload_max_filesize directive in php.ini.
01082 return new WikiErrorMsg( 'importuploaderrorsize' );
01083 case 2: # The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
01084 return new WikiErrorMsg( 'importuploaderrorsize' );
01085 case 3: # The uploaded file was only partially uploaded
01086 return new WikiErrorMsg( 'importuploaderrorpartial' );
01087 case 6: #Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.
01088 return new WikiErrorMsg( 'importuploaderrortemp' );
01089 # case else: # Currently impossible
01090 }
01091
01092 }
01093 $fname = $upload['tmp_name'];
01094 if( is_uploaded_file( $fname ) ) {
01095 return ImportStreamSource::newFromFile( $fname );
01096 } else {
01097 return new WikiErrorMsg( 'importnofile' );
01098 }
01099 }
01100
01101 static function newFromURL( $url, $method = 'GET' ) {
01102 wfDebug( __METHOD__ . ": opening $url\n" );
01103 # Use the standard HTTP fetch function; it times out
01104 # quicker and sorts out user-agent problems which might
01105 # otherwise prevent importing from large sites, such
01106 # as the Wikimedia cluster, etc.
01107 $data = Http::request( $method, $url );
01108 if( $data !== false ) {
01109 $file = tmpfile();
01110 fwrite( $file, $data );
01111 fflush( $file );
01112 fseek( $file, 0 );
01113 return new ImportStreamSource( $file );
01114 } else {
01115 return new WikiErrorMsg( 'importcantopen' );
01116 }
01117 }
01118
01119 public static function newFromInterwiki( $interwiki, $page, $history = false, $templates = false, $pageLinkDepth = 0 ) {
01120 if( $page == '' ) {
01121 return new WikiErrorMsg( 'import-noarticle' );
01122 }
01123 $link = Title::newFromText( "$interwiki:Special:Export/$page" );
01124 if( is_null( $link ) || $link->getInterwiki() == '' ) {
01125 return new WikiErrorMsg( 'importbadinterwiki' );
01126 } else {
01127 $params = array();
01128 if ( $history ) $params['history'] = 1;
01129 if ( $templates ) $params['templates'] = 1;
01130 if ( $pageLinkDepth ) $params['pagelink-depth'] = $pageLinkDepth;
01131 $url = $link->getFullUrl( $params );
01132 # For interwikis, use POST to avoid redirects.
01133 return ImportStreamSource::newFromURL( $url, "POST" );
01134 }
01135 }
01136 }