]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialMovepage.php
MediaWiki 1.15.0
[autoinstallsdev/mediawiki.git] / includes / specials / SpecialMovepage.php
1 <?php
2 /**
3  * @file
4  * @ingroup SpecialPage
5  */
6
7 /**
8  * Constructor
9  */
10 function wfSpecialMovepage( $par = null ) {
11         global $wgUser, $wgOut, $wgRequest, $action;
12
13         # Check for database lock
14         if ( wfReadOnly() ) {
15                 $wgOut->readOnlyPage();
16                 return;
17         }
18
19         $target = isset( $par ) ? $par : $wgRequest->getVal( 'target' );
20         $oldTitleText = $wgRequest->getText( 'wpOldTitle', $target );
21         $newTitleText = $wgRequest->getText( 'wpNewTitle' );
22
23         $oldTitle = Title::newFromText( $oldTitleText );
24         $newTitle = Title::newFromText( $newTitleText );
25
26         if( is_null( $oldTitle ) ) {
27                 $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
28                 return;
29         }
30         if( !$oldTitle->exists() ) {
31                 $wgOut->showErrorPage( 'nopagetitle', 'nopagetext' );
32                 return;
33         }
34
35         # Check rights
36         $permErrors = $oldTitle->getUserPermissionsErrors( 'move', $wgUser );
37         if( !empty( $permErrors ) ) {
38                 $wgOut->showPermissionsErrorPage( $permErrors );
39                 return;
40         }
41
42         $form = new MovePageForm( $oldTitle, $newTitle );
43
44         if ( 'submit' == $action && $wgRequest->wasPosted()
45                 && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
46                 $form->doSubmit();
47         } else {
48                 $form->showForm( '' );
49         }
50 }
51
52 /**
53  * HTML form for Special:Movepage
54  * @ingroup SpecialPage
55  */
56 class MovePageForm {
57         var $oldTitle, $newTitle; # Objects
58         var $reason; # Text input
59         var $moveTalk, $deleteAndMove, $moveSubpages, $fixRedirects, $leaveRedirect; # Checks
60
61         private $watch = false;
62
63         function __construct( $oldTitle, $newTitle ) {
64                 global $wgRequest;
65                 $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
66                 $this->oldTitle = $oldTitle;
67                 $this->newTitle = $newTitle;
68                 $this->reason = $wgRequest->getText( 'wpReason' );
69                 if ( $wgRequest->wasPosted() ) {
70                         $this->moveTalk = $wgRequest->getBool( 'wpMovetalk', false );
71                         $this->fixRedirects = $wgRequest->getBool( 'wpFixRedirects', false );
72                         $this->leaveRedirect = $wgRequest->getBool( 'wpLeaveRedirect', false );
73                 } else {
74                         $this->moveTalk = $wgRequest->getBool( 'wpMovetalk', true );
75                         $this->fixRedirects = $wgRequest->getBool( 'wpFixRedirects', true );
76                         $this->leaveRedirect = $wgRequest->getBool( 'wpLeaveRedirect', true );
77                 }
78                 $this->moveSubpages = $wgRequest->getBool( 'wpMovesubpages', false );
79                 $this->deleteAndMove = $wgRequest->getBool( 'wpDeleteAndMove' ) && $wgRequest->getBool( 'wpConfirm' );
80                 $this->watch = $wgRequest->getCheck( 'wpWatch' );
81         }
82
83         /**
84          * Show the form
85          * @param mixed $err Error message. May either be a string message name or 
86          *    array message name and parameters, like the second argument to 
87          *    OutputPage::wrapWikiMsg(). 
88          */
89         function showForm( $err ) {
90                 global $wgOut, $wgUser, $wgFixDoubleRedirects;
91
92                 $skin = $wgUser->getSkin();
93
94                 $oldTitleLink = $skin->makeLinkObj( $this->oldTitle );
95
96                 $wgOut->setPagetitle( wfMsg( 'move-page', $this->oldTitle->getPrefixedText() ) );
97                 $wgOut->setSubtitle( wfMsg( 'move-page-backlink', $oldTitleLink ) );
98
99                 $newTitle = $this->newTitle;
100
101                 if( !$newTitle ) {
102                         # Show the current title as a default
103                         # when the form is first opened.
104                         $newTitle = $this->oldTitle;
105                 }
106                 else {
107                         if( empty($err) ) {
108                                 # If a title was supplied, probably from the move log revert
109                                 # link, check for validity. We can then show some diagnostic
110                                 # information and save a click.
111                                 $newerr = $this->oldTitle->isValidMoveOperation( $newTitle );
112                                 if( $newerr ) {
113                                         $err = $newerr[0];
114                                 }
115                         }
116                 }
117
118                 if ( !empty($err) && $err[0] == 'articleexists' && $wgUser->isAllowed( 'delete' ) ) {
119                         $wgOut->addWikiMsg( 'delete_and_move_text', $newTitle->getPrefixedText() );
120                         $movepagebtn = wfMsg( 'delete_and_move' );
121                         $submitVar = 'wpDeleteAndMove';
122                         $confirm = "
123                                 <tr>
124                                         <td></td>
125                                         <td class='mw-input'>" .
126                                                 Xml::checkLabel( wfMsg( 'delete_and_move_confirm' ), 'wpConfirm', 'wpConfirm' ) .
127                                         "</td>
128                                 </tr>";
129                         $err = '';
130                 } else {
131                         $wgOut->addWikiMsg( 'movepagetext' );
132                         $movepagebtn = wfMsg( 'movepagebtn' );
133                         $submitVar = 'wpMove';
134                         $confirm = false;
135                 }
136
137                 $oldTalk = $this->oldTitle->getTalkPage();
138                 $considerTalk = ( !$this->oldTitle->isTalkPage() && $oldTalk->exists() );
139
140                 $dbr = wfGetDB( DB_SLAVE );
141                 if ( $wgFixDoubleRedirects ) {
142                         $hasRedirects = $dbr->selectField( 'redirect', '1', 
143                                 array( 
144                                         'rd_namespace' => $this->oldTitle->getNamespace(),
145                                         'rd_title' => $this->oldTitle->getDBkey(),
146                                 ) , __METHOD__ );
147                 } else {
148                         $hasRedirects = false;
149                 }
150
151                 if ( $considerTalk ) {
152                         $wgOut->addWikiMsg( 'movepagetalktext' );
153                 }
154
155                 $titleObj = SpecialPage::getTitleFor( 'Movepage' );
156                 $token = htmlspecialchars( $wgUser->editToken() );
157
158                 if ( !empty($err) ) {
159                         $wgOut->setSubtitle( wfMsg( 'formerror' ) );
160                         if( $err[0] == 'hookaborted' ) {
161                                 $hookErr = $err[1];
162                                 $errMsg = "<p><strong class=\"error\">$hookErr</strong></p>\n";
163                                 $wgOut->addHTML( $errMsg );
164                         } else {
165                                 $wgOut->wrapWikiMsg( '<p><strong class="error">$1</strong></p>', $err );
166                         }
167                 }
168
169                 $wgOut->addHTML(
170                          Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), 'id' => 'movepage' ) ) .
171                          Xml::openElement( 'fieldset' ) .
172                          Xml::element( 'legend', null, wfMsg( 'move-page-legend' ) ) .
173                          Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-movepage-table' ) ) .
174                          "<tr>
175                                 <td class='mw-label'>" .
176                                         wfMsgHtml( 'movearticle' ) .
177                                 "</td>
178                                 <td class='mw-input'>
179                                         <strong>{$oldTitleLink}</strong>
180                                 </td>
181                         </tr>
182                         <tr>
183                                 <td class='mw-label'>" .
184                                         Xml::label( wfMsg( 'newtitle' ), 'wpNewTitle' ) .
185                                 "</td>
186                                 <td class='mw-input'>" .
187                                         Xml::input( 'wpNewTitle', 40, $newTitle->getPrefixedText(), array( 'type' => 'text', 'id' => 'wpNewTitle' ) ) .
188                                         Xml::hidden( 'wpOldTitle', $this->oldTitle->getPrefixedText() ) .
189                                 "</td>
190                         </tr>
191                         <tr>
192                                 <td class='mw-label'>" .
193                                         Xml::label( wfMsg( 'movereason' ), 'wpReason' ) .
194                                 "</td>
195                                 <td class='mw-input'>" .
196                                         Xml::tags( 'textarea', array( 'name' => 'wpReason', 'id' => 'wpReason', 'cols' => 60, 'rows' => 2 ), htmlspecialchars( $this->reason ) ) .
197                                 "</td>
198                         </tr>"
199                 );
200
201                 if( $considerTalk ) {
202                         $wgOut->addHTML( "
203                                 <tr>
204                                         <td></td>
205                                         <td class='mw-input'>" .
206                                                 Xml::checkLabel( wfMsg( 'movetalk' ), 'wpMovetalk', 'wpMovetalk', $this->moveTalk ) .
207                                         "</td>
208                                 </tr>"
209                         );
210                 }
211
212                 if ( $wgUser->isAllowed( 'suppressredirect' ) ) {
213                         $wgOut->addHTML( "
214                                 <tr>
215                                         <td></td>
216                                         <td class='mw-input' >" .
217                                                 Xml::checkLabel( wfMsg( 'move-leave-redirect' ), 'wpLeaveRedirect', 
218                                                         'wpLeaveRedirect', $this->leaveRedirect ) .
219                                         "</td>
220                                 </tr>"
221                         );
222                 }
223
224                 if ( $hasRedirects ) {
225                         $wgOut->addHTML( "
226                                 <tr>
227                                         <td></td>
228                                         <td class='mw-input' >" .
229                                                 Xml::checkLabel( wfMsg( 'fix-double-redirects' ), 'wpFixRedirects', 
230                                                         'wpFixRedirects', $this->fixRedirects ) .
231                                         "</td>
232                                 </tr>"
233                         );
234                 }
235
236                 if( ($this->oldTitle->hasSubpages() || $this->oldTitle->getTalkPage()->hasSubpages())
237                         && $this->oldTitle->userCan( 'move-subpages' ) )
238                 {
239                         global $wgMaximumMovedPages, $wgLang;
240
241                         $wgOut->addHTML( "
242                                 <tr>
243                                         <td></td>
244                                         <td class=\"mw-input\">" .
245                                 Xml::checkLabel( wfMsgExt(
246                                                 ( $this->oldTitle->hasSubpages()
247                                                         ? 'move-subpages'
248                                                         : 'move-talk-subpages' ),
249                                                 array( 'parsemag' ),
250                                                 $wgLang->formatNum( $wgMaximumMovedPages ),
251                                                 # $2 to allow use of PLURAL in message.
252                                                 $wgMaximumMovedPages
253                                         ),
254                                         'wpMovesubpages', 'wpMovesubpages',
255                                         # Don't check the box if we only have talk subpages to
256                                         # move and we aren't moving the talk page.
257                                         $this->moveSubpages && ($this->oldTitle->hasSubpages() || $this->moveTalk)
258                                 ) .
259                                         "</td>
260                                 </tr>"
261                         );
262                 }
263
264                 $watchChecked = $this->watch || $wgUser->getBoolOption( 'watchmoves' ) 
265                         || $this->oldTitle->userIsWatching();
266                 $wgOut->addHTML( "
267                         <tr>
268                                 <td></td>
269                                 <td class='mw-input'>" .
270                                         Xml::checkLabel( wfMsg( 'move-watch' ), 'wpWatch', 'watch', $watchChecked ) .
271                                 "</td>
272                         </tr>
273                                 {$confirm}
274                         <tr>
275                                 <td>&nbsp;</td>
276                                 <td class='mw-submit'>" .
277                                         Xml::submitButton( $movepagebtn, array( 'name' => $submitVar ) ) .
278                                 "</td>
279                         </tr>" .
280                         Xml::closeElement( 'table' ) .
281                         Xml::hidden( 'wpEditToken', $token ) .
282                         Xml::closeElement( 'fieldset' ) .
283                         Xml::closeElement( 'form' ) .
284                         "\n"
285                 );
286
287                 $this->showLogFragment( $this->oldTitle, $wgOut );
288                 $this->showSubpages( $this->oldTitle, $wgOut );
289
290         }
291
292         function doSubmit() {
293                 global $wgOut, $wgUser, $wgRequest, $wgMaximumMovedPages, $wgLang;
294                 global $wgFixDoubleRedirects;
295
296                 if ( $wgUser->pingLimiter( 'move' ) ) {
297                         $wgOut->rateLimited();
298                         return;
299                 }
300
301                 $ot = $this->oldTitle;
302                 $nt = $this->newTitle;
303
304                 # Delete to make way if requested
305                 if ( $wgUser->isAllowed( 'delete' ) && $this->deleteAndMove ) {
306                         $article = new Article( $nt );
307
308                         # Disallow deletions of big articles
309                         $bigHistory = $article->isBigDeletion();
310                         if( $bigHistory && !$nt->userCan( 'bigdelete' ) ) {
311                                 global $wgLang, $wgDeleteRevisionsLimit;
312                                 $this->showForm( array('delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
313                                 return;
314                         }
315
316                         // Delete an associated image if there is
317                         $file = wfLocalFile( $nt );
318                         if( $file->exists() ) {
319                                 $file->delete( wfMsgForContent( 'delete_and_move_reason' ), false );
320                         }
321
322                         // This may output an error message and exit
323                         $article->doDelete( wfMsgForContent( 'delete_and_move_reason' ) );
324                 }
325
326                 # don't allow moving to pages with # in
327                 if ( !$nt || $nt->getFragment() != '' ) {
328                         $this->showForm( 'badtitletext' );
329                         return;
330                 }
331
332                 if ( $wgUser->isAllowed( 'suppressredirect' ) ) {
333                         $createRedirect = $this->leaveRedirect;
334                 } else {
335                         $createRedirect = true;
336                 }
337
338                 $error = $ot->moveTo( $nt, true, $this->reason, $createRedirect );
339                 if ( $error !== true ) {
340                         # FIXME: show all the errors in a list, not just the first one
341                         $this->showForm( reset( $error ) );
342                         return;
343                 }
344
345                 if ( $wgFixDoubleRedirects && $this->fixRedirects ) {
346                         DoubleRedirectJob::fixRedirects( 'move', $ot, $nt );
347                 }
348
349                 wfRunHooks( 'SpecialMovepageAfterMove', array( &$this , &$ot , &$nt ) ) ;
350
351                 $wgOut->setPagetitle( wfMsg( 'pagemovedsub' ) );
352
353                 $oldUrl = $ot->getFullUrl( 'redirect=no' );
354                 $newUrl = $nt->getFullUrl();
355                 $oldText = $ot->getPrefixedText();
356                 $newText = $nt->getPrefixedText();
357                 $oldLink = "<span class='plainlinks'>[$oldUrl $oldText]</span>";
358                 $newLink = "<span class='plainlinks'>[$newUrl $newText]</span>";
359
360                 $msgName = $createRedirect ? 'movepage-moved-redirect' : 'movepage-moved-noredirect';
361                 $wgOut->addWikiMsg( 'movepage-moved', $oldLink, $newLink, $oldText, $newText );
362                 $wgOut->addWikiMsg( $msgName );
363
364                 # Now we move extra pages we've been asked to move: subpages and talk
365                 # pages.  First, if the old page or the new page is a talk page, we
366                 # can't move any talk pages: cancel that.
367                 if( $ot->isTalkPage() || $nt->isTalkPage() ) {
368                         $this->moveTalk = false;
369                 }
370
371                 if( !$ot->userCan( 'move-subpages' ) ) {
372                         $this->moveSubpages = false;
373                 }
374
375                 # Next make a list of id's.  This might be marginally less efficient
376                 # than a more direct method, but this is not a highly performance-cri-
377                 # tical code path and readable code is more important here.
378                 #
379                 # Note: this query works nicely on MySQL 5, but the optimizer in MySQL
380                 # 4 might get confused.  If so, consider rewriting as a UNION.
381                 #
382                 # If the target namespace doesn't allow subpages, moving with subpages
383                 # would mean that you couldn't move them back in one operation, which
384                 # is bad.  FIXME: A specific error message should be given in this
385                 # case.
386                 
387                 // FIXME: Use Title::moveSubpages() here
388                 $dbr = wfGetDB( DB_MASTER );
389                 if( $this->moveSubpages && (
390                         MWNamespace::hasSubpages( $nt->getNamespace() ) || (
391                                 $this->moveTalk &&
392                                 MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() )
393                         )
394                 ) ) {
395                         $conds = array(
396                                 'page_title LIKE '.$dbr->addQuotes( $dbr->escapeLike( $ot->getDBkey() ) . '/%' )
397                                         .' OR page_title = ' . $dbr->addQuotes( $ot->getDBkey() )
398                         );
399                         $conds['page_namespace'] = array();
400                         if( MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
401                                 $conds['page_namespace'] []= $ot->getNamespace();
402                         }
403                         if( $this->moveTalk && MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() ) ) {
404                                 $conds['page_namespace'] []= $ot->getTalkPage()->getNamespace();
405                         }
406                 } elseif( $this->moveTalk ) {
407                         $conds = array(
408                                 'page_namespace' => $ot->getTalkPage()->getNamespace(),
409                                 'page_title' => $ot->getDBKey()
410                         );
411                 } else {
412                         # Skip the query
413                         $conds = null;
414                 }
415
416                 $extraPages = array();
417                 if( !is_null( $conds ) ) {
418                         $extraPages = TitleArray::newFromResult(
419                                 $dbr->select( 'page',
420                                         array( 'page_id', 'page_namespace', 'page_title' ),
421                                         $conds,
422                                         __METHOD__
423                                 )
424                         );
425                 }
426
427                 $extraOutput = array();
428                 $skin = $wgUser->getSkin();
429                 $count = 1;
430                 foreach( $extraPages as $oldSubpage ) {
431                         if( $oldSubpage->getArticleId() == $ot->getArticleId() ) {
432                                 # Already did this one.
433                                 continue;
434                         }
435
436                         $newPageName = preg_replace(
437                                 '#^'.preg_quote( $ot->getDBKey(), '#' ).'#',
438                                 $nt->getDBKey(),
439                                 $oldSubpage->getDBKey()
440                         );
441                         if( $oldSubpage->isTalkPage() ) {
442                                 $newNs = $nt->getTalkPage()->getNamespace();
443                         } else {
444                                 $newNs = $nt->getSubjectPage()->getNamespace();
445                         }
446                         # Bug 14385: we need makeTitleSafe because the new page names may
447                         # be longer than 255 characters.
448                         $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
449                         if( !$newSubpage ) {
450                                 $oldLink = $skin->makeKnownLinkObj( $oldSubpage );
451                                 $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink,
452                                         htmlspecialchars(Title::makeName( $newNs, $newPageName )));
453                                 continue;
454                         }
455
456                         # This was copy-pasted from Renameuser, bleh.
457                         if ( $newSubpage->exists() && !$oldSubpage->isValidMoveTarget( $newSubpage ) ) {
458                                 $link = $skin->makeKnownLinkObj( $newSubpage );
459                                 $extraOutput []= wfMsgHtml( 'movepage-page-exists', $link );
460                         } else {
461                                 $success = $oldSubpage->moveTo( $newSubpage, true, $this->reason, $createRedirect );
462                                 if( $success === true ) {
463                                         if ( $this->fixRedirects ) {
464                                                 DoubleRedirectJob::fixRedirects( 'move', $oldSubpage, $newSubpage );
465                                         }
466                                         $oldLink = $skin->makeKnownLinkObj( $oldSubpage, '', 'redirect=no' );
467                                         $newLink = $skin->makeKnownLinkObj( $newSubpage );
468                                         $extraOutput []= wfMsgHtml( 'movepage-page-moved', $oldLink, $newLink );
469                                 } else {
470                                         $oldLink = $skin->makeKnownLinkObj( $oldSubpage );
471                                         $newLink = $skin->makeLinkObj( $newSubpage );
472                                         $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink, $newLink );
473                                 }
474                         }
475
476                         ++$count;
477                         if( $count >= $wgMaximumMovedPages ) {
478                                 $extraOutput []= wfMsgExt( 'movepage-max-pages', array( 'parsemag', 'escape' ), $wgLang->formatNum( $wgMaximumMovedPages ) );
479                                 break;
480                         }
481                 }
482
483                 if( $extraOutput !== array() ) {
484                         $wgOut->addHTML( "<ul>\n<li>" . implode( "</li>\n<li>", $extraOutput ) . "</li>\n</ul>" );
485                 }
486
487                 # Deal with watches (we don't watch subpages)
488                 if( $this->watch ) {
489                         $wgUser->addWatch( $ot );
490                         $wgUser->addWatch( $nt );
491                 } else {
492                         $wgUser->removeWatch( $ot );
493                         $wgUser->removeWatch( $nt );
494                 }
495         }
496
497         function showLogFragment( $title, &$out ) {
498                 $out->addHTML( Xml::element( 'h2', NULL, LogPage::logName( 'move' ) ) );
499                 LogEventsList::showLogExtract( $out, 'move', $title->getPrefixedText() );
500         }
501
502         function showSubpages( $title, $out ) {
503                 global $wgUser, $wgLang;
504
505                 if( !MWNamespace::hasSubpages( $title->getNamespace() ) )
506                         return;
507
508                 $subpages = $title->getSubpages();
509                 $count = $subpages instanceof TitleArray ? $subpages->count() : 0;
510
511                 $out->wrapWikiMsg( '== $1 ==', array( 'movesubpage', $count ) );
512
513                 # No subpages.
514                 if ( $count == 0 ) {
515                         $out->addWikiMsg( 'movenosubpage' );
516                         return;
517                 }
518
519                 $out->addWikiMsg( 'movesubpagetext', $wgLang->formatNum( $count ) );
520                 $skin = $wgUser->getSkin();
521                 $out->addHTML( "<ul>\n" );
522
523                 foreach( $subpages as $subpage ) {
524                         $link = $skin->link( $subpage );
525                         $out->addHTML( "<li>$link</li>\n" );
526                 }
527                 $out->addHTML( "</ul>\n" );
528         }
529 }
530