content diff
[lhc/web/wiklou.git] / includes / Content.php
1 <?php
2
3 /**
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 *
7 * @since 1.WD
8 */
9 abstract class Content {
10
11 /**
12 * Name of the content model this Content object represents.
13 * Use with CONTENT_MODEL_XXX constants
14 *
15 * @var String $model_id
16 */
17 protected $model_id;
18
19 /**
20 * @since WD.1
21 *
22 * @return String a string representing the content in a way useful for building a full text search index.
23 * If no useful representation exists, this method returns an empty string.
24 */
25 public abstract function getTextForSearchIndex( );
26
27 /**
28 * @since WD.1
29 *
30 * @return String the wikitext to include when another page includes this content, or false if the content is not
31 * includable in a wikitext page.
32 *
33 * @TODO: allow native handling, bypassing wikitext representation, like for includable special pages.
34 * @TODO: use in parser, etc!
35 */
36 public abstract function getWikitextForTransclusion( );
37
38 /**
39 * Returns a textual representation of the content suitable for use in edit summaries and log messages.
40 *
41 * @since WD.1
42 *
43 * @param int $maxlength maximum length of the summary text
44 * @return String the summary text
45 */
46 public abstract function getTextForSummary( $maxlength = 250 );
47
48 /**
49 * Returns native represenation of the data. Interpretation depends on the data model used,
50 * as given by getDataModel().
51 *
52 * @since WD.1
53 *
54 * @return mixed the native representation of the content. Could be a string, a nested array
55 * structure, an object, a binary blob... anything, really.
56 *
57 * @NOTE: review all calls carefully, caller must be aware of content model!
58 */
59 public abstract function getNativeData( );
60
61 /**
62 * returns the content's nominal size in bogo-bytes.
63 *
64 * @return int
65 */
66 public abstract function getSize( );
67
68 /**
69 * @param int $model_id
70 */
71 public function __construct( $model_id = null ) {
72 $this->model_id = $model_id;
73 }
74
75 /**
76 * Returns the id of the content model used by this content objects.
77 * Corresponds to the CONTENT_MODEL_XXX constants.
78 *
79 * @since WD.1
80 *
81 * @return int the model id
82 */
83 public function getModel() {
84 return $this->model_id;
85 }
86
87 /**
88 * Throws an MWException if $model_id is not the id of the content model
89 * supported by this Content object.
90 *
91 * @param int $model_id the model to check
92 */
93 protected function checkModelID( $model_id ) {
94 if ( $model_id !== $this->model_id ) {
95 $model_name = ContentHandler::getContentModelName( $model_id );
96 $own_model_name = ContentHandler::getContentModelName( $this->model_id );
97
98 throw new MWException( "Bad content model: expected {$this->model_id} ($own_model_name) but got found $model_id ($model_name)." );
99 }
100 }
101
102 /**
103 * Conveniance method that returns the ContentHandler singleton for handling the content
104 * model this Content object uses.
105 *
106 * Shorthand for ContentHandler::getForContent( $this )
107 *
108 * @since WD.1
109 *
110 * @return ContentHandler
111 */
112 public function getContentHandler() {
113 return ContentHandler::getForContent( $this );
114 }
115
116 /**
117 * Conveniance method that returns the default serialization format for the content model
118 * model this Content object uses.
119 *
120 * Shorthand for $this->getContentHandler()->getDefaultFormat()
121 *
122 * @since WD.1
123 *
124 * @return ContentHandler
125 */
126 public function getDefaultFormat() {
127 return $this->getContentHandler()->getDefaultFormat();
128 }
129
130 /**
131 * Conveniance method that returns the list of serialization formats supported
132 * for the content model model this Content object uses.
133 *
134 * Shorthand for $this->getContentHandler()->getSupportedFormats()
135 *
136 * @since WD.1
137 *
138 * @return array of supported serialization formats
139 */
140 public function getSupportedFormats() {
141 return $this->getContentHandler()->getSupportedFormats();
142 }
143
144 /**
145 * Returns true if $format is a supported serialization format for this Content object,
146 * false if it isn't.
147 *
148 * Note that this will always return true if $format is null, because null stands for the
149 * default serialization.
150 *
151 * Shorthand for $this->getContentHandler()->isSupportedFormat( $format )
152 *
153 * @since WD.1
154 *
155 * @param String $format the format to check
156 * @return bool whether the format is supported
157 */
158 public function isSupportedFormat( $format ) {
159 if ( !$format ) {
160 return true; // this means "use the default"
161 }
162
163 return $this->getContentHandler()->isSupportedFormat( $format );
164 }
165
166 /**
167 * Throws an MWException if $this->isSupportedFormat( $format ) doesn't return true.
168 *
169 * @param $format
170 * @throws MWException
171 */
172 protected function checkFormat( $format ) {
173 if ( !$this->isSupportedFormat( $format ) ) {
174 throw new MWException( "Format $format is not supported for content model " . $this->getModel() );
175 }
176 }
177
178 /**
179 * Conveniance method for serializing this Content object.
180 *
181 * Shorthand for $this->getContentHandler()->serializeContent( $this, $format )
182 *
183 * @since WD.1
184 *
185 * @param null|String $format the desired serialization format (or null for the default format).
186 * @return String serialized form of this Content object
187 */
188 public function serialize( $format = null ) {
189 return $this->getContentHandler()->serializeContent( $this, $format );
190 }
191
192 /**
193 * Returns true if this Content object represents empty content.
194 *
195 * @since WD.1
196 *
197 * @return bool whether this Content object is empty
198 */
199 public function isEmpty() {
200 return $this->getSize() == 0;
201 }
202
203 /**
204 * Returns if the content is valid.
205 * It needs to be valid before it can be saved.
206 *
207 * @since WD.1
208 *
209 * @return boolean
210 */
211 public function isValid() {
212 // TODO
213 return true;
214 }
215
216 /**
217 * Diff this content object with another content object..
218 *
219 * @since WD.diff
220 *
221 * @return DiffResult
222 */
223 public abstract function diff( Content $that );
224
225 /**
226 * Returns true if this Content objects is conceptually equivalent to the given Content object.
227 *
228 * Will returns false if $that is null.
229 * Will return true if $that === $this.
230 * Will return false if $that->getModleName() != $this->getModel().
231 * Will return false if $that->getNativeData() is not equal to $this->getNativeData(),
232 * where the meaning of "equal" depends on the actual data model.
233 *
234 * Implementations should be careful to make equals() transitive and reflexive:
235 *
236 * * $a->equals( $b ) <=> $b->equals( $b )
237 * * $a->equals( $b ) && $b->equals( $c ) ==> $a->equals( $c )
238 *
239 * @since WD.1
240 *
241 * @param Content $that the Content object to compare to
242 * @return bool true if this Content object is euqual to $that, false otherwise.
243 */
244 public function equals( Content $that = null ) {
245 if ( is_null( $that ) ){
246 return false;
247 }
248
249 if ( $that === $this ) {
250 return true;
251 }
252
253 if ( $that->getModel() !== $this->getModel() ) {
254 return false;
255 }
256
257 return $this->getNativeData() === $that->getNativeData();
258 }
259
260 /**
261 * Return a copy of this Content object. The following must be true for the object returned
262 * if $copy = $original->copy()
263 *
264 * * get_class($original) === get_class($copy)
265 * * $original->getModel() === $copy->getModel()
266 * * $original->equals( $copy )
267 *
268 * If and only if the Content object is imutable, the copy() method can and should
269 * return $this. That is, $copy === $original may be true, but only for imutable content
270 * objects.
271 *
272 * @since WD.1
273 *
274 * @return Content. A copy of this object
275 */
276 public abstract function copy( );
277
278 /**
279 * Returns true if this content is countable as a "real" wiki page, provided
280 * that it's also in a countable location (e.g. a current revision in the main namespace).
281 *
282 * @since WD.1
283 *
284 * @param $hasLinks Bool: if it is known whether this content contains links, provide this information here,
285 * to avoid redundant parsing to find out.
286 * @return boolean
287 */
288 public abstract function isCountable( $hasLinks = null ) ;
289
290 /**
291 * @param Title $title
292 * @param null $revId
293 * @param null|ParserOptions $options
294 * @param Boolean $generateHtml whether to generate Html (default: true). If false,
295 * the result of calling getText() on the ParserOutput object returned by
296 * this method is undefined.
297 *
298 * @since WD.1
299 *
300 * @return ParserOutput
301 */
302 public abstract function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true );
303
304 /**
305 * Returns a list of DataUpdate objects for recording information about this Content in some secondary
306 * data store. If the optional second argument, $old, is given, the updates may model only the changes that
307 * need to be made to replace information about the old content with infomration about the new content.
308 *
309 * This default implementation calls $this->getParserOutput( $title, null, null, false ), and then
310 * calls getSecondaryDataUpdates( $title, $recursive ) on the resulting ParserOutput object.
311 *
312 * Subclasses may implement this to determine the necessary updates more efficiently, or make use of information
313 * about the old content.
314 *
315 * @param Title $title the context for determining the necessary updates
316 * @param Content|null $old a Content object representing the previous content, i.e. the content being
317 * replaced by this Content object.
318 * @param bool $recursive whether to include recursive updates (default: false).
319 *
320 * @return Array. A list of DataUpdate objects for putting information about this content object somewhere.
321 *
322 * @since WD.1
323 */
324 public function getSecondaryDataUpdates( Title $title, Content $old = null, $recursive = false ) {
325 $po = $this->getParserOutput( $title, null, null, false );
326 return $po->getSecondaryDataUpdates( $title, $recursive );
327 }
328
329 /**
330 * Construct the redirect destination from this content and return an
331 * array of Titles, or null if this content doesn't represent a redirect.
332 * The last element in the array is the final destination after all redirects
333 * have been resolved (up to $wgMaxRedirects times).
334 *
335 * @since WD.1
336 *
337 * @return Array of Titles, with the destination last
338 */
339 public function getRedirectChain() {
340 return null;
341 }
342
343 /**
344 * Construct the redirect destination from this content and return an
345 * array of Titles, or null if this content doesn't represent a redirect.
346 * This will only return the immediate redirect target, useful for
347 * the redirect table and other checks that don't need full recursion.
348 *
349 * @since WD.1
350 *
351 * @return Title: The corresponding Title
352 */
353 public function getRedirectTarget() {
354 return null;
355 }
356
357 /**
358 * Construct the redirect destination from this content and return the
359 * Title, or null if this content doesn't represent a redirect.
360 * This will recurse down $wgMaxRedirects times or until a non-redirect target is hit
361 * in order to provide (hopefully) the Title of the final destination instead of another redirect.
362 *
363 * @since WD.1
364 *
365 * @return Title
366 */
367 public function getUltimateRedirectTarget() {
368 return null;
369 }
370
371 /**
372 * @since WD.1
373 *
374 * @return bool
375 */
376 public function isRedirect() {
377 return $this->getRedirectTarget() !== null;
378 }
379
380 /**
381 * Returns the section with the given id.
382 *
383 * The default implementation returns null.
384 *
385 * @since WD.1
386 *
387 * @param String $sectionId the section's id, given as a numeric string. The id "0" retrieves the section before
388 * the first heading, "1" the text between the first heading (inluded) and the second heading (excluded), etc.
389 * @return Content|Boolean|null the section, or false if no such section exist, or null if sections are not supported
390 */
391 public function getSection( $sectionId ) {
392 return null;
393 }
394
395 /**
396 * Replaces a section of the content and returns a Content object with the section replaced.
397 *
398 * @since WD.1
399 *
400 * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...), or "new"
401 * @param $with Content: new content of the section
402 * @param $sectionTitle String: new section's subject, only if $section is 'new'
403 * @return string Complete article text, or null if error
404 */
405 public function replaceSection( $section, Content $with, $sectionTitle = '' ) {
406 return null;
407 }
408
409 /**
410 * Returns a Content object with pre-save transformations applied (or this object if no transformations apply).
411 *
412 * @since WD.1
413 *
414 * @param Title $title
415 * @param User $user
416 * @param null|ParserOptions $popts
417 * @return Content
418 */
419 public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
420 return $this;
421 }
422
423 /**
424 * Returns a new WikitextContent object with the given section heading prepended, if supported.
425 * The default implementation just returns this Content object unmodified, ignoring the section header.
426 *
427 * @since WD.1
428 *
429 * @param $header String
430 * @return Content
431 */
432 public function addSectionHeader( $header ) {
433 return $this;
434 }
435
436 /**
437 * Returns a Content object with preload transformations applied (or this object if no transformations apply).
438 *
439 * @since WD.1
440 *
441 * @param Title $title
442 * @param null|ParserOptions $popts
443 * @return Content
444 */
445 public function preloadTransform( Title $title, ParserOptions $popts ) {
446 return $this;
447 }
448
449 # TODO: handle ImagePage and CategoryPage
450 # TODO: make sure we cover lucene search / wikisearch.
451 # TODO: make sure ReplaceTemplates still works
452 # FUTURE: nice&sane integration of GeSHi syntax highlighting
453 # [11:59] <vvv> Hooks are ugly; make CodeHighlighter interface and a config to set the class which handles syntax highlighting
454 # [12:00] <vvv> And default it to a DummyHighlighter
455
456 # TODO: make sure we cover the external editor interface (does anyone actually use that?!)
457
458 # TODO: tie into API to provide contentModel for Revisions
459 # TODO: tie into API to provide serialized version and contentFormat for Revisions
460 # TODO: tie into API edit interface
461 # FUTURE: make EditForm plugin for EditPage
462 }
463 # FUTURE: special type for redirects?!
464 # FUTURE: MultipartMultipart < WikipageContent (Main + Links + X)
465 # FUTURE: LinksContent < LanguageLinksContent, CategoriesContent
466
467 /**
468 * Content object implementation for representing flat text.
469 *
470 * TextContent instances are imutable
471 *
472 * @since WD.1
473 */
474 abstract class TextContent extends Content {
475
476 public function __construct( $text, $model_id = null ) {
477 parent::__construct( $model_id );
478
479 $this->mText = $text;
480 }
481
482 public function copy() {
483 return $this; #NOTE: this is ok since TextContent are imutable.
484 }
485
486 public function getTextForSummary( $maxlength = 250 ) {
487 global $wgContLang;
488
489 $text = $this->getNativeData();
490
491 $truncatedtext = $wgContLang->truncate(
492 preg_replace( "/[\n\r]/", ' ', $text ),
493 max( 0, $maxlength ) );
494
495 return $truncatedtext;
496 }
497
498 /**
499 * returns the text's size in bytes.
500 *
501 * @return int the size
502 */
503 public function getSize( ) {
504 $text = $this->getNativeData( );
505 return strlen( $text );
506 }
507
508 /**
509 * Returns true if this content is not a redirect, and $wgArticleCountMethod is "any".
510 *
511 * @param $hasLinks Bool: if it is known whether this content contains links, provide this information here,
512 * to avoid redundant parsing to find out.
513 *
514 * @return bool true if the content is countable
515 */
516 public function isCountable( $hasLinks = null ) {
517 global $wgArticleCountMethod;
518
519 if ( $this->isRedirect( ) ) {
520 return false;
521 }
522
523 if ( $wgArticleCountMethod === 'any' ) {
524 return true;
525 }
526
527 return false;
528 }
529
530 /**
531 * Returns the text represented by this Content object, as a string.
532 *
533 * @return String the raw text
534 */
535 public function getNativeData( ) {
536 $text = $this->mText;
537 return $text;
538 }
539
540 /**
541 * Returns the text represented by this Content object, as a string.
542 *
543 * @return String the raw text
544 */
545 public function getTextForSearchIndex( ) {
546 return $this->getNativeData();
547 }
548
549 /**
550 * Returns the text represented by this Content object, as a string.
551 *
552 * @return String the raw text
553 */
554 public function getWikitextForTransclusion( ) {
555 return $this->getNativeData();
556 }
557
558 /**
559 * Returns a generic ParserOutput object, wrapping the HTML returned by getHtml().
560 *
561 * @return ParserOutput representing the HTML form of the text
562 */
563 public function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ) {
564 # generic implementation, relying on $this->getHtml()
565
566 if ( $generateHtml ) $html = $this->getHtml( $options );
567 else $html = '';
568
569 $po = new ParserOutput( $html );
570
571 return $po;
572 }
573
574 protected abstract function getHtml( );
575
576 /**
577 * Diff this content object with another content object..
578 *
579 * @since WD.diff
580 *
581 * @param Content $that the other content object to compare this content object to
582 * @param Language $lang the language object to use for text segmentation. If not given, $wgContentLang is used.
583 *
584 * @return DiffResult a diff representing the changes that would have to be made to this content object
585 * to make it equal to $that.
586 */
587 public function diff( Content $that, Language $lang = null ) {
588 global $wgContLang;
589
590 $this->checkModelID( $that->getModel() );
591
592 #@todo: could implement this in DifferenceEngine and just delegate here?
593
594 if ( !$lang ) $lang = $wgContLang;
595
596 $otext = $this->getNativeData();
597 $ntext = $this->getNativeData();
598
599 # Note: Use native PHP diff, external engines don't give us abstract output
600 $ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
601 $nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
602
603 $diffs = new Diff( $ota, $nta );
604 return $diff;
605 }
606
607
608 }
609
610 /**
611 * @since WD.1
612 */
613 class WikitextContent extends TextContent {
614
615 public function __construct( $text ) {
616 parent::__construct($text, CONTENT_MODEL_WIKITEXT);
617 }
618
619 protected function getHtml( ) {
620 throw new MWException( "getHtml() not implemented for wikitext. Use getParserOutput()->getText()." );
621 }
622
623 /**
624 * Returns a ParserOutput object resulting from parsing the content's text using $wgParser.
625 *
626 * @since WikiData1
627 *
628 * @param IContextSource|null $context
629 * @param null $revId
630 * @param null|ParserOptions $options
631 * @param bool $generateHtml
632 *
633 * @return ParserOutput representing the HTML form of the text
634 */
635 public function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ) {
636 global $wgParser;
637
638 if ( !$options ) {
639 $options = new ParserOptions();
640 }
641
642 $po = $wgParser->parse( $this->mText, $title, $options, true, true, $revId );
643
644 return $po;
645 }
646
647 /**
648 * Returns the section with the given id.
649 *
650 * @param String $sectionId the section's id
651 * @return Content|false|null the section, or false if no such section exist, or null if sections are not supported
652 */
653 public function getSection( $section ) {
654 global $wgParser;
655
656 $text = $this->getNativeData();
657 $sect = $wgParser->getSection( $text, $section, false );
658
659 return new WikitextContent( $sect );
660 }
661
662 /**
663 * Replaces a section in the wikitext
664 *
665 * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...), or "new"
666 * @param $with Content: new content of the section
667 * @param $sectionTitle String: new section's subject, only if $section is 'new'
668 * @return Content Complete article content, or null if error
669 */
670 public function replaceSection( $section, Content $with, $sectionTitle = '' ) {
671 wfProfileIn( __METHOD__ );
672
673 $myModelId = $this->getModel();
674 $sectionModelId = $with->getModel();
675
676 if ( $sectionModelId != $myModelId ) {
677 $myModelName = ContentHandler::getContentModelName( $myModelId );
678 $sectionModelName = ContentHandler::getContentModelName( $sectionModelId );
679
680 throw new MWException( "Incompatible content model for section: document uses $myModelId ($myModelName), "
681 . "section uses $sectionModelId ($sectionModelName)." );
682 }
683
684 $oldtext = $this->getNativeData();
685 $text = $with->getNativeData();
686
687 if ( $section === '' ) {
688 return $with; #XXX: copy first?
689 } if ( $section == 'new' ) {
690 # Inserting a new section
691 $subject = $sectionTitle ? wfMsgForContent( 'newsectionheaderdefaultlevel', $sectionTitle ) . "\n\n" : '';
692 if ( wfRunHooks( 'PlaceNewSection', array( $this, $oldtext, $subject, &$text ) ) ) {
693 $text = strlen( trim( $oldtext ) ) > 0
694 ? "{$oldtext}\n\n{$subject}{$text}"
695 : "{$subject}{$text}";
696 }
697 } else {
698 # Replacing an existing section; roll out the big guns
699 global $wgParser;
700
701 $text = $wgParser->replaceSection( $oldtext, $section, $text );
702 }
703
704 $newContent = new WikitextContent( $text );
705
706 wfProfileOut( __METHOD__ );
707 return $newContent;
708 }
709
710 /**
711 * Returns a new WikitextContent object with the given section heading prepended.
712 *
713 * @param $header String
714 * @return Content
715 */
716 public function addSectionHeader( $header ) {
717 $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $header ) . "\n\n" . $this->getNativeData();
718
719 return new WikitextContent( $text );
720 }
721
722 /**
723 * Returns a Content object with pre-save transformations applied (or this object if no transformations apply).
724 *
725 * @param Title $title
726 * @param User $user
727 * @param ParserOptions $popts
728 * @return Content
729 */
730 public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
731 global $wgParser, $wgConteLang;
732
733 $text = $this->getNativeData();
734 $pst = $wgParser->preSaveTransform( $text, $title, $user, $popts );
735
736 return new WikitextContent( $pst );
737 }
738
739 /**
740 * Returns a Content object with preload transformations applied (or this object if no transformations apply).
741 *
742 * @param Title $title
743 * @param ParserOptions $popts
744 * @return Content
745 */
746 public function preloadTransform( Title $title, ParserOptions $popts ) {
747 global $wgParser, $wgConteLang;
748
749 $text = $this->getNativeData();
750 $plt = $wgParser->getPreloadText( $text, $title, $popts );
751
752 return new WikitextContent( $plt );
753 }
754
755 public function getRedirectChain() {
756 $text = $this->getNativeData();
757 return Title::newFromRedirectArray( $text );
758 }
759
760 public function getRedirectTarget() {
761 $text = $this->getNativeData();
762 return Title::newFromRedirect( $text );
763 }
764
765 public function getUltimateRedirectTarget() {
766 $text = $this->getNativeData();
767 return Title::newFromRedirectRecurse( $text );
768 }
769
770 /**
771 * Returns true if this content is not a redirect, and this content's text is countable according to
772 * the criteria defiend by $wgArticleCountMethod.
773 *
774 * @param Bool $hasLinks if it is known whether this content contains links, provide this information here,
775 * to avoid redundant parsing to find out.
776 * @param IContextSource $context context for parsing if necessary
777 *
778 * @return bool true if the content is countable
779 */
780 public function isCountable( $hasLinks = null, Title $title = null ) {
781 global $wgArticleCountMethod, $wgRequest;
782
783 if ( $this->isRedirect( ) ) {
784 return false;
785 }
786
787 $text = $this->getNativeData();
788
789 switch ( $wgArticleCountMethod ) {
790 case 'any':
791 return true;
792 case 'comma':
793 return strpos( $text, ',' ) !== false;
794 case 'link':
795 if ( $hasLinks === null ) { # not known, find out
796 if ( !$title ) {
797 $context = RequestContext::getMain();
798 $title = $context->getTitle();
799 }
800
801 $po = $this->getParserOutput( $title, null, null, false );
802 $links = $po->getLinks();
803 $hasLinks = !empty( $links );
804 }
805
806 return $hasLinks;
807 }
808 }
809
810 public function getTextForSummary( $maxlength = 250 ) {
811 $truncatedtext = parent::getTextForSummary( $maxlength );
812
813 #clean up unfinished links
814 #XXX: make this optional? wasn't there in autosummary, but required for deletion summary.
815 $truncatedtext = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $truncatedtext );
816
817 return $truncatedtext;
818 }
819
820 }
821
822 /**
823 * @since WD.1
824 */
825 class MessageContent extends TextContent {
826 public function __construct( $msg_key, $params = null, $options = null ) {
827 parent::__construct(null, CONTENT_MODEL_WIKITEXT); #XXX: messages may be wikitext, html or plain text! and maybe even something else entirely.
828
829 $this->mMessageKey = $msg_key;
830
831 $this->mParameters = $params;
832
833 if ( is_null( $options ) ) {
834 $options = array();
835 }
836 elseif ( is_string( $options ) ) {
837 $options = array( $options );
838 }
839
840 $this->mOptions = $options;
841
842 $this->mHtmlOptions = null;
843 }
844
845 /**
846 * Returns the message as rendered HTML, using the options supplied to the constructor plus "parse".
847 */
848 protected function getHtml( ) {
849 $opt = array_merge( $this->mOptions, array('parse') );
850
851 return wfMsgExt( $this->mMessageKey, $this->mParameters, $opt );
852 }
853
854
855 /**
856 * Returns the message as raw text, using the options supplied to the constructor minus "parse" and "parseinline".
857 */
858 public function getNativeData( ) {
859 $opt = array_diff( $this->mOptions, array('parse', 'parseinline') );
860
861 return wfMsgExt( $this->mMessageKey, $this->mParameters, $opt );
862 }
863
864 }
865
866 /**
867 * @since WD.1
868 */
869 class JavaScriptContent extends TextContent {
870 public function __construct( $text ) {
871 parent::__construct($text, CONTENT_MODEL_JAVASCRIPT);
872 }
873
874 protected function getHtml( ) {
875 $html = "";
876 $html .= "<pre class=\"mw-code mw-js\" dir=\"ltr\">\n";
877 $html .= htmlspecialchars( $this->getNativeData() );
878 $html .= "\n</pre>\n";
879
880 return $html;
881 }
882
883 }
884
885 /**
886 * @since WD.1
887 */
888 class CssContent extends TextContent {
889 public function __construct( $text ) {
890 parent::__construct($text, CONTENT_MODEL_CSS);
891 }
892
893 protected function getHtml( ) {
894 $html = "";
895 $html .= "<pre class=\"mw-code mw-css\" dir=\"ltr\">\n";
896 $html .= htmlspecialchars( $this->getNativeData() );
897 $html .= "\n</pre>\n";
898
899 return $html;
900 }
901 }