From: daniel Date: Tue, 18 Sep 2012 15:43:20 +0000 (+0200) Subject: merged master X-Git-Tag: 1.31.0-rc.0~22097^2^2~18 X-Git-Url: http://git.heureux-cyclage.org/?a=commitdiff_plain;h=b6b686c13b1d78525ae16150a201491c8d0b6451;p=lhc%2Fweb%2Fwiklou.git merged master Change-Id: I0ef7c7f33a5dc5855f38b20c03ddc5306f38ec66 --- b6b686c13b1d78525ae16150a201491c8d0b6451 diff --cc includes/Article.php index 555849c228,9ab4b6bacc..93e1dbf315 --- a/includes/Article.php +++ b/includes/Article.php @@@ -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 ) { + //XXX: bypasses mParserOptions and thus setParserOptions() + - $user = is_null( $user ) ? $this->getContext()->getUser() : $user; - $parserOptions = $this->mPage->makeParserOptions( $user ); + if ( $user === null ) { + $parserOptions = $this->getParserOptions(); + } else { + $parserOptions = $this->mPage->makeParserOptions( $user ); + } return $this->mPage->getParserOutput( $parserOptions, $oldid ); } diff --cc includes/EditPage.php index 5102f7aa92,2115eb0a2d..fe0e95eed7 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@@ -2872,61 -2600,70 +2843,61 @@@ HTM 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 = "
\n" . wfMessage( "{$level}csspreview" )->text() . "\n
"; - $class .= " mw-css"; - } elseif ( preg_match( "/\\.js$/", $this->mTitle->getText() ) ) { - $previewtext = "
\n" . wfMessage( "{$level}jspreview" )->text() . "\n
"; - $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 .= "
\n" . htmlspecialchars( $this->textbox1 ) . "\n
\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 = "
" . wfMessage( "{$level}{$format}preview" )->text() . "
"; + } 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 { diff --cc includes/Revision.php index 1a6a76e17f,20cc8f5862..31a96adc2b --- a/includes/Revision.php +++ b/includes/Revision.php @@@ -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, ); + 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(); + - $defaultModel = ContentHandler::getDefaultModelFor( $this->getTitle() ); ++ $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 ) ); @@@ -1580,4 -1290,42 +1586,42 @@@ } 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; + } -} +} diff --cc includes/Title.php index e6e45a8354,1b5e21d2ee..b9f432fd46 --- a/includes/Title.php +++ b/includes/Title.php @@@ -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; } diff --cc includes/WikiPage.php index 38a1cbd961,bc8766def0..a032028144 --- a/includes/WikiPage.php +++ b/includes/WikiPage.php @@@ -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; } diff --cc includes/api/ApiParse.php index ca5fe7e55b,2fcdc38824..7c6129103a --- a/includes/api/ApiParse.php +++ b/includes/api/ApiParse.php @@@ -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? @@@ -161,33 -139,34 +151,40 @@@ $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'] ) { diff --cc includes/diff/DifferenceEngine.php index 82a4afb4b4,c7156fb2f1..a275763722 --- a/includes/diff/DifferenceEngine.php +++ b/includes/diff/DifferenceEngine.php @@@ -556,19 -556,6 +556,17 @@@ class DifferenceEngine extends ContextS wfProfileOut( __METHOD__ ); } + protected function getParserOutput( WikiPage $page, Revision $rev ) { - $parserOptions = ParserOptions::newFromContext( $this->getContext() ); - $parserOptions->enableLimitReport(); - $parserOptions->setTidy( true ); ++ $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 diff --cc tests/phpunit/includes/RevisionStorageTest.php index 02b13b96a6,8a7facece4..4786e3d44f --- a/tests/phpunit/includes/RevisionStorageTest.php +++ b/tests/phpunit/includes/RevisionStorageTest.php @@@ -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 { @@@ -399,10 -305,104 +402,111 @@@ $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 ); + } }