merged master
authordaniel <daniel.kinzler@wikimedia.de>
Tue, 18 Sep 2012 15:43:20 +0000 (17:43 +0200)
committerdaniel <daniel.kinzler@wikimedia.de>
Wed, 19 Sep 2012 10:34:10 +0000 (12:34 +0200)
Change-Id: I0ef7c7f33a5dc5855f38b20c03ddc5306f38ec66

21 files changed:
1  2 
docs/hooks.txt
includes/Article.php
includes/DefaultSettings.php
includes/EditPage.php
includes/GlobalFunctions.php
includes/ImagePage.php
includes/Revision.php
includes/Title.php
includes/WikiPage.php
includes/api/ApiParse.php
includes/diff/DifferenceEngine.php
includes/parser/Parser.php
includes/specials/SpecialUndelete.php
languages/Language.php
languages/messages/MessagesDe.php
languages/messages/MessagesEn.php
languages/messages/MessagesQqq.php
maintenance/Maintenance.php
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/RevisionStorageTest.php
thumb.php

diff --cc docs/hooks.txt
Simple merge
@@@ -1720,10 -1617,11 +1720,13 @@@ class Article extends Page 
         * @return ParserOutput or false if the given revsion ID is not found
         */
        public function getParserOutput( $oldid = null, User $user = null ) {
-               $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
-               $parserOptions = $this->mPage->makeParserOptions( $user );
 +              //XXX: bypasses mParserOptions and thus setParserOptions()
 +
+               if ( $user === null ) {
+                       $parserOptions = $this->getParserOptions();
+               } else {
+                       $parserOptions = $this->mPage->makeParserOptions( $user );
+               }
  
                return $this->mPage->getParserOutput( $parserOptions, $oldid );
        }
Simple merge
                        return $parsedNote;
                }
  
 -              if ( $this->mTriedSave && !$this->mTokenOk ) {
 -                      if ( $this->mTokenOkExceptSuffix ) {
 -                              $note = wfMessage( 'token_suffix_mismatch' )->plain();
 -                      } else {
 -                              $note = wfMessage( 'session_fail_preview' )->plain();
 -                      }
 -              } elseif ( $this->incompleteForm ) {
 -                      $note = wfMessage( 'edit_form_incomplete' )->plain();
 -              } else {
 -                      $note = wfMessage( 'previewnote' )->plain() .
 -                              ' [[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . wfMessage( 'continue-editing' )->text() . ']]';
 -              }
 +              $note = '';
  
 -              $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
 +              try {
 +                      $content = $this->toEditContent( $this->textbox1 );
  
 -              $parserOptions->setEditSection( false );
 -              $parserOptions->setIsPreview( true );
 -              $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
 +                      if ( $this->mTriedSave && !$this->mTokenOk ) {
 +                              if ( $this->mTokenOkExceptSuffix ) {
 +                                      $note = wfMessage( 'token_suffix_mismatch' )->plain() ;
 -              # don't parse non-wikitext pages, show message about preview
 -              if ( $this->mTitle->isCssJsSubpage() || !$this->mTitle->isWikitextPage() ) {
 -                      if ( $this->mTitle->isCssJsSubpage() ) {
 -                              $level = 'user';
 -                      } elseif ( $this->mTitle->isCssOrJsPage() ) {
 -                              $level = 'site';
 -                      } else {
 -                              $level = false;
 -                      }
 -
 -                      # Used messages to make sure grep find them:
 -                      # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
 -                      $class = 'mw-code';
 -                      if ( $level ) {
 -                              if ( preg_match( "/\\.css$/", $this->mTitle->getText() ) ) {
 -                                      $previewtext = "<div id='mw-{$level}csspreview'>\n" . wfMessage( "{$level}csspreview" )->text() . "\n</div>";
 -                                      $class .= " mw-css";
 -                              } elseif ( preg_match( "/\\.js$/", $this->mTitle->getText() ) ) {
 -                                      $previewtext = "<div id='mw-{$level}jspreview'>\n" . wfMessage( "{$level}jspreview" )->text() . "\n</div>";
 -                                      $class .= " mw-js";
                                } else {
 -                                      throw new MWException( 'A CSS/JS (sub)page but which is not css nor js!' );
 +                                      $note = wfMessage( 'session_fail_preview' )->plain() ;
                                }
 -                              $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions );
 -                              $previewHTML = $parserOutput->getText();
 +                      } elseif ( $this->incompleteForm ) {
 +                              $note = wfMessage( 'edit_form_incomplete' )->plain() ;
                        } else {
 -                              $previewHTML = '';
 -                      }
 +                              $note = wfMessage( 'previewnote' )->plain() .
 +                                      ' [[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . wfMessage( 'continue-editing' )->text() . ']]';
 +                      }
 +
