* @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 );
}
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 {
$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;
+ }
+}
* @return Bool
*/
public function isConversionTable() {
++ //@todo: ConversionTable should become a separate content model.
++
return $this->getNamespace() == NS_MEDIAWIKI &&
strpos( $this->getText(), 'Conversiontable/' ) === 0;
}
} 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;
}
}
$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'] ) {
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
/**
* 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 );
+ }
}