Minor bugfix to IP::prettifyIP.
[lhc/web/wiklou.git] / includes / WikiPage.php
index 910aeae..bfa9956 100644 (file)
@@ -2071,7 +2071,9 @@ class WikiPage implements Page, IDBAccessObject {
 
                // Update the links tables and other secondary data
                if ( $content ) {
-                       $updates = $content->getSecondaryDataUpdates( $this->getTitle(), null, true, $editInfo->output );
+                       $recursive = $options['changed']; // bug 50785
+                       $updates = $content->getSecondaryDataUpdates(
+                               $this->getTitle(), null, $recursive, $editInfo->output );
                        DataUpdate::runUpdates( $updates );
                }
 
@@ -2213,14 +2215,14 @@ class WikiPage implements Page, IDBAccessObject {
         * This works for protection both existing and non-existing pages.
         *
         * @param array $limit set of restriction keys
-        * @param $reason String
-        * @param &$cascade Integer. Set to false if cascading protection isn't allowed.
         * @param array $expiry per restriction type expiration
-        * @param $user User The user updating the restrictions
+        * @param int &$cascade Set to false if cascading protection isn't allowed.
+        * @param string $reason
+        * @param User $user The user updating the restrictions
         * @return Status
         */
        public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) {
-               global $wgContLang, $wgCascadingRestrictionLevels;
+               global $wgCascadingRestrictionLevels;
 
                if ( wfReadOnly() ) {
                        return Status::newFatal( 'readonlytext', wfReadOnlyReason() );
@@ -2293,58 +2295,12 @@ class WikiPage implements Page, IDBAccessObject {
                        $logAction = 'protect';
                }
 
-               $encodedExpiry = array();
-               $protectDescription = '';
-               # Some bots may parse IRC lines, which are generated from log entries which contain plain
-               # protect description text. Keep them in old format to avoid breaking compatibility.
-               # TODO: Fix protection log to store structured description and format it on-the-fly.
-               $protectDescriptionLog = '';
-               foreach ( $limit as $action => $restrictions ) {
-                       $encodedExpiry[$action] = $dbw->encodeExpiry( $expiry[$action] );
-                       if ( $restrictions != '' ) {
-                               $protectDescriptionLog .= $wgContLang->getDirMark() . "[$action=$restrictions] (";
-                               # $action is one of $wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' ).
-                               # All possible message keys are listed here for easier grepping:
-                               # * restriction-create
-                               # * restriction-edit
-                               # * restriction-move
-                               # * restriction-upload
-                               $actionText = wfMessage( 'restriction-' . $action )->inContentLanguage()->text();
-                               # $restrictions is one of $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ),
-                               # with '' filtered out. All possible message keys are listed below:
-                               # * protect-level-autoconfirmed
-                               # * protect-level-sysop
-                               $restrictionsText = wfMessage( 'protect-level-' . $restrictions )->inContentLanguage()->text();
-                               if ( $encodedExpiry[$action] != 'infinity' ) {
-                                       $expiryText = wfMessage(
-                                               'protect-expiring',
-                                               $wgContLang->timeanddate( $expiry[$action], false, false ),
-                                               $wgContLang->date( $expiry[$action], false, false ),
-                                               $wgContLang->time( $expiry[$action], false, false )
-                                       )->inContentLanguage()->text();
-                               } else {
-                                       $expiryText = wfMessage( 'protect-expiry-indefinite' )
-                                               ->inContentLanguage()->text();
-                               }
-
-                               if ( $protectDescription !== '' ) {
-                                       $protectDescription .= wfMessage( 'word-separator' )->inContentLanguage()->text();
-                               }
-                               $protectDescription .= wfMessage( 'protect-summary-desc' )
-                                       ->params( $actionText, $restrictionsText, $expiryText )
-                                       ->inContentLanguage()->text();
-                               $protectDescriptionLog .= $expiryText . ') ';
-                       }
-               }
-               $protectDescriptionLog = trim( $protectDescriptionLog );
-
                if ( $id ) { // Protection of existing page
                        if ( !wfRunHooks( 'ArticleProtect', array( &$this, &$user, $limit, $reason ) ) ) {
                                return Status::newGood();
                        }
 
-                       // Only certain restrictions can cascade... Otherwise, users who cannot normally protect pages
-                       // could "protect" them by transcluding them on protected pages they are allowed to edit.
+                       // Only certain restrictions can cascade...
                        $editrestriction = isset( $limit['edit'] ) ? array( $limit['edit'] ) : $this->mTitle->getRestrictions( 'edit' );
                        foreach ( array_keys( $editrestriction, 'sysop' ) as $key ) {
                                $editrestriction[$key] = 'editprotected'; // backwards compatibility
@@ -2366,6 +2322,13 @@ class WikiPage implements Page, IDBAccessObject {
                                $cascade = false;
                        }
 
+                       // insert null revision to identify the page protection change as edit summary
+                       $latest = $this->getLatest();
+                       $nullRevision = $this->insertProtectNullRevision( $revCommentMsg, $limit, $expiry, $cascade, $reason );
+                       if ( $nullRevision === null ) {
+                               return Status::newFatal( 'no-null-revision', $this->mTitle->getPrefixedText() );
+                       }
+
                        // Update restrictions table
                        foreach ( $limit as $action => $restrictions ) {
                                if ( $restrictions != '' ) {
@@ -2374,7 +2337,7 @@ class WikiPage implements Page, IDBAccessObject {
                                                        'pr_type' => $action,
                                                        'pr_level' => $restrictions,
                                                        'pr_cascade' => ( $cascade && $action == 'edit' ) ? 1 : 0,
-                                                       'pr_expiry' => $encodedExpiry[$action]
+                                                       'pr_expiry' => $dbw->encodeExpiry( $expiry[$action] )
                                                ),
                                                __METHOD__
                                        );
@@ -2384,43 +2347,6 @@ class WikiPage implements Page, IDBAccessObject {
                                }
                        }
 
-                       // Prepare a null revision to be added to the history
-                       $editComment = $wgContLang->ucfirst(
-                               wfMessage(
-                                       $revCommentMsg,
-                                       $this->mTitle->getPrefixedText()
-                               )->inContentLanguage()->text()
-                       );
-                       if ( $reason ) {
-                               $editComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
-                       }
-                       if ( $protectDescription ) {
-                               $editComment .= wfMessage( 'word-separator' )->inContentLanguage()->text();
-                               $editComment .= wfMessage( 'parentheses' )->params( $protectDescription )->inContentLanguage()->text();
-                       }
-                       if ( $cascade ) {
-                               $editComment .= wfMessage( 'word-separator' )->inContentLanguage()->text();
-                               $editComment .= wfMessage( 'brackets' )->params(
-                                       wfMessage( 'protect-summary-cascade' )->inContentLanguage()->text()
-                               )->inContentLanguage()->text();
-                       }
-
-                       // Insert a null revision
-                       $nullRevision = Revision::newNullRevision( $dbw, $id, $editComment, true );
-                       $nullRevId = $nullRevision->insertOn( $dbw );
-
-                       $latest = $this->getLatest();
-                       // Update page record
-                       $dbw->update( 'page',
-                               array( /* SET */
-                                       'page_touched' => $dbw->timestamp(),
-                                       'page_restrictions' => '',
-                                       'page_latest' => $nullRevId
-                               ), array( /* WHERE */
-                                       'page_id' => $id
-                               ), __METHOD__
-                       );
-
                        wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $nullRevision, $latest, $user ) );
                        wfRunHooks( 'ArticleProtectComplete', array( &$this, &$user, $limit, $reason ) );
                } else { // Protection of non-existing page (also known as "title protection")
@@ -2435,7 +2361,7 @@ class WikiPage implements Page, IDBAccessObject {
                                                'pt_title' => $this->mTitle->getDBkey(),
                                                'pt_create_perm' => $limit['create'],
                                                'pt_timestamp' => $dbw->encodeExpiry( wfTimestampNow() ),
-                                               'pt_expiry' => $encodedExpiry['create'],
+                                               'pt_expiry' => $dbw->encodeExpiry( $expiry['create'] ),
                                                'pt_user' => $user->getId(),
                                                'pt_reason' => $reason,
                                        ), __METHOD__
@@ -2454,18 +2380,150 @@ class WikiPage implements Page, IDBAccessObject {
                InfoAction::invalidateCache( $this->mTitle );
 
                if ( $logAction == 'unprotect' ) {
-                       $logParams = array();
+                       $params = array();
                } else {
-                       $logParams = array( $protectDescriptionLog, $cascade ? 'cascade' : '' );
+                       $protectDescriptionLog = $this->protectDescriptionLog( $limit, $expiry );
+                       $params = array( $protectDescriptionLog, $cascade ? 'cascade' : '' );
                }
 
                // Update the protection log
                $log = new LogPage( 'protect' );
-               $log->addEntry( $logAction, $this->mTitle, trim( $reason ), $logParams, $user );
+               $log->addEntry( $logAction, $this->mTitle, trim( $reason ), $params, $user );
 
                return Status::newGood();
        }
 
+       /**
+        * Insert a new null revision for this page.
+        *
+        * @param string $revCommentMsg comment message key for the revision
+        * @param array $limit set of restriction keys
+        * @param array $expiry per restriction type expiration
+        * @param int $cascade Set to false if cascading protection isn't allowed.
+        * @param string $reason
+        * @return Revision|null on error
+        */
+       public function insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason ) {
+               global $wgContLang;
+               $dbw = wfGetDB( DB_MASTER );
+
+               // Prepare a null revision to be added to the history
+               $editComment = $wgContLang->ucfirst(
+                       wfMessage(
+                               $revCommentMsg,
+                               $this->mTitle->getPrefixedText()
+                       )->inContentLanguage()->text()
+               );
+               if ( $reason ) {
+                       $editComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
+               }
+               $protectDescription = $this->protectDescription( $limit, $expiry );
+               if ( $protectDescription ) {
+                       $editComment .= wfMessage( 'word-separator' )->inContentLanguage()->text();
+                       $editComment .= wfMessage( 'parentheses' )->params( $protectDescription )->inContentLanguage()->text();
+               }
+               if ( $cascade ) {
+                       $editComment .= wfMessage( 'word-separator' )->inContentLanguage()->text();
+                       $editComment .= wfMessage( 'brackets' )->params(
+                               wfMessage( 'protect-summary-cascade' )->inContentLanguage()->text()
+                       )->inContentLanguage()->text();
+               }
+
+               $nullRev = Revision::newNullRevision( $dbw, $this->getId(), $editComment, true );
+               if ( $nullRev ) {
+                       $nullRev->insertOn( $dbw );
+
+                       // Update page record and touch page
+                       $oldLatest = $nullRev->getParentId();
+                       $this->updateRevisionOn( $dbw, $nullRev, $oldLatest );
+               }
+
+               return $nullRev;
+       }
+
+       /**
+        * @param string $expiry 14-char timestamp or "infinity", or false if the input was invalid
+        * @return string
+        */
+       protected function formatExpiry( $expiry ) {
+               global $wgContLang;
+               $dbr = wfGetDB( DB_SLAVE );
+
+               $encodedExpiry = $dbr->encodeExpiry( $expiry );
+               if ( $encodedExpiry != 'infinity' ) {
+                       return wfMessage(
+                               'protect-expiring',
+                               $wgContLang->timeanddate( $expiry, false, false ),
+                               $wgContLang->date( $expiry, false, false ),
+                               $wgContLang->time( $expiry, false, false )
+                       )->inContentLanguage()->text();
+               } else {
+                       return wfMessage( 'protect-expiry-indefinite' )
+                               ->inContentLanguage()->text();
+               }
+       }
+
+       /**
+        * Builds the description to serve as comment for the edit.
+        *
+        * @param array $limit set of restriction keys
+        * @param array $expiry per restriction type expiration
+        * @return string
+        */
+       public function protectDescription( array $limit, array $expiry ) {
+               $protectDescription = '';
+
+               foreach ( array_filter( $limit ) as $action => $restrictions ) {
+                       # $action is one of $wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' ).
+                       # All possible message keys are listed here for easier grepping:
+                       # * restriction-create
+                       # * restriction-edit
+                       # * restriction-move
+                       # * restriction-upload
+                       $actionText = wfMessage( 'restriction-' . $action )->inContentLanguage()->text();
+                       # $restrictions is one of $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ),
+                       # with '' filtered out. All possible message keys are listed below:
+                       # * protect-level-autoconfirmed
+                       # * protect-level-sysop
+                       $restrictionsText = wfMessage( 'protect-level-' . $restrictions )->inContentLanguage()->text();
+
+                       $expiryText = $this->formatExpiry( $expiry[$action] );
+
+                       if ( $protectDescription !== '' ) {
+                               $protectDescription .= wfMessage( 'word-separator' )->inContentLanguage()->text();
+                       }
+                       $protectDescription .= wfMessage( 'protect-summary-desc' )
+                               ->params( $actionText, $restrictionsText, $expiryText )
+                               ->inContentLanguage()->text();
+               }
+
+               return $protectDescription;
+       }
+
+       /**
+        * Builds the description to serve as comment for the log entry.
+        *
+        * Some bots may parse IRC lines, which are generated from log entries which contain plain
+        * protect description text. Keep them in old format to avoid breaking compatibility.
+        * TODO: Fix protection log to store structured description and format it on-the-fly.
+        *
+        * @param array $limit set of restriction keys
+        * @param array $expiry per restriction type expiration
+        * @return string
+        */
+       public function protectDescriptionLog( array $limit, array $expiry ) {
+               global $wgContLang;
+
+               $protectDescriptionLog = '';
+
+               foreach ( array_filter( $limit ) as $action => $restrictions ) {
+                       $expiryText = $this->formatExpiry( $expiry[$action] );
+                       $protectDescriptionLog .= $wgContLang->getDirMark() . "[$action=$restrictions] ($expiryText)";
+               }
+
+               return trim( $protectDescriptionLog );
+       }
+
        /**
         * Take an array of page restrictions and flatten it to a string
         * suitable for insertion into the page_restrictions field.
@@ -2481,10 +2539,8 @@ class WikiPage implements Page, IDBAccessObject {
                $bits = array();
                ksort( $limit );
 
-               foreach ( $limit as $action => $restrictions ) {
-                       if ( $restrictions != '' ) {
-                               $bits[] = "$action=$restrictions";
-                       }
+               foreach ( array_filter( $limit ) as $action => $restrictions ) {
+                       $bits[] = "$action=$restrictions";
                }
 
                return implode( ':', $bits );
@@ -2973,6 +3029,29 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**#@-*/
 
+       /**
+        * Returns a list of categories this page is a member of.
+        * Results will include hidden categories
+        *
+        * @return TitleArray
+        */
+       public function getCategories() {
+               $id = $this->getId();
+               if ( $id == 0 ) {
+                       return TitleArray::newFromResult( new FakeResultWrapper( array() ) );
+               }
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'categorylinks',
+                       array( 'cl_to AS page_title, ' . NS_CATEGORY . ' AS page_namespace' ),
+                       // Have to do that since DatabaseBase::fieldNamesWithAlias treats numeric indexes
+                       // as not being aliases, and NS_CATEGORY is numeric
+                       array( 'cl_from' => $id ),
+                       __METHOD__ );
+
+               return TitleArray::newFromResult( $res );
+       }
+
        /**
         * Returns a list of hidden categories this page is a member of.
         * Uses the page_props and categorylinks tables.