* (bug 17326) BREAKING CHANGE: Changed output format for iiprop=metadata
* (bug 17355) Added auwitheditsonly parameter to list=allusers
* (bug 17007) Added action=import
-* BREAKING CHANGE: Removed rctitles parameter from list=recentchanges because of
- performance concerns
-* Listing (semi-)deleted revisions and log entries as well in prop=revisions and
- list=logevents
-* (bug 11430) BREAKING CHANGE: Modules may return fewer results than the limit
- and still set a query-continue in some cases
+* BREAKING CHANGE: Removed rctitles parameter from list=recentchanges because
+ of performance concerns
+* Listing (semi-)deleted revisions and log entries as well in prop=revisions
+ and list=logevents
+* (bug 11430) BREAKING CHANGE: Modules may return fewer results than the
+ limit and still set a query-continue in some cases
+* (bug 17357) Added movesubpages parameter to action=move
=== Languages updated in 1.15 ===
}
$db = wfGetDB( DB_SLAVE );
- return $this->mHasSubpages = (bool)$db->selectField( 'page', '1',
- "page_namespace = {$this->mNamespace} AND page_title LIKE '"
- . $db->escapeLike( $this->mDbkeyform ) . "/%'",
- __METHOD__
+ $subpages = $this->getSubpages( 1 );
+ if( $subpages instanceof TitleArray )
+ return $this->mHasSubpages = (bool)$subpages->count();
+ return $this->mHasSubpages = false;
+ }
+
+ /**
+ * Get all subpages of this page.
+ * @param $limit Maximum number of subpages to fetch; -1 for no limit
+ * @return mixed TitleArray, or empty array if this page's namespace
+ * doesn't allow subpages
+ */
+ public function getSubpages($limit = -1) {
+ if( !MWNamespace::hasSubpages( $this->getNamespace() ) )
+ return array();
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $conds['page_namespace'] = $this->getNamespace();
+ $conds[] = 'page_title LIKE ' . $dbr->addQuotes(
+ $dbr->escapeLike( $this->getDBkey() ) . '/%' );
+ $options = array();
+ if( $limit > -1 )
+ $options['LIMIT'] = $limit;
+ return $this->mSubpages = TitleArray::newFromResult(
+ $dbr->select( 'page',
+ array( 'page_id', 'page_namespace', 'page_title' ),
+ $conds,
+ __METHOD__,
+ $options
+ )
);
}
}
+ /**
+ * Move this page's subpages to be subpages of $nt
+ * @param $nt Title Move target
+ * @param $auth bool Whether $wgUser's permissions should be checked
+ * @param $reason string The reason for the move
+ * @param $createRedirect bool Whether to create redirects from the old subpages to the new ones
+ * Ignored if the user doesn't have the 'suppressredirect' right
+ * @return mixed array with old page titles as keys, and strings (new page titles) or
+ * arrays (errors) as values, or an error array with numeric indices if no pages were moved
+ */
+ public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) {
+ global $wgUser, $wgMaximumMovedPages;
+ // Check permissions
+ if( !$this->userCan( 'move-subpages' ) )
+ return array( 'cant-move-subpages' );
+ // Do the source and target namespaces support subpages?
+ if( !MWNamespace::hasSubpages( $this->getNamespace() ) )
+ return array( 'namespace-nosubpages',
+ MWNamespace::getCanonicalName( $this->getNamespace() ) );
+ if( !MWNamespace::hasSubpages( $nt->getNamespace() ) )
+ return array( 'namespace-nosubpages',
+ MWNamespace::getCanonicalName( $nt->getNamespace() ) );
+
+ $subpages = $this->getSubpages($wgMaximumMovedPages + 1);
+ $retval = array();
+ $count = 0;
+ foreach( $subpages as $oldSubpage ) {
+ $count++;
+ if( $count > $wgMaximumMovedPages ) {
+ $retval[$oldSubpage->getPrefixedTitle()] =
+ array( 'movepage-max-pages',
+ $wgMaximumMovedPages );
+ break;
+ }
+
+ if( $oldSubpage->getArticleId() == $this->getArticleId() )
+ // When moving a page to a subpage of itself,
+ // don't move it twice
+ continue;
+ $newPageName = preg_replace(
+ '#^'.preg_quote( $this->getDBKey(), '#' ).'#',
+ $nt->getDBKey(), $oldSubpage->getDBKey() );
+ if( $oldSubpage->isTalkPage() ) {
+ $newNs = $nt->getTalkPage()->getNamespace();
+ } else {
+ $newNs = $nt->getSubjectPage()->getNamespace();
+ }
+ # Bug 14385: we need makeTitleSafe because the new page names may
+ # be longer than 255 characters.
+ $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
+
+ $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect );
+ if( $success === true ) {
+ $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
+ } else {
+ $retval[$oldSubpage->getPrefixedText()] = $success;
+ }
+ }
+ return $retval;
+ }
+
/**
* Checks if this page is just a one-rev redirect.
* Adds lock, so don't use just for light purposes.
/**
* Output the error message related to a certain array
- * @param array $error Element of a getUserPermissionsErrors()
+ * @param array $error Element of a getUserPermissionsErrors()-style array
*/
public function dieUsageMsg($error) {
+ $parsed = $this->parseMsg($error);
+ $this->dieUsage($parsed['code'], $parsed['info']);
+ }
+
+ /**
+ * Return the error message related to a certain array
+ * @param array $error Element of a getUserPermissionsErrors()-style array
+ * @return array('code' => code, 'info' => info)
+ */
+ public function parseMsg($error) {
$key = array_shift($error);
if(isset(self::$messageMap[$key]))
- $this->dieUsage(wfMsgReplaceArgs(self::$messageMap[$key]['info'], $error), wfMsgReplaceArgs(self::$messageMap[$key]['code'], $error));
+ return array( 'code' =>
+ wfMsgReplaceArgs(self::$messageMap[$key]['code'], $error),
+ 'info' =>
+ wfMsgReplaceArgs(self::$messageMap[$key]['info'], $error)
+ );
// If the key isn't present, throw an "unknown error"
- $this->dieUsageMsg(array('unknownerror', $key));
+ return $this->parseMsg(array('unknownerror', $key));
}
/**
$this->dieUsageMsg(array('invalidtitle', $params['to']));
$toTalk = $toTitle->getTalkPage();
+ # Move the page
$hookErr = null;
$retval = $fromTitle->moveTo($toTitle, true, $params['reason'], !$params['noredirect']);
if($retval !== true)
if(!$params['noredirect'] || !$wgUser->isAllowed('suppressredirect'))
$r['redirectcreated'] = '';
+ # Move the talk page
if($params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage())
{
- // We need to move the talk page as well
- $toTalk = $toTitle->getTalkPage();
$retval = $fromTalk->moveTo($toTalk, true, $params['reason'], !$params['noredirect']);
if($retval === true)
{
// We're not gonna dieUsage() on failure, since we already changed something
else
{
- $r['talkmove-error-code'] = ApiBase::$messageMap[reset($retval)]['code'];
- $r['talkmove-error-info'] = ApiBase::$messageMap[reset($retval)]['info'];
+ $parsed = $this->parseMsg(reset($retval));
+ $r['talkmove-error-code'] = $parsed['code'];
+ $r['talkmove-error-info'] = $parsed['info'];
+ }
+ }
+
+ # Move subpages
+ if($params['movesubpages'])
+ {
+ $r['subpages'] = $this->moveSubpages($fromTitle, $toTitle,
+ $params['reason'], $params['noredirect']);
+ $this->getResult()->setIndexedTagName($r['subpages'], 'subpage');
+ // TODO: Should we move talk subpages if moving the talk page failed?
+ if($params['movetalk'])
+ {
+ $r['subpages-talk'] = $this->moveSubpages($fromTalk, $toTalk,
+ $params['reason'], $params['noredirect']);
+ $this->getResult()->setIndexedTagName($r['subpages-talk'], 'subpage');
}
}
}
$this->getResult()->addValue(null, $this->getModuleName(), $r);
}
+
+ public function moveSubpages($fromTitle, $toTitle, $reason, $noredirect)
+ {
+ $retval = array();
+ $success = $fromTitle->moveSubpages($toTitle, true, $reason, !$noredirect);
+ if(isset($success[0]))
+ return array('error' => $this->parseMsg($success));
+ else
+ {
+ // At least some pages could be moved
+ // Report each of them separately
+ foreach($success as $oldTitle => $newTitle)
+ {
+ $r = array('from' => $oldTitle);
+ if(is_array($newTitle))
+ $r['error'] = $this->parseMsg(reset($newTitle));
+ else
+ // Success
+ $r['to'] = $newTitle;
+ $retval[] = $r;
+ }
+ }
+ return $retval;
+ }
public function mustBePosted() { return true; }
'token' => null,
'reason' => null,
'movetalk' => false,
+ 'movesubpages' => false,
'noredirect' => false,
'watch' => false,
'unwatch' => false
'token' => 'A move token previously retrieved through prop=info',
'reason' => 'Reason for the move (optional).',
'movetalk' => 'Move the talk page, if it exists.',
+ 'movesubpages' => 'Move subpages, if applicable',
'noredirect' => 'Don\'t create a redirect',
'watch' => 'Add the page and the redirect to your watchlist',
'unwatch' => 'Remove the page and the redirect from your watchlist'