public $mParserOptions;
/**
- * Content of the revision we are working on
+ * Text of the revision we are working on
* @var string $mContent
*/
- var $mContent; // !<
+ var $mContent; // !< #BC cruft
+
+ /**
+ * Content of the revision we are working on
+ * @var Content
+ * @since 1.WD
+ */
+ var $mContentObject; // !<
/**
* Is the content ($mContent) already loaded?
* This function has side effects! Do not use this function if you
* only want the real revision text if any.
*
+ * @deprecated in 1.WD; use getContentObject() instead
+ *
* @return string Return the text of this revision
*/
public function getContent() {
+ wfDeprecated( __METHOD__, '1.WD' );
+ $content = $this->getContentObject();
+ return ContentHandler::getContentText( $content );
+ }
+
+ /**
+ * Returns a Content object representing the pages effective display content,
+ * not necessarily the revision's content!
+ *
+ * Note that getContent/loadContent do not follow redirects anymore.
+ * If you need to fetch redirectable content easily, try
+ * the shortcut in WikiPage::getRedirectTarget()
+ *
+ * This function has side effects! Do not use this function if you
+ * only want the real revision text if any.
+ *
+ * @return Content Return the content of this revision
+ *
+ * @since 1.WD
+ */
+ protected function getContentObject() {
+ global $wgUser;
wfProfileIn( __METHOD__ );
if ( $this->mPage->getID() === 0 ) {
if ( $text === false ) {
$text = '';
}
+
+ $content = ContentHandler::makeContent( $text, $this->getTitle() );
} else {
- $text = wfMsgExt( $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon', 'parsemag' );
+ $content = new MessageContent( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon', null, 'parsemag' );
}
wfProfileOut( __METHOD__ );
- return $text;
+ return $content;
} else {
- $this->fetchContent();
+ $this->fetchContentObject();
wfProfileOut( __METHOD__ );
- return $this->mContent;
+ return $this->mContentObject;
}
}
* Get text of an article from database
* Does *NOT* follow redirects.
*
+ * @protected
+ * @note this is really internal functionality that should really NOT be used by other functions. For accessing
+ * article content, use the WikiPage class, especially WikiBase::getContent(). However, a lot of legacy code
+ * uses this method to retrieve page text from the database, so the function has to remain public for now.
+ *
* @return mixed string containing article contents, or false if null
+ * @deprecated in 1.WD, use WikiPage::getContent() instead
*/
- function fetchContent() {
- if ( $this->mContentLoaded ) {
+ function fetchContent() { #BC cruft!
+ wfDeprecated( __METHOD__, '1.WD' );
+
+ if ( $this->mContentLoaded && $this->mContent ) {
return $this->mContent;
}
wfProfileIn( __METHOD__ );
+ $content = $this->fetchContentObject();
+
+ $this->mContent = ContentHandler::getContentText( $content ); #@todo: get rid of mContent everywhere!
+ wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) ); #BC cruft! #XXX: can we deprecate that hook?
+
+ wfProfileOut( __METHOD__ );
+
+ return $this->mContent;
+ }
+
+
+ /**
+ * Get text content object
+ * Does *NOT* follow redirects.
+ * TODO: when is this null?
+ *
+ * @note code that wants to retrieve page content from the database should use WikiPage::getContent().
+ *
+ * @return Content|null
+ *
+ * @since 1.WD
+ */
+ protected function fetchContentObject() {
+ if ( $this->mContentLoaded ) {
+ return $this->mContentObject;
+ }
+
+ wfProfileIn( __METHOD__ );
+
$this->mContentLoaded = true;
+ $this->mContent = null;
$oldid = $this->getOldID();
# fails we'll have something telling us what we intended.
$t = $this->getTitle()->getPrefixedText();
$d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
- $this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ;
+ $this->mContentObject = new MessageContent( 'missing-article', array($t, $d), array() ) ; // @todo: this isn't page content but a UI message. horrible.
if ( $oldid ) {
# $this->mRevision might already be fetched by getOldIDFromRequest()
}
$this->mRevision = $this->mPage->getRevision();
+
if ( !$this->mRevision ) {
wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
wfProfileOut( __METHOD__ );
// @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
// We should instead work with the Revision object when we need it...
- $this->mContent = $this->mRevision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed
+ $this->mContentObject = $this->mRevision->getContent( Revision::FOR_THIS_USER ); // Loads if user is allowed
$this->mRevIdFetched = $this->mRevision->getId();
- wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
+ wfRunHooks( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) );
wfProfileOut( __METHOD__ );
- return $this->mContent;
+ return $this->mContentObject;
}
/**
* @return Revision|null
*/
public function getRevisionFetched() {
- $this->fetchContent();
+ $this->fetchContentObject();
return $this->mRevision;
}
break;
case 3:
# This will set $this->mRevision if needed
- $this->fetchContent();
+ $this->fetchContentObject();
# Are we looking at an old revision
if ( $oldid && $this->mRevision ) {
wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
$this->showCssOrJsPage();
$outputDone = true;
- } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) {
+ } elseif( !wfRunHooks( 'ArticleContentViewCustom', array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
+ # Allow extensions do their own custom view for certain pages
+ $outputDone = true;
+ } elseif( Hooks::isRegistered( 'ArticleViewCustom' ) && !wfRunHooks( 'ArticleViewCustom', array( $this->fetchContent(), $this->getTitle(), $outputPage ) ) ) { #FIXME: fetchContent() is deprecated!
# Allow extensions do their own custom view for certain pages
$outputDone = true;
} else {
- $text = $this->getContent();
- $rt = Title::newFromRedirectArray( $text );
+ $content = $this->getContentObject();
+ $rt = $content->getRedirectChain();
if ( $rt ) {
wfDebug( __METHOD__ . ": showing redirect=no page\n" );
# Viewing a redirect page (e.g. with parameter redirect=no)
$outputPage->addHTML( $this->viewRedirect( $rt ) );
# Parse just to get categories, displaytitle, etc.
- $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions );
+ $this->mParserOutput = $content->getParserOutput( $this->getTitle(), $oldid, $parserOptions, false );
$outputPage->addParserOutputNoText( $this->mParserOutput );
$outputDone = true;
}
# Run the parse, protected by a pool counter
wfDebug( __METHOD__ . ": doing uncached parse\n" );
+ // @todo: shouldn't we be passing $this->getPage() to PoolWorkArticleView instead of plain $this?
$poolArticleView = new PoolWorkArticleView( $this, $parserOptions,
- $this->getRevIdFetched(), $useParserCache, $this->getContent() );
+ $this->getRevIdFetched(), $useParserCache, $this->getContentObject(), $this->getContext() );
if ( !$poolArticleView->execute() ) {
$error = $poolArticleView->getError();
$unhide = $request->getInt( 'unhide' ) == 1;
$oldid = $this->getOldID();
- $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
+ $contentHandler = ContentHandler::getForTitle( $this->getTitle() );
+ $de = $contentHandler->createDifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
+
// DifferenceEngine directly fetched the revision:
$this->mRevIdFetched = $de->mNewid;
$de->showDiffPage( $diffOnly );
* This is hooked by SyntaxHighlight_GeSHi to do syntax highlighting of these
* page views.
*/
- protected function showCssOrJsPage() {
- $dir = $this->getContext()->getLanguage()->getDir();
- $lang = $this->getContext()->getLanguage()->getCode();
+ protected function showCssOrJsPage( $showCacheHint = true ) {
+ global $wgOut;
- $outputPage = $this->getContext()->getOutput();
- $outputPage->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
- 'clearyourcache' );
+ if ( $showCacheHint ) {
+ $dir = $this->getContext()->getLanguage()->getDir();
+ $lang = $this->getContext()->getLanguage()->getCode();
+
+ $wgOut->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
+ 'clearyourcache' );
+ }
// Give hooks a chance to customise the output
- if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) {
- // Wrap the whole lot in a <pre> and don't parse
- $m = array();
- preg_match( '!\.(css|js)$!u', $this->getTitle()->getText(), $m );
- $outputPage->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
- $outputPage->addHTML( htmlspecialchars( $this->mContent ) );
- $outputPage->addHTML( "\n</pre>\n" );
+ if ( !Hooks::isRegistered('ShowRawCssJs') || wfRunHooks( 'ShowRawCssJs', array( $this->fetchContent(), $this->getTitle(), $wgOut ) ) ) { #FIXME: fetchContent() is deprecated
+ $po = $this->mContentObject->getParserOutput( $this->getTitle() );
+ $wgOut->addHTML( $po->getText() );
}
}
$this->doDelete( $reason, $suppress );
if ( $request->getCheck( 'wpWatch' ) && $user->isLoggedIn() ) {
- $this->doWatch();
+ WatchAction::doWatch( $title, $user );
} elseif ( $title->userIsWatching() ) {
- $this->doUnwatch();
+ WatchAction::doUnwatch( $title, $user );
}
return;
// Generate deletion reason
$hasHistory = false;
if ( !$reason ) {
- $reason = $this->generateReason( $hasHistory );
+ try {
+ $reason = $this->generateReason( $hasHistory );
+ } catch (MWException $e) {
+ # if a page is horribly broken, we still want to be able to delete it. so be lenient about errors here.
+ wfDebug("Error while building auto delete summary: $e");
+ $reason = '';
+ }
}
// If the page has a history, insert a warning
* @return mixed
*/
public function generateReason( &$hasHistory ) {
- return $this->mPage->getAutoDeleteReason( $hasHistory );
+ $title = $this->mPage->getTitle();
+ $handler = ContentHandler::getForTitle( $title );
+ return $handler->getAutoDeleteReason( $title, $hasHistory );
}
// ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** //
* @param $newtext
* @param $flags
* @return string
+ * @deprecated since 1.WD, use ContentHandler::getAutosummary() instead
*/
public static function getAutosummary( $oldtext, $newtext, $flags ) {
return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
'DeferredUpdates' => 'includes/DeferredUpdates.php',
'DeprecatedGlobal' => 'includes/DeprecatedGlobal.php',
'DerivativeRequest' => 'includes/WebRequest.php',
- 'DeviceDetection' => 'includes/DeviceDetection.php',
- 'DeviceProperties' => 'includes/DeviceDetection.php',
+ 'DeviceDetection' => 'includes/mobile/DeviceDetection.php',
+ 'DeviceProperties' => 'includes/mobile/DeviceDetection.php',
'DiffHistoryBlob' => 'includes/HistoryBlob.php',
'DoubleReplacer' => 'includes/StringUtils.php',
'DummyLinker' => 'includes/Linker.php',
'HttpRequest' => 'includes/HttpFunctions.old.php',
'ICacheHelper' => 'includes/CacheHelper.php',
'IcuCollation' => 'includes/Collation.php',
- 'IDeviceProperties' => 'includes/DeviceDetection.php',
- 'IDeviceDetector' => 'includes/DeviceDetection.php',
+ 'IDeviceProperties' => 'includes/mobile/DeviceDetection.php',
+ 'IDeviceDetector' => 'includes/mobile/DeviceDetection.php',
'IdentityCollation' => 'includes/Collation.php',
'ImageGallery' => 'includes/ImageGallery.php',
'ImageHistoryList' => 'includes/ImagePage.php',
'ZhClient' => 'includes/ZhClient.php',
'ZipDirectoryReader' => 'includes/ZipDirectoryReader.php',
+ # content handler
+ 'Content' => 'includes/Content.php',
+ 'ContentHandler' => 'includes/ContentHandler.php',
+ 'CssContent' => 'includes/Content.php',
+ 'CssContentHandler' => 'includes/ContentHandler.php',
+ 'JavaScriptContent' => 'includes/Content.php',
+ 'JavaScriptContentHandler' => 'includes/ContentHandler.php',
+ 'MessageContent' => 'includes/Content.php',
+ 'TextContent' => 'includes/Content.php',
+ 'WikitextContent' => 'includes/Content.php',
+ 'WikitextContentHandler' => 'includes/ContentHandler.php',
+
# includes/actions
'CachedAction' => 'includes/actions/CachedAction.php',
'CreditsAction' => 'includes/actions/CreditsAction.php',
'ApiFormatDump' => 'includes/api/ApiFormatDump.php',
'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php',
'ApiFormatJson' => 'includes/api/ApiFormatJson.php',
+ 'ApiFormatNone' => 'includes/api/ApiFormatNone.php',
'ApiFormatPhp' => 'includes/api/ApiFormatPhp.php',
'ApiFormatRaw' => 'includes/api/ApiFormatRaw.php',
'ApiFormatTxt' => 'includes/api/ApiFormatTxt.php',
'DBConnectionError' => 'includes/db/DatabaseError.php',
'DBError' => 'includes/db/DatabaseError.php',
'DBObject' => 'includes/db/DatabaseUtility.php',
+ 'IORMRow' => 'includes/db/IORMRow.php',
+ 'IORMTable' => 'includes/db/IORMTable.php',
'DBMasterPos' => 'includes/db/DatabaseUtility.php',
'DBQueryError' => 'includes/db/DatabaseError.php',
'DBUnexpectedError' => 'includes/db/DatabaseError.php',
'TestFileIterator' => 'tests/testHelpers.inc',
'TestRecorder' => 'tests/testHelpers.inc',
+ # tests/phpunit
+ 'RevisionStorageTest' => 'tests/phpunit/includes/RevisionStorageTest.php',
+ 'WikiPageTest' => 'tests/phpunit/includes/WikiPageTest.php',
+ 'WikitextContentTest' => 'tests/phpunit/includes/WikitextContentTest.php',
+ 'JavascriptContentTest' => 'tests/phpunit/includes/JavascriptContentTest.php',
+ 'DummyContentHandlerForTesting' => 'tests/phpunit/includes/ContentHandlerTest.php',
+ 'DummyContentForTesting' => 'tests/phpunit/includes/ContentHandlerTest.php',
+
# tests/parser
'ParserTest' => 'tests/parser/parserTest.inc',
'ParserTestParserHook' => 'tests/parser/parserTestsParserHook.php',
* @return string
*/
function schemaVersion() {
- return "0.6";
+ return "0.7";
}
/**
$out .= " " . Xml::elementClean( 'comment', array(), strval( $row->rev_comment ) ) . "\n";
}
- if ( $row->rev_sha1 && !( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
- $out .= " " . Xml::element('sha1', null, strval( $row->rev_sha1 ) ) . "\n";
- } else {
- $out .= " <sha1/>\n";
+ if ( isset( $row->rev_content_model ) && !is_null( $row->rev_content_model ) ) {
+ $name = ContentHandler::getContentModelName( $row->rev_content_model );
+ $out .= " " . Xml::element('model', array( 'name' => $name ), strval( $row->rev_content_model ) ) . "\n";
+ }
+
+ if ( isset( $row->rev_content_format ) && !is_null( $row->rev_content_format ) ) {
+ $mime = ContentHandler::getContentFormatMimeType( $row->rev_content_format );
+ $out .= " " . Xml::element('format', array( 'mime' => $mime ), strval( $row->rev_content_format ) ) . "\n";
}
$text = '';
"" ) . "\n";
}
- if ( $row->rev_sha1 && !( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
- $out .= " " . Xml::element('sha1', null, strval( $row->rev_sha1 ) ) . "\n";
- } else {
- $out .= " <sha1/>\n";
- }
-
wfRunHooks( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
$out .= " </revision>\n";
function writeLogItem( $row ) {
wfProfileIn( __METHOD__ );
- $out = " <logitem>\n";
- $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
+ $out = " <logitem>\n";
+ $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
- $out .= $this->writeTimestamp( $row->log_timestamp );
+ $out .= $this->writeTimestamp( $row->log_timestamp, " " );
if ( $row->log_deleted & LogPage::DELETED_USER ) {
- $out .= " " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
+ $out .= " " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
} else {
- $out .= $this->writeContributor( $row->log_user, $row->user_name );
+ $out .= $this->writeContributor( $row->log_user, $row->user_name, " " );
}
if ( $row->log_deleted & LogPage::DELETED_COMMENT ) {
- $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
+ $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
} elseif ( $row->log_comment != '' ) {
- $out .= " " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n";
+ $out .= " " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n";
}
- $out .= " " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
- $out .= " " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
+ $out .= " " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
+ $out .= " " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
if ( $row->log_deleted & LogPage::DELETED_ACTION ) {
- $out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
+ $out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
} else {
$title = Title::makeTitle( $row->log_namespace, $row->log_title );
- $out .= " " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
- $out .= " " . Xml::elementClean( 'params',
+ $out .= " " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
+ $out .= " " . Xml::elementClean( 'params',
array( 'xml:space' => 'preserve' ),
strval( $row->log_params ) ) . "\n";
}
- $out .= " </logitem>\n";
+ $out .= " </logitem>\n";
wfProfileOut( __METHOD__ );
return $out;
* @param $timestamp string
* @return string
*/
- function writeTimestamp( $timestamp ) {
+ function writeTimestamp( $timestamp, $indent = " " ) {
$ts = wfTimestamp( TS_ISO_8601, $timestamp );
- return " " . Xml::element( 'timestamp', null, $ts ) . "\n";
+ return $indent . Xml::element( 'timestamp', null, $ts ) . "\n";
}
/**
* @param $text string
* @return string
*/
- function writeContributor( $id, $text ) {
- $out = " <contributor>\n";
+ function writeContributor( $id, $text, $indent = " " ) {
+ $out = $indent . "<contributor>\n";
if ( $id || !IP::isValid( $text ) ) {
- $out .= " " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
- $out .= " " . Xml::element( 'id', null, strval( $id ) ) . "\n";
+ $out .= $indent . " " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
+ $out .= $indent . " " . Xml::element( 'id', null, strval( $id ) ) . "\n";
} else {
- $out .= " " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
+ $out .= $indent . " " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
}
- $out .= " </contributor>\n";
+ $out .= $indent . "</contributor>\n";
return $out;
}
} else {
$contents = '';
}
+ if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
+ $comment = Xml::element( 'comment', array( 'deleted' => 'deleted' ) );
+ } else {
+ $comment = Xml::elementClean( 'comment', null, $file->getDescription() );
+ }
return " <upload>\n" .
$this->writeTimestamp( $file->getTimestamp() ) .
$this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) .
- " " . Xml::elementClean( 'comment', null, $file->getDescription() ) . "\n" .
+ " " . $comment . "\n" .
" " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
$archiveName .
" " . Xml::element( 'src', null, $file->getCanonicalUrl() ) . "\n" .
$out->addHTML( Xml::openElement( 'div', array( 'id' => 'mw-imagepage-content',
'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
'class' => 'mw-content-'.$pageLang->getDir() ) ) );
- parent::view();
- $out->addHTML( Xml::closeElement( 'div' ) );
+
+ parent::view(); #FIXME: use ContentHandler::makeArticle() !!
+
+ $wgOut->addHTML( Xml::closeElement( 'div' ) );
} else {
# Just need to set the right headers
$out->setArticleFlag( true );
return $r;
}
- /**
- * Overloading Article's getContent method.
- *
- * Omit noarticletext if sharedupload; text will be fetched from the
- * shared upload server if possible.
- * @return string
- */
- public function getContent() {
- $this->loadFile();
- if ( $this->mPage->getFile() && !$this->mPage->getFile()->isLocal() && 0 == $this->getID() ) {
- return '';
- }
- return parent::getContent();
- }
+ /**
+ * Overloading Article's getContentObject method.
+ *
+ * Omit noarticletext if sharedupload; text will be fetched from the
+ * shared upload server if possible.
+ * @return string
+ */
+ public function getContentObject() {
+ $this->loadFile();
+ if ( $this->mPage->getFile() && !$this->mPage->getFile()->isLocal() && 0 == $this->getID() ) {
+ return null;
+ }
+ return parent::getContentObject();
+ }
protected function openShowImage() {
global $wgImageLimits, $wgEnableUploads, $wgSend404Code;
$img = $iscur ? $file->getName() : $file->getArchiveName();
$userId = $file->getUser( 'id' );
$userText = $file->getUser( 'text' );
- $description = $file->getDescription();
+ $description = $file->getDescription( File::FOR_THIS_USER, $user );
$local = $this->current->isLocal();
$row = $selected = '';
$row .= '<td>';
if ( $iscur ) {
$row .= wfMsgHtml( 'filehist-current' );
- } elseif ( $local && $user->isLoggedIn() && $this->title->userCan( 'edit' ) ) {
+ } elseif ( $local && $this->title->quickUserCan( 'edit' )
+ && $this->title->quickUserCan( 'upload' )
+ ) {
if ( $file->isDeleted( File::DELETED_FILE ) ) {
$row .= wfMsgHtml( 'filehist-revert' );
} else {
$mCategories, //!< Map of category names to sort keys
$mInterlangs, //!< Map of language codes to titles
$mProperties, //!< Map of arbitrary name to value
- $mDb, //!< Database connection reference
- $mOptions, //!< SELECT options to be used (array)
$mRecursive; //!< Whether to queue jobs for recursive updates
/**
$this->mTitle = $title;
$this->mId = $title->getArticleID();
+ assert( $this->mId > 0 );
+ if ( !$this->mId ) {
+ throw new MWException( "The Title object did not provide an article ID. Perhaps the page doesn't exist?" );
+ }
+
$this->mParserOutput = $parserOutput;
+
$this->mLinks = $parserOutput->getLinks();
$this->mImages = $parserOutput->getImages();
$this->mTemplates = $parserOutput->getTemplates();
protected $mTextRow;
protected $mTitle;
protected $mCurrent;
+ protected $mContentModel;
+ protected $mContentFormat;
+ protected $mContent;
+ protected $mContentHandler;
const DELETED_TEXT = 1;
const DELETED_COMMENT = 2;
* @return Revision
*/
public static function newFromArchiveRow( $row, $overrides = array() ) {
+ global $wgContentHandlerUseDB;
+
$attribs = $overrides + array(
'page' => isset( $row->ar_page_id ) ? $row->ar_page_id : null,
'id' => isset( $row->ar_rev_id ) ? $row->ar_rev_id : null,
'deleted' => $row->ar_deleted,
'len' => $row->ar_len,
'sha1' => isset( $row->ar_sha1 ) ? $row->ar_sha1 : null,
+ 'content_model' => isset( $row->ar_content_model ) ? $row->ar_content_model : null,
+ 'content_format' => isset( $row->ar_content_format ) ? $row->ar_content_format : null,
);
+
+ if ( !$wgContentHandlerUseDB ) {
+ unset( $attribs['content_model'] );
+ unset( $attribs['content_format'] );
+ }
+
if ( isset( $row->ar_text ) && !$row->ar_text_id ) {
// Pre-1.5 ar_text row
$attribs['text'] = self::getRevisionText( $row, 'ar_' );
* @return array
*/
public static function selectFields() {
- return array(
+ global $wgContentHandlerUseDB;
+
+ $fields = array(
'rev_id',
'rev_page',
'rev_text_id',
'rev_deleted',
'rev_len',
'rev_parent_id',
- 'rev_sha1'
+ 'rev_sha1',
);
+
+ if ( $wgContentHandlerUseDB ) {
+ $fields[] = 'rev_content_format';
+ $fields[] = 'rev_content_model';
+ }
+
+ return $fields;
}
/**
'page_namespace',
'page_title',
'page_id',
- 'page_latest'
+ 'page_latest',
+ 'page_is_redirect',
+ 'page_len',
);
}
$this->mTitle = null;
}
+ if( !isset( $row->rev_content_model ) || is_null( $row->rev_content_model ) ) {
+ $this->mContentModel = null; # determine on demand if needed
+ } else {
+ $this->mContentModel = intval( $row->rev_content_model );
+ }
+
+ if( !isset( $row->rev_content_format ) || is_null( $row->rev_content_format ) ) {
+ $this->mContentFormat = null; # determine on demand if needed
+ } else {
+ $this->mContentFormat = intval( $row->rev_content_format );
+ }
+
// Lazy extraction...
$this->mText = null;
if( isset( $row->old_text ) ) {
// Build a new revision to be saved...
global $wgUser; // ugh
+
+ # if we have a content object, use it to set the model and type
+ if ( !empty( $row['content'] ) ) {
+ if ( !empty( $row['text_id'] ) ) { #FIXME: when is that set? test with external store setup! check out insertOn()
+ throw new MWException( "Text already stored in external store (id {$row['text_id']}), can't serialize content object" );
+ }
+
+ $row['content_model'] = $row['content']->getModel();
+ # note: mContentFormat is initializes later accordingly
+ # note: content is serialized later in this method!
+ # also set text to null?
+ }
+
$this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null;
$this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null;
$this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null;
$this->mParentId = isset( $row['parent_id'] ) ? intval( $row['parent_id'] ) : null;
$this->mSha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null;
+ $this->mContentModel = isset( $row['content_model'] ) ? intval( $row['content_model'] ) : null;
+ $this->mContentFormat = isset( $row['content_format'] ) ? intval( $row['content_format'] ) : null;
+
// Enforce spacing trimming on supplied text
$this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
$this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
$this->mTextRow = null;
+ # if we have a content object, override mText and mContentModel
+ if ( !empty( $row['content'] ) ) {
+ $handler = $this->getContentHandler();
+ $this->mContent = $row['content'];
+
+ $this->mContentModel = $this->mContent->getModel();
+ $this->mContentHandler = null;
+
+ $this->mText = $handler->serializeContent( $row['content'], $this->getContentFormat() );
+ } elseif ( !is_null( $this->mText ) ) {
+ $handler = $this->getContentHandler();
+ $this->mContent = $handler->unserializeContent( $this->mText );
+ }
+
$this->mTitle = null; # Load on demand if needed
- $this->mCurrent = false;
+ $this->mCurrent = false; #XXX: really? we are about to create a revision. it will usually then be the current one.
+
# If we still have no length, see it we have the text to figure it out
if ( !$this->mSize ) {
- $this->mSize = is_null( $this->mText ) ? null : strlen( $this->mText );
+ if ( !is_null( $this->mContent ) ) {
+ $this->mSize = $this->mContent->getSize();
+ } else {
+ #XXX: my be inconsistent with the notion of "size" use for the present content model
+ #NOTE: should never happen if we have either text or content object!
+ $this->mSize = is_null( $this->mText ) ? null : strlen( $this->mText );
+ }
}
+
# Same for sha1
if ( $this->mSha1 === null ) {
$this->mSha1 = is_null( $this->mText ) ? null : self::base36Sha1( $this->mText );
}
+
+ $this->getContentModel(); # force lazy init
+ $this->getContentFormat(); # force lazy init
} else {
throw new MWException( 'Revision constructor passed invalid row format.' );
}
$this->mUnpatrolled = null;
+
+ // @TODO: add support for ar_content_format, ar_content_model, rev_content_format, rev_content_model to API
+ // @TODO: get rid of $mText
}
/**
* Get revision ID
*
- * @return Integer
+ * @return Integer|null
*/
public function getId() {
return $this->mId;
/**
* Get text row ID
*
- * @return Integer
+ * @return Integer|null
*/
public function getTextId() {
return $this->mTextId;
/**
* Returns the length of the text in this revision, or null if unknown.
*
- * @return Integer
+ * @return Integer|null
*/
public function getSize() {
return $this->mSize;
/**
* Returns the base36 sha1 of the text in this revision, or null if unknown.
*
- * @return String
+ * @return String|null
*/
public function getSha1() {
return $this->mSha1;
}
/**
- * Returns the title of the page associated with this entry.
+ * Returns the title of the page associated with this entry or null.
*
- * @return Title
+ * Will do a query, when title is not set and id is given.
+ *
+ * @return Title|null
*/
public function getTitle() {
if( isset( $this->mTitle ) ) {
return $this->mTitle;
}
- $dbr = wfGetDB( DB_SLAVE );
- $row = $dbr->selectRow(
- array( 'page', 'revision' ),
- self::selectPageFields(),
- array( 'page_id=rev_page',
- 'rev_id' => $this->mId ),
- __METHOD__ );
- if ( $row ) {
- $this->mTitle = Title::newFromRow( $row );
+ if( !is_null( $this->mId ) ) { //rev_id is defined as NOT NULL
+ $dbr = wfGetDB( DB_SLAVE );
+ $row = $dbr->selectRow(
+ array( 'page', 'revision' ),
+ self::selectPageFields(),
+ array( 'page_id=rev_page',
+ 'rev_id' => $this->mId ),
+ __METHOD__ );
+ if ( $row ) {
+ $this->mTitle = Title::newFromRow( $row );
+ }
}
return $this->mTitle;
}
/**
* Get the page ID
*
- * @return Integer
+ * @return Integer|null
*/
public function getPage() {
return $this->mPage;
*
* @param $audience Integer: one of:
* Revision::FOR_PUBLIC to be displayed to all users
- * Revision::FOR_THIS_USER to be displayed to $wgUser
+ * Revision::FOR_THIS_USER to be displayed to the given user
* Revision::RAW get the ID regardless of permissions
* @param $user User object to check for, only if FOR_THIS_USER is passed
* to the $audience parameter
*
* @param $audience Integer: one of:
* Revision::FOR_PUBLIC to be displayed to all users
- * Revision::FOR_THIS_USER to be displayed to $wgUser
+ * Revision::FOR_THIS_USER to be displayed to the given user
* Revision::RAW get the text regardless of permissions
* @param $user User object to check for, only if FOR_THIS_USER is passed
* to the $audience parameter
*
* @param $audience Integer: one of:
* Revision::FOR_PUBLIC to be displayed to all users
- * Revision::FOR_THIS_USER to be displayed to $wgUser
+ * Revision::FOR_THIS_USER to be displayed to the given user
* Revision::RAW get the text regardless of permissions
* @param $user User object to check for, only if FOR_THIS_USER is passed
* to the $audience parameter
*
* @param $audience Integer: one of:
* Revision::FOR_PUBLIC to be displayed to all users
- * Revision::FOR_THIS_USER to be displayed to $wgUser
+ * Revision::FOR_THIS_USER to be displayed to the given user
* Revision::RAW get the text regardless of permissions
* @param $user User object to check for, only if FOR_THIS_USER is passed
* to the $audience parameter
* @return String
+ * @deprecated in 1.WD, use getContent() instead
+ */
+ public function getText( $audience = self::FOR_PUBLIC, User $user = null ) { #FIXME: deprecated, replace usage! #FIXME: used a LOT!
+ wfDeprecated( __METHOD__, '1.WD' );
+
+ $content = $this->getContent();
+ return ContentHandler::getContentText( $content ); # returns the raw content text, if applicable
+ }
+
+ /**
+ * Fetch revision content if it's available to the specified audience.
+ * If the specified audience does not have the ability to view this
+ * revision, null will be returned.
+ *
+ * @param $audience Integer: one of:
+ * Revision::FOR_PUBLIC to be displayed to all users
+ * Revision::FOR_THIS_USER to be displayed to $wgUser
+ * Revision::RAW get the text regardless of permissions
+ * @param $user User object to check for, only if FOR_THIS_USER is passed
+ * to the $audience parameter
+ * @return Content
+ *
+ * @since 1.WD
*/
- public function getText( $audience = self::FOR_PUBLIC, User $user = null ) {
+ public function getContent( $audience = self::FOR_PUBLIC, User $user = null ) {
if( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_TEXT ) ) {
- return '';
+ return null;
} elseif( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_TEXT, $user ) ) {
- return '';
+ return null;
} else {
- return $this->getRawText();
+ return $this->getContentInternal();
}
}
*
* @return String
*/
- public function getRawText() {
- if( is_null( $this->mText ) ) {
- // Revision text is immutable. Load on demand:
- $this->mText = $this->loadText();
+ public function getRawText() { #FIXME: deprecated, replace usage!
+ return $this->getText( self::RAW );
+ }
+
+ protected function getContentInternal() {
+ if( is_null( $this->mContent ) ) {
+ // Revision is immutable. Load on demand:
+
+ $handler = $this->getContentHandler();
+ $format = $this->getContentFormat();
+ $title = $this->getTitle();
+
+ if( is_null( $this->mText ) ) {
+ // Load text on demand:
+ $this->mText = $this->loadText();
+ }
+
+ $this->mContent = is_null( $this->mText ) ? null : $handler->unserializeContent( $this->mText, $format );
+ }
+
+ return $this->mContent;
+ }
+
+ /**
+ * Returns the content model for this revision.
+ *
+ * If no content model was stored in the database, $this->getTitle()->getContentModel() is
+ * used to determine the content model to use. If no title is know, CONTENT_MODEL_WIKITEXT
+ * is used as a last resort.
+ *
+ * @return int the content model id associated with this revision, see the CONTENT_MODEL_XXX constants.
+ **/
+ public function getContentModel() {
+ if ( !$this->mContentModel ) {
+ $title = $this->getTitle();
+ $this->mContentModel = ( $title ? $title->getContentModel() : CONTENT_MODEL_WIKITEXT );
+
+ assert( !empty( $this->mContentModel ) );
+ }
+
+ return $this->mContentModel;
+ }
+
+ /**
+ * Returns the content format for this revision.
+ *
+ * If no content format was stored in the database, the default format for this
+ * revision's content model is returned.
+ *
+ * @return int the content format id associated with this revision, see the CONTENT_FORMAT_XXX constants.
+ **/
+ public function getContentFormat() {
+ if ( !$this->mContentFormat ) {
+ $handler = $this->getContentHandler();
+ $this->mContentFormat = $handler->getDefaultFormat();
+
+ assert( !empty( $this->mContentFormat ) );
+ }
+
+ return $this->mContentFormat;
+ }
+
+ /**
+ * Returns the content handler appropriate for this revision's content model.
+ *
+ * @return ContentHandler
+ */
+ public function getContentHandler() {
+ if ( !$this->mContentHandler ) {
+ $model = $this->getContentModel();
+ $this->mContentHandler = ContentHandler::getForModelID( $model );
+
+ assert( $this->mContentHandler->isSupportedFormat( $this->getContentFormat() ) );
}
- return $this->mText;
+
+ return $this->mContentHandler;
}
/**
* @return Integer
*/
public function insertOn( $dbw ) {
- global $wgDefaultExternalStore;
+ global $wgDefaultExternalStore, $wgContentHandlerUseDB;
wfProfileIn( __METHOD__ );
$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 )
- ? Revision::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 ) {
+ $row[ 'rev_content_model' ] = $this->getContentModel();
+ $row[ 'rev_content_format' ] = $this->getContentFormat();
+ }
+
+ $dbw->insert( 'revision', $row, __METHOD__ );
+
$this->mId = !is_null( $rev_id ) ? $rev_id : $dbw->insertId();
wfRunHooks( 'RevisionInsertComplete', array( &$this, $data, $flags ) );
* @return Revision|null on error
*/
public static function newNullRevision( $dbw, $pageId, $summary, $minor ) {
+ global $wgContentHandlerUseDB;
+
wfProfileIn( __METHOD__ );
+ $fields = array( 'page_latest', 'page_namespace', 'page_title',
+ 'rev_text_id', 'rev_len', 'rev_sha1' );
+
+ if ( $wgContentHandlerUseDB ) {
+ $fields[] = 'rev_content_model';
+ $fields[] = 'rev_content_format';
+ }
+
$current = $dbw->selectRow(
array( 'page', 'revision' ),
- array( 'page_latest', 'page_namespace', 'page_title',
- 'rev_text_id', 'rev_len', 'rev_sha1' ),
+ $fields,
array(
'page_id' => $pageId,
'page_latest=rev_id',
__METHOD__ );
if( $current ) {
- $revision = new Revision( array(
+ $row = array(
'page' => $pageId,
'comment' => $summary,
'minor_edit' => $minor,
'parent_id' => $current->page_latest,
'len' => $current->rev_len,
'sha1' => $current->rev_sha1
- ) );
+ );
+
+ if ( $wgContentHandlerUseDB ) {
+ $row[ 'content_model' ] = $current->rev_content_model;
+ $row[ 'content_format' ] = $current->rev_content_format;
+ }
+
+ $revision = new Revision( $row );
$revision->setTitle( Title::makeTitle( $current->page_namespace, $current->page_title ) );
} else {
$revision = null;
* @return Array
*/
public function getActionOverrides() {
- return array();
+ $content_handler = $this->getContentHandler();
+ return $content_handler->getActionOverrides();
+ }
+
+ /**
+ * Returns the ContentHandler instance to be used to deal with the content of this WikiPage.
+ *
+ * Shorthand for ContentHandler::getForModelID( $this->getContentModel() );
+ *
+ * @return ContentHandler
+ *
+ * @since 1.WD
+ */
+ public function getContentHandler() {
+ return ContentHandler::getForModelID( $this->getContentModel() );
}
/**
* @return array
*/
public static function selectFields() {
- return array(
+ global $wgContentHandlerUseDB;
+
+ $fields = array(
'page_id',
'page_namespace',
'page_title',
'page_latest',
'page_len',
);
+
+ if ( $wgContentHandlerUseDB ) {
+ $fields[] = 'page_content_model';
+ }
+
+ return $fields;
}
/**
* @return bool
*/
public function isRedirect( $text = false ) {
- if ( $text === false ) {
- if ( !$this->mDataLoaded ) {
- $this->loadPageData();
- }
+ if ( $text === false ) $content = $this->getContent();
+ else $content = ContentHandler::makeContent( $text, $this->mTitle ); # TODO: allow model and format to be provided; or better, expect a Content object
- return (bool)$this->mIsRedirect;
- } else {
- return Title::newFromRedirect( $text ) !== null;
+
+ if ( empty( $content ) ) return false;
+ else return $content->isRedirect();
+ }
+
+ /**
+ * Returns the page's content model id (see the CONTENT_MODEL_XXX constants).
+ *
+ * Will use the revisions actual content model if the page exists,
+ * and the page's default if the page doesn't exist yet.
+ *
+ * @return int
+ *
+ * @since 1.WD
+ */
+ public function getContentModel() {
+ if ( $this->exists() ) {
+ # look at the revision's actual content model
+ $rev = $this->getRevision();
+
+ if ( $rev !== null ) {
+ return $rev->getContentModel();
+ } else {
+ wfWarn( "Page exists but has no revision!" );
+ }
}
+
+ # use the default model for this page
+ return $this->mTitle->getContentModel();
}
/**
}
wfProfileOut( __METHOD__ );
- if ( $row ) {
- return Revision::newFromRow( $row );
- } else {
- return null;
- }
+ return $row ? Revision::newFromRow( $row ) : null;
}
/**
}
/**
- * Get the text of the current revision. No side-effects...
+ * Get the content of the current revision. No side-effects...
*
* @param $audience Integer: one of:
* Revision::FOR_PUBLIC to be displayed to all users
* Revision::FOR_THIS_USER to be displayed to $wgUser
* Revision::RAW get the text regardless of permissions
- * @return String|bool The text of the current revision. False on failure
+ * @return Content|null The content of the current revision
+ *
+ * @since 1.WD
*/
- public function getText( $audience = Revision::FOR_PUBLIC ) {
+ public function getContent( $audience = Revision::FOR_PUBLIC ) {
$this->loadLastEdit();
if ( $this->mLastRevision ) {
- return $this->mLastRevision->getText( $audience );
+ return $this->mLastRevision->getContent( $audience );
}
- return false;
+ return null;
}
/**
* Get the text of the current revision. No side-effects...
*
- * @return String|bool The text of the current revision. False on failure
+ * @param $audience Integer: one of:
+ * Revision::FOR_PUBLIC to be displayed to all users
+ * Revision::FOR_THIS_USER to be displayed to $wgUser
+ * Revision::RAW get the text regardless of permissions
+ * @return String|false The text of the current revision
+ * @deprecated as of 1.WD, getContent() should be used instead.
*/
- public function getRawText() {
+ public function getText( $audience = Revision::FOR_PUBLIC ) { #@todo: deprecated, replace usage!
+ wfDeprecated( __METHOD__, '1.WD' );
+
$this->loadLastEdit();
if ( $this->mLastRevision ) {
- return $this->mLastRevision->getRawText();
+ return $this->mLastRevision->getText( $audience );
}
return false;
}
+ /**
+ * Get the text of the current revision. No side-effects...
+ *
+ * @return String|bool The text of the current revision. False on failure
+ * @deprecated as of 1.WD, getContent() should be used instead.
+ */
+ public function getRawText() { #@todo: deprecated, replace usage!
+ wfDeprecated( __METHOD__, '1.WD' );
+
+ return $this->getText( Revision::RAW );
+ }
+
/**
* @return string MW timestamp of last article revision
*/
if ( !$this->mTimestamp ) {
$this->loadLastEdit();
}
-
+
return wfTimestamp( TS_MW, $this->mTimestamp );
}
return false;
}
- $text = $editInfo ? $editInfo->pst : false;
+ if ( $editInfo ) {
+ $content = $editInfo->pstContent;
+ } else {
+ $content = $this->getContent();
+ }
- if ( $this->isRedirect( $text ) ) {
+ if ( !$content || $content->isRedirect( ) ) {
return false;
}
- switch ( $wgArticleCountMethod ) {
- case 'any':
- return true;
- case 'comma':
- if ( $text === false ) {
- $text = $this->getRawText();
- }
- return strpos( $text, ',' ) !== false;
- case 'link':
+ $hasLinks = null;
+
+ if ( $wgArticleCountMethod === 'link' ) {
+ # nasty special case to avoid re-parsing to detect links
+
if ( $editInfo ) {
// ParserOutput::getLinks() is a 2D array of page links, so
// to be really correct we would need to recurse in the array
// but the main array should only have items in it if there are
// links.
- return (bool)count( $editInfo->output->getLinks() );
+ $hasLinks = (bool)count( $editInfo->output->getLinks() );
} else {
- return (bool)wfGetDB( DB_SLAVE )->selectField( 'pagelinks', 1,
+ $hasLinks = (bool)wfGetDB( DB_SLAVE )->selectField( 'pagelinks', 1,
array( 'pl_from' => $this->getId() ), __METHOD__ );
}
}
+
+ return $content->isCountable( $hasLinks );
}
/**
*/
public function insertRedirect() {
// recurse through to only get the final target
- $retval = Title::newFromRedirectRecurse( $this->getRawText() );
+ $content = $this->getContent();
+ $retval = $content ? $content->getUltimateRedirectTarget() : null;
if ( !$retval ) {
return null;
}
&& $parserOptions->getStubThreshold() == 0
&& $this->mTitle->exists()
&& ( $oldid === null || $oldid === 0 || $oldid === $this->getLatest() )
- && $this->mTitle->isWikitextPage();
+ && $this->getContentHandler()->isParserCacheSupported();
}
/**
* @param $parserOptions ParserOptions to use for the parse operation
* @param $oldid Revision ID to get the text from, passing null or 0 will
* get the current revision (default value)
+ *
* @return ParserOutput or false if the revision was not found
*/
public function getParserOutput( ParserOptions $parserOptions, $oldid = null ) {
$oldid = $this->getLatest();
}
- $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache );
+ $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache, null );
$pool->execute();
wfProfileOut( __METHOD__ );
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
if ( $this->mTitle->exists() ) {
- $text = $this->getRawText();
+ $text = ContentHandler::getContentText( $this->getContent() );
} else {
$text = false;
}
* @private
*/
public function updateRevisionOn( $dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null ) {
+ global $wgContentHandlerUseDB;
+
wfProfileIn( __METHOD__ );
- $text = $revision->getText();
- $len = strlen( $text );
- $rt = Title::newFromRedirectRecurse( $text );
+ $content = $revision->getContent();
+ $len = $content->getSize();
+ $rt = $content->getUltimateRedirectTarget();
$conditions = array( 'page_id' => $this->getId() );
}
$now = wfTimestampNow();
+ $row = array( /* SET */
+ 'page_latest' => $revision->getId(),
+ 'page_touched' => $dbw->timestamp( $now ),
+ 'page_is_new' => ( $lastRevision === 0 ) ? 1 : 0,
+ 'page_is_redirect' => $rt !== null ? 1 : 0,
+ 'page_len' => $len,
+ );
+
+ if ( $wgContentHandlerUseDB ) {
+ $row[ 'page_content_model' ] = $revision->getContentModel();
+ }
+
$dbw->update( 'page',
- array( /* SET */
- 'page_latest' => $revision->getId(),
- 'page_touched' => $dbw->timestamp( $now ),
- 'page_is_new' => ( $lastRevision === 0 ) ? 1 : 0,
- 'page_is_redirect' => $rt !== null ? 1 : 0,
- 'page_len' => $len,
- ),
+ $row,
$conditions,
__METHOD__ );
* @param $undo Revision
* @param $undoafter Revision Must be an earlier revision than $undo
* @return mixed string on success, false on failure
+ * @deprecated since 1.WD: use ContentHandler::getUndoContent() instead.
*/
public function getUndoText( Revision $undo, Revision $undoafter = null ) {
- $cur_text = $this->getRawText();
- if ( $cur_text === false ) {
- return false; // no page
- }
- $undo_text = $undo->getText();
- $undoafter_text = $undoafter->getText();
+ wfDeprecated( __METHOD__, '1.WD' );
- if ( $cur_text == $undo_text ) {
- # No use doing a merge if it's just a straight revert.
- return $undoafter_text;
- }
+ $this->loadLastEdit();
- $undone_text = '';
+ if ( $this->mLastRevision ) {
+ if ( is_null( $undoafter ) ) {
+ $undoafter = $undo->getPrevious();
+ }
- if ( !wfMerge( $undo_text, $undoafter_text, $cur_text, $undone_text ) ) {
- return false;
+ $handler = $this->getContentHandler();
+ $undone = $handler->getUndoContent( $this->mLastRevision, $undo, $undoafter );
+
+ if ( !$undone ) {
+ return false;
+ } else {
+ return ContentHandler::getContentText( $undone );
+ }
}
- return $undone_text;
+ return false;
}
/**
* @param $text String: new text of the section
* @param $sectionTitle String: new section's subject, only if $section is 'new'
* @param $edittime String: revision timestamp or null to use the current revision
- * @return string Complete article text, or null if error
+ * @return String new complete article text, or null if error
+ *
+ * @deprecated since 1.WD, use replaceSectionContent() instead
*/
public function replaceSection( $section, $text, $sectionTitle = '', $edittime = null ) {
+ wfDeprecated( __METHOD__, '1.WD' );
+
+ $sectionContent = ContentHandler::makeContent( $text, $this->getTitle() ); #XXX: could make section title, but that's not required.
+
+ $newContent = $this->replaceSectionContent( $section, $sectionContent, $sectionTitle, $edittime );
+
+ return ContentHandler::getContentText( $newContent ); #XXX: unclear what will happen for non-wikitext!
+ }
+
+ /**
+ * @param $section null|bool|int or a section number (0, 1, 2, T1, T2...)
+ * @param $content Content: new content of the section
+ * @param $sectionTitle String: new section's subject, only if $section is 'new'
+ * @param $edittime String: revision timestamp or null to use the current revision
+ *
+ * @return Content new complete article content, or null if error
+ *
+ * @since 1.WD
+ */
+ public function replaceSectionContent( $section, Content $sectionContent, $sectionTitle = '', $edittime = null ) {
wfProfileIn( __METHOD__ );
if ( strval( $section ) == '' ) {
// Whole-page edit; let the whole text through
+ $newContent = $sectionContent;
} else {
// Bug 30711: always use current version when adding a new section
if ( is_null( $edittime ) || $section == 'new' ) {
- $oldtext = $this->getRawText();
- if ( $oldtext === false ) {
+ $oldContent = $this->getContent();
+ if ( ! $oldContent ) {
wfDebug( __METHOD__ . ": no page text\n" );
wfProfileOut( __METHOD__ );
return null;
return null;
}
- $oldtext = $rev->getText();
+ $oldContent = $rev->getContent();
}
- if ( $section == 'new' ) {
- # Inserting a new section
- $subject = $sectionTitle ? wfMsgForContent( 'newsectionheaderdefaultlevel', $sectionTitle ) . "\n\n" : '';
- if ( wfRunHooks( 'PlaceNewSection', array( $this, $oldtext, $subject, &$text ) ) ) {
- $text = strlen( trim( $oldtext ) ) > 0
- ? "{$oldtext}\n\n{$subject}{$text}"
- : "{$subject}{$text}";
- }
- } else {
- # Replacing an existing section; roll out the big guns
- global $wgParser;
-
- $text = $wgParser->replaceSection( $oldtext, $section, $text );
- }
+ $newContent = $oldContent->replaceSection( $section, $sectionContent, $sectionTitle );
}
wfProfileOut( __METHOD__ );
- return $text;
+ return $newContent;
}
/**
* revision: The revision object for the inserted revision, or null
*
* Compatibility note: this function previously returned a boolean value indicating success/failure
+ *
+ * @deprecated since 1.WD: use doEditContent() instead.
*/
- public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
+ public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) { #@todo: use doEditContent() instead
+ wfDeprecated( __METHOD__, '1.WD' );
+
+ $content = ContentHandler::makeContent( $text, $this->getTitle() );
+
+ return $this->doEditContent( $content, $summary, $flags, $baseRevId, $user );
+ }
+
+ /**
+ * Change an existing article or create a new article. Updates RC and all necessary caches,
+ * optionally via the deferred update array.
+ *
+ * @param $content Content: new content
+ * @param $summary String: edit summary
+ * @param $flags Integer bitfield:
+ * EDIT_NEW
+ * Article is known or assumed to be non-existent, create a new one
+ * EDIT_UPDATE
+ * Article is known or assumed to be pre-existing, update it
+ * EDIT_MINOR
+ * Mark this edit minor, if the user is allowed to do so
+ * EDIT_SUPPRESS_RC
+ * Do not log the change in recentchanges
+ * EDIT_FORCE_BOT
+ * Mark the edit a "bot" edit regardless of user rights
+ * EDIT_DEFER_UPDATES
+ * Defer some of the updates until the end of index.php
+ * EDIT_AUTOSUMMARY
+ * Fill in blank summaries with generated text where possible
+ *
+ * If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected.
+ * If EDIT_UPDATE is specified and the article doesn't exist, the function will return an
+ * edit-gone-missing error. If EDIT_NEW is specified and the article does exist, an
+ * 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 the revision ID this edit was based off, if any
+ * @param $user User the user doing the edit
+ * @param $serialisation_format String: format for storing the content in the database
+ *
+ * @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
+ * edit-conflict: In update mode, the article changed unexpectedly
+ * edit-no-change: Warning that the text was the same as before
+ * edit-already-exists: In creation mode, but the article already exists
+ *
+ * Extensions may define additional errors.
+ *
+ * $return->value will contain an associative array with members as follows:
+ * new: Boolean indicating if the function attempted to create a new article
+ * revision: The revision object for the inserted revision, or null
+ *
+ * @since 1.WD
+ */
+ public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
+ User $user = null, $serialisation_format = null ) {
global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
# Low-level sanity check
$flags = $this->checkFlags( $flags );
- if ( !wfRunHooks( 'ArticleSave', array( &$this, &$user, &$text, &$summary,
- $flags & EDIT_MINOR, null, null, &$flags, &$status ) ) )
- {
- wfDebug( __METHOD__ . ": ArticleSave hook aborted save!\n" );
+ # call legacy hook
+ $hook_ok = wfRunHooks( 'ArticleContentSave', array( &$this, &$user, &$content, &$summary,
+ $flags & EDIT_MINOR, null, null, &$flags, &$status ) );
+
+ if ( $hook_ok && !empty( $wgHooks['ArticleSave'] ) ) { # avoid serialization overhead if the hook isn't present
+ $content_text = $content->serialize();
+ $txt = $content_text; # clone
+
+ $hook_ok = wfRunHooks( 'ArticleSave', array( &$this, &$user, &$txt, &$summary,
+ $flags & EDIT_MINOR, null, null, &$flags, &$status ) );
+
+ 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() ) {
$status->fatal( 'edit-hook-aborted' );
$isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' );
$bot = $flags & EDIT_FORCE_BOT;
- $oldtext = $this->getRawText(); // current revision
- $oldsize = strlen( $oldtext );
+ $old_content = $this->getContent( Revision::RAW ); // current revision's content
+
+ $oldsize = $old_content ? $old_content->getSize() : 0;
$oldid = $this->getLatest();
$oldIsRedirect = $this->isRedirect();
$oldcountable = $this->isCountable();
+ $handler = $content->getContentHandler();
+
# Provide autosummaries if one is not provided and autosummaries are enabled.
if ( $wgUseAutomaticEditSummaries && $flags & EDIT_AUTOSUMMARY && $summary == '' ) {
- $summary = self::getAutosummary( $oldtext, $text, $flags );
+ if ( !$old_content ) $old_content = null;
+ $summary = $handler->getAutosummary( $old_content, $content, $flags );
}
- $editInfo = $this->prepareTextForEdit( $text, null, $user );
- $text = $editInfo->pst;
- $newsize = strlen( $text );
+ $editInfo = $this->prepareContentForEdit( $content, null, $user, $serialisation_format );
+ $serialized = $editInfo->pst;
+ $content = $editInfo->pstContent;
+ $newsize = $content->getSize();
$dbw = wfGetDB( DB_MASTER );
$now = wfTimestampNow();
'page' => $this->getId(),
'comment' => $summary,
'minor_edit' => $isminor,
- 'text' => $text,
+ 'text' => $serialized,
+ 'len' => $newsize,
'parent_id' => $oldid,
'user' => $user->getId(),
'user_text' => $user->getName(),
- 'timestamp' => $now
+ 'timestamp' => $now,
+ 'content_model' => $content->getModel(),
+ 'content_format' => $serialisation_format,
) );
- $changed = ( strcmp( $text, $oldtext ) != 0 );
+ $changed = !$content->equals( $old_content );
if ( $changed ) {
+ // TODO: validate!
+ if ( $content->isValid() ) {
+
+ }
+
$dbw->begin( __METHOD__ );
$revisionId = $revision->insertOn( $dbw );
return $status;
}
+ // TODO: create content diff to pass to update objects that might need it
+
# Update links tables, site stats, etc.
- $this->doEditUpdates( $revision, $user, array( 'changed' => $changed,
- 'oldcountable' => $oldcountable ) );
+ $this->doEditUpdates(
+ $revision,
+ $user,
+ array(
+ 'changed' => $changed,
+ 'oldcountable' => $oldcountable
+ )
+ );
if ( !$changed ) {
$status->warning( 'edit-no-change' );
'page' => $newid,
'comment' => $summary,
'minor_edit' => $isminor,
- 'text' => $text,
+ 'text' => $serialized,
+ 'len' => $newsize,
'user' => $user->getId(),
'user_text' => $user->getName(),
- 'timestamp' => $now
+ 'timestamp' => $now,
+ 'content_model' => $content->getModel(),
+ 'content_format' => $serialisation_format,
) );
$revisionId = $revision->insertOn( $dbw );
$this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
# Add RC row to the DB
$rc = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $user, $summary, $bot,
- '', strlen( $text ), $revisionId, $patrolled );
+ '', $content->getSize(), $revisionId, $patrolled );
# Log auto-patrolled edits
if ( $patrolled ) {
# Update links, etc.
$this->doEditUpdates( $revision, $user, array( 'created' => true ) );
- wfRunHooks( 'ArticleInsertComplete', array( &$this, &$user, $text, $summary,
+ wfRunHooks( 'ArticleInsertComplete', array( &$this, &$user, $serialized, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+
+ wfRunHooks( 'ArticleContentInsertComplete', array( &$this, &$user, $content, $summary,
$flags & EDIT_MINOR, null, null, &$flags, $revision ) );
}
// Return the new revision (or null) to the caller
$status->value['revision'] = $revision;
- wfRunHooks( 'ArticleSaveComplete', array( &$this, &$user, $text, $summary,
+ wfRunHooks( 'ArticleSaveComplete', array( &$this, &$user, $serialized, $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 ) );
# Promote user to any groups they meet the criteria for
/**
* Prepare text which is about to be saved.
* Returns a stdclass with source, pst and output members
- * @return bool|object
+ *
+ * @deprecated in 1.WD: use prepareContentForEdit instead.
*/
public function prepareTextForEdit( $text, $revid = null, User $user = null ) {
+ wfDeprecated( __METHOD__, '1.WD' );
+ $content = ContentHandler::makeContent( $text, $this->getTitle() );
+ return $this->prepareContentForEdit( $content, $revid , $user );
+ }
+
+ /**
+ * Prepare content which is about to be saved.
+ * Returns a stdclass with source, pst and output members
+ *
+ * @param \Content $content
+ * @param null $revid
+ * @param null|\User $user
+ * @param null $serialization_format
+ *
+ * @return bool|object
+ *
+ * @since 1.WD
+ */
+ public function prepareContentForEdit( Content $content, $revid = null, User $user = null, $serialization_format = null ) {
global $wgParser, $wgContLang, $wgUser;
$user = is_null( $user ) ? $wgUser : $user;
// @TODO fixme: check $user->getId() here???
+
if ( $this->mPreparedEdit
- && $this->mPreparedEdit->newText == $text
+ && $this->mPreparedEdit->newContent
+ && $this->mPreparedEdit->newContent->equals( $content )
&& $this->mPreparedEdit->revid == $revid
+ && $this->mPreparedEdit->format == $serialization_format
+ #XXX: also check $user here?
) {
// Already prepared
return $this->mPreparedEdit;
$edit = (object)array();
$edit->revid = $revid;
- $edit->newText = $text;
- $edit->pst = $wgParser->preSaveTransform( $text, $this->mTitle, $user, $popts );
+
+ $edit->pstContent = $content->preSaveTransform( $this->mTitle, $user, $popts );
+ $edit->pst = $edit->pstContent->serialize( $serialization_format );
+ $edit->format = $serialization_format;
+
$edit->popts = $this->makeParserOptions( 'canonical' );
- $edit->output = $wgParser->parse( $edit->pst, $this->mTitle, $edit->popts, true, true, $revid );
- $edit->oldText = $this->getRawText();
+
+ $edit->output = $edit->pstContent->getParserOutput( $this->mTitle, $revid, $edit->popts );
+
+ $edit->newContent = $content;
+ $edit->oldContent = $this->getContent( Revision::RAW );
+
+ $edit->newText = ContentHandler::getContentText( $edit->newContent ); #FIXME: B/C only! don't use this field!
+ $edit->oldText = $edit->oldContent ? ContentHandler::getContentText( $edit->oldContent ) : ''; #FIXME: B/C only! don't use this field!
$this->mPreparedEdit = $edit;
* Purges pages that include this page if the text was changed here.
* Every 100th edit, prune the recent changes table.
*
- * @private
* @param $revision Revision object
* @param $user User object that did the revision
* @param $options Array of options, following indexes are used:
wfProfileIn( __METHOD__ );
$options += array( 'changed' => true, 'created' => false, 'oldcountable' => null );
- $text = $revision->getText();
+ $content = $revision->getContent();
# Parse the text
# Be careful not to double-PST: $text is usually already PST-ed once
if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" );
- $editInfo = $this->prepareTextForEdit( $text, $revision->getId(), $user );
+ $editInfo = $this->prepareContentForEdit( $content, $revision->getId(), $user );
} else {
wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" );
$editInfo = $this->mPreparedEdit;
}
# Update the links tables and other secondary data
- $updates = $editInfo->output->getSecondaryDataUpdates( $this->mTitle );
+ $updates = $editInfo->output->getSecondaryDataUpdates( $this->getTitle() );
DataUpdate::runUpdates( $updates );
wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) );
}
DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, $good, $total ) );
- DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $text ) );
+ DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content->getTextForSearchIndex() ) );
# If this is another user's talk page, update newtalk.
# Don't do this if $options['changed'] = false (null-edits) nor if
}
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- MessageCache::singleton()->replace( $shortTitle, $text );
+ $msgtext = ContentHandler::getContentText( $content ); #XXX: could skip pseudo-messages like js/css here, based on content model.
+ if ( $msgtext === false || $msgtext === null ) $msgtext = '';
+
+ MessageCache::singleton()->replace( $shortTitle, $msgtext );
}
if( $options['created'] ) {
* @param $user User The relevant user
* @param $comment String: comment submitted
* @param $minor Boolean: whereas it's a minor modification
+ *
+ * @deprecated since 1.WD, use doEditContent() instead.
*/
public function doQuickEdit( $text, User $user, $comment = '', $minor = 0 ) {
+ wfDeprecated( __METHOD__, "1.WD" );
+
+ $content = ContentHandler::makeContent( $text, $this->getTitle() );
+ return $this->doQuickEditContent( $content, $user, $comment , $minor );
+ }
+
+ /**
+ * Edit an article without doing all that other stuff
+ * The article must already exist; link tables etc
+ * are not updated, caches are not flushed.
+ *
+ * @param $content Content: content submitted
+ * @param $user User The relevant user
+ * @param $comment String: comment submitted
+ * @param $serialisation_format String: format for storing the content in the database
+ * @param $minor Boolean: whereas it's a minor modification
+ */
+ public function doQuickEditContent( Content $content, User $user, $comment = '', $minor = 0, $serialisation_format = null ) {
wfProfileIn( __METHOD__ );
+ $serialized = $content->serialize( $serialisation_format );
+
$dbw = wfGetDB( DB_MASTER );
$revision = new Revision( array(
'page' => $this->getId(),
- 'text' => $text,
+ 'text' => $serialized,
+ 'length' => $content->getSize(),
'comment' => $comment,
'minor_edit' => $minor ? 1 : 0,
) );
public function doDeleteArticleReal(
$reason, $suppress = false, $id = 0, $commit = true, &$error = '', User $user = null
) {
- global $wgUser;
+ global $wgUser, $wgContentHandlerUseDB;
wfDebug( __METHOD__ . "\n" );
//
// In the future, we may keep revisions and mark them with
// the rev_deleted field, which is reserved for this purpose.
+
+ $row = array(
+ 'ar_namespace' => 'page_namespace',
+ 'ar_title' => 'page_title',
+ 'ar_comment' => 'rev_comment',
+ 'ar_user' => 'rev_user',
+ 'ar_user_text' => 'rev_user_text',
+ 'ar_timestamp' => 'rev_timestamp',
+ 'ar_minor_edit' => 'rev_minor_edit',
+ 'ar_rev_id' => 'rev_id',
+ 'ar_parent_id' => 'rev_parent_id',
+ 'ar_text_id' => 'rev_text_id',
+ 'ar_text' => '\'\'', // Be explicit to appease
+ 'ar_flags' => '\'\'', // MySQL's "strict mode"...
+ 'ar_len' => 'rev_len',
+ 'ar_page_id' => 'page_id',
+ 'ar_deleted' => $bitfield,
+ 'ar_sha1' => 'rev_sha1',
+ );
+
+ if ( $wgContentHandlerUseDB ) {
+ $row[ 'ar_content_model' ] = 'rev_content_model';
+ $row[ 'ar_content_format' ] = 'rev_content_format';
+ }
+
$dbw->insertSelect( 'archive', array( 'page', 'revision' ),
+ $row,
array(
- 'ar_namespace' => 'page_namespace',
- 'ar_title' => 'page_title',
- 'ar_comment' => 'rev_comment',
- 'ar_user' => 'rev_user',
- 'ar_user_text' => 'rev_user_text',
- 'ar_timestamp' => 'rev_timestamp',
- 'ar_minor_edit' => 'rev_minor_edit',
- 'ar_rev_id' => 'rev_id',
- 'ar_parent_id' => 'rev_parent_id',
- 'ar_text_id' => 'rev_text_id',
- 'ar_text' => '\'\'', // Be explicit to appease
- 'ar_flags' => '\'\'', // MySQL's "strict mode"...
- 'ar_len' => 'rev_len',
- 'ar_page_id' => 'page_id',
- 'ar_deleted' => $bitfield,
- 'ar_sha1' => 'rev_sha1'
- ), array(
'page_id' => $id,
'page_id = rev_page'
), __METHOD__
$this->mTitle->resetArticleID( 0 );
}
- public function getDeletionUpdates() {
- $updates = array(
- new LinksDeletionUpdate( $this ),
- );
-
- //@todo: make a hook to add update objects
- //NOTE: deletion updates will be determined by the ContentHandler in the future
- return $updates;
- }
-
/**
* Roll back the most recent consecutive set of edits to a page
* from the same user; fails if there are no eligible edits to
}
# Actually store the edit
- $status = $this->doEdit( $target->getText(), $summary, $flags, $target->getId(), $guser );
+ $status = $this->doEditContent( $target->getContent(), $summary, $flags, $target->getId(), $guser );
if ( !empty( $status->value['revision'] ) ) {
$revId = $status->value['revision']->getId();
} else {
/**
* Return an applicable autosummary if one exists for the given edit.
- * @param $oldtext String: the previous text of the page.
- * @param $newtext String: The submitted text of the page.
+ * @param $oldtext String|null: the previous text of the page.
+ * @param $newtext String|null: The submitted text of the page.
* @param $flags Int bitmask: a bitmask of flags submitted for the edit.
* @return string An appropriate autosummary, or an empty string.
+ * @deprecated since 1.WD, use ContentHandler::getAutosummary() instead
*/
public static function getAutosummary( $oldtext, $newtext, $flags ) {
- global $wgContLang;
-
- # Decide what kind of autosummary is needed.
-
- # Redirect autosummaries
- $ot = Title::newFromRedirect( $oldtext );
- $rt = Title::newFromRedirect( $newtext );
-
- if ( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) {
- $truncatedtext = $wgContLang->truncate(
- str_replace( "\n", ' ', $newtext ),
- max( 0, 250
- - strlen( wfMsgForContent( 'autoredircomment' ) )
- - strlen( $rt->getFullText() )
- ) );
- return wfMsgForContent( 'autoredircomment', $rt->getFullText(), $truncatedtext );
- }
-
- # New page autosummaries
- if ( $flags & EDIT_NEW && strlen( $newtext ) ) {
- # If they're making a new article, give its text, truncated, in the summary.
-
- $truncatedtext = $wgContLang->truncate(
- str_replace( "\n", ' ', $newtext ),
- max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new' ) ) ) );
-
- return wfMsgForContent( 'autosumm-new', $truncatedtext );
- }
+ # NOTE: stub for backwards-compatibility. assumes the given text is wikitext. will break horribly if it isn't.
- # Blanking autosummaries
- if ( $oldtext != '' && $newtext == '' ) {
- return wfMsgForContent( 'autosumm-blank' );
- } elseif ( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500 ) {
- # Removing more than 90% of the article
+ wfDeprecated( __METHOD__, '1.WD' );
- $truncatedtext = $wgContLang->truncate(
- $newtext,
- max( 0, 200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) ) );
+ $handler = ContentHandler::getForModelID( CONTENT_MODEL_WIKITEXT );
+ $oldContent = is_null( $oldtext ) ? null : $handler->unserializeContent( $oldtext );
+ $newContent = is_null( $newtext ) ? null : $handler->unserializeContent( $newtext );
- return wfMsgForContent( 'autosumm-replace', $truncatedtext );
- }
-
- # If we reach this point, there's no applicable autosummary for our case, so our
- # autosummary is empty.
- return '';
+ return $handler->getAutosummary( $oldContent, $newContent, $flags );
}
/**
* @param &$hasHistory Boolean: whether the page has a history
* @return mixed String containing deletion reason or empty string, or boolean false
* if no revision occurred
+ * @deprecated since 1.WD, use ContentHandler::getAutoDeleteReason() instead
*/
public function getAutoDeleteReason( &$hasHistory ) {
+ #NOTE: stub for backwards-compatibility.
+
+ wfDeprecated( __METHOD__, '1.WD' );
+
+ $handler = ContentHandler::getForTitle( $this->getTitle() );
+ $handler->getAutoDeleteReason( $this->getTitle(), $hasHistory );
global $wgContLang;
// Get the last revision
global $wgUser;
return $this->isParserCacheUsed( ParserOptions::newFromUser( $wgUser ), $oldid );
}
+
+ public function getDeletionUpdates() {
+ $updates = $this->getContentHandler()->getDeletionUpdates( $this );
+
+ wfRunHooks( 'WikiPageDeletionUpdates', array( $this, &$updates ) );
+ return $updates;
+ }
++
}
class PoolWorkArticleView extends PoolCounterWork {
private $parserOptions;
/**
- * @var string|null
+ * @var Content|null
*/
- private $text;
+ private $content = null;
/**
* @var ParserOutput|bool
* @param $revid Integer: ID of the revision being parsed
* @param $useParserCache Boolean: whether to use the parser cache
* @param $parserOptions parserOptions to use for the parse operation
- * @param $text String: text to parse or null to load it
+ * @param $content Content|String: content to parse or null to load it; may also be given as a wikitext string, for BC
*/
- function __construct( Page $page, ParserOptions $parserOptions, $revid, $useParserCache, $text = null ) {
+ function __construct( Page $page, ParserOptions $parserOptions, $revid, $useParserCache, $content = null ) {
+ if ( is_string($content) ) { #BC: old style call
+ $modelId = $page->getRevision()->getContentModel();
+ $format = $page->getRevision()->getContentFormat();
+ $content = ContentHandler::makeContent( $content, $page->getTitle(), $modelId, $format );
+ }
+
$this->page = $page;
$this->revid = $revid;
$this->cacheable = $useParserCache;
$this->parserOptions = $parserOptions;
- $this->text = $text;
+ $this->content = $content;
$this->cacheKey = ParserCache::singleton()->getKey( $page, $parserOptions );
parent::__construct( 'ArticleView', $this->cacheKey . ':revid:' . $revid );
}
* @return bool
*/
function doWork() {
- global $wgParser, $wgUseFileCache;
+ global $wgUseFileCache;
+
+ // @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->text !== null ) {
- $text = $this->text;
+ if ( $this->content !== null ) {
+ $content = $this->content;
} elseif ( $isCurrent ) {
- $text = $this->page->getRawText();
+ $content = $this->page->getContent( Revision::RAW ); #XXX: why use RAW audience here, and PUBLIC (default) below?
} else {
$rev = Revision::newFromTitle( $this->page->getTitle(), $this->revid );
if ( $rev === null ) {
return false;
}
- $text = $rev->getText();
+ $content = $rev->getContent(); #XXX: why use PUBLIC audience here (default), and RAW above?
}
$time = - microtime( true );
- $this->parserOutput = $wgParser->parse( $text, $this->page->getTitle(),
- $this->parserOptions, true, true, $this->revid );
+ // TODO: page might not have this method? Hard to tell what page is supposed to be here...
+ $this->parserOutput = $content->getParserOutput( $this->page->getTitle(), $this->revid, $this->parserOptions );
$time += microtime( true );
# Timing hack
return false;
}
}
+
*
* Based on HistoryPage and SpecialExport
*
- * License: GPL (http://www.gnu.org/copyleft/gpl.html)
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
* @author Gabriel Wicke <wicke@wikidev.net>
* @file
$request->response()->header( "Last-modified: $lastmod" );
// Public-only due to cache headers
- $text = $rev->getText();
+ $content = $rev->getContent();
+
+ if ( !$content instanceof TextContent ) {
+ wfHttpError( 406, "Not Acceptable", "The requeste page uses the content model `"
+ . $content->getModel() . "` which is not supported via this interface." );
+ die();
+ }
+
$section = $request->getIntOrNull( 'section' );
if ( $section !== null ) {
- $text = $wgParser->getSection( $text, $section );
+ $content = $content->getSection( $section );
}
+
+ $text = $content->getNativeData();
}
}
$popts = ParserOptions::newFromContext( $this->getContext() );
$popts->setTidy( true );
$p_result = $wgParser->parse( $page->getRawText(), $title, $popts,
- true, true, $page->getLatest() );
+ true, true, $page->getLatest() ); #FIXME: content!
# Update the links tables
$updates = $p_result->getSecondaryDataUpdates( $title );
$pcache->save( $p_result, $page, $popts );
}
} else {
- $this->setWarning( $this->parseMsg( array( 'actionthrottledtext' ) ) );
+ $error = $this->parseMsg( array( 'actionthrottledtext' ) );
+ $this->setWarning( $error['info'] );
$forceLinkUpdate = false;
}
}
* @return ResultWrapper
*/
function listRevisions() {
+ global $wgContentHandlerNoDB;
+
$dbr = wfGetDB( DB_SLAVE );
+
+ $fields = array(
+ 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text',
+ 'ar_comment', 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1',
+ );
+
+ if ( !$wgContentHandlerNoDB ) {
+ $fields[] = 'ar_content_format';
+ $fields[] = 'ar_content_model';
+ }
+
$res = $dbr->select( 'archive',
- array(
- 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text',
- 'ar_comment', 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1'
- ),
+ $fields,
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey() ),
'PageArchive::listRevisions',
* @return Revision
*/
function getRevision( $timestamp ) {
+ global $wgContentHandlerNoDB;
+
$dbr = wfGetDB( DB_SLAVE );
+
+ $fields = array(
+ 'ar_rev_id',
+ 'ar_text',
+ 'ar_comment',
+ 'ar_user',
+ 'ar_user_text',
+ 'ar_timestamp',
+ 'ar_minor_edit',
+ 'ar_flags',
+ 'ar_text_id',
+ 'ar_deleted',
+ 'ar_len',
+ 'ar_sha1',
+ );
+
+ if ( !$wgContentHandlerNoDB ) {
+ $fields[] = 'ar_content_format';
+ $fields[] = 'ar_content_model';
+ }
+
$row = $dbr->selectRow( 'archive',
- array(
- 'ar_rev_id',
- 'ar_text',
- 'ar_comment',
- 'ar_user',
- 'ar_user_text',
- 'ar_timestamp',
- 'ar_minor_edit',
- 'ar_flags',
- 'ar_text_id',
- 'ar_deleted',
- 'ar_len',
- 'ar_sha1',
- ),
+ $fields,
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey(),
'ar_timestamp' => $dbr->timestamp( $timestamp ) ),
* @return Mixed: number of revisions restored or false on failure
*/
private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) {
+ global $wgContentHandlerNoDB;
+
if ( wfReadOnly() ) {
return false;
}
$oldones = "ar_timestamp IN ( {$oldts} )";
}
+ $fields = array(
+ 'ar_rev_id',
+ 'ar_text',
+ 'ar_comment',
+ 'ar_user',
+ 'ar_user_text',
+ 'ar_timestamp',
+ 'ar_minor_edit',
+ 'ar_flags',
+ 'ar_text_id',
+ 'ar_deleted',
+ 'ar_page_id',
+ 'ar_len',
+ 'ar_sha1');
+
+ if ( !$wgContentHandlerNoDB ) {
+ $fields[] = 'ar_content_format';
+ $fields[] = 'ar_content_model';
+ }
+
/**
* Select each archived revision...
*/
$result = $dbw->select( 'archive',
- /* fields */ array(
- 'ar_rev_id',
- 'ar_text',
- 'ar_comment',
- 'ar_user',
- 'ar_user_text',
- 'ar_timestamp',
- 'ar_minor_edit',
- 'ar_flags',
- 'ar_text_id',
- 'ar_deleted',
- 'ar_page_id',
- 'ar_len',
- 'ar_sha1' ),
+ $fields,
/* WHERE */ array(
'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey(),
* @return String: HTML
*/
function showDiff( $previousRev, $currentRev ) {
- $diffEngine = new DifferenceEngine( $this->getContext() );
+ $contentHandler = ContentHandler::getForTitle( $this->getTitle() );
+ $diffEngine = $contentHandler->createDifferenceEngine( $this->getContext() );
$diffEngine->showDiffStyle();
$this->getOutput()->addHTML(
"<div>" .
$this->diffHeader( $currentRev, 'n' ) .
"</td>\n" .
"</tr>" .
- $diffEngine->generateDiffBody(
- $previousRev->getText(), $currentRev->getText() ) .
+ $diffEngine->generateContentDiffBody(
+ $previousRev->getContent(), $currentRev->getContent() ) .
"</table>" .
"</div>\n"
);
private function formatRevisionRow( $row, $earliestLiveTime, $remaining ) {
$rev = Revision::newFromArchiveRow( $row,
array( 'page' => $this->mTargetObj->getArticleID() ) );
- $stxt = '';
+ $revTextSize = '';
$ts = wfTimestamp( TS_MW, $row->ar_timestamp );
// Build checkboxen...
if( $this->mAllowed ) {
// Revision text size
$size = $row->ar_len;
if( !is_null( $size ) ) {
- $stxt = Linker::formatRevisionSize( $size );
+ $revTextSize = Linker::formatRevisionSize( $size );
}
// Edit summary
$comment = Linker::revComment( $rev );
// Revision delete links
$revdlink = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj );
- return "<li>$checkBox $revdlink ($last) $pageLink . . $userLink $stxt $comment</li>";
+
+ $revisionRow = $this->msg( 'undelete-revisionrow' )->rawParams( $checkBox, $revdlink, $last, $pageLink , $userLink, $revTextSize, $comment )->escaped();
+ return "<li>$revisionRow</li>";
}
private function formatFileRow( $row ) {
}
if ( !defined( 'MW_COMPILED' ) ) {
- // Preload base classes to work around APC/PHP5 bug
- if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) {
- include_once( "$IP/languages/classes/$class.deps.php" );
- }
if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
include_once( "$IP/languages/classes/$class.php" );
}
*/
public function setNamespaces( array $namespaces ) {
$this->namespaceNames = $namespaces;
+ $this->mNamespaceIds = null;
+ }
+
+ /**
+ * Resets all of the namespace caches. Mainly used for testing
+ */
+ public function resetNamespaces( ) {
+ $this->namespaceNames = null;
+ $this->mNamespaceIds = null;
+ $this->namespaceAliases = null;
}
/**
$mwNames = $wgExtraLanguageNames + $coreLanguageNames;
foreach ( $mwNames as $mwCode => $mwName ) {
# - Prefer own MediaWiki native name when not using the hook
- # TODO: prefer it always to make it consistent, but casing is different in CLDR
# - For other names just add if not added through the hook
- if ( ( $mwCode === $inLanguage && !$inLanguage ) || !isset( $names[$mwCode] ) ) {
+ if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
$names[$mwCode] = $mwName;
}
}
return $s;
}
- $isutf8 = preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
- '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s );
+ if ( function_exists( 'mb_check_encoding' ) ) {
+ $isutf8 = mb_check_encoding( $s, 'UTF-8' );
+ } else {
+ $isutf8 = preg_match( '/^(?>[\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
+ '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s );
+ }
if ( $isutf8 ) {
return $s;
}
* Customisable syntax for wikitext and elsewhere.
*
* IDs must be valid identifiers, they cannot contain hyphens.
+ * CASE is 0 to match all case variants, 1 for case-sensitive
*
* Note to translators:
* Please include the English words as synonyms. This allows people
'index-category' => 'Indexed pages',
'noindex-category' => 'Noindexed pages',
'broken-file-category' => 'Pages with broken file links',
+ 'categoryviewer-pagedlinks' => '($1) ($2)',
'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD', # only translate this message to other languages if you have to change it
'portal-url' => 'Project:Community portal',
'privacy' => 'Privacy policy',
'privacypage' => 'Project:Privacy policy',
+'content-failed-to-parse' => "Failed to parse $2 content for $1 model: $3",
'badaccess' => 'Permission error',
'badaccess-group0' => 'You are not allowed to execute the action you have requested.',
'mergehistory-comment' => 'Merged [[:$1]] into [[:$2]]: $3',
'mergehistory-same-destination' => 'Source and destination pages cannot be the same',
'mergehistory-reason' => 'Reason:',
+ 'mergehistory-revisionrow' => '$1 ($2) $3 . . $4 $5 $6',
# Merge log
'mergelog' => 'Merge log',
'pubmedurl' => '//www.ncbi.nlm.nih.gov/pubmed/$1?dopt=Abstract', # do not translate or duplicate this message to other languages
# Special:Log
- 'specialloguserlabel' => 'Performer:',
- 'speciallogtitlelabel' => 'Target (title or user):',
- 'log' => 'Logs',
- 'all-logs-page' => 'All public logs',
- 'alllogstext' => 'Combined display of all available logs of {{SITENAME}}.
+ 'specialloguserlabel' => 'Performer:',
+ 'speciallogtitlelabel' => 'Target (title or user):',
+ 'log' => 'Logs',
+ 'all-logs-page' => 'All public logs',
+ 'alllogstext' => 'Combined display of all available logs of {{SITENAME}}.
You can narrow down the view by selecting a log type, the username (case-sensitive), or the affected page (also case-sensitive).',
- 'logempty' => 'No matching items in log.',
- 'log-title-wildcard' => 'Search titles starting with this text',
+ 'logempty' => 'No matching items in log.',
+ 'log-title-wildcard' => 'Search titles starting with this text',
+ 'showhideselectedlogentries' => 'Show/hide selected log entries',
# Special:AllPages
'allpages' => 'All pages',
$1',
'undelete-show-file-confirm' => 'Are you sure you want to view the deleted revision of the file "<nowiki>$1</nowiki>" from $2 at $3?',
'undelete-show-file-submit' => 'Yes',
+ 'undelete-revisionrow' => "$1 $2 $3 $4 . . $5 $6 $7",
# Namespace form on various pages
'namespace' => 'Namespace:',
'ellipsis' => '...', # only translate this message to other languages if you have to change it
'percent' => '$1%', # only translate this message to other languages if you have to change it
'parentheses' => '($1)', # only translate this message to other languages if you have to change it
+ 'brackets' => '[$1]', # only translate this message to other languages if you have to change it
# Multipage image navigation
'imgmultipageprev' => '← previous page',
'duration-centuries' => '$1 {{PLURAL:$1|century|centuries}}',
'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennia}}',
+# Content model IDs for the ContentHandler facility; used by ContentHander::getContentModel()
+'content-model-1' => 'wikitext',
+'content-model-2' => 'JavaScript',
+'content-model-3' => 'CSS',
+'content-model-4' => 'plain text',
+
);
'index-category' => 'Name of the [[mw:Help:Tracking categories|tracking category]] where pages with the <nowiki>__INDEX__</nowiki> behaviour switch are listed. For description of this behaviour switch see [//www.mediawiki.org/wiki/Help:Magic_words#Behavior_switches mediawiki].',
'noindex-category' => 'Name of the [[mw:Help:Tracking categories|tracking category]] where pages with the <nowiki>__NOINDEX__</nowiki> behaviour switch are listed. For description of this behaviour switch see [//www.mediawiki.org/wiki/Help:Magic_words#Behavior_switches mediawiki].',
'broken-file-category' => 'Name of [[mw:Help:Tracking categories|tracking category]] where pages that embed files that do not exist ("broken images") are listed.',
+ 'categoryviewer-pagedlinks' => 'The pagination links in category viewer. Parameters:
+ * $1 is the previous link,
+ * $2 is the next link',
'linkprefix' => '{{optional}}',
'nologin' => 'A message shown in the log in form. $1 is a link to the account creation form, and the text of it is "[[MediaWiki:Nologinlink/{{SUBPAGENAME}}|{{int:nologinlink}}]]".',
'nologinlink' => 'Text of the link to the account creation form. Before that link, the message [[MediaWiki:Nologin/{{SUBPAGENAME}}]] appears.
{{Identical|Create an account}}',
- 'createaccount' => 'The title of Special:CreateAccount, where users can register a new account. Used on Special:SpecialPages, and also on the submit button in the form where you register a new account.
+ 'createaccount' => 'The title of Special:CreateAccount, where users can register a new account. Used on Special:SpecialPages and on the submit button in the form where you register a new account.
+
+ It is also used on the top of the page for logged out users, where it appears next to {{msg-mw|login}}, so consider making them similar.
{{Identical|Create account}}',
'gotaccount' => 'A message shown in the account creation form. $1 is a link to the log in form, and the text of it is "[[MediaWiki:Gotaccountlink/{{SUBPAGENAME}}|{{int:gotaccountlink}}]]".',
'gotaccountlink' => 'Text of the link to the log in form. Before that link, the message [[MediaWiki:Gotaccount/{{SUBPAGENAME}}]] appears.
*Parameter $4 is a URL to the wiki',
'login-throttled' => 'Error message shown at [[Special:UserLogin]] after 5 wrong passwords. The hardcoded waiting time is 300 seconds.',
'login-abort-generic' => 'The generic unsuccessful login message is used unless otherwise specified by hook writers',
- 'loginlanguagelabel' => 'Used on [[Special:UserLogin]] if $wgLoginLanguageSelector is true.
+ 'loginlanguagelabel' => 'Used on [[Special:UserLogin]] if $wgLoginLanguageSelector is true. $1 is a pipe-separated list built from the names that appear in the message {{msg-mw|Loginlanguagelinks}}.
{{Identical|Language}}',
# E-mail sending
*Parameter $3 is a log comment for the merge',
'mergehistory-same-destination' => 'Error message shown on [[Special:MergeHistory]] when the user entered the same page title to both source and destination',
'mergehistory-reason' => '{{Identical|Reason}}',
+ 'mergehistory-revisionrow' => 'A revision row in the merge history page. Parameters:
+ * $1 is a radio button to indicate a merge point,
+ * $2 is a link to the last revision of a page ({{msg-mw|last}}),
+ * $3 is a page link,
+ * $4 is a user link,
+ * $5 is a revision size,
+ * $6 is a revision comment',
# Merge log
'mergelog' => 'This is the name of a log of merge actions done on [[Special:MergeHistory]]. This special page and this log is not enabled by default.',
See also {{msg-mw|difference}}.',
'lineno' => 'Message used when comparing different versions of a page (diff). $1 is a line number.',
'compareselectedversions' => 'Used as button in history pages.',
- 'showhideselectedversions' => 'Text of the button which brings up the [[mw:RevisionDelete|RevisionDelete]] menu.',
+ 'showhideselectedversions' => 'Text of the button which brings up the [[mw:RevisionDelete|RevisionDelete]] menu on history pages.',
'editundo' => 'Undo link when viewing diffs
{{Identical|Undo}}
'alllogstext' => 'Header of [[Special:Log]]',
'log-title-wildcard' => '* Appears in: [[Special:Log]]
* Description: A check box to enable prefix search option',
+ 'showhideselectedlogentries' => 'Text of the button which brings up the [[mw:RevisionDelete|RevisionDelete]] menu on [[Special:Log]].',
# Special:AllPages
'allpages' => 'First part of the navigation bar for the special page [[Special:AllPages]] and [[Special:PrefixIndex]]. The other parts are {{msg-mw|Prevpage}} and {{msg-mw|Nextpage}}.
{{identical|Are you sure you want to view the deleted revision of the file...}}',
'undelete-show-file-submit' => '{{Identical|Yes}}',
+ 'undelete-revisionrow' => "A revision row in the undelete page. Parameters:
+ * $1 is a checkBox to indicate whether to restore this specific revision
+ * $2 is a link to the revision
+ * $3 is a link to the last revision of a page ({{msg-mw|last}})
+ * $4 is a link to the page
+ * $5 is a link to the revision's user
+ * $6 is the revision size
+ * $7 is the revision comment",
# Namespace form on various pages
'namespace' => 'This message is located at [[Special:Contributions]].',
*{{msg-mw|Days}}',
# Bad image list
- 'bad_image_list' => 'This is only message appears to guide administrators to add links with right format. This will not appear anywhere else in Mediawiki.',
+ 'bad_image_list' => 'This message only appears to guide administrators to add links with the right format. This will not appear anywhere else in MediaWiki.',
/*
Short names for language variants used for language conversion links.
'api-error-uploaddisabled' => 'API error message that can be used for client side localisation of API errors.',
'api-error-verification-error' => 'The word "extension" refers to the part behind the last dot in a file name, that by convention gives a hint about the kind of data format which a files contents are in.',
+# Content model IDs for the ContentHandler facility; used by ContentHander::getContentModel()
+'content-model-1' => 'Name for the wikitext content model, used when decribing what type of content a page contains.',
+'content-model-2' => 'Name for the JavaScript content model, used when decribing what type of content a page contains.',
+'content-model-3' => 'Name for the CSS content model, used when decribing what type of content a page contains.',
+'content-model-4' => 'Name for the plain text content model, used when decribing what type of content a page contains.',
+
);
<?php
/**
+ * @group medium
+ * ^---- causes phpunit to use a higher timeout threshold
+ *
* @group FileRepo
* @group FileBackend
*/
private function doTestGetFileList() {
$backendName = $this->backendClass();
-
$base = $this->baseStorePath();
+
+ // Should have no errors
+ $iter = $this->backend->getFileList( array( 'dir' => "$base/unittest-cont-notexists" ) );
+
$files = array(
"$base/unittest-cont1/test1.txt",
"$base/unittest-cont1/test2.txt",
<?php
- global $IP;
- require_once( "$IP/maintenance/backup.inc" );
/**
* Base TestCase for dumps
*/
- abstract class DumpTestCase extends MediaWikiTestCase {
+ abstract class DumpTestCase extends MediaWikiLangTestCase {
/**
* exception to be rethrown once in sound PHPUnit surrounding
*
* Clears $wgUser, and reports errors from addDBData to PHPUnit
*/
- protected function setUp() {
+ public function setUp() {
global $wgUser;
parent::setUp();
$this->skipWhitespace();
$this->assertTextNode( "comment", $summary );
+ $this->skipWhitespace();
+
+ if ( $this->xml->name == "model" ) { // model tag is optional
+ $this->assertTextNode( "model", CONTENT_MODEL_WIKITEXT ); //@todo: make this a test parameter
+ $this->skipWhitespace();
+ }
+
+
+ if ( $this->xml->name == "format" ) { // format tag is optional
+ $this->assertTextNode( "format", CONTENT_FORMAT_WIKITEXT ); //@todo: make this a test parameter
+ $this->skipWhitespace();
+ }
+ $this->assertTextNode( "sha1", $text_sha1 );
+
$this->assertNodeStart( "text", false );
if ( $text_bytes !== false ) {
$this->assertEquals( $this->xml->getAttribute( "bytes" ), $text_bytes,
"Attribute 'bytes' of revision " . $id );
}
-
if ( $text === false ) {
// Testing for a stub
$this->assertEquals( $this->xml->getAttribute( "id" ), $text_id,
$this->skipWhitespace();
}
- $this->assertTextNode( "sha1", $text_sha1 );
-
$this->assertNodeEnd( "revision" );
$this->skipWhitespace();
}