public $mParserOptions;
var $mContent; // !< #FIXME: use Content object!
+ var $mContentObject; // !<
var $mContentLoaded = false; // !<
var $mOldId; // !<
* only want the real revision text if any.
*
* @return Return the text of this revision
- */
- public function getContent() {
+ * @deprecated in 1.20; use getContentObject() instead
+ */
+ public function getContent() { #FIXME: deprecated! replace usage! use content object!
+ wfDeprecated( __METHOD__, '1.20' );
+ $content = $this->getContentObject();
+ return ContentHandler::getContentText( $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 Return the content of this revision
+ */
+ public function getContentObject() {
global $wgUser;
wfProfileIn( __METHOD__ );
}
wfProfileOut( __METHOD__ );
- return $text;
+ return new WikitextContent( $text, $this->getTitle() );
} else {
- $this->fetchContent();
+ $this->fetchContentObject();
wfProfileOut( __METHOD__ );
- return $this->mContent;
+ return $this->mContentObject;
}
}
* Does *NOT* follow redirects.
*
* @return mixed string containing article contents, or false if null
+ * @deprecated in 1.20, use getContentObject() instead
*/
- function fetchContent() {
- if ( $this->mContentLoaded ) {
+ function fetchContent() { #BC cruft! #FIXME: deprecated, replace usage
+ wfDeprecated( __METHOD__, '1.20' );
+
+ if ( $this->mContentLoaded && $this->mContent ) {
return $this->mContent;
}
wfProfileIn( __METHOD__ );
- $this->mContentLoaded = true;
-
- $oldid = $this->getOldID();
-
- # Pre-fill content with error message so that if something
- # 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 ) ;
-
- if ( $oldid ) {
- # $this->mRevision might already be fetched by getOldIDFromRequest()
- if ( !$this->mRevision ) {
- $this->mRevision = Revision::newFromId( $oldid );
- if ( !$this->mRevision ) {
- wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
- wfProfileOut( __METHOD__ );
- return false;
- }
- }
- } else {
- if ( !$this->mPage->getLatest() ) {
- wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $this->mRevision = $this->mPage->getRevision();
- if ( !$this->mRevision ) {
- wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
- wfProfileOut( __METHOD__ );
- return false;
- }
- }
-
- // @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->mRevIdFetched = $this->mRevision->getId();
+ $content = $this->fetchContentObject();
- wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
+ $this->mContent = ContentHandler::getContentText( $content );
+ wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) ); #BC cruft!
wfProfileOut( __METHOD__ );
return $this->mContent;
}
+
+ /**
+ * Get text content object
+ * Does *NOT* follow redirects.
+ *
+ * @return Content object containing article contents, or null
+ */
+ function fetchContentObject() {
+ if ( $this->mContentLoaded ) {
+ return $this->mContentObject;
+ }
+
+ wfProfileIn( __METHOD__ );
+
+ $this->mContentLoaded = true;
+ $this->mContent = null;
+
+ $oldid = $this->getOldID();
+
+ # Pre-fill content with error message so that if something
+ # fails we'll have something telling us what we intended.
+ $t = $this->getTitle()->getPrefixedText();
+ $d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
+ $this->mContentObject = new MessageContent( 'missing-article', array($t, $d), array() ) ;
+
+ if ( $oldid ) {
+ # $this->mRevision might already be fetched by getOldIDFromRequest()
+ if ( !$this->mRevision ) {
+ $this->mRevision = Revision::newFromId( $oldid );
+ if ( !$this->mRevision ) {
+ wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ }
+ } else {
+ if ( !$this->mPage->getLatest() ) {
+ wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ $this->mRevision = $this->mPage->getRevision();
+ if ( !$this->mRevision ) {
+ wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ }
+
+ // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
+ // We should instead work with the Revision object when we need it...
+ $this->mContentObject = $this->mRevision->getContent( Revision::FOR_THIS_USER ); // Loads if user is allowed
+ $this->mRevIdFetched = $this->mRevision->getId();
+
+ wfRunHooks( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) ); #FIXME: register new hook
+
+ wfProfileOut( __METHOD__ );
+
+ return $this->mContentObject;
+ }
+
/**
* No-op
* @deprecated since 1.18
$html = $this->getHtml( $options );
$po = new ParserOutput( $html );
+ if ( $this->mTitle ) $po->setTitleText( $this->mTitle->getText() );
+
#TODO: cache settings, etc?
return $po;
public function getRawData( ) {
- global $wgParser, $wgUser;
-
$text = $this->mText;
return $text;
}
}
+class MessageContent extends TextContent {
+ public function __construct( $msg_key, $params = null, $options = null ) {
+ parent::__construct(null, null, null, CONTENT_MODEL_WIKITEXT);
+
+ $this->mMessageKey = $msg_key;
+
+ $this->mParameters = $params;
+
+ if ( !$options ) $options = array();
+ $this->mOptions = $options;
+
+ $this->mHtmlOptions = null;
+ }
+
+
+ public function getHtml( ParserOptions $options ) {
+ $opt = array_merge( $this->mOptions, array('parse') );
+ return wfMsgExt( $this->mMessageKey, $this->mParameters, $opt );
+ }
+
+
+ public function getRawData( ) {
+ $opt = array_diff( $this->mOptions, array('parse', 'parseinline') );
+
+ return wfMsgExt( $this->mMessageKey, $this->mParameters, $opt );
+ }
+
+}
+
class JavaScriptContent extends TextContent {
public function __construct( $text, Title $title, $revId = null ) {
}
}
+#FIXME: special type for redirects?!
+#FIXME: special type for message-based pseudo-content? with raw html?
+
#TODO: MultipartMultipart < WikipageContent (Main + Links + X)
#TODO: LinksContent < LanguageLinksContent, CategoriesContent
#EXAMPLE: CoordinatesContent
*/
abstract class ContentHandler {
+ public static function getContentText( Content $content ) {
+ if ( !$content ) return '';
+
+ if ( $content instanceof TextContent ) {
+ #XXX: or check by model name?
+ #XXX: or define $content->allowRawData()?
+ return $content->getRawData();
+ }
+
+ return null;
+ }
+
public static function getDefaultModelFor( Title $title ) {
global $wgNamespaceContentModels;
protected $mTitle;
protected $mCurrent;
protected $mContentModelName;
- protected $mContentType;
+ protected $mContentFormat;
+ protected $mContent;
+ protected $mContentHandler;
const DELETED_TEXT = 1;
const DELETED_COMMENT = 2;
'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_type' => isset( $row->ar_content_type ) ? $row->ar_content_type : null,
+ 'content_format' => isset( $row->ar_content_format ) ? $row->ar_content_format : null,
);
if ( isset( $row->ar_text ) && !$row->ar_text_id ) {
// Pre-1.5 ar_text row
'rev_deleted',
'rev_len',
'rev_parent_id',
- 'rev_sha1'
+ 'rev_sha1',
+ 'rev_content_format',
+ 'rev_content_model'
);
}
$this->mContentModelName = strval( $row->rev_content_model );
}
- if( !isset( $row->rev_content_type ) || is_null( $row->rev_content_type ) ) {
- $this->mContentType = null; # determine on demand if needed
+ if( !isset( $row->rev_content_format ) || is_null( $row->rev_content_format ) ) {
+ $this->mContentFormat = null; # determine on demand if needed
} else {
- $this->mContentType = strval( $row->rev_content_type );
+ $this->mContentFormat = strval( $row->rev_content_format );
}
// Lazy extraction...
// 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']->getModelName();
+ # 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->mSha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null;
$this->mContentModelName = isset( $row['content_model'] ) ? strval( $row['content_model'] ) : null;
- $this->mContentType = isset( $row['content_type'] ) ? strval( $row['content_type'] ) : null;
+ $this->mContentFormat = isset( $row['content_format'] ) ? strval( $row['content_format'] ) : null;
- if( !isset( $row->rev_content_type ) || is_null( $row->rev_content_type ) ) {
- $this->mContentType = null; # determine on demand if needed
+ if( !isset( $row->rev_content_format ) || is_null( $row->rev_content_format ) ) {
+ $this->mContentFormat = null; # determine on demand if needed
} else {
- $this->mContentType = $row->rev_content_type;
+ $this->mContentFormat = $row->rev_content_format;
}
// Enforce spacing trimming on supplied text
}
$this->getContentModelName(); # force lazy init
- $this->getContentType(); # force lazy init
+ $this->getContentFormat(); # force lazy init
+
+ # if we have a content object, serialize it, overriding mText
+ if ( !empty( $row['content'] ) ) {
+ $handler = $this->getContentHandler();
+ $this->mText = $handler->serialize( $row['content'], $this->getContentFormat() );
+ }
} else {
throw new MWException( 'Revision constructor passed invalid row format.' );
}
$this->mUnpatrolled = null;
+
+ #FIXME: add patch for ar_content_format, ar_content_model, rev_content_format, rev_content_model to installer
+ #FIXME: add support for ar_content_format, ar_content_model, rev_content_format, rev_content_model to API
}
/**
*/
public function getText( $audience = self::FOR_PUBLIC, User $user = null ) { #FIXME: deprecated, replace usage!
wfDeprecated( __METHOD__, '1.20' );
- $content = $this->getContent();
-
- if ( $content == null ) {
- return ""; # not allowed
- }
- if ( $content instanceof TextContent ) {
- #FIXME: or check by model name? or define $content->allowRawData()?
- return $content->getRawData();
- }
-
- #TODO: log this failure!
- return null;
+ $content = $this->getContent();
+ return ContentHandler::getContentText( $content ); # returns the raw content text, if applicable
}
/**
// Revision is immutable. Load on demand:
$handler = $this->getContentHandler();
- $type = $this->getContentType();
+ $format = $this->getContentFormat();
if( is_null( $this->mText ) ) {
// Load text on demand:
$this->mText = $this->loadText();
}
- $this->mContent = $handler->unserialize( $this->mText, $type );
+ $this->mContent = $handler->unserialize( $this->mText, $format );
}
return $this->mContent;
return $this->mContentModelName;
}
- public function getContentType() {
- if ( !$this->mContentType ) {
+ public function getContentFormat() {
+ if ( !$this->mContentFormat ) {
$handler = $this->getContentHandler();
- $this->mContentType = $handler->getDefaultFormat();
+ $this->mContentFormat = $handler->getDefaultFormat();
}
- return $this->mContentType;
+ return $this->mContentFormat;
}
public function getContentHandler() {
if ( !$this->mContentHandler ) {
$m = $this->getModelName();
$this->mContentHandler = ContentHandler::getForModelName( $m );
+
+ #XXX: do we need to verify that mContentHandler supports mContentFormat?
+ # otherwise, a fixed content format may cause problems on insert.
}
return $this->mContentHandler;
? Revision::base36Sha1( $this->mText )
: $this->mSha1,
'rev_content_model' => $this->getContentModelName(),
- 'rev_content_type' => $this->getContentType(),
+ 'rev_content_format' => $this->getContentFormat(),
), __METHOD__
);
$current = $dbw->selectRow(
array( 'page', 'revision' ),
array( 'page_latest', 'rev_text_id', 'rev_len', 'rev_sha1',
- 'rev_content_model', 'rev_content_type' ),
+ 'rev_content_model', 'rev_content_format' ),
array(
'page_id' => $pageId,
'page_latest=rev_id',
'len' => $current->rev_len,
'sha1' => $current->rev_sha1,
'content_model' => $current->rev_content_model,
- 'content_type' => $current->rev_content_type
+ 'content_format' => $current->rev_content_format
) );
} else {
$revision = null;
'page_touched',
'page_latest',
'page_len',
+ 'page_content_model',
);
}
return null;
}
+ /**
+ * 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|null The content of the current revision
+ */
+ public function getContent( $audience = Revision::FOR_PUBLIC ) {
+ $this->loadLastEdit();
+ if ( $this->mLastRevision ) {
+ return $this->mLastRevision->getContent( $audience );
+ }
+ return false;
+ }
+
/**
* Get the text of the current revision. No side-effects...
*
* 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.20, getContent() should be used instead.
*/
- public function getText( $audience = Revision::FOR_PUBLIC ) {
+ public function getText( $audience = Revision::FOR_PUBLIC ) { #FIXME: deprecated, replace usage!
+ wfDeprecated( __METHOD__, '1.20' );
$this->loadLastEdit();
if ( $this->mLastRevision ) {
return $this->mLastRevision->getText( $audience );
*
* @return String|false The text of the current revision
*/
- public function getRawText() {
- $this->loadLastEdit();
- if ( $this->mLastRevision ) {
- return $this->mLastRevision->getRawText();
- }
- return false;
+ public function getRawText() { #FIXME: deprecated, replace usage!
+ return $this->getText( Revision::RAW );
}
/**
'page' => $this->getId(),
'comment' => $summary,
'minor_edit' => $isminor,
- 'text' => $text,
+ 'text' => $text, #FIXME: set content instead, leavfe serialization to revision?!
'parent_id' => $oldid,
'user' => $user->getId(),
'user_text' => $user->getName(),
'ar_len' => 'rev_len',
'ar_page_id' => 'page_id',
'ar_deleted' => $bitfield,
- 'ar_sha1' => 'rev_sha1'
+ 'ar_sha1' => 'rev_content_model',
+ 'ar_content_format' => 'rev_content_format',
+ 'ar_content_format' => 'rev_sha1'
), array(
'page_id' => $id,
'page_id = rev_page'
$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'
+ 'ar_comment', 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1',
+ 'ar_content_format', 'ar_content_model'
),
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey() ),
'ar_deleted',
'ar_len',
'ar_sha1',
+ 'ar_content_format',
+ 'ar_content_model',
),
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey(),
'ar_deleted',
'ar_page_id',
'ar_len',
- 'ar_sha1' ),
+ 'ar_sha1',
+ 'ar_content_format',
+ 'ar_content_model' ),
/* WHERE */ array(
'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey(),