Made loadFromFileCache() always disable $wgOut regardless of whether compression...
[lhc/web/wiklou.git] / includes / Title.php
index 31bfbf8..b5f9db4 100644 (file)
@@ -63,7 +63,6 @@ class Title {
        var $mFragment;                   // /< Title fragment (i.e. the bit after the #)
        var $mArticleID = -1;             // /< Article ID, fetched from the link cache on demand
        var $mLatestID = false;           // /< ID of most recent revision
-       var $mCounter = -1;               // /< Number of times this page has been viewed (-1 means "not loaded")
        private $mEstimateRevisions;      // /< Estimated number of revisions; null of not loaded
        var $mRestrictions = array();     // /< Array of groups allowed to edit this article
        var $mOldRestrictions = false;
@@ -261,7 +260,7 @@ class 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
+        * @param $row Object|bool database row
         * @return void
         */
        public function loadFromRow( $row ) {
@@ -274,14 +273,11 @@ class Title {
                                $this->mRedirect = (bool)$row->page_is_redirect;
                        if ( isset( $row->page_latest ) )
                                $this->mLatestID = (int)$row->page_latest;
-                       if ( isset( $row->page_counter ) )
-                               $this->mCounter = (int)$row->page_counter;
                } else { // page not found
                        $this->mArticleID = 0;
                        $this->mLength = 0;
                        $this->mRedirect = false;
                        $this->mLatestID = 0;
-                       $this->mCounter = 0;
                }
        }
 
@@ -1223,8 +1219,18 @@ class Title {
 
        /**
         * Helper to fix up the get{Local,Full,Link,Canonical}URL args
+        * get{Canonical,Full,Link,Local}URL methods accepted an optional
+        * second argument named variant. This was deprecated in favor
+        * of passing an array of option with a "variant" key
+        * Once $query2 is removed for good, this helper can be dropped
+        * andthe wfArrayToCGI moved to getLocalURL();
+        *
+        * @since 1.19 (r105919)
         */
-       private static function fixUrlQueryArgs( $query, $query2 ) {
+       private static function fixUrlQueryArgs( $query, $query2 = false ) {
+               if( $query2 !== false ) {
+                       wfDeprecated( "Title::get{Canonical,Full,Link,Local} method called with a second parameter is deprecated. Add your parameter to an array passed as the first parameter.", "1.19" );
+               }
                if ( is_array( $query ) ) {
                        $query = wfArrayToCGI( $query );
                }
@@ -1286,6 +1292,9 @@ class Title {
         *   be an array. If a string is passed it will be interpreted as a deprecated
         *   variant argument and urlencoded into a variant= argument.
         *   This second query argument will be added to the $query
+        *   The second parameter is deprecated since 1.19. Pass it as a key,value
+        *   pair in the first parameter array instead.
+        *
         * @return String the URL
         */
        public function getLocalURL( $query = '', $query2 = false ) {
@@ -1965,11 +1974,11 @@ class Title {
         * @return Array list of errors
         */
        private function checkReadPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
+               global $wgWhitelistRead, $wgGroupPermissions, $wgRevokePermissions;
                static $useShortcut = null;
 
                # Initialize the $useShortcut boolean, to determine if we can skip quite a bit of code below
                if ( is_null( $useShortcut ) ) {
-                       global $wgGroupPermissions, $wgRevokePermissions;
                        $useShortcut = true;
                        if ( empty( $wgGroupPermissions['*']['read'] ) ) {
                                # Not a public wiki, so no shortcut
@@ -1991,61 +2000,56 @@ class Title {
                        }
                }
 
-               # Shortcut for public wikis, allows skipping quite a bit of code
+               $whitelisted = false;
                if ( $useShortcut ) {
-                       return $errors;
-               }
-
-               # If the user is allowed to read pages, he is allowed to read all pages
-               if ( $user->isAllowed( 'read' ) ) {
-                       return $errors;
-               }
-
-               # Always grant access to the login page.
-               # Even anons need to be able to log in.
-               if ( $this->isSpecial( 'Userlogin' )
+                       # Shortcut for public wikis, allows skipping quite a bit of code
+                       $whitelisted = true;
+               } elseif ( $user->isAllowed( 'read' ) ) {
+                       # If the user is allowed to read pages, he is allowed to read all pages
+                       $whitelisted = true;
+               } elseif ( $this->isSpecial( 'Userlogin' )
                        || $this->isSpecial( 'ChangePassword' )
                        || $this->isSpecial( 'PasswordReset' )
                ) {
-                       return $errors;
-               }
-
-               # Time to check the whitelist
-               global $wgWhitelistRead;
-
-               # Only do these checks is there's something to check against
-               if ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
-                       # Check for explicit whitelisting
+                       # Always grant access to the login page.
+                       # Even anons need to be able to log in.
+                       $whitelisted = true;
+               } elseif ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
+                       # Time to check the whitelist
+                       # Only do these checks is there's something to check against
                        $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 ) ) {
-                               return $errors;
-                       }
-
-                       # Old settings might have the title prefixed with
-                       # a colon for main-namespace pages
-                       if ( $this->getNamespace() == NS_MAIN ) {
+                               $whitelisted = true;
+                       } elseif ( $this->getNamespace() == NS_MAIN ) {
+                               # Old settings might have the title prefixed with
+                               # a colon for main-namespace pages
                                if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
-                                       return $errors;
+                                       $whitelisted = true;
                                }
-                       }
-
-                       # If it's a special page, ditch the subpage bit and check again
-                       if ( $this->isSpecialPage() ) {
+                       } elseif ( $this->isSpecialPage() ) {
+                               # If it's a special page, ditch the subpage bit and check again
                                $name = $this->getDBkey();
                                list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
                                if ( $name !== false ) {
                                        $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
                                        if ( in_array( $pure, $wgWhitelistRead, true ) ) {
-                                               return $errors;
+                                               $whitelisted = true;
                                        }
                                }
                        }
                }
 
-               $errors[] = $this->missingPermissionError( $action, $short );
+               if ( !$whitelisted ) {
+                       # If the title is not whitelisted, give extensions a chance to do so...
+                       wfRunHooks( 'TitleReadWhitelist', array( $this, $user, &$whitelisted ) );
+                       if ( !$whitelisted ) {
+                               $errors[] = $this->missingPermissionError( $action, $short );
+                       }
+               }
+
                return $errors;
        }
 
@@ -2761,28 +2765,6 @@ class Title {
                return $deleted;
        }
 
-       /**
-        * Get the number of views of this page
-        *
-        * @return int The view count for the page
-        */
-       public function getCount() {
-               if ( $this->mCounter == -1 ) {
-                       if ( $this->exists() ) {
-                               $dbr = wfGetDB( DB_SLAVE );
-                               $this->mCounter = $dbr->selectField( 'page',
-                                       'page_counter',
-                                       array( 'page_id' => $this->getArticleID() ),
-                                       __METHOD__
-                               );
-                       } else {
-                               $this->mCounter = 0;
-                       }
-               }
-
-               return $this->mCounter;
-       }
-
        /**
         * Get the article ID for this Title from the link cache,
         * adding it if necessary
@@ -2875,9 +2857,9 @@ class Title {
         * This clears some fields in this object, and clears any associated
         * keys in the "bad links" section of the link cache.
         *
-        * - This is called from Article::doEdit() and Article::insertOn() to allow
+        * - This is called from WikiPage::doEdit() and WikiPage::insertOn() to allow
         * loading of the new page_id. It's also called from
-        * Article::doDeleteArticle()
+        * WikiPage::doDeleteArticle()
         *
         * @param $newid Int the new Article ID
         */
@@ -2895,7 +2877,6 @@ class Title {
                $this->mRedirect = null;
                $this->mLength = -1;
                $this->mLatestID = false;
-               $this->mCounter = -1;
                $this->mEstimateRevisions = null;
        }
 
@@ -3126,8 +3107,6 @@ class Title {
         * @return Array of Title objects linking here
         */
        public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
-               $linkCache = LinkCache::singleton();
-
                if ( count( $options ) > 0 ) {
                        $db = wfGetDB( DB_MASTER );
                } else {
@@ -3146,7 +3125,8 @@ class Title {
                );
 
                $retVal = array();
-               if ( $db->numRows( $res ) ) {
+               if ( $res->numRows() ) {
+                       $linkCache = LinkCache::singleton();
                        foreach ( $res as $row ) {
                                $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
                                if ( $titleObj ) {
@@ -3172,6 +3152,76 @@ class Title {
                return $this->getLinksTo( $options, 'templatelinks', 'tl' );
        }
 
+       /**
+        * Get an array of Title objects linked from this Title
+        * Also stores the IDs in the link cache.
+        *
+        * WARNING: do not use this function on arbitrary user-supplied titles!
+        * On heavily-used templates it will max out the memory.
+        *
+        * @param $options Array: may be FOR UPDATE
+        * @param $table String: table name
+        * @param $prefix String: fields prefix
+        * @return Array of Title objects linking here
+        */
+       public function getLinksFrom( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
+               $id = $this->getArticleId();
+
+               # If the page doesn't exist; there can't be any link from this page
+               if ( !$id ) {
+                       return array();
+               }
+
+               if ( count( $options ) > 0 ) {
+                       $db = wfGetDB( DB_MASTER );
+               } else {
+                       $db = wfGetDB( DB_SLAVE );
+               }
+
+               $namespaceFiled = "{$prefix}_namespace";
+               $titleField = "{$prefix}_title";
+
+               $res = $db->select(
+                       array( $table, 'page' ),
+                       array( $namespaceFiled, $titleField, 'page_id', 'page_len', 'page_is_redirect', 'page_latest' ),
+                       array( "{$prefix}_from" => $id ),
+                       __METHOD__,
+                       $options,
+                       array( 'page' => array( 'LEFT JOIN', array( "page_namespace=$namespaceFiled", "page_title=$titleField" ) ) )
+               );
+
+               $retVal = array();
+               if ( $res->numRows() ) {
+                       $linkCache = LinkCache::singleton();
+                       foreach ( $res as $row ) {
+                               $titleObj = Title::makeTitle( $row->$namespaceFiled, $row->$titleField );
+                               if ( $titleObj ) {
+                                       if ( $row->page_id ) {
+                                               $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
+                                       } else {
+                                               $linkCache->addBadLinkObj( $titleObj );
+                                       }
+                                       $retVal[] = $titleObj;
+                               }
+                       }
+               }
+               return $retVal;
+       }
+
+       /**
+        * Get an array of Title objects used on this Title as a template
+        * Also stores the IDs in the link cache.
+        *
+        * WARNING: do not use this function on arbitrary user-supplied titles!
+        * On heavily-used templates it will max out the memory.
+        *
+        * @param $options Array: may be FOR UPDATE
+        * @return Array of Title the Title objects used here
+        */
+       public function getTemplateLinksFrom( $options = array() ) {
+               return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
+       }
+
        /**
         * Get an array of Title objects referring to non-existent articles linked from this page
         *
@@ -3419,15 +3469,14 @@ class Title {
                                        return $status->getErrorsArray();
                                }
                        }
+                       // Clear RepoGroup process cache
+                       RepoGroup::singleton()->clearCache( $this );
+                       RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache
                }
-               // Clear RepoGroup process cache
-               RepoGroup::singleton()->clearCache( $this );
-               RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache
 
                $dbw->begin(); # If $file was a LocalFile, its transaction would have closed our own.
                $pageid = $this->getArticleID( self::GAID_FOR_UPDATE );
                $protected = $this->isProtected();
-               $pageCountChange = ( $createRedirect ? 1 : 0 ) - ( $nt->exists() ? 1 : 0 );
 
                // Do the actual move
                $err = $this->moveToInternal( $nt, $reason, $createRedirect );
@@ -3437,8 +3486,6 @@ class Title {
                        return $err;
                }
 
-               $redirid = $this->getArticleID();
-
                // Refresh the sortkey for this row.  Be careful to avoid resetting
                // cl_timestamp, which may disturb time-based lists on some sites.
                $prefixes = $dbw->select(
@@ -3462,6 +3509,8 @@ class Title {
                        );
                }
 
+               $redirid = $this->getArticleID();
+
                if ( $protected ) {
                        # Protect the redirect title as the title used to be...
                        $dbw->insertSelect( 'page_restrictions', 'page_restrictions',
@@ -3497,50 +3546,8 @@ class Title {
                        WatchedItem::duplicateEntries( $this, $nt );
                }
 
-               # Update search engine
-               $u = new SearchUpdate( $pageid, $nt->getPrefixedDBkey() );
-               $u->doUpdate();
-               $u = new SearchUpdate( $redirid, $this->getPrefixedDBkey(), '' );
-               $u->doUpdate();
-
                $dbw->commit();
 
-               # Update site_stats
-               if ( $this->isContentPage() && !$nt->isContentPage() ) {
-                       # No longer a content page
-                       # Not viewed, edited, removing
-                       $u = new SiteStatsUpdate( 0, 1, -1, $pageCountChange );
-               } elseif ( !$this->isContentPage() && $nt->isContentPage() ) {
-                       # Now a content page
-                       # Not viewed, edited, adding
-                       $u = new SiteStatsUpdate( 0, 1, + 1, $pageCountChange );
-               } elseif ( $pageCountChange ) {
-                       # Redirect added
-                       $u = new SiteStatsUpdate( 0, 0, 0, 1 );
-               } else {
-                       # Nothing special
-                       $u = false;
-               }
-               if ( $u ) {
-                       $u->doUpdate();
-               }
-
-               # Update message cache for interface messages
-               if ( $this->getNamespace() == NS_MEDIAWIKI ) {
-                       # @bug 17860: old article can be deleted, if this the case,
-                       # delete it from message cache
-                       if ( $this->getArticleID() === 0 ) {
-                               MessageCache::singleton()->replace( $this->getDBkey(), false );
-                       } else {
-                               $rev = Revision::newFromTitle( $this );
-                               MessageCache::singleton()->replace( $this->getDBkey(), $rev->getText() );
-                       }
-               }
-               if ( $nt->getNamespace() == NS_MEDIAWIKI ) {
-                       $rev = Revision::newFromTitle( $nt );
-                       MessageCache::singleton()->replace( $nt->getDBkey(), $rev->getText() );
-               }
-
                wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid ) );
                return true;
        }
@@ -3590,36 +3597,18 @@ class Title {
 
                $dbw = wfGetDB( DB_MASTER );
 
-               if ( $moveOverRedirect ) {
-                       $rcts = $dbw->timestamp( $nt->getEarliestRevTime() );
+               $newpage = WikiPage::factory( $nt );
 
+               if ( $moveOverRedirect ) {
                        $newid = $nt->getArticleID();
-                       $newns = $nt->getNamespace();
-                       $newdbk = $nt->getDBkey();
 
                        # Delete the old redirect. We don't save it to history since
                        # by definition if we've got here it's rather uninteresting.
                        # We have to remove it so that the next step doesn't trigger
                        # a conflict on the unique namespace+title index...
                        $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ );
-                       if ( !$dbw->cascadingDeletes() ) {
-                               $dbw->delete( 'revision', array( 'rev_page' => $newid ), __METHOD__ );
-
-                               $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ );
-                               $dbw->delete( 'imagelinks', array( 'il_from' => $newid ), __METHOD__ );
-                               $dbw->delete( 'categorylinks', array( 'cl_from' => $newid ), __METHOD__ );
-                               $dbw->delete( 'templatelinks', array( 'tl_from' => $newid ), __METHOD__ );
-                               $dbw->delete( 'externallinks', array( 'el_from' => $newid ), __METHOD__ );
-                               $dbw->delete( 'langlinks', array( 'll_from' => $newid ), __METHOD__ );
-                               $dbw->delete( 'iwlinks', array( 'iwl_from' => $newid ), __METHOD__ );
-                               $dbw->delete( 'redirect', array( 'rd_from' => $newid ), __METHOD__ );
-                               $dbw->delete( 'page_props', array( 'pp_page' => $newid ), __METHOD__ );
-                       }
-                       // If the target page was recently created, it may have an entry in recentchanges still
-                       $dbw->delete( 'recentchanges',
-                               array( 'rc_timestamp' => $rcts, 'rc_namespace' => $newns, 'rc_title' => $newdbk, 'rc_new' => 1 ),
-                               __METHOD__
-                       );
+
+                       $newpage->doDeleteUpdates( $newid );
                }
 
                # Save a null revision in the page's history notifying of the move
@@ -3629,27 +3618,30 @@ class Title {
                }
                $nullRevId = $nullRevision->insertOn( $dbw );
 
-               $now = wfTimestampNow();
                # Change the name of the target page:
                $dbw->update( 'page',
                        /* SET */ array(
-                               'page_touched'   => $dbw->timestamp( $now ),
                                'page_namespace' => $nt->getNamespace(),
                                'page_title'     => $nt->getDBkey(),
-                               'page_latest'    => $nullRevId,
                        ),
                        /* WHERE */ array( 'page_id' => $oldid ),
                        __METHOD__
                );
+
+               $this->resetArticleID( 0 );
                $nt->resetArticleID( $oldid );
 
-               $article = WikiPage::factory( $nt );
+               $newpage->updateRevisionOn( $dbw, $nullRevision );
+
                wfRunHooks( 'NewRevisionFromEditComplete',
-                       array( $article, $nullRevision, $latest, $wgUser ) );
-               $article->setCachedLastEditTime( $now );
+                       array( $newpage, $nullRevision, $latest, $wgUser ) );
+
+               $newpage->doEditUpdates( $nullRevision, $wgUser, array( 'changed' => false ) );
 
                # Recreate the redirect, this time in the other direction.
-               if ( $createRedirect || !$wgUser->isAllowed( 'suppressredirect' ) ) {
+               if ( $redirectSuppressed ) {
+                       WikiPage::onArticleDelete( $this );
+               } else {
                        $mwRedir = MagicWord::get( 'redirect' );
                        $redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $nt->getPrefixedText() . "]]\n";
                        $redirectArticle = WikiPage::factory( $this );
@@ -3665,33 +3657,13 @@ class Title {
                                wfRunHooks( 'NewRevisionFromEditComplete',
                                        array( $redirectArticle, $redirectRevision, false, $wgUser ) );
 
-                               # Now, we record the link from the redirect to the new title.
-                               # It should have no other outgoing links...
-                               $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ );
-                               $dbw->insert( 'pagelinks',
-                                       array(
-                                               'pl_from'      => $newid,
-                                               'pl_namespace' => $nt->getNamespace(),
-                                               'pl_title'     => $nt->getDBkey() ),
-                                       __METHOD__ );
+                               $redirectArticle->doEditUpdates( $redirectRevision, $wgUser, array( 'created' => true ) );
                        }
-               } else {
-                       $this->resetArticleID( 0 );
                }
 
                # Log the move
                $logid = $logEntry->insert();
                $logEntry->publish( $logid );
-
-               # Purge caches for old and new titles
-               if ( $moveOverRedirect ) {
-                       # A simple purge is enough when moving over a redirect
-                       $nt->purgeSquid();
-               } else {
-                       # Purge caches as per article creation, including any pages that link to this title
-                       Article::onArticleCreate( $nt );
-               }
-               $this->purgeSquid();
        }
 
        /**
@@ -4191,7 +4163,21 @@ class Title {
         * @return Bool
         */
        public function isKnown() {
-               return $this->isAlwaysKnown() || $this->exists();
+               $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( 'TitleIsKnown', array( $this, &$isKnown ) );
+               
+               return is_null( $isKnown ) ? ( $this->isAlwaysKnown() || $this->exists() ) : $isKnown;
        }
 
        /**