SpecialPageTranslationMovePage.php

Go to the documentation of this file.
00001 <?php
00017 class SpecialPageTranslationMovePage extends UnlistedSpecialPage {
00018     // Basic form parameters both as text and as titles
00019     protected $newText, $oldText;
00020 
00024     protected $newTitle, $oldTitle;
00025 
00026     // Other form parameters
00030     protected $subaction;
00031 
00035     protected $reason;
00036 
00040     protected $moveSubpages;
00041 
00045     protected $page;
00046 
00050     protected $old;
00051 
00055     protected $translationPages = null;
00056 
00060     protected $sectionPages = null;
00061 
00062     public function __construct( $old ) {
00063         parent::__construct( 'Movepage' );
00064         $this->old = $old;
00065     }
00066 
00071     public function execute( $par ) {
00072         $request = $this->getRequest();
00073         $user = $this->getUser();
00074 
00075         // Yes, the use of getVal() and getText() is wanted, see bug 20365
00076         $this->oldText = $request->getVal( 'wpOldTitle', $request->getVal( 'target', $par ) );
00077         $this->newText = $request->getText( 'wpNewTitle' );
00078 
00079         $this->oldTitle = Title::newFromText( $this->oldText );
00080         $this->newTitle = Title::newFromText( $this->newText );
00081 
00082         $this->reason = $request->getText( 'reason' );
00083         // Checkboxes that default being checked are tricky
00084         $this->moveSubpages = $request->getBool( 'subpages', !$request->wasPosted() );
00085 
00086         if ( $this->doBasicChecks() !== true ) {
00087             return;
00088         }
00089 
00090         // Real stuff starts here
00091         $page = TranslatablePage::newFromTitle( $this->oldTitle );
00092         if ( $page->getMarkedTag() !== false ) {
00093             $this->page = $page;
00094 
00095             $this->getOutput()->setPagetitle( $this->msg( 'pt-movepage-title', $this->oldText ) );
00096 
00097             if ( !$user->isAllowed( 'pagetranslation' ) ) {
00098                 throw new PermissionsError( 'pagetranslation' );
00099             }
00100 
00101             // Is there really no better way to do this?
00102             $subactionText = $request->getText( 'subaction' );
00103             switch ( $subactionText ) {
00104                 case $this->msg( 'pt-movepage-action-check' )->text():
00105                     $subaction = 'check';
00106                     break;
00107                 case $this->msg( 'pt-movepage-action-perform' )->text():
00108                     $subaction = 'perform';
00109                     break;
00110                 case $this->msg( 'pt-movepage-action-other' )->text():
00111                     $subaction = '';
00112                     break;
00113                 default:
00114                     $subaction = '';
00115             }
00116 
00117             if ( $subaction === 'check' && $this->checkToken() && $request->wasPosted() ) {
00118                 $blockers = $this->checkMoveBlockers();
00119                 if ( count( $blockers ) ) {
00120                     $this->showErrors( $blockers );
00121                     $this->showForm();
00122                 } else {
00123                     $this->showConfirmation();
00124                 }
00125             } elseif ( $subaction === 'perform' && $this->checkToken() && $request->wasPosted() ) {
00126                 $this->performAction();
00127             } else {
00128                 $this->showForm();
00129             }
00130         } else {
00131             // Delegate... don't want to reimplement this
00132             if ( $this->old ) {
00133                 $this->doOldNormalMovePage();
00134             } else {
00135                 $this->doNormalMovePage( $par );
00136             }
00137         }
00138     }
00139 
00146     protected function doBasicChecks() {
00147         # Check for database lock
00148         if ( wfReadOnly() ) {
00149             throw new ReadOnlyError;
00150         }
00151 
00152         if ( $this->oldTitle === null ) {
00153             throw new ErrorPageError( 'notargettitle', 'notargettext' );
00154         }
00155 
00156         if ( !$this->oldTitle->exists() ) {
00157             throw new ErrorPageError( 'nopagetitle', 'nopagetext' );
00158         }
00159 
00160         # Check rights
00161         $permErrors = $this->oldTitle->getUserPermissionsErrors( 'move', $this->getUser() );
00162         if ( !empty( $permErrors ) ) {
00163             throw new PermissionsError( 'move', $permErrors );
00164         }
00165 
00166         // Let the caller know it's safe to continue
00167         return true;
00168     }
00169 
00170     protected function doNormalMovePage( $par ) {
00171         $sp = new MovePageForm();
00172         $sp->execute( $par );
00173     }
00174 
00175     protected function doOldNormalMovePage() {
00176         $form = new MovePageForm( $this->oldTitle, $this->newTitle );
00177         $request = $this->getRequest();
00178 
00179         if ( 'submit' == $request->getVal( 'action' ) &&
00180             $this->checkToken() &&
00181             $request->wasPosted()
00182         ) {
00183             $form->doSubmit();
00184         } else {
00185             $form->showForm( '' );
00186         }
00187     }
00188 
00195     protected function checkToken() {
00196         return $this->getUser()->matchEditToken( $this->getRequest()->getVal( 'wpEditToken' ) );
00197     }
00198 
00203     protected function showErrors( array $errors ) {
00204         if ( count( $errors ) ) {
00205             $out = $this->getOutput();
00206 
00207             $out->addHTML( Html::openElement( 'div', array( 'class' => 'error' ) ) );
00208             $out->addWikiMsg(
00209                 'pt-movepage-blockers',
00210                 $this->getLanguage()->formatNum( count( $errors ) )
00211             );
00212             $out->addHTML( '<ul>' );
00213             foreach ( $errors as $error ) {
00214                 // I have no idea what the parser is doing, but this is mad.
00215                 // <li>$1</li> doesn't work.
00216                 $out->wrapWikiMsg( "<li>$1", $error );
00217             }
00218             $out->addHTML( '</ul></div>' );
00219         }
00220     }
00221 
00225     protected function showForm() {
00226         $this->getOutput()->addWikiMsg( 'pt-movepage-intro' );
00227 
00228         $br = Html::element( 'br' );
00229         $subaction = array( 'name' => 'subaction' );
00230         $readonly = array( 'readonly' => 'readonly' );
00231         $formParams = array(
00232             'method' => 'post',
00233             'action' => $this->getTitle( $this->oldText )->getLocalURL()
00234         );
00235 
00236         $form = array();
00237         $form[] = Xml::fieldset( $this->msg( 'pt-movepage-legend' )->text() );
00238         $form[] = Html::openElement( 'form', $formParams );
00239         $form[] = Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
00240         $this->addInputLabel(
00241             $form,
00242             $this->msg( 'pt-movepage-current' )->text(),
00243             'wpOldTitle',
00244             30,
00245             $this->oldText,
00246             $readonly
00247         );
00248         $this->addInputLabel(
00249             $form,
00250             $this->msg( 'pt-movepage-new' )->text(),
00251             'wpNewTitle',
00252             30,
00253             $this->newText
00254         );
00255         $this->addInputLabel(
00256             $form,
00257             $this->msg( 'pt-movepage-reason' )->text(),
00258             'reason',
00259             45,
00260             $this->reason
00261         );
00262         $form[] = Xml::checkLabel(
00263             $this->msg( 'pt-movepage-subpages' )->text(),
00264             'subpages',
00265             'mw-subpages',
00266             $this->moveSubpages
00267         ) . $br;
00268         $form[] = Xml::submitButton( $this->msg( 'pt-movepage-action-check' )->text(), $subaction );
00269         $form[] = Xml::closeElement( 'form' );
00270         $form[] = Xml::closeElement( 'fieldset' );
00271         $this->getOutput()->addHTML( implode( "\n", $form ) );
00272     }
00273 
00285     protected function addInputLabel( &$form, $label, $name, $size = false, $text = false,
00286         $attribs = array()
00287     ) {
00288         $br = Html::element( 'br' );
00289         list( $label, $input ) = Xml::inputLabelSep(
00290             $label,
00291             $name,
00292             $name,
00293             $size,
00294             $text,
00295             $attribs
00296         );
00297         $form[] = $label . $br;
00298         $form[] = $input . $br;
00299     }
00300 
00305     protected function showConfirmation() {
00306         $out = $this->getOutput();
00307 
00308         $out->addWikiMsg( 'pt-movepage-intro' );
00309 
00310         $base = $this->oldTitle->getPrefixedText();
00311         $target = $this->newTitle;
00312         $count = 0;
00313 
00314         $types = array(
00315             'pt-movepage-list-pages' => array( $this->oldTitle ),
00316             'pt-movepage-list-translation' => $this->getTranslationPages(),
00317             'pt-movepage-list-section' => $this->getSectionPages(),
00318             'pt-movepage-list-other' => $this->getSubpages(),
00319         );
00320 
00321         foreach ( $types as $type => $pages ) {
00322             $out->wrapWikiMsg( '=== $1 ===', array( $type, count( $pages ) ) );
00323 
00324             $lines = array();
00325             foreach ( $pages as $old ) {
00326                 $toBeMoved = true;
00327 
00328                 // These pages need specific checks
00329                 if ( $type === 'pt-movepage-list-other' ) {
00330                     $toBeMoved = $this->moveSubpages;
00331 
00332                     if ( TranslatablePage::isTranslationPage( $old ) ) {
00333                         continue;
00334                     }
00335                 }
00336 
00337                 if ( $toBeMoved ) {
00338                     $count++;
00339                 }
00340 
00341                 $lines[] = $this->getChangeLine( $base, $old, $target, $toBeMoved );
00342             }
00343 
00344             $out->addWikiText( implode( "\n", $lines ) );
00345         }
00346 
00347         $out->addWikiText( "----\n" );
00348         $out->addWikiMsg( 'pt-movepage-list-count', $this->getLanguage()->formatNum( $count ) );
00349 
00350         $br = Html::element( 'br' );
00351         $readonly = array( 'readonly' => 'readonly' );
00352         $subaction = array( 'name' => 'subaction' );
00353         $formParams = array(
00354             'method' => 'post',
00355             'action' => $this->getTitle( $this->oldText )->getLocalURL()
00356         );
00357 
00358         $form = array();
00359         $form[] = Xml::fieldset( $this->msg( 'pt-movepage-legend' )->text() );
00360         $form[] = Html::openElement( 'form', $formParams );
00361         $form[] = Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
00362         $this->addInputLabel(
00363             $form,
00364             $this->msg( 'pt-movepage-current' )->text(),
00365             'wpOldTitle',
00366             30,
00367             $this->oldText,
00368             $readonly
00369         );
00370         $this->addInputLabel(
00371             $form,
00372             $this->msg( 'pt-movepage-new' )->text(),
00373             'wpNewTitle',
00374             30,
00375             $this->newText,
00376             $readonly
00377         );
00378         $this->addInputLabel(
00379             $form,
00380             $this->msg( 'pt-movepage-reason' )->text(),
00381             'reason',
00382             60,
00383             $this->reason
00384         );
00385         $form[] = Html::hidden( 'subpages', $this->moveSubpages );
00386         $form[] = Xml::checkLabel(
00387             $this->msg( 'pt-movepage-subpages' )->text(),
00388             'subpagesFake',
00389             'mw-subpages',
00390             $this->moveSubpages,
00391             $readonly
00392         ) . $br;
00393         $form[] = Xml::submitButton( $this->msg( 'pt-movepage-action-perform' )->text(), $subaction );
00394         $form[] = Xml::submitButton( $this->msg( 'pt-movepage-action-other' )->text(), $subaction );
00395         $form[] = Xml::closeElement( 'form' );
00396         $form[] = Xml::closeElement( 'fieldset' );
00397         $out->addHTML( implode( "\n", $form ) );
00398     }
00399 
00407     protected function getChangeLine( $base, Title $old, Title $target, $enabled = true ) {
00408         $to = $this->newPageTitle( $base, $old, $target );
00409 
00410         if ( $enabled ) {
00411             return '* ' . $old->getPrefixedText() . ' → ' . $to;
00412         } else {
00413             return '* ' . $old->getPrefixedText();
00414         }
00415     }
00416 
00417     protected function performAction() {
00418         $jobs = array();
00419         $user = $this->getUser();
00420         $target = $this->newTitle;
00421         $base = $this->oldTitle->getPrefixedText();
00422         $oldLatest = $this->oldTitle->getLatestRevId();
00423 
00424         $params = array(
00425             'base-source' => $this->oldTitle->getPrefixedText(),
00426             'base-target' => $this->newTitle->getPrefixedText(),
00427         );
00428 
00429         $translationPages = $this->getTranslationPages();
00430         foreach ( $translationPages as $old ) {
00431             $to = $this->newPageTitle( $base, $old, $target );
00432             $jobs[$old->getPrefixedText()] = TranslateMoveJob::newJob( $old, $to, $params, $user );
00433         }
00434 
00435         $sectionPages = $this->getSectionPages();
00436         foreach ( $sectionPages as $old ) {
00437             $to = $this->newPageTitle( $base, $old, $target );
00438             $jobs[$old->getPrefixedText()] = TranslateMoveJob::newJob( $old, $to, $params, $user );
00439         }
00440 
00441         if ( $this->moveSubpages ) {
00442             $subpages = $this->getSubpages();
00443             foreach ( $subpages as $old ) {
00444                 if ( TranslatablePage::isTranslationPage( $old ) ) {
00445                     continue;
00446                 }
00447 
00448                 $to = $this->newPageTitle( $base, $old, $target );
00449                 $jobs[$old->getPrefixedText()] = TranslateMoveJob::newJob(
00450                     $old,
00451                     $to,
00452                     $params,
00453                     $user
00454                 );
00455             }
00456         }
00457 
00458         // This is used by TranslateMoveJob
00459         wfGetCache( CACHE_ANYTHING )->set( wfMemcKey( 'translate-pt-move', $base ), count( $jobs ) );
00460         Job::batchInsert( $jobs );
00461 
00462         TranslateMoveJob::forceRedirects( false );
00463 
00464         $errors = $this->oldTitle->moveTo( $this->newTitle, true, $this->reason, false );
00465         if ( is_array( $errors ) ) {
00466             $this->showErrors( $errors );
00467         }
00468 
00469         TranslateMoveJob::forceRedirects( true );
00470 
00471         $newTpage = TranslatablePage::newFromTitle( $this->newTitle );
00472         $newTpage->addReadyTag( $this->newTitle->getLatestRevId( Title::GAID_FOR_UPDATE ) );
00473 
00474         if ( $newTpage->getMarkedTag() === $oldLatest ) {
00475             $newTpage->addMarkedTag( $this->newTitle->getLatestRevId( Title::GAID_FOR_UPDATE ) );
00476         }
00477 
00478         // remove the entries from metadata table.
00479         $oldGroupId = $this->page->getMessageGroupId();
00480         $newGroupId = $newTpage->getMessageGroupId();
00481         $this->moveMetadata( $oldGroupId, $newGroupId );
00482 
00483         MessageGroups::clearCache();
00484         MessageIndexRebuildJob::newJob()->insert();
00485 
00486         $this->getOutput()->addWikiMsg( 'pt-movepage-started' );
00487     }
00488 
00489     protected function moveMetadata( $oldGroupId, $newGroupId ) {
00490         $prioritylangs = TranslateMetadata::get( $oldGroupId, 'prioritylangs' );
00491         $priorityforce = TranslateMetadata::get( $oldGroupId, 'priorityforce' );
00492         $priorityreason = TranslateMetadata::get( $oldGroupId, 'priorityreason' );
00493         TranslateMetadata::set( $oldGroupId, 'prioritylangs', false );
00494         TranslateMetadata::set( $oldGroupId, 'priorityforce', false );
00495         TranslateMetadata::set( $oldGroupId, 'priorityreason', false );
00496         if ( $prioritylangs ) {
00497             TranslateMetadata::set( $newGroupId, 'prioritylangs', $prioritylangs );
00498         }
00499         if ( $priorityforce ) {
00500             TranslateMetadata::set( $newGroupId, 'priorityforce', $priorityforce );
00501         }
00502         if ( $priorityreason !== false ) {
00503             TranslateMetadata::set( $newGroupId, 'priorityreason', $priorityreason );
00504         }
00505         // make the changes in aggregate groups metadata, if present in any of them.
00506         $groups = MessageGroups::getAllGroups();
00507         foreach ( $groups as $group ) {
00508             if ( $group instanceof AggregateMessageGroup ) {
00509                 $subgroups = TranslateMetadata::get( $group->getId(), 'subgroups' );
00510                 if ( $subgroups !== false ) {
00511                     $subgroups = explode( ',', $subgroups );
00512                     $subgroups = array_flip( $subgroups );
00513                     if ( isset( $subgroups[$oldGroupId] ) ) {
00514                         $subgroups[$newGroupId] = $subgroups[$oldGroupId];
00515                         unset( $subgroups[$oldGroupId] );
00516                         $subgroups = array_flip( $subgroups );
00517                         TranslateMetadata::set(
00518                             $group->getId(),
00519                             'subgroups',
00520                             implode( ',', $subgroups )
00521                         );
00522                     }
00523                 }
00524             }
00525         }
00526     }
00527 
00528     protected function checkMoveBlockers() {
00529         $blockers = array();
00530 
00531         $target = $this->newTitle;
00532 
00533         if ( !$target ) {
00534             $blockers[] = array( 'pt-movepage-block-base-invalid' );
00535 
00536             return $blockers;
00537         }
00538 
00539         if ( $target->getNamespace() == NS_MEDIAWIKI ||
00540             $target->getNamespace() == NS_TRANSLATIONS
00541         ) {
00542             $blockers[] = array( 'immobile-target-namespace', $target->getNsText() );
00543 
00544             return $blockers;
00545         }
00546 
00547         $base = $this->oldTitle->getPrefixedText();
00548 
00549         if ( $target->exists() ) {
00550             $blockers[] = array( 'pt-movepage-block-base-exists', $target->getPrefixedText() );
00551         } else {
00552             $errors = $this->oldTitle->isValidMoveOperation( $target, true, $this->reason );
00553             if ( is_array( $errors ) ) {
00554                 $blockers = array_merge( $blockers, $errors );
00555             }
00556         }
00557 
00558         // Don't spam the same errors for all pages if base page fails
00559         if ( $blockers ) {
00560             return $blockers;
00561         }
00562 
00563         // Collect all the old and new titles for checcks
00564         $titles = array();
00565 
00566         $pages = $this->getTranslationPages();
00567         foreach ( $pages as $old ) {
00568             $titles['tp'][] = array( $old, $this->newPageTitle( $base, $old, $target ) );
00569         }
00570 
00571         $pages = $this->getSectionPages();
00572         foreach ( $pages as $old ) {
00573             $titles['section'][] = array( $old, $this->newPageTitle( $base, $old, $target ) );
00574         }
00575 
00576         $subpages = array();
00577         if ( $this->moveSubpages ) {
00578             $subpages = $this->getSubpages();
00579         }
00580         foreach ( $subpages as $old ) {
00581             if ( !TranslatablePage::isTranslationPage( $old ) ) {
00582                 $titles['subpage'][] = array( $old, $this->newPageTitle( $base, $old, $target ) );
00583             }
00584         }
00585 
00586         // Check that all new titles are valid
00587         $lb = new LinkBatch();
00588         foreach ( $titles as $type => $list ) {
00589             // Give grep a chance to find the usages:
00590             // pt-movepage-block-tp-invalid, pt-movepage-block-section-invalid,
00591             // pt-movepage-block-subpage-invalid
00592             foreach ( $list as $pair ) {
00593                 list( $old, $new ) = $pair;
00594                 if ( $new === null ) {
00595                     $blockers[] = array(
00596                         "pt-movepage-block-$type-invalid",
00597                         $old->getPrefixedText()
00598                     );
00599                     continue;
00600                 }
00601                 $lb->addObj( $old );
00602                 $lb->addObj( $new );
00603             }
00604         }
00605 
00606         if ( $blockers ) {
00607             return $blockers;
00608         }
00609 
00610         // Check that there are no move blockers
00611         $lb->execute();
00612         foreach ( $titles as $type => $list ) {
00613             // Give grep a chance to find the usages:
00614             // pt-movepage-block-tp-exists, pt-movepage-block-section-exists,
00615             // pt-movepage-block-subpage-exists
00616             foreach ( $list as $pair ) {
00617                 list( $old, $new ) = $pair;
00618                 if ( $new->exists() ) {
00619                     $blockers[] = array(
00620                         "pt-movepage-block-$type-exists",
00621                         $old->getPrefixedText(),
00622                         $new->getPrefixedText()
00623                     );
00624                 } else {
00625                     /* This method has terrible performance:
00626                      * - 2 queries by core
00627                      * - 3 queries by lqt
00628                      * - and no obvious way to preload the data! */
00629                     $errors = $old->isValidMoveOperation( $target, false );
00630                     if ( is_array( $errors ) ) {
00631                         $blockers = array_merge( $blockers, $errors );
00632                     }
00633 
00634                     /* Because of the above, check only one of the possibly thousands
00635                      * of section pages and assume rest are fine. */
00636                     if ( $type === 'section' ) {
00637                         break;
00638                     }
00639                 }
00640             }
00641         }
00642 
00643         return $blockers;
00644     }
00645 
00654     protected function newPageTitle( $base, Title $old, Title $target ) {
00655         $search = preg_quote( $base, '~' );
00656 
00657         if ( $old->getNamespace() == NS_TRANSLATIONS ) {
00658             $new = $old->getText();
00659             $new = preg_replace( "~^$search~", $target->getPrefixedText(), $new, 1 );
00660 
00661             return Title::makeTitleSafe( NS_TRANSLATIONS, $new );
00662         } else {
00663             $new = $old->getPrefixedText();
00664             $new = preg_replace( "~^$search~", $target->getPrefixedText(), $new, 1 );
00665 
00666             return Title::newFromText( $new );
00667         }
00668     }
00669 
00674     protected function getSectionPages() {
00675         if ( !isset( $this->sectionPages ) ) {
00676             $this->sectionPages = $this->page->getTranslationUnitPages( 'all' );
00677         }
00678 
00679         return $this->sectionPages;
00680     }
00681 
00686     protected function getTranslationPages() {
00687         if ( !isset( $this->translationPages ) ) {
00688             $this->translationPages = $this->page->getTranslationPages();
00689         }
00690 
00691         return $this->translationPages;
00692     }
00693 
00698     protected function getSubpages() {
00699         return $this->page->getTitle()->getSubpages();
00700     }
00701 }
Generated on Tue Oct 29 00:00:24 2013 for MediaWiki Translate Extension by  doxygen 1.6.3