-                       $parserOptions = ParserOptions::newFromUser( $wgUser );
++                      $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
 +                      $parserOptions->setEditSection( false );
 +                      $parserOptions->setTidy( true );
 +                      $parserOptions->setIsPreview( true );
 +                      $parserOptions->setIsSectionPreview( !is_null($this->section) && $this->section !== '' );
 +
 +                      # don't parse non-wikitext pages, show message about preview
 +                      if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
 +                              if( $this->mTitle->isCssJsSubpage() ) {
 +                                      $level = 'user';
 +                              } elseif( $this->mTitle->isCssOrJsPage() ) {
 +                                      $level = 'site';
 +                              } else {
 +                                      $level = false;
 +                              }
  
 -                      $previewHTML .= "<pre class=\"$class\" dir=\"ltr\">\n" . htmlspecialchars( $this->textbox1 ) . "\n</pre>\n";
 -              } else {
 -                      $toparse = $this->textbox1;
 +                              if ( $content->getModel() == CONTENT_MODEL_CSS ) {
 +                                      $format = 'css';
 +                              } elseif ( $content->getModel() == CONTENT_MODEL_JAVASCRIPT ) {
 +                                      $format = 'js';
 +                              } else {
 +                                      $format = false;
 +                              }
  
 -                      # If we're adding a comment, we need to show the
 -                      # summary as the headline
 -                      if ( $this->section == "new" && $this->summary != "" ) {
 -                              $toparse = wfMessage( 'newsectionheaderdefaultlevel', $this->summary )->inContentLanguage()->text() . "\n\n" . $toparse;
 +                              # Used messages to make sure grep find them:
 +                              # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
 +                              if( $level && $format ) {
 +                                      $note = "<div id='mw-{$level}{$format}preview'>" . wfMessage( "{$level}{$format}preview" )->text()  . "</div>";
 +                              } else {
 +                                      $note = wfMessage( 'previewnote' )->text() ;
 +                              }
 +                      } else {
 +                              $note = wfMessage( 'previewnote' )->text() ;
                        }
  
 -                      wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) );
 -
 -                      $toparse = $wgParser->preSaveTransform( $toparse, $this->mTitle, $wgUser, $parserOptions );
 -                      $parserOutput = $wgParser->parse( $toparse, $this->mTitle, $parserOptions );
 -
 -                      $rt = Title::newFromRedirectArray( $this->textbox1 );
 +                      $rt = $content->getRedirectChain();
                        if ( $rt ) {
                                $previewHTML = $this->mArticle->viewRedirect( $rt, false );
                        } else {
Simple merge
Simple merge
@@@ -1260,41 -1046,27 +1260,47 @@@ class Revision implements IDBAccessObje
                $rev_id = isset( $this->mId )
                        ? $this->mId
                        : $dbw->nextSequenceValue( 'revision_rev_id_seq' );
 -              $dbw->insert( 'revision',
 -                      array(
 -                              'rev_id'         => $rev_id,
 -                              'rev_page'       => $this->mPage,
 -                              'rev_text_id'    => $this->mTextId,
 -                              'rev_comment'    => $this->mComment,
 -                              'rev_minor_edit' => $this->mMinorEdit ? 1 : 0,
 -                              'rev_user'       => $this->mUser,
 -                              'rev_user_text'  => $this->mUserText,
 -                              'rev_timestamp'  => $dbw->timestamp( $this->mTimestamp ),
 -                              'rev_deleted'    => $this->mDeleted,
 -                              'rev_len'        => $this->mSize,
 -                              'rev_parent_id'  => is_null( $this->mParentId )
 -                                      ? $this->getPreviousRevisionId( $dbw )
 -                                      : $this->mParentId,
 -                              'rev_sha1'       => is_null( $this->mSha1 )
 -                                      ? self::base36Sha1( $this->mText )
 -                                      : $this->mSha1
 -                      ), __METHOD__
 +              $row = array(
 +                      'rev_id'         => $rev_id,
 +                      'rev_page'       => $this->mPage,
 +                      'rev_text_id'    => $this->mTextId,
 +                      'rev_comment'    => $this->mComment,
 +                      'rev_minor_edit' => $this->mMinorEdit ? 1 : 0,
 +                      'rev_user'       => $this->mUser,
 +                      'rev_user_text'  => $this->mUserText,
 +                      'rev_timestamp'  => $dbw->timestamp( $this->mTimestamp ),
 +                      'rev_deleted'    => $this->mDeleted,
 +                      'rev_len'        => $this->mSize,
 +                      'rev_parent_id'  => is_null( $this->mParentId )
 +                              ? $this->getPreviousRevisionId( $dbw )
 +                              : $this->mParentId,
 +                      'rev_sha1'       => is_null( $this->mSha1 )
 +                              ? Revision::base36Sha1( $this->mText )
 +                              : $this->mSha1,
                );
  
-                       $defaultModel = ContentHandler::getDefaultModelFor( $this->getTitle() );
 +              if ( $wgContentHandlerUseDB ) {
 +                      //NOTE: Store null for the default model and format, to save space.
 +                      //XXX: Makes the DB sensitive to changed defaults. Make this behaviour optional? Only in miser mode?
 +
 +                      $model = $this->getContentModel();
 +                      $format = $this->getContentFormat();
 +
++                      $title = $this->getTitle();
++
++                      if ( $title === null ) {
++                              throw new MWException( "Insufficient information to determine the title of the revision's page!" );
++                      }
++
++                      $defaultModel = ContentHandler::getDefaultModelFor( $title );
 +                      $defaultFormat = ContentHandler::getForModelID( $defaultModel )->getDefaultFormat();
 +
 +                      $row[ 'rev_content_model' ] = ( $model === $defaultModel ) ? null : $model;
 +                      $row[ 'rev_content_format' ] = ( $format === $defaultFormat ) ? null : $format;
 +              }
 +
 +              $dbw->insert( 'revision', $row, __METHOD__ );
 +
                $this->mId = !is_null( $rev_id ) ? $rev_id : $dbw->insertId();
  
                wfRunHooks( 'RevisionInsertComplete', array( &$this, $data, $flags ) );
                }
                return 0;
        }
 -}
