Expose the log_id of the deletion log entry in the action=delete API
[lhc/web/wiklou.git] / includes / Title.php
index fd72309..6e3f16c 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 /**
+ * Representation a title within %MediaWiki.
+ *
  * See title.txt
  *
  * This program is free software; you can redistribute it and/or modify
@@ -28,8 +30,7 @@
  *
  * @internal documentation reviewed 15 Mar 2010
  */
-class
-Title {
+class Title {
        /** @name Static cache variables */
        // @{
        static private $titleCache = array();
@@ -84,6 +85,7 @@ Title {
        var $mRedirect = null;            // /< Is the article at this title a redirect?
        var $mNotificationTimestamp = array(); // /< Associative array of user ID -> timestamp/false
        var $mBacklinkCache = null;       // /< Cache of links to this title
+       var $mHasSubpage;                 // /< Whether a page has any subpages
        // @}
 
 
@@ -120,7 +122,8 @@ Title {
         *   fied by a prefix.  If you want to force a specific namespace even if
         *   $text might begin with a namespace prefix, use makeTitle() or
         *   makeTitleSafe().
-        * @return Title, or null on an error.
+        * @throws MWException
+        * @return Title|null - Title or null on an error.
         */
        public static function newFromText( $text, $defaultNamespace = NS_MAIN ) {
                if ( is_object( $text ) ) {
@@ -180,13 +183,12 @@ Title {
         * @return Title the new object, or NULL on an error
         */
        public static function newFromURL( $url ) {
-               global $wgLegalTitleChars;
                $t = new Title();
 
                # For compatibility with old buggy URLs. "+" is usually not valid in titles,
                # but some URLs used it as a space replacement and they still come
                # from some external search tools.
-               if ( strpos( $wgLegalTitleChars, '+' ) === false ) {
+               if ( strpos( self::legalChars(), '+' ) === false ) {
                        $url = str_replace( '+', ' ', $url );
                }
 
@@ -261,8 +263,7 @@ Title {
         * Load Title object fields from a DB row.
         * If false is given, the title will be treated as non-existing.
         *
-        * @param $row Object|false database row
-        * @return void
+        * @param $row Object|bool database row
         */
        public function loadFromRow( $row ) {
                if ( $row ) { // page found
@@ -319,6 +320,10 @@ Title {
         * @return Title the new object, or NULL on an error
         */
        public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
+               if ( !MWNamespace::exists( $ns ) ) {
+                       return null;
+               }
+
                $t = new Title();
                $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki );
                if ( $t->secureAndSplit() ) {
@@ -709,17 +714,9 @@ Title {
                        }
                }
 
-               // Strip off subpages
-               $pagename = $this->getText();
-               if ( strpos( $pagename, '/' ) !== false ) {
-                       list( $username , ) = explode( '/', $pagename, 2 );
-               } else {
-                       $username = $pagename;
-               }
-
                if ( $wgContLang->needsGenderDistinction() &&
                                MWNamespace::hasGenderDistinction( $this->mNamespace ) ) {
-                       $gender = GenderCache::singleton()->getGenderOf( $username, __METHOD__ );
+                       $gender = GenderCache::singleton()->getGenderOf( $this->getText(), __METHOD__ );
                        return $wgContLang->getGenderNsText( $this->mNamespace, $gender );
                }
 
@@ -864,6 +861,8 @@ Title {
         * This is MUCH simpler than individually testing for equivilance
         * against both NS_USER and NS_USER_TALK, and is also forward compatible.
         * @since 1.19
+        * @param $ns int
+        * @return bool
         */
        public function hasSubjectNamespace( $ns ) {
                return MWNamespace::subjectEquals( $this->getNamespace(), $ns );
@@ -1227,6 +1226,9 @@ Title {
         * andthe wfArrayToCGI moved to getLocalURL();
         *
         * @since 1.19 (r105919)
+        * @param $query
+        * @param $query2 bool
+        * @return String
         */
        private static function fixUrlQueryArgs( $query, $query2 = false ) {
                if( $query2 !== false ) {
@@ -1285,7 +1287,7 @@ Title {
         * with action=render, $wgServer is prepended.
         *
 
-        * @param $query \twotypes{\string,\array} an optional query string,
+        * @param $query string|array an optional query string,
         *   not used for interwiki     links. Can be specified as an associative array as well,
         *   e.g., array( 'action' => 'edit' ) (keys and values will be URL-escaped).
         *   Some query patterns will trigger various shorturl path replacements.
@@ -1408,6 +1410,8 @@ Title {
         * See getLocalURL for the arguments.
         *
         * @see self::getLocalURL
+        * @param $query string
+        * @param $query2 bool|string
         * @return String the URL
         */
        public function escapeLocalURL( $query = '', $query2 = false ) {
@@ -1479,6 +1483,7 @@ Title {
         *
         * @see self::getLocalURL
         * @since 1.18
+        * @return string
         */
        public function escapeCanonicalURL( $query = '', $query2 = false ) {
                wfDeprecated( __METHOD__, '1.19' );
@@ -1926,7 +1931,7 @@ Title {
                        // Don't block the user from editing their own talk page unless they've been
                        // explicitly blocked from that too.
                } elseif( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) {
-                       $block = $user->mBlock;
+                       $block = $user->getBlock();
 
                        // This is from OutputPage::blockedPage
                        // Copied at r23888 by werdna
@@ -1946,15 +1951,15 @@ Title {
 
                        $link = '[[' . $wgContLang->getNsText( NS_USER ) . ":{$name}|{$name}]]";
                        $blockid = $block->getId();
-                       $blockExpiry = $user->mBlock->mExpiry;
-                       $blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $user->mBlock->mTimestamp ), true );
+                       $blockExpiry = $block->getExpiry();
+                       $blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $block->mTimestamp ), true );
                        if ( $blockExpiry == 'infinity' ) {
                                $blockExpiry = wfMessage( 'infiniteblock' )->text();
                        } else {
                                $blockExpiry = $wgLang->timeanddate( wfTimestamp( TS_MW, $blockExpiry ), true );
                        }
 
-                       $intended = strval( $user->mBlock->getTarget() );
+                       $intended = strval( $block->getTarget() );
 
                        $errors[] = array( ( $block->mAuto ? 'autoblockedtext' : 'blockedtext' ), $link, $reason, $ip, $name,
                                $blockid, $blockExpiry, $intended, $blockTimestamp );
@@ -2021,9 +2026,8 @@ Title {
                        $name = $this->getPrefixedText();
                        $dbName = $this->getPrefixedDBKey();
 
-                       // Check with and without underscores
+                       // Check for explicit whitelisting with and without underscores
                        if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
-                               # Check for explicit whitelisting
                                $whitelisted = true;
                        } elseif ( $this->getNamespace() == NS_MAIN ) {
                                # Old settings might have the title prefixed with
@@ -2472,8 +2476,9 @@ Title {
        /**
         * Get the expiry time for the restriction against a given action
         *
+        * @param $action
         * @return String|Bool 14-char timestamp, or 'infinity' if the page is protected forever
-        *      or not protected at all, or false if the action is not recognised.
+        *     or not protected at all, or false if the action is not recognised.
         */
        public function getRestrictionExpiry( $action ) {
                if ( !$this->mRestrictionsLoaded ) {
@@ -2538,7 +2543,7 @@ Title {
 
                if ( $oldFashionedRestrictions === null ) {
                        $oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions',
-                               array( 'page_id' => $this->getArticleId() ), __METHOD__ );
+                               array( 'page_id' => $this->getArticleID() ), __METHOD__ );
                }
 
                if ( $oldFashionedRestrictions != '' ) {
@@ -2609,7 +2614,7 @@ Title {
                                $res = $dbr->select(
                                        'page_restrictions',
                                        '*',
-                                       array( 'pr_page' => $this->getArticleId() ),
+                                       array( 'pr_page' => $this->getArticleID() ),
                                        __METHOD__
                                );
 
@@ -2861,7 +2866,7 @@ Title {
         *
         * - This is called from WikiPage::doEdit() and WikiPage::insertOn() to allow
         * loading of the new page_id. It's also called from
-        * WikiPage::doDeleteArticle()
+        * WikiPage::doDeleteArticleReal()
         *
         * @param $newid Int the new Article ID
         */
@@ -3167,7 +3172,7 @@ Title {
         * @return Array of Title objects linking here
         */
        public function getLinksFrom( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
-               $id = $this->getArticleId();
+               $id = $this->getArticleID();
 
                # If the page doesn't exist; there can't be any link from this page
                if ( !$id ) {
@@ -3231,7 +3236,7 @@ Title {
         * @return Array of Title the Title objects
         */
        public function getBrokenLinksFrom() {
-               if ( $this->getArticleId() == 0 ) {
+               if ( $this->getArticleID() == 0 ) {
                        # All links from article ID 0 are false positives
                        return array();
                }
@@ -3241,7 +3246,7 @@ Title {
                        array( 'page', 'pagelinks' ),
                        array( 'pl_namespace', 'pl_title' ),
                        array(
-                               'pl_from' => $this->getArticleId(),
+                               'pl_from' => $this->getArticleID(),
                                'page_namespace IS NULL'
                        ),
                        __METHOD__, array(),
@@ -3476,7 +3481,7 @@ Title {
                        RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache
                }
 
-               $dbw->begin(); # If $file was a LocalFile, its transaction would have closed our own.
+               $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own.
                $pageid = $this->getArticleID( self::GAID_FOR_UPDATE );
                $protected = $this->isProtected();
 
@@ -3484,7 +3489,7 @@ Title {
                $err = $this->moveToInternal( $nt, $reason, $createRedirect );
                if ( is_array( $err ) ) {
                        # @todo FIXME: What about the File we have already moved?
-                       $dbw->rollback();
+                       $dbw->rollback( __METHOD__ );
                        return $err;
                }
 
@@ -3548,7 +3553,7 @@ Title {
                        WatchedItem::duplicateEntries( $this, $nt );
                }
 
-               $dbw->commit();
+               $dbw->commit( __METHOD__ );
 
                wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid ) );
                return true;
@@ -3595,7 +3600,6 @@ Title {
                $comment = $wgContLang->truncate( $comment, 255 );
 
                $oldid = $this->getArticleID();
-               $latest = $this->getLatestRevID();
 
                $dbw = wfGetDB( DB_MASTER );
 
@@ -3636,10 +3640,14 @@ Title {
                $newpage->updateRevisionOn( $dbw, $nullRevision );
 
                wfRunHooks( 'NewRevisionFromEditComplete',
-                       array( $newpage, $nullRevision, $latest, $wgUser ) );
+                       array( $newpage, $nullRevision, $nullRevision->getParentId(), $wgUser ) );
 
                $newpage->doEditUpdates( $nullRevision, $wgUser, array( 'changed' => false ) );
 
+               if ( !$moveOverRedirect ) {
+                       WikiPage::onArticleCreate( $nt );
+               }
+
                # Recreate the redirect, this time in the other direction.
                if ( $redirectSuppressed ) {
                        WikiPage::onArticleDelete( $this );
@@ -3711,8 +3719,8 @@ Title {
                        // 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() )
+                       if ( $oldSubpage->getArticleID() == $this->getArticleID() ||
+                                       $oldSubpage->getArticleID() == $nt->getArticleID() )
                        {
                                // When moving a page to a subpage of itself,
                                // don't move it twice
@@ -3836,7 +3844,7 @@ Title {
 
                $data = array();
 
-               $titleKey = $this->getArticleId();
+               $titleKey = $this->getArticleID();
 
                if ( $titleKey === 0 ) {
                        return $data;
@@ -3912,14 +3920,20 @@ Title {
         */
        public function getPreviousRevisionID( $revId, $flags = 0 ) {
                $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
-               return $db->selectField( 'revision', 'rev_id',
+               $revId = $db->selectField( 'revision', 'rev_id',
                        array(
-                               'rev_page' => $this->getArticleId( $flags ),
+                               'rev_page' => $this->getArticleID( $flags ),
                                'rev_id < ' . intval( $revId )
                        ),
                        __METHOD__,
                        array( 'ORDER BY' => 'rev_id DESC' )
                );
+
+               if ( $revId === false ) {
+                       return false;
+               } else {
+                       return intval( $revId );
+               }
        }
 
        /**
@@ -3931,14 +3945,20 @@ Title {
         */
        public function getNextRevisionID( $revId, $flags = 0 ) {
                $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
-               return $db->selectField( 'revision', 'rev_id',
+               $revId = $db->selectField( 'revision', 'rev_id',
                        array(
-                               'rev_page' => $this->getArticleId( $flags ),
+                               'rev_page' => $this->getArticleID( $flags ),
                                'rev_id > ' . intval( $revId )
                        ),
                        __METHOD__,
                        array( 'ORDER BY' => 'rev_id' )
                );
+
+               if ( $revId === false ) {
+                       return false;
+               } else {
+                       return intval( $revId );
+               }
        }
 
        /**
@@ -3948,7 +3968,7 @@ Title {
         * @return Revision|Null if page doesn't exist
         */
        public function getFirstRevision( $flags = 0 ) {
-               $pageId = $this->getArticleId( $flags );
+               $pageId = $this->getArticleID( $flags );
                if ( $pageId ) {
                        $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
                        $row = $db->selectRow( 'revision', '*',
@@ -4013,7 +4033,7 @@ Title {
                if ( $this->mEstimateRevisions === null ) {
                        $dbr = wfGetDB( DB_SLAVE );
                        $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
-                               array( 'rev_page' => $this->getArticleId() ), __METHOD__ );
+                               array( 'rev_page' => $this->getArticleID() ), __METHOD__ );
                }
 
                return $this->mEstimateRevisions;
@@ -4040,7 +4060,7 @@ Title {
                $dbr = wfGetDB( DB_SLAVE );
                return (int)$dbr->selectField( 'revision', 'count(*)',
                        array(
-                               'rev_page' => $this->getArticleId(),
+                               'rev_page' => $this->getArticleID(),
                                'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
                                'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
                        ),
@@ -4114,7 +4134,7 @@ Title {
         * @return Bool
         */
        public function exists() {
-               return $this->getArticleId() != 0;
+               return $this->getArticleID() != 0;
        }
 
        /**
@@ -4134,9 +4154,28 @@ Title {
         * @return Bool
         */
        public function isAlwaysKnown() {
+               $isKnown = null;
+
+               /**
+                * Allows overriding default behaviour for determining if a page exists.
+                * If $isKnown is kept as null, regular checks happen. If it's
+                * a boolean, this value is returned by the isKnown method.
+                *
+                * @since 1.20
+                *
+                * @param Title $title
+                * @param boolean|null $isKnown
+                */
+               wfRunHooks( 'TitleIsAlwaysKnown', array( $this, &$isKnown ) );
+
+               if ( !is_null( $isKnown ) ) {
+                       return $isKnown;
+               }
+
                if ( $this->mInterwiki != '' ) {
                        return true;  // any interwiki link might be viewable, for all we know
                }
+
                switch( $this->mNamespace ) {
                        case NS_MEDIA:
                        case NS_FILE:
@@ -4161,25 +4200,14 @@ Title {
         * viewed?  In particular, this function may be used to determine if
         * links to the title should be rendered as "bluelinks" (as opposed to
         * "redlinks" to non-existent pages).
+        * Adding something else to this function will cause inconsistency
+        * since LinkHolderArray calls isAlwaysKnown() and does its own
+        * page existence check.
         *
         * @return Bool
         */
        public function isKnown() {
-               $isKnown = null;
-
-               /**
-                * Allows overriding default behaviour for determining if a page exists.
-                * If $isKnown is kept as null, regular checks happen. If it's
-                * a boolean, this value is returned by the isKnown method.
-                *
-                * @since 1.19
-                *
-                * @param Title $title
-                * @param boolean|null $isKnown
-                */
-               wfRunHooks( 'TitleIsKnown', array( $this, &$isKnown ) );
-
-               return is_null( $isKnown ) ? ( $this->isAlwaysKnown() || $this->exists() ) : $isKnown;
+               return $this->isAlwaysKnown() || $this->exists();
        }
 
        /**
@@ -4303,9 +4331,10 @@ Title {
                $dbr = wfGetDB( DB_SLAVE );
                $this->mNotificationTimestamp[$uid] = $dbr->selectField( 'watchlist',
                        'wl_notificationtimestamp',
-                       array( 'wl_namespace' => $this->getNamespace(),
+                       array(
+                               'wl_user' => $user->getId(),
+                               'wl_namespace' => $this->getNamespace(),
                                'wl_title' => $this->getDBkey(),
-                               'wl_user' => $user->getId()
                        ),
                        __METHOD__
                );
@@ -4460,14 +4489,14 @@ Title {
         * $wgLang (such as special pages, which are in the user language).
         *
         * @since 1.18
-        * @return object Language
+        * @return Language
         */
        public function getPageLanguage() {
                global $wgLang;
                if ( $this->isSpecialPage() ) {
                        // special pages are in the user language
                        return $wgLang;
-               } elseif ( $this->isCssOrJsPage() ) {
+               } elseif ( $this->isCssOrJsPage() || $this->isCssJsSubpage() ) {
                        // css/js should always be LTR and is, in fact, English
                        return wfGetLangObj( 'en' );
                } elseif ( $this->getNamespace() == NS_MEDIAWIKI ) {