00001 <?php
00017 class SpecialPageTranslationMovePage extends UnlistedSpecialPage {
00018
00019 protected $newText, $oldText;
00020
00024 protected $newTitle, $oldTitle;
00025
00026
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
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
00084 $this->moveSubpages = $request->getBool( 'subpages', !$request->wasPosted() );
00085
00086 if ( $this->doBasicChecks() !== true ) {
00087 return;
00088 }
00089
00090
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
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
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
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
00215
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
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
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
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
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
00559 if ( $blockers ) {
00560 return $blockers;
00561 }
00562
00563
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
00587 $lb = new LinkBatch();
00588 foreach ( $titles as $type => $list ) {
00589
00590
00591
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
00611 $lb->execute();
00612 foreach ( $titles as $type => $list ) {
00613
00614
00615
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
00626
00627
00628
00629 $errors = $old->isValidMoveOperation( $target, false );
00630 if ( is_array( $errors ) ) {
00631 $blockers = array_merge( $blockers, $errors );
00632 }
00633
00634
00635
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 }