MediaWiki  REL1_24
SpecialImport.php
Go to the documentation of this file.
00001 <?php
00032 class SpecialImport extends SpecialPage {
00033     private $interwiki = false;
00034     private $subproject;
00035     private $fullInterwikiPrefix;
00036     private $namespace;
00037     private $rootpage = '';
00038     private $frompage = '';
00039     private $logcomment = false;
00040     private $history = true;
00041     private $includeTemplates = false;
00042     private $pageLinkDepth;
00043 
00047     public function __construct() {
00048         parent::__construct( 'Import', 'import' );
00049         $this->namespace = $this->getConfig()->get( 'ImportTargetNamespace' );
00050     }
00051 
00056     function execute( $par ) {
00057         $this->setHeaders();
00058         $this->outputHeader();
00059 
00060         $this->getOutput()->addModules( 'mediawiki.special.import' );
00061 
00062         $user = $this->getUser();
00063         if ( !$user->isAllowedAny( 'import', 'importupload' ) ) {
00064             throw new PermissionsError( 'import' );
00065         }
00066 
00067         # @todo Allow Title::getUserPermissionsErrors() to take an array
00068         # @todo FIXME: Title::checkSpecialsAndNSPermissions() has a very wierd expectation of what
00069         # getUserPermissionsErrors() might actually be used for, hence the 'ns-specialprotected'
00070         $errors = wfMergeErrorArrays(
00071             $this->getPageTitle()->getUserPermissionsErrors(
00072                 'import', $user, true,
00073                 array( 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' )
00074             ),
00075             $this->getPageTitle()->getUserPermissionsErrors(
00076                 'importupload', $user, true,
00077                 array( 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' )
00078             )
00079         );
00080 
00081         if ( $errors ) {
00082             throw new PermissionsError( 'import', $errors );
00083         }
00084 
00085         $this->checkReadOnly();
00086 
00087         $request = $this->getRequest();
00088         if ( $request->wasPosted() && $request->getVal( 'action' ) == 'submit' ) {
00089             $this->doImport();
00090         }
00091         $this->showForm();
00092     }
00093 
00097     private function doImport() {
00098         $isUpload = false;
00099         $request = $this->getRequest();
00100         $this->namespace = $request->getIntOrNull( 'namespace' );
00101         $sourceName = $request->getVal( "source" );
00102 
00103         $this->logcomment = $request->getText( 'log-comment' );
00104         $this->pageLinkDepth = $this->getConfig()->get( 'ExportMaxLinkDepth' ) == 0
00105             ? 0
00106             : $request->getIntOrNull( 'pagelink-depth' );
00107         $this->rootpage = $request->getText( 'rootpage' );
00108 
00109         $user = $this->getUser();
00110         if ( !$user->matchEditToken( $request->getVal( 'editToken' ) ) ) {
00111             $source = Status::newFatal( 'import-token-mismatch' );
00112         } elseif ( $sourceName == 'upload' ) {
00113             $isUpload = true;
00114             if ( $user->isAllowed( 'importupload' ) ) {
00115                 $source = ImportStreamSource::newFromUpload( "xmlimport" );
00116             } else {
00117                 throw new PermissionsError( 'importupload' );
00118             }
00119         } elseif ( $sourceName == "interwiki" ) {
00120             if ( !$user->isAllowed( 'import' ) ) {
00121                 throw new PermissionsError( 'import' );
00122             }
00123             $this->interwiki = $this->fullInterwikiPrefix = $request->getVal( 'interwiki' );
00124             // does this interwiki have subprojects?
00125             $importSources = $this->getConfig()->get( 'ImportSources' );
00126             $hasSubprojects = array_key_exists( $this->interwiki, $importSources );
00127             if ( !$hasSubprojects && !in_array( $this->interwiki, $importSources ) ) {
00128                 $source = Status::newFatal( "import-invalid-interwiki" );
00129             } else {
00130                 if ( $hasSubprojects ) {
00131                     $this->subproject = $request->getVal( 'subproject' );
00132                     $this->fullInterwikiPrefix .= ':' . $request->getVal( 'subproject' );
00133                 }
00134                 if ( $hasSubprojects && !in_array( $this->subproject, $importSources[$this->interwiki] ) ) {
00135                     $source = Status::newFatal( "import-invalid-interwiki" );
00136                 } else {
00137                     $this->history = $request->getCheck( 'interwikiHistory' );
00138                     $this->frompage = $request->getText( "frompage" );
00139                     $this->includeTemplates = $request->getCheck( 'interwikiTemplates' );
00140                     $source = ImportStreamSource::newFromInterwiki(
00141                         $this->fullInterwikiPrefix,
00142                         $this->frompage,
00143                         $this->history,
00144                         $this->includeTemplates,
00145                         $this->pageLinkDepth );
00146                 }
00147             }
00148         } else {
00149             $source = Status::newFatal( "importunknownsource" );
00150         }
00151 
00152         $out = $this->getOutput();
00153         if ( !$source->isGood() ) {
00154             $out->wrapWikiMsg(
00155                 "<p class=\"error\">\n$1\n</p>",
00156                 array( 'importfailed', $source->getWikiText() )
00157             );
00158         } else {
00159             $importer = new WikiImporter( $source->value );
00160             if ( !is_null( $this->namespace ) ) {
00161                 $importer->setTargetNamespace( $this->namespace );
00162             }
00163             if ( !is_null( $this->rootpage ) ) {
00164                 $statusRootPage = $importer->setTargetRootPage( $this->rootpage );
00165                 if ( !$statusRootPage->isGood() ) {
00166                     $out->wrapWikiMsg(
00167                         "<p class=\"error\">\n$1\n</p>",
00168                         array(
00169                             'import-options-wrong',
00170                             $statusRootPage->getWikiText(),
00171                             count( $statusRootPage->getErrorsArray() )
00172                         )
00173                     );
00174 
00175                     return;
00176                 }
00177             }
00178 
00179             $out->addWikiMsg( "importstart" );
00180 
00181             $reporter = new ImportReporter(
00182                 $importer,
00183                 $isUpload,
00184                 $this->fullInterwikiPrefix,
00185                 $this->logcomment
00186             );
00187             $reporter->setContext( $this->getContext() );
00188             $exception = false;
00189 
00190             $reporter->open();
00191             try {
00192                 $importer->doImport();
00193             } catch ( MWException $e ) {
00194                 $exception = $e;
00195             }
00196             $result = $reporter->close();
00197 
00198             if ( $exception ) {
00199                 # No source or XML parse error
00200                 $out->wrapWikiMsg(
00201                     "<p class=\"error\">\n$1\n</p>",
00202                     array( 'importfailed', $exception->getMessage() )
00203                 );
00204             } elseif ( !$result->isGood() ) {
00205                 # Zero revisions
00206                 $out->wrapWikiMsg(
00207                     "<p class=\"error\">\n$1\n</p>",
00208                     array( 'importfailed', $result->getWikiText() )
00209                 );
00210             } else {
00211                 # Success!
00212                 $out->addWikiMsg( 'importsuccess' );
00213             }
00214             $out->addHTML( '<hr />' );
00215         }
00216     }
00217 
00218     private function showForm() {
00219         $action = $this->getPageTitle()->getLocalURL( array( 'action' => 'submit' ) );
00220         $user = $this->getUser();
00221         $out = $this->getOutput();
00222         $importSources = $this->getConfig()->get( 'ImportSources' );
00223 
00224         if ( $user->isAllowed( 'importupload' ) ) {
00225             $out->addHTML(
00226                 Xml::fieldset( $this->msg( 'import-upload' )->text() ) .
00227                     Xml::openElement(
00228                         'form',
00229                         array(
00230                             'enctype' => 'multipart/form-data',
00231                             'method' => 'post',
00232                             'action' => $action,
00233                             'id' => 'mw-import-upload-form'
00234                         )
00235                     ) .
00236                     $this->msg( 'importtext' )->parseAsBlock() .
00237                     Html::hidden( 'action', 'submit' ) .
00238                     Html::hidden( 'source', 'upload' ) .
00239                     Xml::openElement( 'table', array( 'id' => 'mw-import-table-upload' ) ) .
00240                     "<tr>
00241                     <td class='mw-label'>" .
00242                     Xml::label( $this->msg( 'import-upload-filename' )->text(), 'xmlimport' ) .
00243                     "</td>
00244                     <td class='mw-input'>" .
00245                     Html::input( 'xmlimport', '', 'file', array( 'id' => 'xmlimport' ) ) . ' ' .
00246                     "</td>
00247                 </tr>
00248                 <tr>
00249                     <td class='mw-label'>" .
00250                     Xml::label( $this->msg( 'import-comment' )->text(), 'mw-import-comment' ) .
00251                     "</td>
00252                     <td class='mw-input'>" .
00253                     Xml::input( 'log-comment', 50, '',
00254                         array( 'id' => 'mw-import-comment', 'type' => 'text' ) ) . ' ' .
00255                     "</td>
00256                 </tr>
00257                 <tr>
00258                     <td class='mw-label'>" .
00259                     Xml::label(
00260                         $this->msg( 'import-interwiki-rootpage' )->text(),
00261                         'mw-interwiki-rootpage-upload'
00262                     ) .
00263                     "</td>
00264                     <td class='mw-input'>" .
00265                     Xml::input( 'rootpage', 50, $this->rootpage,
00266                         array( 'id' => 'mw-interwiki-rootpage-upload', 'type' => 'text' ) ) . ' ' .
00267                     "</td>
00268                 </tr>
00269                 <tr>
00270                     <td></td>
00271                     <td class='mw-submit'>" .
00272                     Xml::submitButton( $this->msg( 'uploadbtn' )->text() ) .
00273                     "</td>
00274                 </tr>" .
00275                     Xml::closeElement( 'table' ) .
00276                     Html::hidden( 'editToken', $user->getEditToken() ) .
00277                     Xml::closeElement( 'form' ) .
00278                     Xml::closeElement( 'fieldset' )
00279             );
00280         } else {
00281             if ( empty( $importSources ) ) {
00282                 $out->addWikiMsg( 'importnosources' );
00283             }
00284         }
00285 
00286         if ( $user->isAllowed( 'import' ) && !empty( $importSources ) ) {
00287             # Show input field for import depth only if $wgExportMaxLinkDepth > 0
00288             $importDepth = '';
00289             if ( $this->getConfig()->get( 'ExportMaxLinkDepth' ) > 0 ) {
00290                 $importDepth = "<tr>
00291                             <td class='mw-label'>" .
00292                     $this->msg( 'export-pagelinks' )->parse() .
00293                     "</td>
00294                             <td class='mw-input'>" .
00295                     Xml::input( 'pagelink-depth', 3, 0 ) .
00296                     "</td>
00297                 </tr>";
00298             }
00299 
00300             $out->addHTML(
00301                 Xml::fieldset( $this->msg( 'importinterwiki' )->text() ) .
00302                     Xml::openElement(
00303                         'form',
00304                         array(
00305                             'method' => 'post',
00306                             'action' => $action,
00307                             'id' => 'mw-import-interwiki-form'
00308                         )
00309                     ) .
00310                     $this->msg( 'import-interwiki-text' )->parseAsBlock() .
00311                     Html::hidden( 'action', 'submit' ) .
00312                     Html::hidden( 'source', 'interwiki' ) .
00313                     Html::hidden( 'editToken', $user->getEditToken() ) .
00314                     Xml::openElement( 'table', array( 'id' => 'mw-import-table-interwiki' ) ) .
00315                     "<tr>
00316                     <td class='mw-label'>" .
00317                     Xml::label( $this->msg( 'import-interwiki-sourcewiki' )->text(), 'interwiki' ) .
00318                     "</td>
00319                     <td class='mw-input'>" .
00320                     Xml::openElement(
00321                         'select',
00322                         array( 'name' => 'interwiki', 'id' => 'interwiki' )
00323                     )
00324             );
00325 
00326             $needSubprojectField = false;
00327             foreach ( $importSources as $key => $value ) {
00328                 if ( is_int( $key ) ) {
00329                     $key = $value;
00330                 } elseif ( $value !== $key ) {
00331                     $needSubprojectField = true;
00332                 }
00333 
00334                 $attribs = array(
00335                     'value' => $key,
00336                 );
00337                 if ( is_array( $value ) ) {
00338                     $attribs['data-subprojects'] = implode( ' ', $value );
00339                 }
00340                 if ( $this->interwiki === $key ) {
00341                     $attribs['selected'] = 'selected';
00342                 }
00343                 $out->addHTML( Html::element( 'option', $attribs, $key ) );
00344             }
00345 
00346             $out->addHTML(
00347                 Xml::closeElement( 'select' )
00348             );
00349 
00350             if ( $needSubprojectField ) {
00351                 $out->addHTML(
00352                     Xml::openElement(
00353                         'select',
00354                         array( 'name' => 'subproject', 'id' => 'subproject' )
00355                     )
00356                 );
00357 
00358                 $subprojectsToAdd = array();
00359                 foreach ( $importSources as $key => $value ) {
00360                     if ( is_array( $value ) ) {
00361                         $subprojectsToAdd = array_merge( $subprojectsToAdd, $value );
00362                     }
00363                 }
00364                 $subprojectsToAdd = array_unique( $subprojectsToAdd );
00365                 sort( $subprojectsToAdd );
00366                 foreach ( $subprojectsToAdd as $subproject ) {
00367                     $out->addHTML( Xml::option( $subproject, $subproject, $this->subproject === $subproject ) );
00368                 }
00369 
00370                 $out->addHTML(
00371                     Xml::closeElement( 'select' )
00372                 );
00373             }
00374 
00375             $out->addHTML(
00376                     "</td>
00377                 </tr>
00378                 <tr>
00379                     <td class='mw-label'>" .
00380                     Xml::label( $this->msg( 'import-interwiki-sourcepage' )->text(), 'frompage' ) .
00381                     "</td>
00382                     <td class='mw-input'>" .
00383                     Xml::input( 'frompage', 50, $this->frompage, array( 'id' => 'frompage' ) ) .
00384                     "</td>
00385                 </tr>
00386                 <tr>
00387                     <td>
00388                     </td>
00389                     <td class='mw-input'>" .
00390                     Xml::checkLabel(
00391                         $this->msg( 'import-interwiki-history' )->text(),
00392                         'interwikiHistory',
00393                         'interwikiHistory',
00394                         $this->history
00395                     ) .
00396                     "</td>
00397                 </tr>
00398                 <tr>
00399                     <td>
00400                     </td>
00401                     <td class='mw-input'>" .
00402                     Xml::checkLabel(
00403                         $this->msg( 'import-interwiki-templates' )->text(),
00404                         'interwikiTemplates',
00405                         'interwikiTemplates',
00406                         $this->includeTemplates
00407                     ) .
00408                     "</td>
00409                 </tr>
00410                 $importDepth
00411                 <tr>
00412                     <td class='mw-label'>" .
00413                     Xml::label( $this->msg( 'import-interwiki-namespace' )->text(), 'namespace' ) .
00414                     "</td>
00415                     <td class='mw-input'>" .
00416                     Html::namespaceSelector(
00417                         array(
00418                             'selected' => $this->namespace,
00419                             'all' => '',
00420                         ), array(
00421                             'name' => 'namespace',
00422                             'id' => 'namespace',
00423                             'class' => 'namespaceselector',
00424                         )
00425                     ) .
00426                     "</td>
00427                 </tr>
00428                 <tr>
00429                     <td class='mw-label'>" .
00430                     Xml::label( $this->msg( 'import-comment' )->text(), 'mw-interwiki-comment' ) .
00431                     "</td>
00432                     <td class='mw-input'>" .
00433                     Xml::input( 'log-comment', 50, '',
00434                         array( 'id' => 'mw-interwiki-comment', 'type' => 'text' ) ) . ' ' .
00435                     "</td>
00436                 </tr>
00437                 <tr>
00438                     <td class='mw-label'>" .
00439                     Xml::label(
00440                         $this->msg( 'import-interwiki-rootpage' )->text(),
00441                         'mw-interwiki-rootpage-interwiki'
00442                     ) .
00443                     "</td>
00444                     <td class='mw-input'>" .
00445                     Xml::input( 'rootpage', 50, $this->rootpage,
00446                         array( 'id' => 'mw-interwiki-rootpage-interwiki', 'type' => 'text' ) ) . ' ' .
00447                     "</td>
00448                 </tr>
00449                 <tr>
00450                     <td>
00451                     </td>
00452                     <td class='mw-submit'>" .
00453                     Xml::submitButton(
00454                         $this->msg( 'import-interwiki-submit' )->text(),
00455                         Linker::tooltipAndAccesskeyAttribs( 'import' )
00456                     ) .
00457                     "</td>
00458                 </tr>" .
00459                     Xml::closeElement( 'table' ) .
00460                     Xml::closeElement( 'form' ) .
00461                     Xml::closeElement( 'fieldset' )
00462             );
00463         }
00464     }
00465 
00466     protected function getGroupName() {
00467         return 'pagetools';
00468     }
00469 }
00470 
00475 class ImportReporter extends ContextSource {
00476     private $reason = false;
00477     private $mOriginalLogCallback = null;
00478     private $mOriginalPageOutCallback = null;
00479     private $mLogItemCount = 0;
00480 
00487     function __construct( $importer, $upload, $interwiki, $reason = false ) {
00488         $this->mOriginalPageOutCallback =
00489             $importer->setPageOutCallback( array( $this, 'reportPage' ) );
00490         $this->mOriginalLogCallback =
00491             $importer->setLogItemCallback( array( $this, 'reportLogItem' ) );
00492         $importer->setNoticeCallback( array( $this, 'reportNotice' ) );
00493         $this->mPageCount = 0;
00494         $this->mIsUpload = $upload;
00495         $this->mInterwiki = $interwiki;
00496         $this->reason = $reason;
00497     }
00498 
00499     function open() {
00500         $this->getOutput()->addHTML( "<ul>\n" );
00501     }
00502 
00503     function reportNotice( $msg, array $params ) {
00504         $this->getOutput()->addHTML(
00505             Html::element( 'li', array(), $this->msg( $msg, $params )->text() )
00506         );
00507     }
00508 
00509     function reportLogItem( /* ... */ ) {
00510         $this->mLogItemCount++;
00511         if ( is_callable( $this->mOriginalLogCallback ) ) {
00512             call_user_func_array( $this->mOriginalLogCallback, func_get_args() );
00513         }
00514     }
00515 
00524     function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) {
00525         $args = func_get_args();
00526         call_user_func_array( $this->mOriginalPageOutCallback, $args );
00527 
00528         if ( $title === null ) {
00529             # Invalid or non-importable title; a notice is already displayed
00530             return;
00531         }
00532 
00533         $this->mPageCount++;
00534 
00535         if ( $successCount > 0 ) {
00536             $this->getOutput()->addHTML(
00537                 "<li>" . Linker::linkKnown( $title ) . " " .
00538                     $this->msg( 'import-revision-count' )->numParams( $successCount )->escaped() .
00539                     "</li>\n"
00540             );
00541 
00542             $log = new LogPage( 'import' );
00543             if ( $this->mIsUpload ) {
00544                 $detail = $this->msg( 'import-logentry-upload-detail' )->numParams(
00545                     $successCount )->inContentLanguage()->text();
00546                 if ( $this->reason ) {
00547                     $detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text()
00548                         . $this->reason;
00549                 }
00550                 $log->addEntry( 'upload', $title, $detail, array(), $this->getUser() );
00551             } else {
00552                 $interwiki = '[[:' . $this->mInterwiki . ':' .
00553                     $origTitle->getPrefixedText() . ']]';
00554                 $detail = $this->msg( 'import-logentry-interwiki-detail' )->numParams(
00555                     $successCount )->params( $interwiki )->inContentLanguage()->text();
00556                 if ( $this->reason ) {
00557                     $detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text()
00558                         . $this->reason;
00559                 }
00560                 $log->addEntry( 'interwiki', $title, $detail, array(), $this->getUser() );
00561             }
00562 
00563             $comment = $detail; // quick
00564             $dbw = wfGetDB( DB_MASTER );
00565             $latest = $title->getLatestRevID();
00566             $nullRevision = Revision::newNullRevision(
00567                 $dbw,
00568                 $title->getArticleID(),
00569                 $comment,
00570                 true,
00571                 $this->getUser()
00572             );
00573 
00574             if ( !is_null( $nullRevision ) ) {
00575                 $nullRevision->insertOn( $dbw );
00576                 $page = WikiPage::factory( $title );
00577                 # Update page record
00578                 $page->updateRevisionOn( $dbw, $nullRevision );
00579                 wfRunHooks(
00580                     'NewRevisionFromEditComplete',
00581                     array( $page, $nullRevision, $latest, $this->getUser() )
00582                 );
00583             }
00584         } else {
00585             $this->getOutput()->addHTML( "<li>" . Linker::linkKnown( $title ) . " " .
00586                 $this->msg( 'import-nonewrevisions' )->escaped() . "</li>\n" );
00587         }
00588     }
00589 
00590     function close() {
00591         $out = $this->getOutput();
00592         if ( $this->mLogItemCount > 0 ) {
00593             $msg = $this->msg( 'imported-log-entries' )->numParams( $this->mLogItemCount )->parse();
00594             $out->addHTML( Xml::tags( 'li', null, $msg ) );
00595         } elseif ( $this->mPageCount == 0 && $this->mLogItemCount == 0 ) {
00596             $out->addHTML( "</ul>\n" );
00597 
00598             return Status::newFatal( 'importnopages' );
00599         }
00600         $out->addHTML( "</ul>\n" );
00601 
00602         return Status::newGood( $this->mPageCount );
00603     }
00604 }