+       /**
+        * Check if no edits were made by other users since
+        * the time a user started editing the page. Limit to
+        * 50 revisions for the sake of performance.
+        *
+        * @since 1.20
+        *
+        * @param DatabaseBase|int $db the Database to perform the check on. May be given as a Database object or
+        *        a database identifier usable with wfGetDB.
+        * @param int $pageId the ID of the page in question
+        * @param int $userId the ID of the user in question
+        * @param string $since look at edits since this time
+        *
+        * @return bool True if the given user was the only one to edit since the given timestamp
+        */
+       public static function userWasLastToEdit( $db, $pageId, $userId, $since ) {
+               if ( !$userId ) return false;
+               if ( is_int( $db ) ) {
+                       $db = wfGetDB( $db );
+               }
+               $res = $db->select( 'revision',
+                       'rev_user',
+                       array(
+                               'rev_page' => $pageId,
+                               'rev_timestamp > ' . $db->addQuotes( $db->timestamp( $since ) )
+                       ),
+                       __METHOD__,
+                       array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ) );
+               foreach ( $res as $row ) {
+                       if ( $row->rev_user != $userId ) {
+                               return false;
+                       }
+               }
+               return true;
+       }
 +}
@@@ -931,6 -934,6 +931,8 @@@ class Title 
         * @return Bool
         */
        public function isConversionTable() {
++              //@todo: ConversionTable should become a separate content model.
++
                return $this->getNamespace() == NS_MEDIAWIKI &&
                        strpos( $this->getText(), 'Conversiontable/' ) === 0;
        }
@@@ -1903,8 -1660,14 +1913,15 @@@ class WikiPage extends Page implements 
                } else { // canonical settings
                        $options = ParserOptions::newFromUserAndLang( new User, $wgContLang );
                }
+               if ( $this->getTitle()->isConversionTable() ) {
++                      //@todo: ConversionTable should become a separate content model.
+                       $options->disableContentConversion();
+               }
                $options->enableLimitReport(); // show inclusion/loop reports
                $options->setTidy( true ); // fix bad HTML
                return $options;
        }
  
@@@ -100,19 -85,21 +96,22 @@@ class ApiParse extends ApiBase 
                                }
  
                                $titleObj = $rev->getTitle();
                                $wgTitle = $titleObj;
+                               $pageObj = WikiPage::factory( $titleObj );
+                               $popts = $pageObj->makeParserOptions( $this->getContext() );
+                               $popts->enableLimitReport( !$params['disablepp'] );
  
                                // If for some reason the "oldid" is actually the current revision, it may be cached
