Added new success message when CLI Installer completes its work succesfuly.
[lhc/web/wiklou.git] / includes / page / WikiPage.php
index 21c298e..8b34928 100644 (file)
@@ -88,12 +88,6 @@ class WikiPage implements Page, IDBAccessObject {
         */
        protected $mLinksUpdated = '19700101000000';
 
-       /** @deprecated since 1.29. Added in 1.28 for partial purging, no longer used. */
-       const PURGE_CDN_CACHE = 1;
-       const PURGE_CLUSTER_PCACHE = 2;
-       const PURGE_GLOBAL_PCACHE = 4;
-       const PURGE_ALL = 7;
-
        /**
         * Constructor and clear the article
         * @param Title $title Reference to a Title object.
@@ -164,8 +158,11 @@ class WikiPage implements Page, IDBAccessObject {
 
                $from = self::convertSelectType( $from );
                $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_REPLICA );
+               $pageQuery = self::getQueryInfo();
                $row = $db->selectRow(
-                       'page', self::selectFields(), [ 'page_id' => $id ], __METHOD__ );
+                       $pageQuery['tables'], $pageQuery['fields'], [ 'page_id' => $id ], __METHOD__,
+                       [], $pageQuery['joins']
+               );
                if ( !$row ) {
                        return null;
                }
@@ -213,6 +210,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @todo Move this UI stuff somewhere else
         *
         * @see ContentHandler::getActionOverrides
+        * @return array
         */
        public function getActionOverrides() {
                return $this->getContentHandler()->getActionOverrides();
@@ -282,11 +280,14 @@ class WikiPage implements Page, IDBAccessObject {
         * Return the list of revision fields that should be selected to create
         * a new page.
         *
+        * @deprecated since 1.31, use self::getQueryInfo() instead.
         * @return array
         */
        public static function selectFields() {
                global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
 
+               wfDeprecated( __METHOD__, '1.31' );
+
                $fields = [
                        'page_id',
                        'page_namespace',
@@ -312,6 +313,47 @@ class WikiPage implements Page, IDBAccessObject {
                return $fields;
        }
 
+       /**
+        * Return the tables, fields, and join conditions to be selected to create
+        * a new page object.
+        * @since 1.31
+        * @return array With three keys:
+        *   - tables: (string[]) to include in the `$table` to `IDatabase->select()`
+        *   - fields: (string[]) to include in the `$vars` to `IDatabase->select()`
+        *   - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
+        */
+       public static function getQueryInfo() {
+               global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
+
+               $ret = [
+                       'tables' => [ 'page' ],
+                       'fields' => [
+                               'page_id',
+                               'page_namespace',
+                               'page_title',
+                               'page_restrictions',
+                               'page_is_redirect',
+                               'page_is_new',
+                               'page_random',
+                               'page_touched',
+                               'page_links_updated',
+                               'page_latest',
+                               'page_len',
+                       ],
+                       'joins' => [],
+               ];
+
+               if ( $wgContentHandlerUseDB ) {
+                       $ret['fields'][] = 'page_content_model';
+               }
+
+               if ( $wgPageLanguageUseDB ) {
+                       $ret['fields'][] = 'page_lang';
+               }
+
+               return $ret;
+       }
+
        /**
         * Fetch a page record with the given conditions
         * @param IDatabase $dbr
@@ -320,14 +362,23 @@ class WikiPage implements Page, IDBAccessObject {
         * @return object|bool Database result resource, or false on failure
         */
        protected function pageData( $dbr, $conditions, $options = [] ) {
-               $fields = self::selectFields();
+               $pageQuery = self::getQueryInfo();
 
                // Avoid PHP 7.1 warning of passing $this by reference
                $wikiPage = $this;
 
-               Hooks::run( 'ArticlePageDataBefore', [ &$wikiPage, &$fields ] );
+               Hooks::run( 'ArticlePageDataBefore', [
+                       &$wikiPage, &$pageQuery['fields'], &$pageQuery['tables'], &$pageQuery['joins']
+               ] );
 
-               $row = $dbr->selectRow( 'page', $fields, $conditions, __METHOD__, $options );
+               $row = $dbr->selectRow(
+                       $pageQuery['tables'],
+                       $pageQuery['fields'],
+                       $conditions,
+                       __METHOD__,
+                       $options,
+                       $pageQuery['joins']
+               );
 
                Hooks::run( 'ArticlePageDataAfter', [ &$wikiPage, &$row ] );
 
@@ -511,7 +562,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $cache = ObjectCache::getMainWANInstance();
 
                        return $cache->getWithSetCallback(
-                               $cache->makeKey( 'page', 'content-model', $this->getLatest() ),
+                               $cache->makeKey( 'page-content-model', $this->getLatest() ),
                                $cache::TTL_MONTH,
                                function () {
                                        $rev = $this->getRevision();
@@ -982,18 +1033,12 @@ class WikiPage implements Page, IDBAccessObject {
 
                $dbr = wfGetDB( DB_REPLICA );
 
-               if ( $dbr->implicitGroupby() ) {
-                       $realNameField = 'user_real_name';
-               } else {
-                       $realNameField = 'MIN(user_real_name) AS user_real_name';
-               }
-
                $tables = [ 'revision', 'user' ];
 
                $fields = [
                        'user_id' => 'rev_user',
                        'user_name' => 'rev_user_text',
-                       $realNameField,
+                       'user_real_name' => 'MIN(user_real_name)',
                        'timestamp' => 'MAX(rev_timestamp)',
                ];
 
@@ -1070,7 +1115,8 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                if ( $useParserCache ) {
-                       $parserOutput = ParserCache::singleton()->get( $this, $parserOptions );
+                       $parserOutput = MediaWikiServices::getInstance()->getParserCache()
+                               ->get( $this, $parserOptions );
                        if ( $parserOutput !== false ) {
                                return $parserOutput;
                        }
@@ -1138,18 +1184,6 @@ class WikiPage implements Page, IDBAccessObject {
                return true;
        }
 
-       /**
-        * Get the last time a user explicitly purged the page via action=purge
-        *
-        * @return string|bool TS_MW timestamp or false
-        * @since 1.28
-        * @deprecated since 1.29. It will always return false.
-        */
-       public function getLastPurgeTimestamp() {
-               wfDeprecated( __METHOD__, '1.29' );
-               return false;
-       }
-
        /**
         * Insert a new empty page record for this article.
         * This *must* be followed up by creating a revision
@@ -1671,27 +1705,27 @@ class WikiPage implements Page, IDBAccessObject {
                        throw new MWException( "Could not find text for current revision {$oldid}." );
                }
 
-               // @TODO: pass content object?!
-               $revision = new Revision( [
-                       'page'       => $this->getId(),
-                       'title'      => $this->mTitle, // for determining the default content model
-                       'comment'    => $summary,
-                       'minor_edit' => $meta['minor'],
-                       'text'       => $meta['serialized'],
-                       'len'        => $newsize,
-                       'parent_id'  => $oldid,
-                       'user'       => $user->getId(),
-                       'user_text'  => $user->getName(),
-                       'timestamp'  => $now,
-                       'content_model' => $content->getModel(),
-                       'content_format' => $meta['serialFormat'],
-               ] );
-
                $changed = !$content->equals( $oldContent );
 
                $dbw = wfGetDB( DB_MASTER );
 
                if ( $changed ) {
+                       // @TODO: pass content object?!
+                       $revision = new Revision( [
+                               'page'       => $this->getId(),
+                               'title'      => $this->mTitle, // for determining the default content model
+                               'comment'    => $summary,
+                               'minor_edit' => $meta['minor'],
+                               'text'       => $meta['serialized'],
+                               'len'        => $newsize,
+                               'parent_id'  => $oldid,
+                               'user'       => $user->getId(),
+                               'user_text'  => $user->getName(),
+                               'timestamp'  => $now,
+                               'content_model' => $content->getModel(),
+                               'content_format' => $meta['serialFormat'],
+                       ] );
+
                        $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
                        $status->merge( $prepStatus );
                        if ( !$status->isOK() ) {
@@ -1757,11 +1791,9 @@ class WikiPage implements Page, IDBAccessObject {
                } else {
                        // T34948: revision ID must be set to page {{REVISIONID}} and
                        // related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
-                       $revision->setId( $this->getLatest() );
-                       $revision->setUserIdAndName(
-                               $this->getUser( Revision::RAW ),
-                               $this->getUserText( Revision::RAW )
-                       );
+                       // Since we don't insert a new revision into the database, the least
+                       // error-prone way is to reuse given old revision.
+                       $revision = $meta['oldRevision'];
                }
 
                if ( $changed ) {
@@ -1919,10 +1951,10 @@ class WikiPage implements Page, IDBAccessObject {
                                        $wikiPage = $this;
                                        // Trigger post-create hook
                                        $params = [ &$wikiPage, &$user, $content, $summary,
-                                               $flags & EDIT_MINOR, null, null, &$flags, $revision ];
+                                                               $flags & EDIT_MINOR, null, null, &$flags, $revision ];
                                        Hooks::run( 'PageContentInsertComplete', $params );
                                        // Trigger post-save hook
-                                       $params = array_merge( $params, [ &$status, $meta['baseRevId'] ] );
+                                       $params = array_merge( $params, [ &$status, $meta['baseRevId'], 0 ] );
                                        Hooks::run( 'PageContentSaveComplete', $params );
                                }
                        ),
@@ -1988,6 +2020,7 @@ class WikiPage implements Page, IDBAccessObject {
                        // This code path is deprecated, and nothing is known to
                        // use it, so performance here shouldn't be a worry.
                        if ( $revid !== null ) {
+                               wfDeprecated( __METHOD__ . ' with $revision = revision ID', '1.25' );
                                $revision = Revision::newFromId( $revid, Revision::READ_LATEST );
                        } else {
                                $revision = null;
@@ -2162,7 +2195,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                // Save it to the parser cache.
                // Make sure the cache time matches page_touched to avoid double parsing.
-               ParserCache::singleton()->save(
+               MediaWikiServices::getInstance()->getParserCache()->save(
                        $editInfo->output, $this, $editInfo->popts,
                        $revision->getTimestamp(), $editInfo->revid
                );
@@ -2174,6 +2207,7 @@ class WikiPage implements Page, IDBAccessObject {
                                $this->getTitle(), null, $recursive, $editInfo->output
                        );
                        foreach ( $updates as $update ) {
+                               $update->setCause( 'edit-page', $user->getName() );
                                if ( $update instanceof LinksUpdate ) {
                                        $update->setRevision( $revision );
                                        $update->setTriggeringUser( $user );
@@ -2297,7 +2331,7 @@ class WikiPage implements Page, IDBAccessObject {
        public function doUpdateRestrictions( array $limit, array $expiry,
                &$cascade, $reason, User $user, $tags = null
        ) {
-               global $wgCascadingRestrictionLevels, $wgContLang;
+               global $wgCascadingRestrictionLevels;
 
                if ( wfReadOnly() ) {
                        return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
@@ -2370,9 +2404,6 @@ class WikiPage implements Page, IDBAccessObject {
                        $logAction = 'protect';
                }
 
-               // Truncate for whole multibyte characters
-               $reason = $wgContLang->truncate( $reason, 255 );
-
                $logRelationsValues = [];
                $logRelationsField = null;
                $logParamsDetails = [];
@@ -2735,6 +2766,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param array|string &$error Array of errors to append to
         * @param User $user The deleting user
         * @param array $tags Tags to apply to the deletion action
+        * @param string $logsubtype
         * @return Status Status object; if successful, $status->value is the log_id of the
         *   deletion log entry. If the page couldn't be deleted because it wasn't
         *   found, $status is a non-fatal 'cannotdelete' error
@@ -2810,13 +2842,13 @@ class WikiPage implements Page, IDBAccessObject {
                $revCommentStore = new CommentStore( 'rev_comment' );
                $arCommentStore = new CommentStore( 'ar_comment' );
 
-               $fields = Revision::selectFields();
+               $revQuery = Revision::getQueryInfo();
                $bitfield = false;
 
                // Bitfields to further suppress the content
                if ( $suppress ) {
                        $bitfield = Revision::SUPPRESSED_ALL;
-                       $fields = array_diff( $fields, [ 'rev_deleted' ] );
+                       $revQuery['fields'] = array_diff( $revQuery['fields'], [ 'rev_deleted' ] );
                }
 
                // For now, shunt the revision data into the archive table.
@@ -2827,18 +2859,22 @@ class WikiPage implements Page, IDBAccessObject {
                // the rev_deleted field, which is reserved for this purpose.
 
                // Get all of the page revisions
-               $commentQuery = $revCommentStore->getJoin();
                $res = $dbw->select(
-                       [ 'revision' ] + $commentQuery['tables'],
-                       $fields + $commentQuery['fields'],
+                       $revQuery['tables'],
+                       $revQuery['fields'],
                        [ 'rev_page' => $id ],
                        __METHOD__,
                        'FOR UPDATE',
-                       $commentQuery['joins']
+                       $revQuery['joins']
                );
+
                // Build their equivalent archive rows
                $rowsInsert = [];
                $revids = [];
+
+               /** @var int[] Revision IDs of edits that were made by IPs */
+               $ipRevIds = [];
+
                foreach ( $res as $row ) {
                        $comment = $revCommentStore->getComment( $row );
                        $rowInsert = [
@@ -2864,6 +2900,12 @@ class WikiPage implements Page, IDBAccessObject {
                        }
                        $rowsInsert[] = $rowInsert;
                        $revids[] = $row->rev_id;
+
+                       // Keep track of IP edits, so that the corresponding rows can
+                       // be deleted in the ip_changes table.
+                       if ( (int)$row->rev_user === 0 && IP::isValid( $row->rev_user_text ) ) {
+                               $ipRevIds[] = $row->rev_id;
+                       }
                }
                // Copy them into the archive table
                $dbw->insert( 'archive', $rowsInsert, __METHOD__ );
@@ -2882,6 +2924,11 @@ class WikiPage implements Page, IDBAccessObject {
                        $dbw->delete( 'revision_comment_temp', [ 'revcomment_rev' => $revids ], __METHOD__ );
                }
 
+               // Also delete records from ip_changes as applicable.
+               if ( count( $ipRevIds ) > 0 ) {
+                       $dbw->delete( 'ip_changes', [ 'ipc_rev_id' => $ipRevIds ], __METHOD__ );
+               }
+
                // Log the deletion, if the page was suppressed, put it in the suppression log instead
                $logtype = $suppress ? 'suppress' : 'delete';
 
@@ -2902,7 +2949,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                $dbw->endAtomic( __METHOD__ );
 
-               $this->doDeleteUpdates( $id, $content, $revision );
+               $this->doDeleteUpdates( $id, $content, $revision, $user );
 
                Hooks::run( 'ArticleDeleteComplete', [
                        &$wikiPageBeforeDelete,
@@ -2953,8 +3000,11 @@ class WikiPage implements Page, IDBAccessObject {
         *   the required updates. This may be needed because $this->getContent()
         *   may already return null when the page proper was deleted.
         * @param Revision|null $revision The latest page revision
+        * @param User|null $user The user that caused the deletion
         */
-       public function doDeleteUpdates( $id, Content $content = null, Revision $revision = null ) {
+       public function doDeleteUpdates(
+               $id, Content $content = null, Revision $revision = null, User $user = null
+       ) {
                try {
                        $countable = $this->isCountable();
                } catch ( Exception $ex ) {
@@ -2972,12 +3022,14 @@ class WikiPage implements Page, IDBAccessObject {
                        DeferredUpdates::addUpdate( $update );
                }
 
+               $causeAgent = $user ? $user->getName() : 'unknown';
                // Reparse any pages transcluding this page
-               LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
-
+               LinksUpdate::queueRecursiveJobsForTable(
+                       $this->mTitle, 'templatelinks', 'delete-page', $causeAgent );
                // Reparse any pages including this image
                if ( $this->mTitle->getNamespace() == NS_FILE ) {
-                       LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'imagelinks' );
+                       LinksUpdate::queueRecursiveJobsForTable(
+                               $this->mTitle, 'imagelinks', 'delete-page', $causeAgent );
                }
 
                // Clear caches
@@ -3145,9 +3197,6 @@ class WikiPage implements Page, IDBAccessObject {
                // Trim spaces on user supplied text
                $summary = trim( $summary );
 
-               // Truncate for whole multibyte characters.
-               $summary = $wgContLang->truncate( $summary, 255 );
-
                // Save
                $flags = EDIT_UPDATE | EDIT_INTERNAL;
 
@@ -3268,7 +3317,9 @@ class WikiPage implements Page, IDBAccessObject {
                MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );
 
                // Invalidate caches of articles which include this page
-               DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'templatelinks' ) );
+               DeferredUpdates::addUpdate(
+                       new HTMLCacheUpdate( $title, 'templatelinks', 'page-create' )
+               );
 
                if ( $title->getNamespace() == NS_CATEGORY ) {
                        // Load the Category object, which will schedule a job to create
@@ -3306,7 +3357,9 @@ class WikiPage implements Page, IDBAccessObject {
 
                // Images
                if ( $title->getNamespace() == NS_FILE ) {
-                       DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'imagelinks' ) );
+                       DeferredUpdates::addUpdate(
+                               new HTMLCacheUpdate( $title, 'imagelinks', 'page-delete' )
+                       );
                }
 
                // User talk pages
@@ -3329,10 +3382,14 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public static function onArticleEdit( Title $title, Revision $revision = null ) {
                // Invalidate caches of articles which include this page
-               DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'templatelinks' ) );
+               DeferredUpdates::addUpdate(
+                       new HTMLCacheUpdate( $title, 'templatelinks', 'page-edit' )
+               );
 
                // Invalidate the caches of all pages which redirect here
-               DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'redirect' ) );
+               DeferredUpdates::addUpdate(
+                       new HTMLCacheUpdate( $title, 'redirect', 'page-edit' )
+               );
 
                MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );