getNativeData()
[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 * Content objects are imutable.
7 *
8 */
9 abstract class Content {
10
11 public function __construct( $modelName = null ) { #FIXME: really need revId? annoying! #FIXME: really $title? or just when parsing, every time?
12 $this->mModelName = $modelName;
13 }
14
15 public function getModelName() {
16 return $this->mModelName;
17 }
18
19 public abstract function getSearchText( );
20
21 public abstract function getWikitextForTransclusion( );
22
23 /**
24 * Returns native represenation of the data. Interpretation depends on the data model used,
25 * as given by getDataModel().
26 *
27 */
28 public abstract function getNativeData( );
29
30 public abstract function getSize( );
31
32 public abstract function getParserOutput( Title $title = null, $revId = null, ParserOptions $options = NULL );
33
34 public function getRedirectChain() {
35 return null;
36 }
37
38 /**
39 * Returns the section with the given id.
40 *
41 * The default implementation returns null.
42 *
43 * @param String $sectionId the section's id
44 * @return Content|Boolean|null the section, or false if no such section exist, or null if sections are not supported
45 */
46 public function getSection( $sectionId ) {
47 return null;
48 }
49
50 /**
51 * Replaces a section of the content.
52 *
53 * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...), or "new"
54 * @param $with Content: new content of the section
55 * @param $sectionTitle String: new section's subject, only if $section is 'new'
56 * @return string Complete article text, or null if error
57 */
58 public function replaceSection( $section, Content $with, $sectionTitle = '' ) {
59 return $this;
60 }
61
62 #TODO: implement specialized ParserOutput for Wikidata model
63 #TODO: provide "combined" ParserOutput for Multipart... somehow.
64
65 # TODO: Wikipage::isCountable(Content $a)
66
67 # TODO: isCacheable( )
68 # TODO: getSize( )
69
70 # TODO: WikiPage::getUndoText( Revision $undo, Revision $undoafter = null )
71 # TODO: WikiPage::getAutosummary( $oldtext, $text, $flags )
72
73 # TODO: EditPage::getPreloadedText( $preload ) // $wgParser->getPreloadText
74 # TODO: tie into EditPage, make it use Content-objects throughout, make edit form aware of content model and format
75 # TODO: make model-aware diff view!
76 # TODO: handle ImagePage and CategoryPage
77
78 # TODO: Title::newFromRedirectRecurse( $this->getRawText() );
79
80 # TODO: tie into API to provide contentModel for Revisions
81 # TODO: tie into API to provide serialized version and contentFormat for Revisions
82 # TODO: tie into API edit interface
83
84 }
85
86 /**
87 * Content object implementation for representing flat text. The
88 */
89 abstract class TextContent extends Content {
90 public function __construct( $text, $modelName = null ) {
91 parent::__construct($modelName);
92
93 $this->mText = $text;
94 }
95
96 /**
97 * Returns the text represented by this Content object, as a string.
98 *
99 * @return String the raw text
100 */
101 public function getNativeData( ) {
102 $text = $this->mText;
103 return $text;
104 }
105
106 /**
107 * Returns the text represented by this Content object, as a string.
108 *
109 * @return String the raw text
110 */
111 public function getSearchText( ) { #FIXME: use!
112 return $this->getNativeData();
113 }
114
115 /**
116 * Returns the text represented by this Content object, as a string.
117 *
118 * @return String the raw text
119 */
120 public function getWikitextForTransclusion( ) { #FIXME: use!
121 return $this->getNativeData();
122 }
123
124 /**
125 * Returns a generic ParserOutput object, wrapping the HTML returned by getHtml().
126 *
127 * @return ParserOutput representing the HTML form of the text
128 */
129 public function getParserOutput( Title $title = null, $revId = null, ParserOptions $options = null ) {
130 # generic implementation, relying on $this->getHtml()
131
132 $html = $this->getHtml( $options );
133 $po = new ParserOutput( $html );
134
135 if ( $this->mTitle ) $po->setTitleText( $this->mTitle->getText() );
136
137 #TODO: cache settings, etc?
138
139 return $po;
140 }
141
142 protected abstract function getHtml( );
143
144 }
145
146 class WikitextContent extends TextContent {
147 public function __construct( $text ) {
148 parent::__construct($text, CONTENT_MODEL_WIKITEXT);
149
150 $this->mDefaultParserOptions = null; #TODO: use per-class static member?!
151 }
152
153 protected function getHtml( ) {
154 throw new MWException( "getHtml() not implemented for wikitext. Use getParserOutput()->getText()." );
155 }
156
157 public function getDefaultParserOptions() {
158 global $wgUser, $wgContLang;
159
160 if ( !$this->mDefaultParserOptions ) { #TODO: use per-class static member?!
161 $this->mDefaultParserOptions = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
162 }
163
164 return $this->mDefaultParserOptions;
165 }
166
167 /**
168 * Returns a ParserOutput object reesulting from parsing the content's text using $wgParser
169 *
170 * @return ParserOutput representing the HTML form of the text
171 */
172 public function getParserOutput( Title $title = null, $revId = null, ParserOptions $options = null ) {
173 global $wgParser;
174
175 if ( !$options ) {
176 $options = $this->getDefaultParserOptions();
177 }
178
179 $po = $wgParser->parse( $this->mText, $this->getTitle(), $options, true, true, $this->mRevId );
180
181 return $po;
182 }
183
184 /**
185 * Returns the section with the given id.
186 *
187 * @param String $sectionId the section's id
188 * @return Content|false|null the section, or false if no such section exist, or null if sections are not supported
189 */
190 public function getSection( $section ) {
191 global $wgParser;
192
193 $text = $this->getNativeData();
194 $sect = $wgParser->getSection( $text, $section, false );
195
196 return new WikitextContent( $sect );
197 }
198
199 /**
200 * Replaces a section in the wikitext
201 *
202 * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...), or "new"
203 * @param $with Content: new content of the section
204 * @param $sectionTitle String: new section's subject, only if $section is 'new'
205 * @return string Complete article text, or null if error
206 */
207 public function replaceSection( $section, Content $with, $sectionTitle = '' ) {
208 global $wgParser;
209
210 wfProfileIn( __METHOD__ );
211
212 $myModelName = $this->getModelName();
213 $sectionModelName = $with->getModelName();
214
215 if ( $sectionModelName != $myModelName ) {
216 throw new MWException( "Incompatible content model for section: document uses $myModelName, section uses $sectionModelName." );
217 }
218
219 $oldtext = $this->getNativeData();
220 $text = $with->getNativeData();
221
222 if ( $section == 'new' ) {
223 # Inserting a new section
224 $subject = $sectionTitle ? wfMsgForContent( 'newsectionheaderdefaultlevel', $sectionTitle ) . "\n\n" : '';
225 if ( wfRunHooks( 'PlaceNewSection', array( $this, $oldtext, $subject, &$text ) ) ) {
226 $text = strlen( trim( $oldtext ) ) > 0
227 ? "{$oldtext}\n\n{$subject}{$text}"
228 : "{$subject}{$text}";
229 }
230 } else {
231 # Replacing an existing section; roll out the big guns
232 global $wgParser;
233
234 $text = $wgParser->replaceSection( $oldtext, $section, $text );
235 }
236
237 $newContent = new WikitextContent( $text );
238
239 wfProfileOut( __METHOD__ );
240 return $newContent;
241 }
242
243 public function getRedirectChain() {
244 $text = $this->getNativeData();
245 return Title::newFromRedirectArray( $text );
246 }
247
248 }
249
250 class MessageContent extends TextContent {
251 public function __construct( $msg_key, $params = null, $options = null ) {
252 parent::__construct(null, CONTENT_MODEL_WIKITEXT);
253
254 $this->mMessageKey = $msg_key;
255
256 $this->mParameters = $params;
257
258 if ( !$options ) $options = array();
259 $this->mOptions = $options;
260
261 $this->mHtmlOptions = null;
262 }
263
264 /**
265 * Returns the message as rendered HTML, using the options supplied to the constructor plus "parse".
266 */
267 protected function getHtml( ) {
268 $opt = array_merge( $this->mOptions, array('parse') );
269
270 return wfMsgExt( $this->mMessageKey, $this->mParameters, $opt );
271 }
272
273
274 /**
275 * Returns the message as raw text, using the options supplied to the constructor minus "parse" and "parseinline".
276 */
277 public function getNativeData( ) {
278 $opt = array_diff( $this->mOptions, array('parse', 'parseinline') );
279
280 return wfMsgExt( $this->mMessageKey, $this->mParameters, $opt );
281 }
282
283 }
284
285
286 class JavaScriptContent extends TextContent {
287 public function __construct( $text ) {
288 parent::__construct($text, CONTENT_MODEL_JAVASCRIPT);
289 }
290
291 protected function getHtml( ) {
292 $html = "";
293 $html .= "<pre class=\"mw-code mw-js\" dir=\"ltr\">\n";
294 $html .= htmlspecialchars( $this->getNativeData() );
295 $html .= "\n</pre>\n";
296
297 return $html;
298 }
299
300 }
301
302 class CssContent extends TextContent {
303 public function __construct( $text ) {
304 parent::__construct($text, CONTENT_MODEL_CSS);
305 }
306
307 protected function getHtml( ) {
308 $html = "";
309 $html .= "<pre class=\"mw-code mw-css\" dir=\"ltr\">\n";
310 $html .= htmlspecialchars( $this->getNativeData() );
311 $html .= "\n</pre>\n";
312
313 return $html;
314 }
315 }
316
317 #FUTURE: special type for redirects?!
318 #FUTURE: MultipartMultipart < WikipageContent (Main + Links + X)
319 #FUTURE: LinksContent < LanguageLinksContent, CategoriesContent
320 #EXAMPLE: CoordinatesContent
321 #EXAMPLE: WikidataContent