Merge "Add and use Title::getOtherPage()"
[lhc/web/wiklou.git] / includes / page / WikiPage.php
index 81c93a1..dcebe54 100644 (file)
@@ -50,7 +50,7 @@ class WikiPage implements Page, IDBAccessObject {
        public $mLatest = false;             // !< Integer (false means "not loaded")
        /**@}}*/
 
-       /** @var stdclass Map of cache fields (text, parser output, ect) for a proposed/new edit */
+       /** @var stdClass Map of cache fields (text, parser output, ect) for a proposed/new edit */
        public $mPreparedEdit = false;
 
        /**
@@ -278,7 +278,6 @@ class WikiPage implements Page, IDBAccessObject {
                        'page_namespace',
                        'page_title',
                        'page_restrictions',
-                       'page_counter',
                        'page_is_redirect',
                        'page_is_new',
                        'page_random',
@@ -309,11 +308,11 @@ class WikiPage implements Page, IDBAccessObject {
        protected function pageData( $dbr, $conditions, $options = array() ) {
                $fields = self::selectFields();
 
-               wfRunHooks( 'ArticlePageDataBefore', array( &$this, &$fields ) );
+               Hooks::run( 'ArticlePageDataBefore', array( &$this, &$fields ) );
 
                $row = $dbr->selectRow( 'page', $fields, $conditions, __METHOD__, $options );
 
-               wfRunHooks( 'ArticlePageDataAfter', array( &$this, &$row ) );
+               Hooks::run( 'ArticlePageDataAfter', array( &$this, &$row ) );
 
                return $row;
        }
@@ -346,8 +345,7 @@ class WikiPage implements Page, IDBAccessObject {
        }
 
        /**
-        * Set the general counter, title etc data loaded from
-        * some source.
+        * Load the object from a given source by title
         *
         * @param object|string|int $from One of the following:
         *   - A DB query result object.
@@ -397,7 +395,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param string|int $from One of the following:
         *        - "fromdb" or WikiPage::READ_NORMAL if the data comes from a slave DB
         *        - "fromdbmaster" or WikiPage::READ_LATEST if the data comes from the master DB
-        *        - "forupdate"  or WikiPage::READ_LOCKING if the data comes from from
+        *        - "forupdate"  or WikiPage::READ_LOCKING if the data comes from
         *          the master DB using SELECT FOR UPDATE
         */
        public function loadFromRow( $data, $from ) {
@@ -977,7 +975,7 @@ class WikiPage implements Page, IDBAccessObject {
                                $source = $this->mTitle->getFullURL( 'redirect=no' );
                                return $rt->getFullURL( array( 'rdfrom' => $source ) );
                        } else {
-                               // External pages pages without "local" bit set are not valid
+                               // External pages without "local" bit set are not valid
                                // redirect targets
                                return false;
                        }
@@ -1180,7 +1178,7 @@ class WikiPage implements Page, IDBAccessObject {
        public function doPurge() {
                global $wgUseSquid;
 
-               if ( !wfRunHooks( 'ArticlePurge', array( &$this ) ) ) {
+               if ( !Hooks::run( 'ArticlePurge', array( &$this ) ) ) {
                        return false;
                }
 
@@ -1698,7 +1696,7 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @param bool|int $baseRevId The revision ID this edit was based off, if any
         * @param User $user The user doing the edit
-        * @param string $serialisation_format Format for storing the content in the
+        * @param string $serialFormat Format for storing the content in the
         *   database.
         *
         * @throws MWException
@@ -1719,7 +1717,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @since 1.21
         */
        public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
-               User $user = null, $serialisation_format = null
+               User $user = null, $serialFormat = null
        ) {
                global $wgUser, $wgUseAutomaticEditSummaries, $wgUseRCPatrol, $wgUseNPPatrol;
 
@@ -1751,7 +1749,7 @@ class WikiPage implements Page, IDBAccessObject {
                $hook_args = array( &$this, &$user, &$content, &$summary,
                                                        $flags & EDIT_MINOR, null, null, &$flags, &$status );
 
-               if ( !wfRunHooks( 'PageContentSave', $hook_args )
+               if ( !Hooks::run( 'PageContentSave', $hook_args )
                        || !ContentHandler::runLegacyHooks( 'ArticleSave', $hook_args ) ) {
 
                        wfDebug( __METHOD__ . ": ArticleSave or ArticleSaveContent hook aborted save!\n" );
@@ -1785,7 +1783,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $summary = $handler->getAutosummary( $old_content, $content, $flags );
                }
 
-               $editInfo = $this->prepareContentForEdit( $content, null, $user, $serialisation_format );
+               $editInfo = $this->prepareContentForEdit( $content, null, $user, $serialFormat );
                $serialized = $editInfo->pst;
 
                /**
@@ -1827,17 +1825,12 @@ class WikiPage implements Page, IDBAccessObject {
                                'user_text'  => $user->getName(),
                                'timestamp'  => $now,
                                'content_model' => $content->getModel(),
-                               'content_format' => $serialisation_format,
+                               'content_format' => $serialFormat,
                        ) ); // XXX: pass content object?!
 
                        $changed = !$content->equals( $old_content );
 
                        if ( $changed ) {
-                               if ( !$content->isValid() ) {
-                                       wfProfileOut( __METHOD__ );
-                                       throw new MWException( "New content failed validity check!" );
-                               }
-
                                $dbw->begin( __METHOD__ );
                                try {
 
@@ -1867,7 +1860,7 @@ class WikiPage implements Page, IDBAccessObject {
                                                return $status;
                                        }
 
-                                       wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
+                                       Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
                                        // Update recentchanges
                                        if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
                                                // Mark as patrolled if the user can do so
@@ -1958,7 +1951,7 @@ class WikiPage implements Page, IDBAccessObject {
                                        'user_text'  => $user->getName(),
                                        'timestamp'  => $now,
                                        'content_model' => $content->getModel(),
-                                       'content_format' => $serialisation_format,
+                                       'content_format' => $serialFormat,
                                ) );
                                $revisionId = $revision->insertOn( $dbw );
 
@@ -1972,7 +1965,7 @@ class WikiPage implements Page, IDBAccessObject {
                                // Update the page record with revision data
                                $this->updateRevisionOn( $dbw, $revision, 0 );
 
-                               wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
+                               Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
 
                                // Update recentchanges
                                if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
@@ -2003,7 +1996,7 @@ class WikiPage implements Page, IDBAccessObject {
                                                                $flags & EDIT_MINOR, null, null, &$flags, $revision );
 
                        ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $hook_args );
-                       wfRunHooks( 'PageContentInsertComplete', $hook_args );
+                       Hooks::run( 'PageContentInsertComplete', $hook_args );
                }
 
                // Do updates right now unless deferral was requested
@@ -2018,7 +2011,7 @@ class WikiPage implements Page, IDBAccessObject {
                                                        $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
 
                ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
-               wfRunHooks( 'PageContentSaveComplete', $hook_args );
+               Hooks::run( 'PageContentSaveComplete', $hook_args );
 
                // Promote user to any groups they meet the criteria for
                $dbw->onTransactionIdle( function () use ( $user ) {
@@ -2057,7 +2050,7 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Prepare text which is about to be saved.
-        * Returns a stdclass with source, pst and output members
+        * Returns a stdClass with source, pst and output members
         *
         * @deprecated since 1.21: use prepareContentForEdit instead.
         * @return object
@@ -2070,54 +2063,76 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Prepare content which is about to be saved.
-        * Returns a stdclass with source, pst and output members
+        * Returns a stdClass with source, pst and output members
         *
         * @param Content $content
         * @param int|null $revid
         * @param User|null $user
-        * @param string|null $serialization_format
+        * @param string|null $serialFormat
+        * @param bool $useCache Check shared prepared edit cache
         *
-        * @return bool|object
+        * @return object
         *
         * @since 1.21
         */
-       public function prepareContentForEdit( Content $content, $revid = null, User $user = null,
-               $serialization_format = null
+       public function prepareContentForEdit(
+               Content $content, $revid = null, User $user = null, $serialFormat = null, $useCache = true
        ) {
                global $wgContLang, $wgUser;
+
                $user = is_null( $user ) ? $wgUser : $user;
                //XXX: check $user->getId() here???
 
-               // Use a sane default for $serialization_format, see bug 57026
-               if ( $serialization_format === null ) {
-                       $serialization_format = $content->getContentHandler()->getDefaultFormat();
+               // Use a sane default for $serialFormat, see bug 57026
+               if ( $serialFormat === null ) {
+                       $serialFormat = $content->getContentHandler()->getDefaultFormat();
                }
 
                if ( $this->mPreparedEdit
                        && $this->mPreparedEdit->newContent
                        && $this->mPreparedEdit->newContent->equals( $content )
                        && $this->mPreparedEdit->revid == $revid
-                       && $this->mPreparedEdit->format == $serialization_format
+                       && $this->mPreparedEdit->format == $serialFormat
                        // XXX: also check $user here?
                ) {
                        // Already prepared
                        return $this->mPreparedEdit;
                }
 
+               // The edit may have already been prepared via api.php?action=stashedit
+               $cachedEdit = $useCache
+                       ? ApiStashEdit::checkCache( $this->getTitle(), $content, $user )
+                       : false;
+
                $popts = ParserOptions::newFromUserAndLang( $user, $wgContLang );
-               wfRunHooks( 'ArticlePrepareTextForEdit', array( $this, $popts ) );
+               Hooks::run( 'ArticlePrepareTextForEdit', array( $this, $popts ) );
 
                $edit = (object)array();
+               if ( $cachedEdit ) {
+                       $edit->timestamp = $cachedEdit->timestamp;
+               } else {
+                       $edit->timestamp = wfTimestampNow();
+               }
+               // @note: $cachedEdit is not used if the rev ID was referenced in the text
                $edit->revid = $revid;
-               $edit->timestamp = wfTimestampNow();
 
-               $edit->pstContent = $content ? $content->preSaveTransform( $this->mTitle, $user, $popts ) : null;
+               if ( $cachedEdit ) {
+                       $edit->pstContent = $cachedEdit->pstContent;
+               } else {
+                       $edit->pstContent = $content
+                               ? $content->preSaveTransform( $this->mTitle, $user, $popts )
+                               : null;
+               }
 
-               $edit->format = $serialization_format;
+               $edit->format = $serialFormat;
                $edit->popts = $this->makeParserOptions( 'canonical' );
-               $edit->output = $edit->pstContent
-                       ? $edit->pstContent->getParserOutput( $this->mTitle, $revid, $edit->popts )
-                       : null;
+               if ( $cachedEdit ) {
+                       $edit->output = $cachedEdit->output;
+               } else {
+                       $edit->output = $edit->pstContent
+                               ? $edit->pstContent->getParserOutput( $this->mTitle, $revid, $edit->popts )
+                               : null;
+               }
 
                $edit->newContent = $content;
                $edit->oldContent = $this->getContent( Revision::RAW );
@@ -2125,7 +2140,7 @@ class WikiPage implements Page, IDBAccessObject {
                // NOTE: B/C for hooks! don't use these fields!
                $edit->newText = $edit->newContent ? ContentHandler::getContentText( $edit->newContent ) : '';
                $edit->oldText = $edit->oldContent ? ContentHandler::getContentText( $edit->oldContent ) : '';
-               $edit->pst = $edit->pstContent ? $edit->pstContent->serialize( $serialization_format ) : '';
+               $edit->pst = $edit->pstContent ? $edit->pstContent->serialize( $serialFormat ) : '';
 
                $this->mPreparedEdit = $edit;
                return $edit;
@@ -2142,6 +2157,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param array $options Array of options, following indexes are used:
         * - changed: boolean, whether the revision changed the content (default true)
         * - created: boolean, whether the revision created the page (default false)
+        * - moved: boolean, whether the page was moved (default false)
         * - oldcountable: boolean or null (default null):
         *   - boolean: whether the page was counted as an article before that
         *     revision, only used in changed is true and created is false
@@ -2152,7 +2168,12 @@ class WikiPage implements Page, IDBAccessObject {
 
                wfProfileIn( __METHOD__ );
 
-               $options += array( 'changed' => true, 'created' => false, 'oldcountable' => null );
+               $options += array(
+                       'changed' => true,
+                       'created' => false,
+                       'moved' => false,
+                       'oldcountable' => null
+               );
                $content = $revision->getContent();
 
                // Parse the text
@@ -2182,9 +2203,9 @@ class WikiPage implements Page, IDBAccessObject {
                        DataUpdate::runUpdates( $updates );
                }
 
-               wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) );
+               Hooks::run( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) );
 
-               if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
+               if ( Hooks::run( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
                        if ( 0 == mt_rand( 0, 99 ) ) {
                                // Flush old entries from the `recentchanges` table; we do this on
                                // random requests so as to avoid an increase in writes for no good reason
@@ -2201,7 +2222,7 @@ class WikiPage implements Page, IDBAccessObject {
                $title = $this->mTitle->getPrefixedDBkey();
                $shortTitle = $this->mTitle->getDBkey();
 
-               if ( !$options['changed'] ) {
+               if ( !$options['changed'] && !$options['moved'] ) {
                        $good = 0;
                } elseif ( $options['created'] ) {
                        $good = (int)$this->isCountable( $editInfo );
@@ -2229,7 +2250,7 @@ class WikiPage implements Page, IDBAccessObject {
                                wfDebug( __METHOD__ . ": invalid username\n" );
                        } else {
                                // Allow extensions to prevent user notification when a new message is added to their talk page
-                               if ( wfRunHooks( 'ArticleEditUpdateNewTalk', array( &$this, $recipient ) ) ) {
+                               if ( Hooks::run( 'ArticleEditUpdateNewTalk', array( &$this, $recipient ) ) ) {
                                        if ( User::isIP( $shortTitle ) ) {
                                                // An anonymous user
                                                $recipient->setNewtalk( true, $revision );
@@ -2289,14 +2310,14 @@ class WikiPage implements Page, IDBAccessObject {
         * @param User $user The relevant user
         * @param string $comment Comment submitted
         * @param bool $minor Whereas it's a minor modification
-        * @param string $serialisation_format Format for storing the content in the database
+        * @param string $serialFormat Format for storing the content in the database
         */
        public function doQuickEditContent( Content $content, User $user, $comment = '', $minor = false,
-               $serialisation_format = null
+               $serialFormat = null
        ) {
                wfProfileIn( __METHOD__ );
 
-               $serialized = $content->serialize( $serialisation_format );
+               $serialized = $content->serialize( $serialFormat );
 
                $dbw = wfGetDB( DB_MASTER );
                $revision = new Revision( array(
@@ -2312,7 +2333,7 @@ class WikiPage implements Page, IDBAccessObject {
                $revision->insertOn( $dbw );
                $this->updateRevisionOn( $dbw, $revision );
 
-               wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
+               Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
 
                wfProfileOut( __METHOD__ );
        }
@@ -2411,7 +2432,7 @@ class WikiPage implements Page, IDBAccessObject {
                $logRelationsField = null;
 
                if ( $id ) { // Protection of existing page
-                       if ( !wfRunHooks( 'ArticleProtect', array( &$this, &$user, $limit, $reason ) ) ) {
+                       if ( !Hooks::run( 'ArticleProtect', array( &$this, &$user, $limit, $reason ) ) ) {
                                return Status::newGood();
                        }
 
@@ -2491,8 +2512,8 @@ class WikiPage implements Page, IDBAccessObject {
                                __METHOD__
                        );
 
-                       wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $nullRevision, $latest, $user ) );
-                       wfRunHooks( 'ArticleProtectComplete', array( &$this, &$user, $limit, $reason ) );
+                       Hooks::run( 'NewRevisionFromEditComplete', array( $this, $nullRevision, $latest, $user ) );
+                       Hooks::run( 'ArticleProtectComplete', array( &$this, &$user, $limit, $reason ) );
                } else { // Protection of non-existing page (also known as "title protection")
                        // Cascade protection is meaningless in this case
                        $cascade = false;
@@ -2754,7 +2775,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                $user = is_null( $user ) ? $wgUser : $user;
-               if ( !wfRunHooks( 'ArticleDelete', array( &$this, &$user, &$reason, &$error, &$status ) ) ) {
+               if ( !Hooks::run( 'ArticleDelete', array( &$this, &$user, &$reason, &$error, &$status ) ) ) {
                        if ( $status->isOK() ) {
                                // Hook aborted but didn't set a fatal status
                                $status->fatal( 'delete-hook-aborted' );
@@ -2870,7 +2891,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                $this->doDeleteUpdates( $id, $content );
 
-               wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$user, $reason, $id, $content, $logEntry ) );
+               Hooks::run( 'ArticleDeleteComplete', array( &$this, &$user, $reason, $id, $content, $logEntry ) );
                $status->value = $logid;
                return $status;
        }
@@ -3112,7 +3133,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                $revId = $status->value['revision']->getId();
 
-               wfRunHooks( 'ArticleRollbackComplete', array( $this, $guser, $target, $current ) );
+               Hooks::run( 'ArticleRollbackComplete', array( $this, $guser, $target, $current ) );
 
                $resultDetails = array(
                        'summary' => $summary,
@@ -3137,11 +3158,7 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public static function onArticleCreate( $title ) {
                // Update existence markers on article/talk tabs...
-               if ( $title->isTalkPage() ) {
-                       $other = $title->getSubjectPage();
-               } else {
-                       $other = $title->getTalkPage();
-               }
+               $other = $title->getOtherPage();
 
                $other->invalidateCache();
                $other->purgeSquid();
@@ -3158,11 +3175,7 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public static function onArticleDelete( $title ) {
                // Update existence markers on article/talk tabs...
-               if ( $title->isTalkPage() ) {
-                       $other = $title->getSubjectPage();
-               } else {
-                       $other = $title->getTalkPage();
-               }
+               $other = $title->getOtherPage();
 
                $other->invalidateCache();
                $other->purgeSquid();
@@ -3364,12 +3377,12 @@ class WikiPage implements Page, IDBAccessObject {
 
                                foreach ( $added as $catName ) {
                                        $cat = Category::newFromName( $catName );
-                                       wfRunHooks( 'CategoryAfterPageAdded', array( $cat, $that ) );
+                                       Hooks::run( 'CategoryAfterPageAdded', array( $cat, $that ) );
                                }
 
                                foreach ( $deleted as $catName ) {
                                        $cat = Category::newFromName( $catName );
-                                       wfRunHooks( 'CategoryAfterPageRemoved', array( $cat, $that ) );
+                                       Hooks::run( 'CategoryAfterPageRemoved', array( $cat, $that ) );
                                }
                        }
                );
@@ -3522,7 +3535,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $updates = $content->getDeletionUpdates( $this );
                }
 
-               wfRunHooks( 'WikiPageDeletionUpdates', array( $this, $content, &$updates ) );
+               Hooks::run( 'WikiPageDeletionUpdates', array( $this, $content, &$updates ) );
                return $updates;
        }
 }