Tweaked cache size in getCachedRevisionObject()
[lhc/web/wiklou.git] / includes / Title.php
index 4adf2e4..7fdeb05 100644 (file)
@@ -74,6 +74,9 @@ class Title {
        /** @var string Interwiki prefix */
        public $mInterwiki = '';
 
+       /** @var bool Was this Title created from a string with a local interwiki prefix? */
+       private $mLocalInterwiki = false;
+
        /** @var string Title fragment (i.e. the bit after the #) */
        public $mFragment = '';
 
@@ -95,7 +98,7 @@ class Title {
        /** @var array Array of groups allowed to edit this article */
        public $mRestrictions = array();
 
-       /** @var bool  */
+       /** @var bool */
        protected $mOldRestrictions = false;
 
        /** @var bool Cascade restrictions on this page to included templates and images? */
@@ -155,6 +158,9 @@ class Title {
 
        /** @var TitleValue A corresponding TitleValue object */
        private $mTitleValue = null;
+
+       /** @var bool Would deleting this page be a big deletion? */
+       private $mIsBigDeletion = null;
        // @}
 
        /**
@@ -608,7 +614,7 @@ class Title {
         * Note that this doesn't pick up many things that could be wrong with titles, but that
         * replacing this regex with something valid will make many titles valid.
         *
-        * @todo: move this into MediaWikiTitleCodec
+        * @todo move this into MediaWikiTitleCodec
         *
         * @return string Regex string
         */
@@ -823,6 +829,15 @@ class Title {
                return $this->mInterwiki;
        }
 
+       /**
+        * Was this a local interwiki link?
+        *
+        * @return bool
+        */
+       public function wasLocalInterwiki() {
+               return $this->mLocalInterwiki;
+       }
+
        /**
         * Determine whether the object refers to a page within
         * this project and is transcludable.
@@ -853,7 +868,7 @@ class Title {
        /**
         * Get a TitleValue object representing this Title.
         *
-        * @note: Not all valid Titles have a corresponding valid TitleValue
+        * @note Not all valid Titles have a corresponding valid TitleValue
         * (e.g. TitleValues cannot represent page-local links that have a
         * fragment but no title text).
         *
@@ -929,10 +944,12 @@ class Title {
         * Get the page's content model id, see the CONTENT_MODEL_XXX constants.
         *
         * @throws MWException
+        * @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select for update
         * @return string Content model id
         */
-       public function getContentModel() {
-               if ( !$this->mContentModel ) {
+       public function getContentModel( $flags = 0 ) {
+               # Calling getArticleID() loads the field from cache as needed
+               if ( !$this->mContentModel && $this->getArticleID( $flags ) ) {
                        $linkCache = LinkCache::singleton();
                        $this->mContentModel = $linkCache->getGoodLinkFieldObj( $this, 'model' );
                }
@@ -1094,7 +1111,7 @@ class Title {
        /**
         * Returns true if the title is inside one of the specified namespaces.
         *
-        * @param ...$namespaces The namespaces to check for
+        * @param int $namespaces,... The namespaces to check for
         * @return bool
         * @since 1.19
         */
@@ -1643,9 +1660,11 @@ class Title {
         * $wgServer is prepended to make an absolute URL.
         *
         * @see self::getFullURL to always get an absolute URL.
+        * @see self::getLinkURL to always get a URL that's the simplest URL that will be
+        *  valid to link, locally, to the current Title.
         * @see self::newFromText to produce a Title object.
         *
-        * @param string|array $query an optional query string,
+        * @param string|array $query 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.
@@ -1878,11 +1897,11 @@ class Title {
         *
         * @todo FIXME: This *does not* check throttles (User::pingLimiter()).
         *
-        * @param string $action action that permission needs to be checked for
+        * @param string $action Action that permission needs to be checked for
         * @param User $user User to check
         * @param bool $doExpensiveQueries Set this to false to avoid doing unnecessary
         *   queries by skipping checks for cascading protections and user blocks.
-        * @param array $ignoreErrors of Strings Set this to a list of message keys
+        * @param array $ignoreErrors Array of Strings Set this to a list of message keys
         *   whose corresponding errors may be ignored.
         * @return array Array of arguments to wfMessage to explain permissions problems.
         */
@@ -1906,7 +1925,7 @@ class Title {
        /**
         * Permissions checks that fail most often, and which are easiest to test.
         *
-        * @param string $action the action to check
+        * @param string $action The action to check
         * @param User $user User to check
         * @param array $errors List of current errors
         * @param bool $doExpensiveQueries Whether or not to perform expensive queries
@@ -2061,7 +2080,7 @@ class Title {
                        $ns = $this->mNamespace == NS_MAIN ?
                                wfMessage( 'nstab-main' )->text() : $this->getNsText();
                        $errors[] = $this->mNamespace == NS_MEDIAWIKI ?
-                               array( 'protectedinterface' ) : array( 'namespaceprotected', $ns );
+                               array( 'protectedinterface', $action ) : array( 'namespaceprotected', $ns, $action );
                }
 
                return $errors;
@@ -2085,15 +2104,15 @@ class Title {
                if ( $action != 'patrol' && !$user->isAllowed( 'editusercssjs' ) ) {
                        if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
                                if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) {
-                                       $errors[] = array( 'mycustomcssprotected' );
+                                       $errors[] = array( 'mycustomcssprotected', $action );
                                } elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) {
-                                       $errors[] = array( 'mycustomjsprotected' );
+                                       $errors[] = array( 'mycustomjsprotected', $action );
                                }
                        } else {
                                if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) {
-                                       $errors[] = array( 'customcssprotected' );
+                                       $errors[] = array( 'customcssprotected', $action );
                                } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
-                                       $errors[] = array( 'customjsprotected' );
+                                       $errors[] = array( 'customjsprotected', $action );
                                }
                        }
                }
@@ -2128,9 +2147,9 @@ class Title {
                                continue;
                        }
                        if ( !$user->isAllowed( $right ) ) {
-                               $errors[] = array( 'protectedpagetext', $right );
+                               $errors[] = array( 'protectedpagetext', $right, $action );
                        } elseif ( $this->mCascadeRestriction && !$user->isAllowed( 'protect' ) ) {
-                               $errors[] = array( 'protectedpagetext', 'protect' );
+                               $errors[] = array( 'protectedpagetext', 'protect', $action );
                        }
                }
 
@@ -2177,7 +2196,7 @@ class Title {
                                                foreach ( $cascadingSources as $page ) {
                                                        $pages .= '* [[:' . $page->getPrefixedText() . "]]\n";
                                                }
-                                               $errors[] = array( 'cascadeprotected', count( $cascadingSources ), $pages );
+                                               $errors[] = array( 'cascadeprotected', count( $cascadingSources ), $pages, $action );
                                        }
                                }
                        }
@@ -2244,6 +2263,16 @@ class Title {
                                $errors[] = array( 'immobile-target-page' );
                        }
                } elseif ( $action == 'delete' ) {
+                       $tempErrors = $this->checkPageRestrictions( 'edit',
+                               $user, array(), $doExpensiveQueries, true );
+                       if ( !$tempErrors ) {
+                               $tempErrors = $this->checkCascadingSourcesRestrictions( 'edit',
+                                       $user, $tempErrors, $doExpensiveQueries, true );
+                       }
+                       if ( $tempErrors ) {
+                               // If protection keeps them from editing, they shouldn't be able to delete.
+                               $errors[] = array( 'deleteprotected' );
+                       }
                        if ( $doExpensiveQueries && $wgDeleteRevisionsLimit
                                && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion()
                        ) {
@@ -2417,6 +2446,19 @@ class Title {
                                'checkPermissionHooks',
                                'checkReadPermissions',
                        );
+               # Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions
+               # here as it will lead to duplicate error messages. This is okay to do
+               # since anywhere that checks for create will also check for edit, and
+               # those checks are called for edit.
+               } elseif ( $action == 'create' ) {
+                       $checks = array(
+                               'checkQuickPermissions',
+                               'checkPermissionHooks',
+                               'checkPageRestrictions',
+                               'checkCascadingSourcesRestrictions',
+                               'checkActionPermissions',
+                               'checkUserBlock'
+                       );
                } else {
                        $checks = array(
                                'checkQuickPermissions',
@@ -2564,7 +2606,7 @@ class Title {
        /**
         * Does the title correspond to a protected article?
         *
-        * @param string $action the action the page is protected from,
+        * @param string $action The action the page is protected from,
         * by default checks all actions.
         * @return bool
         */
@@ -3238,6 +3280,7 @@ class Title {
                $this->mEstimateRevisions = null;
                $this->mPageLanguage = false;
                $this->mDbPageLanguage = null;
+               $this->mIsBigDeletion = null;
        }
 
        /**
@@ -3280,8 +3323,8 @@ class Title {
                        // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
                        //        the parsing code with Title, while avoiding massive refactoring.
                        // @todo: get rid of secureAndSplit, refactor parsing code.
-                       $parser = self::getTitleParser();
-                       $parts = $parser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
+                       $titleParser = self::getTitleParser();
+                       $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
                } catch ( MalformedTitleException $ex ) {
                        return false;
                }
@@ -3289,6 +3332,7 @@ class Title {
                # Fill fields
                $this->setFragment( '#' . $parts['fragment'] );
                $this->mInterwiki = $parts['interwiki'];
+               $this->mLocalInterwiki = $parts['local_interwiki'];
                $this->mNamespace = $parts['namespace'];
                $this->mUserCaseDBKey = $parts['user_case_dbkey'];
 
@@ -3814,13 +3858,31 @@ class Title {
                        $log->addRelations( 'pr_id', $logRelationsValues, $logId );
                }
 
+               // Update *_from_namespace fields as needed
+               if ( $this->getNamespace() != $nt->getNamespace() ) {
+                       $dbw->update( 'pagelinks',
+                               array( 'pl_from_namespace' => $nt->getNamespace() ),
+                               array( 'pl_from' => $pageid ),
+                               __METHOD__
+                       );
+                       $dbw->update( 'templatelinks',
+                               array( 'tl_from_namespace' => $nt->getNamespace() ),
+                               array( 'tl_from' => $pageid ),
+                               __METHOD__
+                       );
+                       $dbw->update( 'imagelinks',
+                               array( 'il_from_namespace' => $nt->getNamespace() ),
+                               array( 'il_from' => $pageid ),
+                               __METHOD__
+                       );
+               }
+
                # Update watchlists
-               $oldnamespace = MWNamespace::getSubject( $this->getNamespace() );
-               $newnamespace = MWNamespace::getSubject( $nt->getNamespace() );
                $oldtitle = $this->getDBkey();
                $newtitle = $nt->getDBkey();
-
-               if ( $oldnamespace != $newnamespace || $oldtitle != $newtitle ) {
+               $oldsnamespace = MWNamespace::getSubject( $this->getNamespace() );
+               $newsnamespace = MWNamespace::getSubject( $nt->getNamespace() );
+               if ( $oldsnamespace != $newsnamespace || $oldtitle != $newtitle ) {
                        WatchedItem::duplicateEntries( $this, $nt );
                }
 
@@ -4325,12 +4387,32 @@ class Title {
                        return false;
                }
 
-               $revCount = $this->estimateRevisionCount();
-               return $revCount > $wgDeleteRevisionsLimit;
+               if ( $this->mIsBigDeletion === null ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+
+                       $innerQuery = $dbr->selectSQLText(
+                               'revision',
+                               '1',
+                               array( 'rev_page' => $this->getArticleID() ),
+                               __METHOD__,
+                               array( 'LIMIT' => $wgDeleteRevisionsLimit + 1 )
+                       );
+
+                       $revCount = $dbr->query(
+                               'SELECT COUNT(*) FROM (' . $innerQuery . ') AS innerQuery',
+                               __METHOD__
+                       );
+                       $revCount = $revCount->fetchRow();
+                       $revCount = $revCount['COUNT(*)'];
+
+                       $this->mIsBigDeletion = $revCount > $wgDeleteRevisionsLimit;
+               }
+
+               return $this->mIsBigDeletion;
        }
 
        /**
-        * Get the  approximate revision count of this page.
+        * Get the approximate revision count of this page.
         *
         * @return int
         */