Introducing ContentHandler::canBeUsedOn()
[lhc/web/wiklou.git] / includes / WikiPage.php
index 91fd9e0..6c7b335 100644 (file)
@@ -92,6 +92,7 @@ class WikiPage extends Page implements IDBAccessObject {
         * Create a WikiPage object of the appropriate class for the given title.
         *
         * @param $title Title
+        * @throws MWException
         * @return WikiPage object of the appropriate type
         */
        public static function factory( Title $title ) {
@@ -468,7 +469,8 @@ class WikiPage extends Page implements IDBAccessObject {
                        if ( $rev !== null ) {
                                return $rev->getContentModel();
                        } else {
-                               wfWarn( "Page exists but has no revision!" );
+                               $title = $this->mTitle->getPrefixedDBkey();
+                               wfWarn( "Page $title exists but has no (visible) revisions!" );
                        }
                }
 
@@ -774,7 +776,7 @@ class WikiPage extends Page implements IDBAccessObject {
         * Determine whether a page would be suitable for being counted as an
         * article in the site_stats table based on the title & its content
         *
-        * @param $editInfo Object or false: object returned by prepareTextForEdit(),
+        * @param $editInfo Object|bool (false): object returned by prepareTextForEdit(),
         *        if false, the current database state will be used
         * @return Boolean
         */
@@ -1259,7 +1261,8 @@ class WikiPage extends Page implements IDBAccessObject {
                        $this->mLatest = $revision->getId();
                        $this->mIsRedirect = (bool)$rt;
                        # Update the LinkCache.
-                       LinkCache::singleton()->addGoodLinkObj( $this->getId(), $this->mTitle, $len, $this->mIsRedirect, $this->mLatest, $revision->getContentModel() );
+                       LinkCache::singleton()->addGoodLinkObj( $this->getId(), $this->mTitle, $len, $this->mIsRedirect,
+                                                                                                       $this->mLatest, $revision->getContentModel() );
                }
 
                wfProfileOut( __METHOD__ );
@@ -1410,7 +1413,8 @@ class WikiPage extends Page implements IDBAccessObject {
                        throw new MWException( "sections not supported for content model " . $this->getContentHandler()->getModelID() );
                }
 
-               $sectionContent = ContentHandler::makeContent( $text, $this->getTitle() ); # could even make section title, but that's not required.
+               # could even make section title, but that's not required.
+               $sectionContent = ContentHandler::makeContent( $text, $this->getTitle() );
 
                $newContent = $this->replaceSectionContent( $section, $sectionContent, $sectionTitle, $edittime );
 
@@ -1524,9 +1528,10 @@ class WikiPage extends Page implements IDBAccessObject {
         * edit-already-exists error will be returned. These two conditions are also possible with
         * auto-detection due to MediaWiki's performance-optimised locking strategy.
         *
-        * @param $baseRevId int the revision ID this edit was based off, if any
+        * @param bool|int $baseRevId int the revision ID this edit was based off, if any
         * @param $user User the user doing the edit
         *
+        * @throws MWException
         * @return Status object. Possible errors:
         *     edit-hook-aborted:       The ArticleSave hook aborted the edit but didn't set the fatal flag of $status
         *     edit-gone-missing:       In update mode, but the article didn't exist
@@ -1543,8 +1548,9 @@ class WikiPage extends Page implements IDBAccessObject {
         *  Compatibility note: this function previously returned a boolean value indicating success/failure
         *
         * @deprecated since 1.WD: use doEditContent() instead.
+        * @todo: use doEditContent() instead everywhere
         */
-       public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) { #@todo: use doEditContent() instead
+       public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
                wfDeprecated( __METHOD__, '1.WD' );
 
                $content = ContentHandler::makeContent( $text, $this->getTitle() );
@@ -1610,6 +1616,13 @@ class WikiPage extends Page implements IDBAccessObject {
 
                wfProfileIn( __METHOD__ );
 
+               if ( !$content->getContentHandler()->canBeUsedOn( $this->getTitle() ) ) {
+                       wfProfileOut( __METHOD__ );
+                       return Status::newFatal( 'content-not-allowed-here',
+                               ContentHandler::getLocalizedName( $content->getModel() ),
+                               $this->getTitle()->getPrefixedText() );
+               }
+
                $user = is_null( $user ) ? $wgUser : $user;
                $status = Status::newGood( array() );
 
@@ -1620,24 +1633,13 @@ class WikiPage extends Page implements IDBAccessObject {
 
                $flags = $this->checkFlags( $flags );
 
-               # call legacy hook
-               $hook_ok = wfRunHooks( 'ArticleContentSave', array( &$this, &$user, &$content, &$summary,
-                       $flags & EDIT_MINOR, null, null, &$flags, &$status ) );
+               # handle hook
+               $hook_args = array( &$this, &$user, &$content, &$summary,
+                                                       $flags & EDIT_MINOR, null, null, &$flags, &$status );
 
-               if ( $hook_ok && Hooks::isRegistered( 'ArticleSave' ) ) { # avoid serialization overhead if the hook isn't present
-                       $content_text = $content->serialize();
-                       $txt = $content_text; # clone
+               if ( !wfRunHooks( 'ArticleContentSave', $hook_args )
+                       || !ContentHandler::runLegacyHooks( 'ArticleSave', $hook_args ) ) {
 
-                       $hook_ok = wfRunHooks( 'ArticleSave', array( &$this, &$user, &$txt, &$summary,
-                               $flags & EDIT_MINOR, null, null, &$flags, &$status ) ); #TODO: survey extensions using this hook
-
-                       if ( $txt !== $content_text ) {
-                               # if the text changed, unserialize the new version to create an updated Content object.
-                               $content = $content->getContentHandler()->unserializeContent( $txt );
-                       }
-               }
-
-               if ( !$hook_ok ) {
                        wfDebug( __METHOD__ . ": ArticleSave or ArticleSaveContent hook aborted save!\n" );
 
                        if ( $status->isOK() ) {
@@ -1707,10 +1709,6 @@ class WikiPage extends Page implements IDBAccessObject {
                                'content_format' => $serialisation_format,
                        ) ); #XXX: pass content object?!
 
-                       # Bug 37225: use accessor to get the text as Revision may trim it.
-                       # After trimming, the text may be a duplicate of the current text.
-                       $content = $revision->getContent(); // sanity; EditPage should trim already
-
                        $changed = !$content->equals( $old_content );
 
                        if ( $changed ) {
@@ -1866,11 +1864,11 @@ class WikiPage extends Page implements IDBAccessObject {
                        # Update links, etc.
                        $this->doEditUpdates( $revision, $user, array( 'created' => true ) );
 
-                       wfRunHooks( 'ArticleInsertComplete', array( &$this, &$user, $serialized, $summary,
-                               $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+                       $hook_args = array( &$this, &$user, $content, $summary,
+                                                               $flags & EDIT_MINOR, null, null, &$flags, $revision );
 
-                       wfRunHooks( 'ArticleContentInsertComplete', array( &$this, &$user, $content, $summary,
-                               $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+                       ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $hook_args );
+                       wfRunHooks( 'ArticleContentInsertComplete', $hook_args );
                }
 
                # Do updates right now unless deferral was requested
@@ -1881,11 +1879,11 @@ class WikiPage extends Page implements IDBAccessObject {
                // Return the new revision (or null) to the caller
                $status->value['revision'] = $revision;
 
-               wfRunHooks( 'ArticleSaveComplete', array( &$this, &$user, $serialized, $summary,
-                       $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId ) );
+               $hook_args = array( &$this, &$user, $content, $summary,
+                                                       $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
 
-               wfRunHooks( 'ArticleContentSaveComplete', array( &$this, &$user, $content, $summary,
-                       $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId ) );
+               ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
+               wfRunHooks( 'ArticleContentSaveComplete', $hook_args );
 
                # Promote user to any groups they meet the criteria for
                $user->addAutopromoteOnceGroups( 'onEdit' );
@@ -2064,7 +2062,8 @@ class WikiPage extends Page implements IDBAccessObject {
                }
 
                DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, $good, $total ) );
-               DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content->getTextForSearchIndex() ) ); #TODO: let the search engine decide what to do with the content object
+               DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content->getTextForSearchIndex() ) );
+               #@TODO: let the search engine decide what to do with the content object
 
                # If this is another user's talk page, update newtalk.
                # Don't do this if $options['changed'] = false (null-edits) nor if
@@ -2090,7 +2089,8 @@ class WikiPage extends Page implements IDBAccessObject {
                }
 
                if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
-                       $msgtext = $content->getWikitextForTransclusion(); #XXX: could skip pseudo-messages like js/css here, based on content model.
+                       #XXX: could skip pseudo-messages like js/css here, based on content model.
+                       $msgtext = $content->getWikitextForTransclusion();
                        if ( $msgtext === false || $msgtext === null ) $msgtext = '';
 
                        MessageCache::singleton()->replace( $shortTitle, $msgtext );
@@ -2248,12 +2248,15 @@ class WikiPage extends Page implements IDBAccessObject {
                        if ( $restrictions != '' ) {
                                $protectDescription .= $wgContLang->getDirMark() . "[$action=$restrictions] (";
                                if ( $encodedExpiry[$action] != 'infinity' ) {
-                                       $protectDescription .= wfMsgForContent( 'protect-expiring',
+                                       $protectDescription .= wfMessage(
+                                               'protect-expiring',
                                                $wgContLang->timeanddate( $expiry[$action], false, false ) ,
                                                $wgContLang->date( $expiry[$action], false, false ) ,
-                                               $wgContLang->time( $expiry[$action], false, false ) );
+                                               $wgContLang->time( $expiry[$action], false, false )
+                                       )->inContentLanguage()->text();
                                } else {
-                                       $protectDescription .= wfMsgForContent( 'protect-expiry-indefinite' );
+                                       $protectDescription .= wfMessage( 'protect-expiry-indefinite' )
+                                               ->inContentLanguage()->text();
                                }
 
                                $protectDescription .= ') ';
@@ -2294,7 +2297,12 @@ class WikiPage extends Page implements IDBAccessObject {
                        }
 
                        # Prepare a null revision to be added to the history
-                       $editComment = $wgContLang->ucfirst( wfMsgForContent( $revCommentMsg, $this->mTitle->getPrefixedText() ) );
+                       $editComment = $wgContLang->ucfirst(
+                               wfMessage(
+                                       $revCommentMsg,
+                                       $this->mTitle->getPrefixedText()
+                               )->inContentLanguage()->text()
+                       );
                        if ( $reason ) {
                                $editComment .= ": $reason";
                        }
@@ -2302,7 +2310,9 @@ class WikiPage extends Page implements IDBAccessObject {
                                $editComment .= " ($protectDescription)";
                        }
                        if ( $cascade ) {
-                               $editComment .= ' [' . wfMsgForContent( 'protect-summary-cascade' ) . ']';
+                               // FIXME: Should use 'brackets' message.
+                               $editComment .= ' [' . wfMessage( 'protect-summary-cascade' )
+                                       ->inContentLanguage()->text() . ']';
                        }
 
                        # Insert a null revision
@@ -2369,6 +2379,7 @@ class WikiPage extends Page implements IDBAccessObject {
         * Take an array of page restrictions and flatten it to a string
         * suitable for insertion into the page_restrictions field.
         * @param $limit Array
+        * @throws MWException
         * @return String
         */
        protected static function flattenRestrictions( $limit ) {
@@ -2705,7 +2716,7 @@ class WikiPage extends Page implements IDBAccessObject {
                                array( /* WHERE */
                                        'rc_cur_id' => $current->getPage(),
                                        'rc_user_text' => $current->getUserText(),
-                                       "rc_timestamp > '{$s->rev_timestamp}'",
+                                       'rc_timestamp > ' . $dbw->addQuotes( $s->rev_timestamp ),
                                ), __METHOD__
                        );
                }
@@ -2714,9 +2725,9 @@ class WikiPage extends Page implements IDBAccessObject {
                $target = Revision::newFromId( $s->rev_id );
                if ( empty( $summary ) ) {
                        if ( $from == '' ) { // no public user name
-                               $summary = wfMsgForContent( 'revertpage-nouser' );
+                               $summary = wfMessage( 'revertpage-nouser' );
                        } else {
-                               $summary = wfMsgForContent( 'revertpage' );
+                               $summary = wfMessage( 'revertpage' );
                        }
                }
 
@@ -2726,7 +2737,14 @@ class WikiPage extends Page implements IDBAccessObject {
                        $wgContLang->timeanddate( wfTimestamp( TS_MW, $s->rev_timestamp ) ),
                        $current->getId(), $wgContLang->timeanddate( $current->getTimestamp() )
                );
-               $summary = wfMsgReplaceArgs( $summary, $args );
+               if( $summary instanceof Message ) {
+                       $summary = $summary->params( $args )->inContentLanguage()->text();
+               } else {
+                       $summary = wfMsgReplaceArgs( $summary, $args );
+               }
+
+               # Truncate for whole multibyte characters.
+               $summary = $wgContLang->truncate( $summary, 255 );
 
                # Save
                $flags = EDIT_UPDATE;
@@ -2741,6 +2759,11 @@ class WikiPage extends Page implements IDBAccessObject {
 
                # Actually store the edit
                $status = $this->doEditContent( $target->getContent(), $summary, $flags, $target->getId(), $guser );
+
+               if ( !$status->isOK() ) {
+                       return $status->getErrorsArray();
+               }
+
                if ( !empty( $status->value['revision'] ) ) {
                        $revId = $status->value['revision']->getId();
                } else {
@@ -3147,6 +3170,7 @@ class WikiPage extends Page implements IDBAccessObject {
 
        /**
         * @deprecated since 1.18
+        * @param $oldid int
         * @return bool
         */
        public function useParserCache( $oldid ) {
@@ -3172,7 +3196,7 @@ class WikiPage extends Page implements IDBAccessObject {
                if ( !$content ) {
                        $updates = array();
                } else {
-                       $updates = $content->getDeletionUpdates( $this->mTitle );
+                       $updates = $content->getDeletionUpdates( $this );
                }
 
                wfRunHooks( 'WikiPageDeletionUpdates', array( $this, $content, &$updates ) );
@@ -3281,20 +3305,24 @@ class PoolWorkArticleView extends PoolCounterWork {
        function doWork() {
                global $wgUseFileCache;
 
-               // @todo: several of the methods called on $this->page are not declared in Page, but present in WikiPage and delegated by Article.
+               // @todo: several of the methods called on $this->page are not declared in Page, but present
+               //        in WikiPage and delegated by Article.
 
                $isCurrent = $this->revid === $this->page->getLatest();
 
                if ( $this->content !== null ) {
                        $content = $this->content;
                } elseif ( $isCurrent ) {
-                       $content = $this->page->getContent( Revision::RAW ); #XXX: why use RAW audience here, and PUBLIC (default) below?
+                       #XXX: why use RAW audience here, and PUBLIC (default) below?
+                       $content = $this->page->getContent( Revision::RAW );
                } else {
                        $rev = Revision::newFromTitle( $this->page->getTitle(), $this->revid );
                        if ( $rev === null ) {
                                return false;
                        }
-                       $content = $rev->getContent(); #XXX: why use PUBLIC audience here (default), and RAW above?
+
+                       #XXX: why use PUBLIC audience here (default), and RAW above?
+                       $content = $rev->getContent();
                }
 
                $time = - microtime( true );