* unless $forceClone is "clone". If $forceClone is "clone" and the given TitleValue
* is already a Title instance, that instance is copied using the clone operator.
*
+ * @deprecated since 1.34, use newFromLinkTarget or castFromLinkTarget
+ *
* @param TitleValue $titleValue Assumed to be safe.
* @param string $forceClone set to NEW_CLONE to ensure a fresh instance is returned.
*
);
}
+ /**
+ * Same as newFromLinkTarget, but if passed null, returns null.
+ *
+ * @param LinkTarget|null $linkTarget Assumed to be safe (if not null).
+ *
+ * @return Title|null
+ */
+ public static function castFromLinkTarget( $linkTarget ) {
+ return $linkTarget ? self::newFromLinkTarget( $linkTarget ) : null;
+ }
+
/**
* Create a new Title from text, such as what one would find in a link. De-
* codes any HTML entities in the text.
// NOTE: ideally, this would just call makeTitle() and then isValid(),
// but presently, that means more overhead on a potential performance hotspot.
- if ( !MWNamespace::exists( $ns ) ) {
+ if ( !MediaWikiServices::getInstance()->getNamespaceInfo()->exists( $ns ) ) {
return null;
}
/**
* Create a new Title for the Main Page
*
+ * This uses the 'mainpage' interface message, which could be specified in
+ * `$wgForceUIMsgAsContentMsg`. If that is the case, then calling this method
+ * will use the user language, which would involve initialising the session
+ * via `RequestContext::getMain()->getLanguage()`. For session-less endpoints,
+ * be sure to pass in a MessageLocalizer (such as your own RequestContext,
+ * or ResourceloaderContext) to prevent an error.
+ *
* @note The Title instance returned by this method is not guaranteed to be a fresh instance.
* It may instead be a cached instance created previously, with references to it remaining
* elsewhere.
*
- * @return Title The new object
+ * @param MessageLocalizer|null $localizer An optional context to use (since 1.34)
+ * @return Title
*/
- public static function newMainPage() {
- $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
- // Don't give fatal errors if the message is broken
+ public static function newMainPage( MessageLocalizer $localizer = null ) {
+ if ( $localizer ) {
+ $msg = $localizer->msg( 'mainpage' );
+ } else {
+ $msg = wfMessage( 'mainpage' );
+ }
+
+ $title = self::newFromText( $msg->inContentLanguage()->text() );
+
+ // Every page renders at least one link to the Main Page (e.g. sidebar).
+ // If the localised value is invalid, don't produce fatal errors that
+ // would make the wiki inaccessible (and hard to fix the invalid message).
+ // Gracefully fallback...
if ( !$title ) {
$title = self::newFromText( 'Main Page' );
}
$canonicalNamespace = false
) {
if ( $canonicalNamespace ) {
- $namespace = MWNamespace::getCanonicalName( $ns );
+ $namespace = MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getCanonicalName( $ns );
} else {
$namespace = MediaWikiServices::getInstance()->getContentLanguage()->getNsText( $ns );
}
* @return bool
*/
public function isValid() {
- if ( !MWNamespace::exists( $this->mNamespace ) ) {
+ $services = MediaWikiServices::getInstance();
+ if ( !$services->getNamespaceInfo()->exists( $this->mNamespace ) ) {
return false;
}
try {
- $parser = MediaWikiServices::getInstance()->getTitleParser();
- $parser->parseTitle( $this->mDbkeyform, $this->mNamespace );
+ $services->getTitleParser()->parseTitle( $this->mDbkeyform, $this->mNamespace );
return true;
} catch ( MalformedTitleException $ex ) {
return false;
if ( $this->isExternal() ) {
// This probably shouldn't even happen, except for interwiki transclusion.
// If possible, use the canonical name for the foreign namespace.
- $nsText = MWNamespace::getCanonicalName( $this->mNamespace );
+ $nsText = MediaWikiServices::getInstance()->getNamespaceInfo()->
+ getCanonicalName( $this->mNamespace );
if ( $nsText !== false ) {
return $nsText;
}
* @return string Namespace text
*/
public function getSubjectNsText() {
- return MediaWikiServices::getInstance()->getContentLanguage()->
- getNsText( MWNamespace::getSubject( $this->mNamespace ) );
+ $services = MediaWikiServices::getInstance();
+ return $services->getContentLanguage()->
+ getNsText( $services->getNamespaceInfo()->getSubject( $this->mNamespace ) );
}
/**
* @return string Namespace text
*/
public function getTalkNsText() {
- return MediaWikiServices::getInstance()->getContentLanguage()->
- getNsText( MWNamespace::getTalk( $this->mNamespace ) );
+ $services = MediaWikiServices::getInstance();
+ return $services->getContentLanguage()->
+ getNsText( $services->getNamespaceInfo()->getTalk( $this->mNamespace ) );
}
/**
* Can this title have a corresponding talk page?
*
- * @see MWNamespace::hasTalkNamespace
+ * @see NamespaceInfo::hasTalkNamespace
* @since 1.30
*
* @return bool True if this title either is a talk page or can have a talk page associated.
*/
public function canHaveTalkPage() {
- return MWNamespace::hasTalkNamespace( $this->mNamespace );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasTalkNamespace( $this->mNamespace );
}
/**
* @return bool
*/
public function isWatchable() {
- return !$this->isExternal() && MWNamespace::isWatchable( $this->mNamespace );
+ return !$this->isExternal() && MediaWikiServices::getInstance()->getNamespaceInfo()->
+ isWatchable( $this->mNamespace );
}
/**
* @since 1.19
*/
public function inNamespace( $ns ) {
- return MWNamespace::equals( $this->mNamespace, $ns );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ equals( $this->mNamespace, $ns );
}
/**
* @return bool
*/
public function hasSubjectNamespace( $ns ) {
- return MWNamespace::subjectEquals( $this->mNamespace, $ns );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ subjectEquals( $this->mNamespace, $ns );
}
/**
* @return bool
*/
public function isContentPage() {
- return MWNamespace::isContent( $this->mNamespace );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ isContent( $this->mNamespace );
}
/**
* @return bool
*/
public function isMovable() {
- if ( !MWNamespace::isMovable( $this->mNamespace ) || $this->isExternal() ) {
+ if (
+ !MediaWikiServices::getInstance()->getNamespaceInfo()->
+ isMovable( $this->mNamespace ) || $this->isExternal()
+ ) {
// Interwiki title or immovable namespace. Hooks don't get to override here
return false;
}
* @return bool
*/
public function isSubpage() {
- return MWNamespace::hasSubpages( $this->mNamespace )
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasSubpages( $this->mNamespace )
? strpos( $this->getText(), '/' ) !== false
: false;
}
* @return bool
*/
public function isTalkPage() {
- return MWNamespace::isTalk( $this->mNamespace );
+ return MediaWikiServices::getInstance()->getNamespaceInfo()->
+ isTalk( $this->mNamespace );
}
/**
* Get a Title object associated with the talk page of this article
*
+ * @deprecated since 1.34, use NamespaceInfo::getTalkPage
* @return Title The object for the talk page
*/
public function getTalkPage() {
- return self::makeTitle( MWNamespace::getTalk( $this->mNamespace ), $this->mDbkeyform );
+ return self::castFromLinkTarget(
+ MediaWikiServices::getInstance()->getNamespaceInfo()->getTalkPage( $this ) );
}
/**
* Get a title object associated with the subject page of this
* talk page
*
+ * @deprecated since 1.34, use NamespaceInfo::getSubjectPage
* @return Title The object for the subject page
*/
public function getSubjectPage() {
- // Is this the same title?
- $subjectNS = MWNamespace::getSubject( $this->mNamespace );
- if ( $this->mNamespace == $subjectNS ) {
- return $this;
- }
- return self::makeTitle( $subjectNS, $this->mDbkeyform );
+ return self::castFromLinkTarget(
+ MediaWikiServices::getInstance()->getNamespaceInfo()->getSubjectPage( $this ) );
}
/**
* Get the other title for this page, if this is a subject page
* get the talk page, if it is a subject page get the talk page
*
+ * @deprecated since 1.34, use NamespaceInfo::getAssociatedPage
* @since 1.25
* @throws MWException If the page doesn't have an other page
* @return Title
*/
public function getOtherPage() {
- if ( $this->isSpecialPage() ) {
- throw new MWException( 'Special pages cannot have other pages' );
- }
- if ( $this->isTalkPage() ) {
- return $this->getSubjectPage();
- } else {
- if ( !$this->canHaveTalkPage() ) {
- throw new MWException( "{$this->getPrefixedText()} does not have an other page" );
- }
- return $this->getTalkPage();
- }
+ return self::castFromLinkTarget(
+ MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociatedPage( $this ) );
}
/**
* @since 1.20
*/
public function getRootText() {
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+ if (
+ !MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasSubpages( $this->mNamespace )
+ ) {
return $this->getText();
}
*/
public function getBaseText() {
$text = $this->getText();
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+ if (
+ !MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasSubpages( $this->mNamespace )
+ ) {
return $text;
}
* @return string Subpage name
*/
public function getSubpageText() {
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+ if (
+ !MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasSubpages( $this->mNamespace )
+ ) {
return $this->mTextform;
}
$parts = explode( '/', $this->mTextform );
* @return bool
*/
public function hasSubpages() {
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+ if (
+ !MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasSubpages( $this->mNamespace )
+ ) {
# Duh
return false;
}
* doesn't allow subpages
*/
public function getSubpages( $limit = -1 ) {
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+ if (
+ !MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasSubpages( $this->mNamespace )
+ ) {
return [];
}
* @return string Containing capitalized title
*/
public static function capitalize( $text, $ns = NS_MAIN ) {
- if ( MWNamespace::isCapitalized( $ns ) ) {
+ $services = MediaWikiServices::getInstance();
+ if ( $services->getNamespaceInfo()->isCapitalized( $ns ) ) {
return MediaWikiServices::getInstance()->getContentLanguage()->ucfirst( $text );
} else {
return $text;
array $changeTags = []
) {
global $wgUser;
- $err = $this->isValidMoveOperation( $nt, $auth, $reason );
- if ( is_array( $err ) ) {
- // Auto-block user's IP if the account was "hard" blocked
- $wgUser->spreadAnyEditBlock();
- return $err;
- }
- // Check suppressredirect permission
- if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
- $createRedirect = true;
- }
$mp = new MovePage( $this, $nt );
- $status = $mp->move( $wgUser, $reason, $createRedirect, $changeTags );
+ $method = $auth ? 'moveIfAllowed' : 'move';
+ $status = $mp->$method( $wgUser, $reason, $createRedirect, $changeTags );
if ( $status->isOK() ) {
return true;
} else {
/**
* Move this page's subpages to be subpages of $nt
*
+ * @deprecated since 1.34, use MovePage instead
* @param Title $nt Move target
* @param bool $auth Whether $wgUser's permissions should be checked
* @param string $reason The reason for the move
public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true,
array $changeTags = []
) {
- global $wgMaximumMovedPages;
// Check permissions
if ( !$this->userCan( 'move-subpages' ) ) {
return [
];
}
// Do the source and target namespaces support subpages?
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+ $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
+ if ( !$nsInfo->hasSubpages( $this->mNamespace ) ) {
return [
- [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $this->mNamespace ) ],
+ [ 'namespace-nosubpages', $nsInfo->getCanonicalName( $this->mNamespace ) ],
];
}
- if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
+ if ( !$nsInfo->hasSubpages( $nt->getNamespace() ) ) {
return [
- [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $nt->getNamespace() ) ],
+ [ 'namespace-nosubpages', $nsInfo->getCanonicalName( $nt->getNamespace() ) ],
];
}
- $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
- $retval = [];
- $count = 0;
- foreach ( $subpages as $oldSubpage ) {
- $count++;
- if ( $count > $wgMaximumMovedPages ) {
- $retval[$oldSubpage->getPrefixedText()] = [
- [ 'movepage-max-pages', $wgMaximumMovedPages ],
- ];
- break;
- }
+ global $wgUser;
+ $mp = new MovePage( $this, $nt );
+ $method = $auth ? 'moveSubpagesIfAllowed' : 'moveSubpages';
+ $result = $mp->$method( $wgUser, $reason, $createRedirect, $changeTags );
- // We don't know whether this function was called before
- // or after moving the root page, so check both
- // $this and $nt
- if ( $oldSubpage->getArticleID() == $this->getArticleID()
- || $oldSubpage->getArticleID() == $nt->getArticleID()
- ) {
- // When moving a page to a subpage of itself,
- // don't move it twice
- continue;
- }
- $newPageName = preg_replace(
- '#^' . preg_quote( $this->mDbkeyform, '#' ) . '#',
- StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # T23234
- $oldSubpage->getDBkey() );
- if ( $oldSubpage->isTalkPage() ) {
- $newNs = $nt->getTalkPage()->getNamespace();
- } else {
- $newNs = $nt->getSubjectPage()->getNamespace();
- }
- # T16385: we need makeTitleSafe because the new page names may
- # be longer than 255 characters.
- $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
+ if ( !$result->isOk() ) {
+ return $result->getErrorsArray();
+ }
- $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
- if ( $success === true ) {
- $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
+ $retval = [];
+ foreach ( $result->getValue() as $key => $status ) {
+ if ( $status->isOK() ) {
+ $retval[$key] = $status->getValue();
} else {
- $retval[$oldSubpage->getPrefixedText()] = $success;
+ $retval[$key] = $status->getErrorsArray();
}
}
return $retval;
* @return int|bool New revision ID, or false if none exists
*/
private function getRelativeRevisionID( $revId, $flags, $dir ) {
- $revId = (int)$revId;
- if ( $dir === 'next' ) {
- $op = '>';
- $sort = 'ASC';
- } elseif ( $dir === 'prev' ) {
- $op = '<';
- $sort = 'DESC';
- } else {
- throw new InvalidArgumentException( '$dir must be "next" or "prev"' );
- }
-
- if ( $flags & self::GAID_FOR_UPDATE ) {
- $db = wfGetDB( DB_MASTER );
- } else {
- $db = wfGetDB( DB_REPLICA, 'contributions' );
- }
-
- // Intentionally not caring if the specified revision belongs to this
- // page. We only care about the timestamp.
- $ts = $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $revId ], __METHOD__ );
- if ( $ts === false ) {
- $ts = $db->selectField( 'archive', 'ar_timestamp', [ 'ar_rev_id' => $revId ], __METHOD__ );
- if ( $ts === false ) {
- // Or should this throw an InvalidArgumentException or something?
- return false;
- }
+ $rl = MediaWikiServices::getInstance()->getRevisionLookup();
+ $rlFlags = $flags === self::GAID_FOR_UPDATE ? IDBAccessObject::READ_LATEST : 0;
+ $rev = $rl->getRevisionById( $revId, $rlFlags );
+ if ( !$rev ) {
+ return false;
}
- $ts = $db->addQuotes( $ts );
-
- $revId = $db->selectField( 'revision', 'rev_id',
- [
- 'rev_page' => $this->getArticleID( $flags ),
- "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op $revId)"
- ],
- __METHOD__,
- [
- 'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
- 'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
- ]
- );
-
- if ( $revId === false ) {
+ $oldRev = $dir === 'next'
+ ? $rl->getNextRevision( $rev, $rlFlags )
+ : $rl->getPreviousRevision( $rev, $rlFlags );
+ if ( !$oldRev ) {
return false;
- } else {
- return intval( $revId );
}
+ return $oldRev->getId();
}
/**
* Get the revision ID of the previous revision
*
+ * @deprecated since 1.34, use RevisionLookup::getPreviousRevision
* @param int $revId Revision ID. Get the revision that was before this one.
* @param int $flags Title::GAID_FOR_UPDATE
* @return int|bool Old revision ID, or false if none exists
/**
* Get the revision ID of the next revision
*
+ * @deprecated since 1.34, use RevisionLookup::getNextRevision
* @param int $revId Revision ID. Get the revision that was after this one.
* @param int $flags Title::GAID_FOR_UPDATE
* @return int|bool Next revision ID, or false if none exists
/**
* Compare with another title.
*
- * @param Title $title
+ * @param LinkTarget $title
* @return bool
*/
- public function equals( Title $title ) {
+ public function equals( LinkTarget $title ) {
// Note: === is necessary for proper matching of number-like titles.
- return $this->mInterwiki === $title->mInterwiki
- && $this->mNamespace == $title->mNamespace
- && $this->mDbkeyform === $title->mDbkeyform;
+ return $this->mInterwiki === $title->getInterwiki()
+ && $this->mNamespace == $title->getNamespace()
+ && $this->mDbkeyform === $title->getDBkey();
}
/**
*/
public function getNamespaceKey( $prepend = 'nstab-' ) {
// Gets the subject namespace of this title
- $subjectNS = MWNamespace::getSubject( $this->mNamespace );
+ $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
+ $subjectNS = $nsInfo->getSubject( $this->mNamespace );
// Prefer canonical namespace name for HTML IDs
- $namespaceKey = MWNamespace::getCanonicalName( $subjectNS );
+ $namespaceKey = $nsInfo->getCanonicalName( $subjectNS );
if ( $namespaceKey === false ) {
// Fallback to localised text
$namespaceKey = $this->getSubjectNsText();
public function canUseNoindex() {
global $wgExemptFromUserRobotsControl;
- $bannedNamespaces = $wgExemptFromUserRobotsControl ?? MWNamespace::getContentNamespaces();
+ $bannedNamespaces = $wgExemptFromUserRobotsControl ??
+ MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces();
return !in_array( $this->mNamespace, $bannedNamespaces );
}
}
}
- if ( MWNamespace::hasSubpages( $this->mNamespace ) ) {
+ if (
+ MediaWikiServices::getInstance()->getNamespaceInfo()->
+ hasSubpages( $this->mNamespace )
+ ) {
// Optional notice for page itself and any parent page
$editnotice_base = $editnotice_ns;
foreach ( explode( '/', $this->mDbkeyform ) as $part ) {