4 * A content object represents page content, e.g. the text to show on a page.
5 * Content objects have no knowledge about how they relate to Wiki pages.
6 * Content objects are imutable.
9 abstract class Content
{
11 public function __construct( $modelName = null ) { #FIXME: really need revId? annoying! #FIXME: really $title? or just when parsing, every time?
12 $this->mModelName
= $modelName;
15 public function getModelName() {
16 return $this->mModelName
;
19 public abstract function getSearchText( );
21 public abstract function getWikitextForTransclusion( );
24 * Returns native represenation of the data. Interpretation depends on the data model used,
25 * as given by getDataModel().
28 public abstract function getNativeData( );
31 * returns the content's nominal size in bogo-bytes.
33 public abstract function getSize( ); #XXX: do we really need/want this here? we could just use the byte syse of the serialized form...
36 * Returns true if this content is countable as a "real" wiki page, provided
37 * that it's also in a countable location (e.g. a current revision in the main namespace).
39 * @param $hasLinks Bool: if it is known whether this content contains links, provide this information here,
40 * to avoid redundant parsing to find out.
42 public abstract function isCountable( $hasLinks = null ) ;
44 public abstract function getParserOutput( Title
$title = null, $revId = null, ParserOptions
$options = NULL );
46 public function getRedirectChain() {
50 public function isRedirect() {
55 * Returns the section with the given id.
57 * The default implementation returns null.
59 * @param String $sectionId the section's id
60 * @return Content|Boolean|null the section, or false if no such section exist, or null if sections are not supported
62 public function getSection( $sectionId ) {
67 * Replaces a section of the content.
69 * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...), or "new"
70 * @param $with Content: new content of the section
71 * @param $sectionTitle String: new section's subject, only if $section is 'new'
72 * @return string Complete article text, or null if error
74 public function replaceSection( $section, Content
$with, $sectionTitle = '' ) {
78 #TODO: implement specialized ParserOutput for Wikidata model
79 #TODO: provide "combined" ParserOutput for Multipart... somehow.
81 # XXX: isCacheable( ) # can/should we do this here?
83 # TODO: WikiPage::getUndoText( Revision $undo, Revision $undoafter = null )
84 # TODO: WikiPage::getAutosummary( $oldtext, $text, $flags )
86 # TODO: EditPage::getPreloadedText( $preload ) // $wgParser->getPreloadText
87 # TODO: tie into EditPage, make it use Content-objects throughout, make edit form aware of content model and format
88 # TODO: make model-aware diff view!
89 # TODO: handle ImagePage and CategoryPage
91 # TODO: Title::newFromRedirectRecurse( $this->getRawText() );
93 # TODO: tie into API to provide contentModel for Revisions
94 # TODO: tie into API to provide serialized version and contentFormat for Revisions
95 # TODO: tie into API edit interface
100 * Content object implementation for representing flat text. The
102 abstract class TextContent
extends Content
{
103 public function __construct( $text, $modelName = null ) {
104 parent
::__construct($modelName);
106 $this->mText
= $text;
110 * returns the content's nominal size in bogo-bytes.
112 public function getSize( ) { #FIXME: use! replace strlen in WikiPage.
113 $text = $this->getNativeData( );
114 return strlen( $text );
118 * Returns true if this content is not a redirect, and $wgArticleCountMethod is "any".
120 * @param $hasLinks Bool: if it is known whether this content contains links, provide this information here,
121 * to avoid redundant parsing to find out.
123 public function isCountable( $hasLinks = null ) {
124 global $wgArticleCountMethod;
126 if ( $this->isRedirect( ) ) {
130 if ( $wgArticleCountMethod === 'any' ) {
138 * Returns the text represented by this Content object, as a string.
140 * @return String the raw text
142 public function getNativeData( ) {
143 $text = $this->mText
;
148 * Returns the text represented by this Content object, as a string.
150 * @return String the raw text
152 public function getSearchText( ) { #FIXME: use!
153 return $this->getNativeData();
157 * Returns the text represented by this Content object, as a string.
159 * @return String the raw text
161 public function getWikitextForTransclusion( ) { #FIXME: use!
162 return $this->getNativeData();
166 * Returns a generic ParserOutput object, wrapping the HTML returned by getHtml().
168 * @return ParserOutput representing the HTML form of the text
170 public function getParserOutput( Title
$title = null, $revId = null, ParserOptions
$options = null ) {
171 # generic implementation, relying on $this->getHtml()
173 $html = $this->getHtml( $options );
174 $po = new ParserOutput( $html );
176 if ( $this->mTitle
) $po->setTitleText( $this->mTitle
->getText() );
178 #TODO: cache settings, etc?
183 protected abstract function getHtml( );
187 class WikitextContent
extends TextContent
{
188 public function __construct( $text ) {
189 parent
::__construct($text, CONTENT_MODEL_WIKITEXT
);
191 $this->mDefaultParserOptions
= null; #TODO: use per-class static member?!
194 protected function getHtml( ) {
195 throw new MWException( "getHtml() not implemented for wikitext. Use getParserOutput()->getText()." );
198 public function getDefaultParserOptions() {
199 global $wgUser, $wgContLang;
201 if ( !$this->mDefaultParserOptions
) { #TODO: use per-class static member?!
202 $this->mDefaultParserOptions
= ParserOptions
::newFromUserAndLang( $wgUser, $wgContLang );
205 return $this->mDefaultParserOptions
;
209 * Returns a ParserOutput object reesulting from parsing the content's text using $wgParser
211 * @return ParserOutput representing the HTML form of the text
213 public function getParserOutput( Title
$title = null, $revId = null, ParserOptions
$options = null ) {
217 $options = $this->getDefaultParserOptions();
220 $po = $wgParser->parse( $this->mText
, $this->getTitle(), $options, true, true, $this->mRevId
);
226 * Returns the section with the given id.
228 * @param String $sectionId the section's id
229 * @return Content|false|null the section, or false if no such section exist, or null if sections are not supported
231 public function getSection( $section ) {
234 $text = $this->getNativeData();
235 $sect = $wgParser->getSection( $text, $section, false );
237 return new WikitextContent( $sect );
241 * Replaces a section in the wikitext
243 * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...), or "new"
244 * @param $with Content: new content of the section
245 * @param $sectionTitle String: new section's subject, only if $section is 'new'
246 * @return string Complete article text, or null if error
248 public function replaceSection( $section, Content
$with, $sectionTitle = '' ) {
251 wfProfileIn( __METHOD__
);
253 $myModelName = $this->getModelName();
254 $sectionModelName = $with->getModelName();
256 if ( $sectionModelName != $myModelName ) {
257 throw new MWException( "Incompatible content model for section: document uses $myModelName, section uses $sectionModelName." );
260 $oldtext = $this->getNativeData();
261 $text = $with->getNativeData();
263 if ( $section == 'new' ) {
264 # Inserting a new section
265 $subject = $sectionTitle ?
wfMsgForContent( 'newsectionheaderdefaultlevel', $sectionTitle ) . "\n\n" : '';
266 if ( wfRunHooks( 'PlaceNewSection', array( $this, $oldtext, $subject, &$text ) ) ) {
267 $text = strlen( trim( $oldtext ) ) > 0
268 ?
"{$oldtext}\n\n{$subject}{$text}"
269 : "{$subject}{$text}";
272 # Replacing an existing section; roll out the big guns
275 $text = $wgParser->replaceSection( $oldtext, $section, $text );
278 $newContent = new WikitextContent( $text );
280 wfProfileOut( __METHOD__
);
284 public function getRedirectChain() {
285 $text = $this->getNativeData();
286 return Title
::newFromRedirectArray( $text );
289 public function isRedirect() {
290 $text = $this->getNativeData();
291 return Title
::newFromRedirect( $text ) !== null;
295 * Returns true if this content is not a redirect, and this content's text is countable according to
296 * the criteria defiend by $wgArticleCountMethod.
298 * @param $hasLinks Bool: if it is known whether this content contains links, provide this information here,
299 * to avoid redundant parsing to find out.
301 public function isCountable( $hasLinks = null ) {
302 global $wgArticleCountMethod;
304 if ( $this->isRedirect( ) ) {
308 $text = $this->getNativeData();
310 switch ( $wgArticleCountMethod ) {
314 if ( $text === false ) {
315 $text = $this->getRawText();
317 return strpos( $text, ',' ) !== false;
319 if ( $hasLinks === null ) { # not know, find out
320 $po = $this->getParserOutput();
321 $links = $po->getLinks();
322 $hasLinks = !empty( $links );
331 class MessageContent
extends TextContent
{
332 public function __construct( $msg_key, $params = null, $options = null ) {
333 parent
::__construct(null, CONTENT_MODEL_WIKITEXT
);
335 $this->mMessageKey
= $msg_key;
337 $this->mParameters
= $params;
339 if ( !$options ) $options = array();
340 $this->mOptions
= $options;
342 $this->mHtmlOptions
= null;
346 * Returns the message as rendered HTML, using the options supplied to the constructor plus "parse".
348 protected function getHtml( ) {
349 $opt = array_merge( $this->mOptions
, array('parse') );
351 return wfMsgExt( $this->mMessageKey
, $this->mParameters
, $opt );
356 * Returns the message as raw text, using the options supplied to the constructor minus "parse" and "parseinline".
358 public function getNativeData( ) {
359 $opt = array_diff( $this->mOptions
, array('parse', 'parseinline') );
361 return wfMsgExt( $this->mMessageKey
, $this->mParameters
, $opt );
367 class JavaScriptContent
extends TextContent
{
368 public function __construct( $text ) {
369 parent
::__construct($text, CONTENT_MODEL_JAVASCRIPT
);
372 protected function getHtml( ) {
374 $html .= "<pre class=\"mw-code mw-js\" dir=\"ltr\">\n";
375 $html .= htmlspecialchars( $this->getNativeData() );
376 $html .= "\n</pre>\n";
383 class CssContent
extends TextContent
{
384 public function __construct( $text ) {
385 parent
::__construct($text, CONTENT_MODEL_CSS
);
388 protected function getHtml( ) {
390 $html .= "<pre class=\"mw-code mw-css\" dir=\"ltr\">\n";
391 $html .= htmlspecialchars( $this->getNativeData() );
392 $html .= "\n</pre>\n";
398 #FUTURE: special type for redirects?!
399 #FUTURE: MultipartMultipart < WikipageContent (Main + Links + X)
400 #FUTURE: LinksContent < LanguageLinksContent, CategoriesContent
401 #EXAMPLE: CoordinatesContent
402 #EXAMPLE: WikidataContent