-                               if ( $titleObj->getLatestRevID() === intval( $oldid ) )  {
+                               if ( $rev->isCurrent() )  {
                                        // May get from/save to parser cache
 -                                      $p_result = $this->getParsedSectionOrText( $pageObj, $popts, $pageid,
 -                                               isset( $prop['wikitext'] ) ) ;
 +                                      $pageObj = WikiPage::factory( $titleObj );
-                                       $p_result = $this->getParsedContent( $pageObj, $popts, $pageid, isset( $prop['wikitext'] ) ) ;
++                                      $p_result = $this->getParsedContent( $pageObj, $popts, $pageid, 
++                                              isset( $prop['wikitext'] ) ) ;
                                } else { // This is an old revision, so get the text differently
 -                                      $this->text = $rev->getText( Revision::FOR_THIS_USER, $this->getUser() );
 +                                      $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
  
                                        if ( $this->section !== false ) {
 -                                              $this->text = $this->getSectionText( $this->text, 'r' . $rev->getId() );
 +                                              $this->content = $this->getSectionContent( $this->content, 'r' . $rev->getId() );
                                        }
  
                                        // Should we save old revision parses to the parser cache?
                                $wgTitle = $titleObj;
  
                                if ( isset( $prop['revid'] ) ) {
-                                       $oldid = $titleObj->getLatestRevID();
+                                       $oldid = $pageObj->getLatest();
                                }
  
-                               $pageObj = WikiPage::factory( $titleObj );
++
+                               $popts = $pageObj->makeParserOptions( $this->getContext() );
+                               $popts->enableLimitReport( !$params['disablepp'] );
  
                                // Potentially cached
-                               $p_result = $this->getParsedContent( $pageObj, $popts, $pageid, isset( $prop['wikitext'] ) ) ;
 -                              $p_result = $this->getParsedSectionOrText( $pageObj, $popts, $pageid,
 -                                       isset( $prop['wikitext'] ) ) ;
++                              $p_result = $this->getParsedContent( $pageObj, $popts, $pageid, 
++                                      isset( $prop['wikitext'] ) ) ;
                        }
                } else { // Not $oldid, $pageid, $page. Hence based on $text
 -
 -                      if ( is_null( $text ) ) {
 -                              $this->dieUsage( 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?', 'params' );
 -                      }
 -                      $this->text = $text;
                        $titleObj = Title::newFromText( $title );
                        if ( !$titleObj ) {
                                $this->dieUsageMsg( array( 'invalidtitle', $title ) );
                        }
                        $wgTitle = $titleObj;
+                       $pageObj = WikiPage::factory( $titleObj );
+                       $popts = $pageObj->makeParserOptions( $this->getContext() );
+                       $popts->enableLimitReport( !$params['disablepp'] );
  
 +                      if ( is_null( $text ) ) {
 +                              $this->dieUsage( 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?', 'params' );
 +                      }
 +
 +                      try {
 +                              $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
 +                      } catch ( MWContentSerializationException $ex ) {
 +                              $this->dieUsage( $ex->getMessage(), 'parseerror' );
 +                      }
 +
                        if ( $this->section !== false ) {
 -                              $this->text = $this->getSectionText( $this->text, $titleObj->getText() );
 +                              $this->content = $this->getSectionContent( $this->content, $titleObj->getText() );
                        }
  
                        if ( $params['pst'] || $params['onlypst'] ) {
@@@ -556,19 -556,6 +556,17 @@@ class DifferenceEngine extends ContextS
                wfProfileOut( __METHOD__ );
        }
  
-               $parserOptions = ParserOptions::newFromContext( $this->getContext() );
-               $parserOptions->enableLimitReport();
-               $parserOptions->setTidy( true );
 +      protected function getParserOutput( WikiPage $page, Revision $rev ) {
++              $parserOptions = $page->makeParserOptions( $this->getContext() );
 +
 +              if ( !$rev->isCurrent() || !$rev->getTitle()->quickUserCan( "edit" ) ) {
 +                      $parserOptions->setEditSection( false );
 +              }
 +
 +              $parserOutput = $page->getParserOutput( $parserOptions, $rev->getId() );
 +              return $parserOutput;
 +      }
 +
        /**
         * Get the diff text, send it to the OutputPage object
         * Returns false if the diff could not be generated, otherwise returns true
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -3,9 -3,11 +3,12 @@@
  /**
   * Test class for Revision storage.
   *
 + * @group ContentHandler
   * @group Database
   * ^--- important, causes temporary tables to be used instead of the real database
+  *
+  * @group medium
+  * ^--- important, causes tests not to fail with timeout
   */
  class RevisionStorageTest extends MediaWikiTestCase {
  
                $dbw = wfGetDB( DB_MASTER );
                $rev = Revision::newNullRevision( $dbw, $page->getId(), 'a null revision', false );
  
 -              $this->assertNotEquals( $orig->getId(), $rev->getId(), 'new null revision shold have a different id from the original revision' );
 -              $this->assertEquals( $orig->getTextId(), $rev->getTextId(), 'new null revision shold have the same text id as the original revision' );
 -              $this->assertEquals( 'some testing text', $rev->getText() );
 +              $this->assertNotEquals( $orig->getId(), $rev->getId(),
 +                                                              'new null revision shold have a different id from the original revision' );
 +              $this->assertEquals( $orig->getTextId(), $rev->getTextId(),
 +                                                              'new null revision shold have the same text id as the original revision' );
 +              $this->assertEquals( 'some testing text', $rev->getContent()->getNativeData() );
        }
+       public function dataUserWasLastToEdit() {
+               return array(
+                       array( #0
+                               3, true, # actually the last edit
+                       ),
+                       array( #1
+                               2, true, # not the current edit, but still by this user
+                       ),
+                       array( #2
+                               1, false, # edit by another user
+                       ),
+                       array( #3
+                               0, false, # first edit, by this user, but another user edited in the mean time
+                       ),
+               );
+       }
+       /**
+        * @dataProvider dataUserWasLastToEdit
+        */
+       public function testUserWasLastToEdit( $sinceIdx, $expectedLast ) {
+               $userA = \User::newFromName( "RevisionStorageTest_userA" );
+               $userB = \User::newFromName( "RevisionStorageTest_userB" );
+               if ( $userA->getId() === 0 ) {
+                       $userA = \User::createNew( $userA->getName() );
+               }
+               if ( $userB->getId() === 0 ) {
+                       $userB = \User::createNew( $userB->getName() );
+               }
+               $dbw = wfGetDB( DB_MASTER );
+               $revisions = array();
+               // create revisions -----------------------------
+               $page = WikiPage::factory( Title::newFromText( 'RevisionStorageTest_testUserWasLastToEdit' ) );
+               # zero
+               $revisions[0] = new Revision( array(
+                       'page' => $page->getId(),
++                      'title' => $page->getTitle(), // we need the title to determine the page's default content model
+                       'timestamp' => '20120101000000',
+                       'user' => $userA->getId(),
+                       'text' => 'zero',
+                       'summary' => 'edit zero'
+               ) );
+               $revisions[0]->insertOn( $dbw );
+               # one
+               $revisions[1] = new Revision( array(
+                       'page' => $page->getId(),
++                      'title' => $page->getTitle(), // still need the title, because $page->getId() is 0 (there's no entry in the page table)
+                       'timestamp' => '20120101000100',
+                       'user' => $userA->getId(),
+                       'text' => 'one',
+                       'summary' => 'edit one'
+               ) );
+               $revisions[1]->insertOn( $dbw );
+               # two
+               $revisions[2] = new Revision( array(
+                       'page' => $page->getId(),
++                      'title' => $page->getTitle(),
+                       'timestamp' => '20120101000200',
+                       'user' => $userB->getId(),
+                       'text' => 'two',
+                       'summary' => 'edit two'
+               ) );
+               $revisions[2]->insertOn( $dbw );
+               # three
+               $revisions[3] = new Revision( array(
+                       'page' => $page->getId(),
++                      'title' => $page->getTitle(),
+                       'timestamp' => '20120101000300',
+                       'user' => $userA->getId(),
+                       'text' => 'three',
+                       'summary' => 'edit three'
+               ) );
+               $revisions[3]->insertOn( $dbw );
+               # four
+               $revisions[4] = new Revision( array(
+                       'page' => $page->getId(),
++                      'title' => $page->getTitle(),
+                       'timestamp' => '20120101000200',
+                       'user' => $userA->getId(),
+                       'text' => 'zero',
+                       'summary' => 'edit four'
+               ) );
+               $revisions[4]->insertOn( $dbw );
+               // test it ---------------------------------
+               $since = $revisions[ $sinceIdx ]->getTimestamp();
+               $wasLast = Revision::userWasLastToEdit( $dbw, $page->getId(), $userA->getId(), $since );
+               $this->assertEquals( $expectedLast, $wasLast );
+       }
  }
diff --cc thumb.php
Simple merge