summaryrefslogtreecommitdiff
path: root/includes/content
diff options
context:
space:
mode:
Diffstat (limited to 'includes/content')
-rw-r--r--includes/content/AbstractContent.php277
-rw-r--r--includes/content/CodeContentHandler.php65
-rw-r--r--includes/content/Content.php163
-rw-r--r--includes/content/ContentHandler.php298
-rw-r--r--includes/content/CssContent.php28
-rw-r--r--includes/content/CssContentHandler.php37
-rw-r--r--includes/content/JavaScriptContent.php20
-rw-r--r--includes/content/JavaScriptContentHandler.php37
-rw-r--r--includes/content/JsonContent.php120
-rw-r--r--includes/content/JsonContentHandler.php26
-rw-r--r--includes/content/MessageContent.php54
-rw-r--r--includes/content/TextContent.php137
-rw-r--r--includes/content/TextContentHandler.php48
-rw-r--r--includes/content/WikitextContent.php175
-rw-r--r--includes/content/WikitextContentHandler.php44
15 files changed, 987 insertions, 542 deletions
diff --git a/includes/content/AbstractContent.php b/includes/content/AbstractContent.php
index 137efb8a..9d257a6a 100644
--- a/includes/content/AbstractContent.php
+++ b/includes/content/AbstractContent.php
@@ -32,7 +32,6 @@
* @ingroup Content
*/
abstract class AbstractContent implements Content {
-
/**
* Name of the content model this Content object represents.
* Use with CONTENT_MODEL_XXX constants
@@ -44,7 +43,7 @@ abstract class AbstractContent implements Content {
protected $model_id;
/**
- * @param string|null $modelId
+ * @param string $modelId
*
* @since 1.21
*/
@@ -53,23 +52,21 @@ abstract class AbstractContent implements Content {
}
/**
- * @see Content::getModel
- *
* @since 1.21
+ *
+ * @see Content::getModel
*/
public function getModel() {
return $this->model_id;
}
/**
- * Throws an MWException if $model_id is not the id of the content model
- * supported by this Content object.
- *
* @since 1.21
*
* @param string $modelId The model to check
*
- * @throws MWException
+ * @throws MWException If the provided ID is not the ID of the content model supported by this
+ * Content object.
*/
protected function checkModelID( $modelId ) {
if ( $modelId !== $this->model_id ) {
@@ -82,40 +79,40 @@ abstract class AbstractContent implements Content {
}
/**
- * @see Content::getContentHandler
- *
* @since 1.21
+ *
+ * @see Content::getContentHandler
*/
public function getContentHandler() {
return ContentHandler::getForContent( $this );
}
/**
- * @see Content::getDefaultFormat
- *
* @since 1.21
+ *
+ * @see Content::getDefaultFormat
*/
public function getDefaultFormat() {
return $this->getContentHandler()->getDefaultFormat();
}
/**
- * @see Content::getSupportedFormats
- *
* @since 1.21
+ *
+ * @see Content::getSupportedFormats
*/
public function getSupportedFormats() {
return $this->getContentHandler()->getSupportedFormats();
}
/**
- * @see Content::isSupportedFormat
+ * @since 1.21
*
* @param string $format
*
- * @since 1.21
+ * @return bool
*
- * @return boolean
+ * @see Content::isSupportedFormat
*/
public function isSupportedFormat( $format ) {
if ( !$format ) {
@@ -126,13 +123,11 @@ abstract class AbstractContent implements Content {
}
/**
- * Throws an MWException if $this->isSupportedFormat( $format ) does not
- * return true.
- *
* @since 1.21
*
- * @param string $format
- * @throws MWException
+ * @param string $format The serialization format to check.
+ *
+ * @throws MWException If the format is not supported by this content handler.
*/
protected function checkFormat( $format ) {
if ( !$this->isSupportedFormat( $format ) ) {
@@ -144,48 +139,50 @@ abstract class AbstractContent implements Content {
}
/**
- * @see Content::serialize
- *
- * @param string|null $format
- *
* @since 1.21
*
+ * @param string $format
+ *
* @return string
+ *
+ * @see Content::serialize
*/
public function serialize( $format = null ) {
return $this->getContentHandler()->serializeContent( $this, $format );
}
/**
- * @see Content::isEmpty
- *
* @since 1.21
*
- * @return boolean
+ * @return bool
+ *
+ * @see Content::isEmpty
*/
public function isEmpty() {
return $this->getSize() === 0;
}
/**
- * @see Content::isValid
+ * Subclasses may override this to implement (light weight) validation.
*
* @since 1.21
*
- * @return boolean
+ * @return bool Always true.
+ *
+ * @see Content::isValid
*/
public function isValid() {
return true;
}
/**
- * @see Content::equals
- *
* @since 1.21
*
- * @param Content|null $that
+ * @param Content $that
+ *
+ * @return bool
*
- * @return boolean
+ * @see Content::equals
*/
public function equals( Content $that = null ) {
if ( is_null( $that ) ) {
@@ -215,26 +212,19 @@ abstract class AbstractContent implements Content {
* Subclasses may override this to determine the secondary data updates more
* efficiently, preferably without the need to generate a parser output object.
*
- * @see Content::getSecondaryDataUpdates()
+ * @since 1.21
*
- * @param $title Title The context for determining the necessary updates
- * @param $old Content|null An optional Content object representing the
- * previous content, i.e. the content being replaced by this Content
- * object.
- * @param $recursive boolean Whether to include recursive updates (default:
- * false).
- * @param $parserOutput ParserOutput|null Optional ParserOutput object.
- * Provide if you have one handy, to avoid re-parsing of the content.
+ * @param Title $title
+ * @param Content $old
+ * @param bool $recursive
+ * @param ParserOutput $parserOutput
*
- * @return Array. A list of DataUpdate objects for putting information
- * about this content object somewhere.
+ * @return DataUpdate[]
*
- * @since 1.21
+ * @see Content::getSecondaryDataUpdates()
*/
- public function getSecondaryDataUpdates( Title $title,
- Content $old = null,
- $recursive = true, ParserOutput $parserOutput = null
- ) {
+ public function getSecondaryDataUpdates( Title $title, Content $old = null,
+ $recursive = true, ParserOutput $parserOutput = null ) {
if ( $parserOutput === null ) {
$parserOutput = $this->getParserOutput( $title, null, null, false );
}
@@ -243,9 +233,11 @@ abstract class AbstractContent implements Content {
}
/**
- * @see Content::getRedirectChain
- *
* @since 1.21
+ *
+ * @return Title[]|null
+ *
+ * @see Content::getRedirectChain
*/
public function getRedirectChain() {
global $wgMaxRedirects;
@@ -264,7 +256,7 @@ abstract class AbstractContent implements Content {
break;
}
// Redirects to some special pages are not permitted
- if ( $newtitle instanceOf Title && $newtitle->isValidRedirectTarget() ) {
+ if ( $newtitle instanceof Title && $newtitle->isValidRedirectTarget() ) {
// The new title passes the checks, so make that our current
// title so that further recursion can be checked
$title = $newtitle;
@@ -273,104 +265,126 @@ abstract class AbstractContent implements Content {
break;
}
}
+
return $titles;
}
/**
- * @see Content::getRedirectTarget
+ * Subclasses that implement redirects should override this.
*
* @since 1.21
+ *
+ * @return null
+ *
+ * @see Content::getRedirectTarget
*/
public function getRedirectTarget() {
return null;
}
/**
- * @see Content::getUltimateRedirectTarget
- * @note: migrated here from Title::newFromRedirectRecurse
+ * @note Migrated here from Title::newFromRedirectRecurse.
*
* @since 1.21
+ *
+ * @return Title|null
+ *
+ * @see Content::getUltimateRedirectTarget
*/
public function getUltimateRedirectTarget() {
$titles = $this->getRedirectChain();
+
return $titles ? array_pop( $titles ) : null;
}
/**
- * @see Content::isRedirect
- *
* @since 1.21
*
* @return bool
+ *
+ * @see Content::isRedirect
*/
public function isRedirect() {
return $this->getRedirectTarget() !== null;
}
/**
- * @see Content::updateRedirect
- *
* This default implementation always returns $this.
- *
- * @param Title $target
+ * Subclasses that implement redirects should override this.
*
* @since 1.21
*
+ * @param Title $target
+ *
* @return Content $this
+ *
+ * @see Content::updateRedirect
*/
public function updateRedirect( Title $target ) {
return $this;
}
/**
- * @see Content::getSection
- *
* @since 1.21
+ *
+ * @return null
+ *
+ * @see Content::getSection
*/
public function getSection( $sectionId ) {
return null;
}
/**
- * @see Content::replaceSection
- *
* @since 1.21
+ *
+ * @return null
+ *
+ * @see Content::replaceSection
*/
- public function replaceSection( $section, Content $with, $sectionTitle = '' ) {
+ public function replaceSection( $sectionId, Content $with, $sectionTitle = '' ) {
return null;
}
/**
- * @see Content::preSaveTransform
- *
* @since 1.21
+ *
+ * @return Content $this
+ *
+ * @see Content::preSaveTransform
*/
public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
return $this;
}
/**
- * @see Content::addSectionHeader
- *
* @since 1.21
+ *
+ * @return Content $this
+ *
+ * @see Content::addSectionHeader
*/
public function addSectionHeader( $header ) {
return $this;
}
/**
- * @see Content::preloadTransform
- *
* @since 1.21
+ *
+ * @return Content $this
+ *
+ * @see Content::preloadTransform
*/
- public function preloadTransform( Title $title, ParserOptions $popts ) {
+ public function preloadTransform( Title $title, ParserOptions $popts, $params = array() ) {
return $this;
}
/**
- * @see Content::prepareSave
- *
* @since 1.21
+ *
+ * @return Status
+ *
+ * @see Content::prepareSave
*/
public function prepareSave( WikiPage $page, $flags, $baseRevId, User $user ) {
if ( $this->isValid() ) {
@@ -381,53 +395,47 @@ abstract class AbstractContent implements Content {
}
/**
- * @see Content::getDeletionUpdates
- *
* @since 1.21
*
- * @param $page WikiPage the deleted page
- * @param $parserOutput null|ParserOutput optional parser output object
- * for efficient access to meta-information about the content object.
- * Provide if you have one handy.
+ * @param WikiPage $page
+ * @param ParserOutput $parserOutput
+ *
+ * @return LinksDeletionUpdate[]
*
- * @return array A list of DataUpdate instances that will clean up the
- * database after deletion.
+ * @see Content::getDeletionUpdates
*/
- public function getDeletionUpdates( WikiPage $page,
- ParserOutput $parserOutput = null )
- {
+ public function getDeletionUpdates( WikiPage $page, ParserOutput $parserOutput = null ) {
return array(
new LinksDeletionUpdate( $page ),
);
}
/**
- * This default implementation always returns false. Subclasses may override this to supply matching logic.
- *
- * @see Content::matchMagicWord
+ * This default implementation always returns false. Subclasses may override
+ * this to supply matching logic.
*
* @since 1.21
*
* @param MagicWord $word
*
- * @return bool
+ * @return bool Always false.
+ *
+ * @see Content::matchMagicWord
*/
public function matchMagicWord( MagicWord $word ) {
return false;
}
/**
- * @see Content::convert()
- *
* This base implementation calls the hook ConvertContent to enable custom conversions.
* Subclasses may override this to implement conversion for "their" content model.
*
- * @param string $toModel the desired content model, use the CONTENT_MODEL_XXX flags.
- * @param string $lossy flag, set to "lossy" to allow lossy conversion. If lossy conversion is
- * not allowed, full round-trip conversion is expected to work without losing information.
+ * @param string $toModel
+ * @param string $lossy
*
- * @return Content|bool A content object with the content model $toModel, or false if
- * that conversion is not supported.
+ * @return Content|bool
+ *
+ * @see Content::convert()
*/
public function convert( $toModel, $lossy = '' ) {
if ( $this->getModel() === $toModel ) {
@@ -439,6 +447,77 @@ abstract class AbstractContent implements Content {
$result = false;
wfRunHooks( 'ConvertContent', array( $this, $toModel, $lossy, &$result ) );
+
return $result;
}
+
+ /**
+ * Returns a ParserOutput object containing information derived from this content.
+ * Most importantly, unless $generateHtml was false, the return value contains an
+ * HTML representation of the content.
+ *
+ * Subclasses that want to control the parser output may override this, but it is
+ * preferred to override fillParserOutput() instead.
+ *
+ * Subclasses that override getParserOutput() itself should take care to call the
+ * ContentGetParserOutput hook.
+ *
+ * @since 1.24
+ *
+ * @param Title $title Context title for parsing
+ * @param int|null $revId Revision ID (for {{REVISIONID}})
+ * @param ParserOptions|null $options Parser options
+ * @param bool $generateHtml Whether or not to generate HTML
+ *
+ * @return ParserOutput Containing information derived from this content.
+ */
+ public function getParserOutput( Title $title, $revId = null,
+ ParserOptions $options = null, $generateHtml = true
+ ) {
+ if ( $options === null ) {
+ $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
+ }
+
+ $po = new ParserOutput();
+
+ if ( wfRunHooks( 'ContentGetParserOutput',
+ array( $this, $title, $revId, $options, $generateHtml, &$po ) ) ) {
+
+ // Save and restore the old value, just in case something is reusing
+ // the ParserOptions object in some weird way.
+ $oldRedir = $options->getRedirectTarget();
+ $options->setRedirectTarget( $this->getRedirectTarget() );
+ $this->fillParserOutput( $title, $revId, $options, $generateHtml, $po );
+ $options->setRedirectTarget( $oldRedir );
+ }
+
+ return $po;
+ }
+
+ /**
+ * Fills the provided ParserOutput with information derived from the content.
+ * Unless $generateHtml was false, this includes an HTML representation of the content.
+ *
+ * This is called by getParserOutput() after consulting the ContentGetParserOutput hook.
+ * Subclasses are expected to override this method (or getParserOutput(), if need be).
+ * Subclasses of TextContent should generally override getHtml() instead.
+ *
+ * This placeholder implementation always throws an exception.
+ *
+ * @since 1.24
+ *
+ * @param Title $title Context title for parsing
+ * @param int|null $revId Revision ID (for {{REVISIONID}})
+ * @param ParserOptions $options Parser options
+ * @param bool $generateHtml Whether or not to generate HTML
+ * @param ParserOutput &$output The output object to fill (reference).
+ *
+ * @throws MWException
+ */
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output
+ ) {
+ // Don't make abstract, so subclasses that override getParserOutput() directly don't fail.
+ throw new MWException( 'Subclasses of AbstractContent must override fillParserOutput!' );
+ }
}
diff --git a/includes/content/CodeContentHandler.php b/includes/content/CodeContentHandler.php
new file mode 100644
index 00000000..447a2a73
--- /dev/null
+++ b/includes/content/CodeContentHandler.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Content handler for the pages with code, such as CSS, JavaScript, JSON.
+ *
+ * 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
+ *
+ * @file
+ * @ingroup Content
+ */
+
+/**
+ * Content handler for code content such as CSS, JavaScript, JSON, etc
+ * @since 1.24
+ * @ingroup Content
+ */
+abstract class CodeContentHandler extends TextContentHandler {
+
+ /**
+ * Returns the english language, because code is english, and should be handled as such.
+ *
+ * @param Title $title
+ * @param Content $content
+ *
+ * @return Language Return of wfGetLangObj( 'en' )
+ *
+ * @see ContentHandler::getPageLanguage()
+ */
+ public function getPageLanguage( Title $title, Content $content = null ) {
+ return wfGetLangObj( 'en' );
+ }
+
+ /**
+ * Returns the english language, because code is english, and should be handled as such.
+ *
+ * @param Title $title
+ * @param Content $content
+ *
+ * @return Language Return of wfGetLangObj( 'en' )
+ *
+ * @see ContentHandler::getPageViewLanguage()
+ */
+ public function getPageViewLanguage( Title $title, Content $content = null ) {
+ return wfGetLangObj( 'en' );
+ }
+
+ /**
+ * @return string
+ */
+ protected function getContentClass() {
+ throw new MWException( 'Subclass must override' );
+ }
+}
diff --git a/includes/content/Content.php b/includes/content/Content.php
index 5a90e092..61b9254f 100644
--- a/includes/content/Content.php
+++ b/includes/content/Content.php
@@ -48,7 +48,7 @@ interface Content {
/**
* @since 1.21
*
- * @return string|false The wikitext to include when another page includes this
+ * @return string|bool The wikitext to include when another page includes this
* content, or false if the content is not includable in a wikitext page.
*
* @todo Allow native handling, bypassing wikitext representation, like
@@ -65,8 +65,9 @@ interface Content {
*
* @since 1.21
*
- * @param int $maxLength Maximum length of the summary text
- * @return string The summary text
+ * @param int $maxLength Maximum length of the summary text.
+ *
+ * @return string The summary text.
*/
public function getTextForSummary( $maxLength = 250 );
@@ -85,7 +86,7 @@ interface Content {
public function getNativeData();
/**
- * Returns the content's nominal size in bogo-bytes.
+ * Returns the content's nominal size in "bogo-bytes".
*
* @return int
*/
@@ -97,7 +98,7 @@ interface Content {
*
* @since 1.21
*
- * @return String The model id
+ * @return string The model id
*/
public function getModel();
@@ -121,7 +122,7 @@ interface Content {
*
* @since 1.21
*
- * @return String
+ * @return string
*/
public function getDefaultFormat();
@@ -133,7 +134,7 @@ interface Content {
*
* @since 1.21
*
- * @return Array of supported serialization formats
+ * @return string[] List of supported serialization formats
*/
public function getSupportedFormats();
@@ -148,7 +149,8 @@ interface Content {
*
* @since 1.21
*
- * @param string $format The format to check
+ * @param string $format The serialization format to check.
+ *
* @return bool Whether the format is supported
*/
public function isSupportedFormat( $format );
@@ -160,9 +162,9 @@ interface Content {
*
* @since 1.21
*
- * @param $format null|string The desired serialization format (or null for
- * the default format).
- * @return string Serialized form of this Content object
+ * @param string $format The desired serialization format, or null for the default format.
+ *
+ * @return string Serialized form of this Content object.
*/
public function serialize( $format = null );
@@ -185,7 +187,7 @@ interface Content {
*
* @since 1.21
*
- * @return boolean
+ * @return bool
*/
public function isValid();
@@ -208,7 +210,8 @@ interface Content {
*
* @since 1.21
*
- * @param $that Content The Content object to compare to
+ * @param Content $that The Content object to compare to.
+ *
* @return bool True if this Content object is equal to $that, false otherwise.
*/
public function equals( Content $that = null );
@@ -229,7 +232,7 @@ interface Content {
*
* @since 1.21
*
- * @return Content. A copy of this object
+ * @return Content A copy of this object
*/
public function copy();
@@ -243,7 +246,8 @@ interface Content {
* @param bool $hasLinks If it is known whether this content contains
* links, provide this information here, to avoid redundant parsing to
* find out.
- * @return boolean
+ *
+ * @return bool
*/
public function isCountable( $hasLinks = null );
@@ -253,10 +257,14 @@ interface Content {
* is needed, $generateHtml can be set to false; in that case,
* $result->getText() may return null.
*
- * @param $title Title The page title to use as a context for rendering
- * @param $revId null|int The revision being rendered (optional)
- * @param $options null|ParserOptions Any parser options
- * @param $generateHtml Boolean Whether to generate HTML (default: true). If false,
+ * @note To control which options are used in the cache key for the
+ * generated parser output, implementations of this method
+ * may call ParserOutput::recordOption() on the output object.
+ *
+ * @param Title $title The page title to use as a context for rendering.
+ * @param int $revId Optional revision ID being rendered.
+ * @param ParserOptions $options Any parser options.
+ * @param bool $generateHtml Whether to generate HTML (default: true). If false,
* the result of calling getText() on the ParserOutput object returned by
* this method is undefined.
*
@@ -264,9 +272,9 @@ interface Content {
*
* @return ParserOutput
*/
- public function getParserOutput( Title $title,
- $revId = null,
+ public function getParserOutput( Title $title, $revId = null,
ParserOptions $options = null, $generateHtml = true );
+
// TODO: make RenderOutput and RenderOptions base classes
/**
@@ -284,24 +292,22 @@ interface Content {
* Subclasses may implement this to determine the necessary updates more
* efficiently, or make use of information about the old content.
*
- * @param $title Title The context for determining the necessary updates
- * @param $old Content|null An optional Content object representing the
+ * @param Title $title The context for determining the necessary updates
+ * @param Content $old An optional Content object representing the
* previous content, i.e. the content being replaced by this Content
* object.
- * @param $recursive boolean Whether to include recursive updates (default:
+ * @param bool $recursive Whether to include recursive updates (default:
* false).
- * @param $parserOutput ParserOutput|null Optional ParserOutput object.
+ * @param ParserOutput $parserOutput Optional ParserOutput object.
* Provide if you have one handy, to avoid re-parsing of the content.
*
- * @return Array. A list of DataUpdate objects for putting information
+ * @return DataUpdate[] A list of DataUpdate objects for putting information
* about this content object somewhere.
*
* @since 1.21
*/
- public function getSecondaryDataUpdates( Title $title,
- Content $old = null,
- $recursive = true, ParserOutput $parserOutput = null
- );
+ public function getSecondaryDataUpdates( Title $title, Content $old = null,
+ $recursive = true, ParserOutput $parserOutput = null );
/**
* Construct the redirect destination from this content and return an
@@ -311,7 +317,7 @@ interface Content {
*
* @since 1.21
*
- * @return Array of Titles, with the destination last
+ * @return Title[]|null List of Titles, with the destination last.
*/
public function getRedirectChain();
@@ -323,7 +329,7 @@ interface Content {
*
* @since 1.21
*
- * @return Title: The corresponding Title
+ * @return Title|null The corresponding Title.
*/
public function getRedirectTarget();
@@ -340,7 +346,7 @@ interface Content {
*
* @since 1.21
*
- * @return Title
+ * @return Title|null
*/
public function getUltimateRedirectTarget();
@@ -360,9 +366,10 @@ interface Content {
*
* @since 1.21
*
- * @param Title $target the new redirect target
+ * @param Title $target The new redirect target
*
- * @return Content a new Content object with the updated redirect (or $this if this Content object isn't a redirect)
+ * @return Content A new Content object with the updated redirect (or $this
+ * if this Content object isn't a redirect)
*/
public function updateRedirect( Title $target );
@@ -371,11 +378,11 @@ interface Content {
*
* @since 1.21
*
- * @param string $sectionId The section's ID, given as a numeric string.
- * The ID "0" retrieves the section before the first heading, "1" the
- * text between the first heading (included) and the second heading
- * (excluded), etc.
- * @return Content|Boolean|null The section, or false if no such section
+ * @param string|number $sectionId Section identifier as a number or string
+ * (e.g. 0, 1 or 'T-1'). The ID "0" retrieves the section before the first heading, "1" the
+ * text between the first heading (included) and the second heading (excluded), etc.
+ *
+ * @return Content|bool|null The section, or false if no such section
* exist, or null if sections are not supported.
*/
public function getSection( $sectionId );
@@ -386,12 +393,15 @@ interface Content {
*
* @since 1.21
*
- * @param $section null/false or a section number (0, 1, 2, T1, T2...), or "new"
- * @param $with Content: new content of the section
- * @param string $sectionTitle new section's subject, only if $section is 'new'
- * @return string Complete article text, or null if error
+ * @param string|number|null|bool $sectionId Section identifier as a number or string
+ * (e.g. 0, 1 or 'T-1'), null/false or an empty string for the whole page
+ * or 'new' for a new section.
+ * @param Content $with New content of the section
+ * @param string $sectionTitle New section's subject, only if $section is 'new'
+ *
+ * @return string|null Complete article text, or null if error
*/
- public function replaceSection( $section, Content $with, $sectionTitle = '' );
+ public function replaceSection( $sectionId, Content $with, $sectionTitle = '' );
/**
* Returns a Content object with pre-save transformations applied (or this
@@ -399,9 +409,10 @@ interface Content {
*
* @since 1.21
*
- * @param $title Title
- * @param $user User
- * @param $parserOptions null|ParserOptions
+ * @param Title $title
+ * @param User $user
+ * @param ParserOptions $parserOptions
+ *
* @return Content
*/
public function preSaveTransform( Title $title, User $user, ParserOptions $parserOptions );
@@ -413,7 +424,8 @@ interface Content {
*
* @since 1.21
*
- * @param $header string
+ * @param string $header
+ *
* @return Content
*/
public function addSectionHeader( $header );
@@ -424,11 +436,13 @@ interface Content {
*
* @since 1.21
*
- * @param $title Title
- * @param $parserOptions null|ParserOptions
+ * @param Title $title
+ * @param ParserOptions $parserOptions
+ * @param array $params
+ *
* @return Content
*/
- public function preloadTransform( Title $title, ParserOptions $parserOptions );
+ public function preloadTransform( Title $title, ParserOptions $parserOptions, $params = array() );
/**
* Prepare Content for saving. Called before Content is saved by WikiPage::doEditContent() and in
@@ -437,24 +451,24 @@ interface Content {
* This may be used to check the content's consistency with global state. This function should
* NOT write any information to the database.
*
- * Note that this method will usually be called inside the same transaction bracket that will be used
- * to save the new revision.
+ * Note that this method will usually be called inside the same transaction
+ * bracket that will be used to save the new revision.
*
- * Note that this method is called before any update to the page table is performed. This means that
- * $page may not yet know a page ID.
+ * Note that this method is called before any update to the page table is
+ * performed. This means that $page may not yet know a page ID.
*
* @since 1.21
*
* @param WikiPage $page The page to be saved.
- * @param int $flags bitfield for use with EDIT_XXX constants, see WikiPage::doEditContent()
- * @param int $baseRevId the ID of the current revision
- * @param User $user
+ * @param int $flags Bitfield for use with EDIT_XXX constants, see WikiPage::doEditContent()
+ * @param int $baseRevId The ID of the current revision
+ * @param User $user
*
- * @return Status A status object indicating whether the content was successfully prepared for saving.
- * If the returned status indicates an error, a rollback will be performed and the
- * transaction aborted.
+ * @return Status A status object indicating whether the content was
+ * successfully prepared for saving. If the returned status indicates
+ * an error, a rollback will be performed and the transaction aborted.
*
- * @see see WikiPage::doEditContent()
+ * @see WikiPage::doEditContent()
*/
public function prepareSave( WikiPage $page, $flags, $baseRevId, User $user );
@@ -465,12 +479,12 @@ interface Content {
*
* @since 1.21
*
- * @param $page WikiPage the deleted page
- * @param $parserOutput null|ParserOutput optional parser output object
+ * @param WikiPage $page The deleted page
+ * @param ParserOutput $parserOutput Optional parser output object
* for efficient access to meta-information about the content object.
* Provide if you have one handy.
*
- * @return array A list of DataUpdate instances that will clean up the
+ * @return DataUpdate[] A list of DataUpdate instances that will clean up the
* database after deletion.
*/
public function getDeletionUpdates( WikiPage $page,
@@ -481,9 +495,9 @@ interface Content {
*
* @since 1.21
*
- * @param MagicWord $word the magic word to match
+ * @param MagicWord $word The magic word to match
*
- * @return bool whether this Content object matches the given magic word.
+ * @return bool Whether this Content object matches the given magic word.
*/
public function matchMagicWord( MagicWord $word );
@@ -491,18 +505,19 @@ interface Content {
* Converts this content object into another content object with the given content model,
* if that is possible.
*
- * @param string $toModel the desired content model, use the CONTENT_MODEL_XXX flags.
- * @param string $lossy flag, set to "lossy" to allow lossy conversion. If lossy conversion is
- * not allowed, full round-trip conversion is expected to work without losing information.
+ * @param string $toModel The desired content model, use the CONTENT_MODEL_XXX flags.
+ * @param string $lossy Optional flag, set to "lossy" to allow lossy conversion. If lossy
+ * conversion is not allowed, full round-trip conversion is expected to work without losing
+ * information.
*
* @return Content|bool A content object with the content model $toModel, or false if
* that conversion is not supported.
*/
public function convert( $toModel, $lossy = '' );
-
- // TODO: ImagePage and CategoryPage interfere with per-content action handlers
- // TODO: nice&sane integration of GeSHi syntax highlighting
+ // @todo ImagePage and CategoryPage interfere with per-content action handlers
+ // @todo nice&sane integration of GeSHi syntax highlighting
// [11:59] <vvv> Hooks are ugly; make CodeHighlighter interface and a
// config to set the class which handles syntax highlighting
// [12:00] <vvv> And default it to a DummyHighlighter
+
}
diff --git a/includes/content/ContentHandler.php b/includes/content/ContentHandler.php
index 2a92e233..ac417223 100644
--- a/includes/content/ContentHandler.php
+++ b/includes/content/ContentHandler.php
@@ -31,7 +31,6 @@
* @ingroup Content
*/
class MWContentSerializationException extends MWException {
-
}
/**
@@ -54,7 +53,6 @@ class MWContentSerializationException extends MWException {
* @ingroup Content
*/
abstract class ContentHandler {
-
/**
* Switch for enabling deprecation warnings. Used by ContentHandler::deprecated()
* and ContentHandler::runLegacyHooks().
@@ -87,10 +85,11 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $content Content|null
- * @return null|string the textual form of $content, if available
- * @throws MWException if $content is not an instance of TextContent and
- * $wgContentHandlerTextFallback was set to 'fail'.
+ * @param Content $content
+ *
+ * @throws MWException If the content is not an instance of TextContent and
+ * wgContentHandlerTextFallback was set to 'fail'.
+ * @return string|null Textual form of the content, if available.
*/
public static function getContentText( Content $content = null ) {
global $wgContentHandlerTextFallback;
@@ -129,24 +128,21 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param string $text the textual representation, will be
+ * @param string $text The textual representation, will be
* unserialized to create the Content object
- * @param $title null|Title the title of the page this text belongs to.
+ * @param Title $title The title of the page this text belongs to.
* Required if $modelId is not provided.
- * @param $modelId null|string the model to deserialize to. If not provided,
+ * @param string $modelId The model to deserialize to. If not provided,
* $title->getContentModel() is used.
- * @param $format null|string the format to use for deserialization. If not
+ * @param string $format The format to use for deserialization. If not
* given, the model's default format is used.
*
- * @throws MWException
- * @return Content a Content object representing $text
- *
- * @throws MWException if $model or $format is not supported or if $text can
- * not be unserialized using $format.
+ * @throws MWException If model ID or format is not supported or if the text can not be
+ * unserialized using the format.
+ * @return Content A Content object representing the text.
*/
public static function makeContent( $text, Title $title = null,
- $modelId = null, $format = null )
- {
+ $modelId = null, $format = null ) {
if ( is_null( $modelId ) ) {
if ( is_null( $title ) ) {
throw new MWException( "Must provide a Title object or a content model ID." );
@@ -156,6 +152,7 @@ abstract class ContentHandler {
}
$handler = ContentHandler::getForModelID( $modelId );
+
return $handler->unserializeContent( $text, $format );
}
@@ -189,8 +186,9 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $title Title
- * @return null|string default model name for the page given by $title
+ * @param Title $title
+ *
+ * @return string Default model name for the page given by $title
*/
public static function getDefaultModelFor( Title $title ) {
// NOTE: this method must not rely on $title->getContentModel() directly or indirectly,
@@ -254,11 +252,13 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $title Title
+ * @param Title $title
+ *
* @return ContentHandler
*/
public static function getForTitle( Title $title ) {
$modelId = $title->getContentModel();
+
return ContentHandler::getForModelID( $modelId );
}
@@ -268,18 +268,20 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $content Content
+ * @param Content $content
+ *
* @return ContentHandler
*/
public static function getForContent( Content $content ) {
$modelId = $content->getModel();
+
return ContentHandler::getForModelID( $modelId );
}
/**
- * @var Array A Cache of ContentHandler instances by model id
+ * @var array A Cache of ContentHandler instances by model id
*/
- static $handlers;
+ protected static $handlers;
/**
* Returns the ContentHandler singleton for the given model ID. Use the
@@ -302,9 +304,9 @@ abstract class ContentHandler {
*
* @param string $modelId The ID of the content model for which to get a
* handler. Use CONTENT_MODEL_XXX constants.
- * @return ContentHandler The ContentHandler singleton for handling the
- * model given by $modelId
- * @throws MWException if no handler is known for $modelId.
+ *
+ * @throws MWException If no handler is known for the model ID.
+ * @return ContentHandler The ContentHandler singleton for handling the model given by the ID.
*/
public static function getForModelID( $modelId ) {
global $wgContentHandlers;
@@ -330,14 +332,16 @@ abstract class ContentHandler {
$handler = new $class( $modelId );
if ( !( $handler instanceof ContentHandler ) ) {
- throw new MWException( "$class from \$wgContentHandlers is not compatible with ContentHandler" );
+ throw new MWException( "$class from \$wgContentHandlers is not " .
+ "compatible with ContentHandler" );
}
}
wfDebugLog( 'ContentHandler', 'Created handler for ' . $modelId
- . ': ' . get_class( $handler ) );
+ . ': ' . get_class( $handler ) );
ContentHandler::$handlers[$modelId] = $handler;
+
return ContentHandler::$handlers[$modelId];
}
@@ -350,10 +354,12 @@ abstract class ContentHandler {
* @param string $name The content model ID, as given by a CONTENT_MODEL_XXX
* constant or returned by Revision::getContentModel().
*
- * @return string The content format's localized name.
- * @throws MWException if the model id isn't known.
+ * @throws MWException If the model ID isn't known.
+ * @return string The content model's localized name.
*/
public static function getLocalizedName( $name ) {
+ // Messages: content-model-wikitext, content-model-text,
+ // content-model-javascript, content-model-css
$key = "content-model-$name";
$msg = wfMessage( $key );
@@ -378,12 +384,20 @@ abstract class ContentHandler {
}
$formats = array_unique( $formats );
+
return $formats;
}
// ------------------------------------------------------------------------
+ /**
+ * @var string
+ */
protected $mModelID;
+
+ /**
+ * @var string[]
+ */
protected $mSupportedFormats;
/**
@@ -392,7 +406,7 @@ abstract class ContentHandler {
* provided as literals by subclass's constructors.
*
* @param string $modelId (use CONTENT_MODEL_XXX constants).
- * @param array $formats List for supported serialization formats
+ * @param string[] $formats List for supported serialization formats
* (typically as MIME types)
*/
public function __construct( $modelId, $formats ) {
@@ -409,24 +423,55 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $content Content The Content object to serialize
- * @param $format null|String The desired serialization format
+ * @param Content $content The Content object to serialize
+ * @param string $format The desired serialization format
+ *
* @return string Serialized form of the content
*/
abstract public function serializeContent( Content $content, $format = null );
/**
+ * Applies transformations on export (returns the blob unchanged per default).
+ * Subclasses may override this to perform transformations such as conversion
+ * of legacy formats or filtering of internal meta-data.
+ *
+ * @param string $blob The blob to be exported
+ * @param string|null $format The blob's serialization format
+ *
+ * @return string
+ */
+ public function exportTransform( $blob, $format = null ) {
+ return $blob;
+ }
+
+ /**
* Unserializes a Content object of the type supported by this ContentHandler.
*
* @since 1.21
*
- * @param string $blob serialized form of the content
- * @param $format null|String the format used for serialization
- * @return Content the Content object created by deserializing $blob
+ * @param string $blob Serialized form of the content
+ * @param string $format The format used for serialization
+ *
+ * @return Content The Content object created by deserializing $blob
*/
abstract public function unserializeContent( $blob, $format = null );
/**
+ * Apply import transformation (per default, returns $blob unchanged).
+ * This gives subclasses an opportunity to transform data blobs on import.
+ *
+ * @since 1.24
+ *
+ * @param string $blob
+ * @param string|null $format
+ *
+ * @return string
+ */
+ public function importTransform( $blob, $format = null ) {
+ return $blob;
+ }
+
+ /**
* Creates an empty Content object of the type supported by this
* ContentHandler.
*
@@ -438,7 +483,7 @@ abstract class ContentHandler {
/**
* Creates a new Content object that acts as a redirect to the given page,
- * or null of redirects are not supported by this content model.
+ * or null if redirects are not supported by this content model.
*
* This default implementation always returns null. Subclasses supporting redirects
* must override this method.
@@ -448,10 +493,10 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param Title $destination the page to redirect to.
- * @param string $text text to include in the redirect, if possible.
+ * @param Title $destination The page to redirect to.
+ * @param string $text Text to include in the redirect, if possible.
*
- * @return Content
+ * @return Content Always null.
*/
public function makeRedirectContent( Title $destination, $text = '' ) {
return null;
@@ -463,21 +508,19 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return String The model ID
+ * @return string The model ID
*/
public function getModelID() {
return $this->mModelID;
}
/**
- * Throws an MWException if $model_id is not the ID of the content model
- * supported by this ContentHandler.
- *
* @since 1.21
*
* @param string $model_id The model to check
*
- * @throws MWException
+ * @throws MWException If the model ID is not the ID of the content model supported by this
+ * ContentHandler.
*/
protected function checkModelID( $model_id ) {
if ( $model_id !== $this->mModelID ) {
@@ -494,7 +537,7 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return array of serialization formats as MIME type like strings
+ * @return string[] List of serialization formats as MIME type like strings
*/
public function getSupportedFormats() {
return $this->mSupportedFormats;
@@ -509,7 +552,7 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return string the name of the default serialization format as a MIME type
+ * @return string The name of the default serialization format as a MIME type
*/
public function getDefaultFormat() {
return $this->mSupportedFormats[0];
@@ -524,11 +567,11 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param string $format the serialization format to check
+ * @param string $format The serialization format to check
+ *
* @return bool
*/
public function isSupportedFormat( $format ) {
-
if ( !$format ) {
return true; // this means "use the default"
}
@@ -537,13 +580,11 @@ abstract class ContentHandler {
}
/**
- * Throws an MWException if isSupportedFormat( $format ) is not true.
- * Convenient for checking whether a format provided as a parameter is
- * actually supported.
+ * Convenient for checking whether a format provided as a parameter is actually supported.
*
- * @param string $format the serialization format to check
+ * @param string $format The serialization format to check
*
- * @throws MWException
+ * @throws MWException If the format is not supported by this content handler.
*/
protected function checkFormat( $format ) {
if ( !$this->isSupportedFormat( $format ) ) {
@@ -562,7 +603,7 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return Array
+ * @return array Always an empty array.
*/
public function getActionOverrides() {
return array();
@@ -573,21 +614,18 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $context IContextSource context to use, anything else will be
- * ignored
- * @param $old Integer Old ID we want to show and diff with.
- * @param int|string $new String either 'prev' or 'next'.
- * @param $rcid Integer ??? FIXME (default 0)
- * @param $refreshCache boolean If set, refreshes the diff cache
- * @param $unhide boolean If set, allow viewing deleted revs
+ * @param IContextSource $context Context to use, anything else will be ignored.
+ * @param int $old Revision ID we want to show and diff with.
+ * @param int|string $new Either a revision ID or one of the strings 'cur', 'prev' or 'next'.
+ * @param int $rcid FIXME: Deprecated, no longer used. Defaults to 0.
+ * @param bool $refreshCache If set, refreshes the diff cache. Defaults to false.
+ * @param bool $unhide If set, allow viewing deleted revs. Defaults to false.
*
* @return DifferenceEngine
*/
- public function createDifferenceEngine( IContextSource $context,
- $old = 0, $new = 0,
- $rcid = 0, # FIXME: use everywhere!
- $refreshCache = false, $unhide = false
- ) {
+ public function createDifferenceEngine( IContextSource $context, $old = 0, $new = 0,
+ $rcid = 0, //FIXME: Deprecated, no longer used
+ $refreshCache = false, $unhide = false ) {
$diffEngineClass = $this->getDiffEngineClass();
return new $diffEngineClass( $context, $old, $new, $rcid, $refreshCache, $unhide );
@@ -596,19 +634,21 @@ abstract class ContentHandler {
/**
* Get the language in which the content of the given page is written.
*
- * This default implementation just returns $wgContLang (except for pages in the MediaWiki namespace)
+ * This default implementation just returns $wgContLang (except for pages
+ * in the MediaWiki namespace)
*
- * Note that the pages language is not cacheable, since it may in some cases depend on user settings.
+ * Note that the pages language is not cacheable, since it may in some
+ * cases depend on user settings.
*
* Also note that the page language may or may not depend on the actual content of the page,
* that is, this method may load the content in order to determine the language.
*
* @since 1.21
*
- * @param Title $title the page to determine the language for.
- * @param Content|null $content the page's content, if you have it handy, to avoid reloading it.
+ * @param Title $title The page to determine the language for.
+ * @param Content $content The page's content, if you have it handy, to avoid reloading it.
*
- * @return Language the page's language
+ * @return Language The page's language
*/
public function getPageLanguage( Title $title, Content $content = null ) {
global $wgContLang, $wgLang;
@@ -621,6 +661,7 @@ abstract class ContentHandler {
}
wfRunHooks( 'PageContentLanguage', array( $title, &$pageLang, $wgLang ) );
+
return wfGetLangObj( $pageLang );
}
@@ -639,10 +680,10 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param Title $title the page to determine the language for.
- * @param Content|null $content the page's content, if you have it handy, to avoid reloading it.
+ * @param Title $title The page to determine the language for.
+ * @param Content $content The page's content, if you have it handy, to avoid reloading it.
*
- * @return Language the page's language for viewing
+ * @return Language The page's language for viewing
*/
public function getPageViewLanguage( Title $title, Content $content = null ) {
$pageLang = $this->getPageLanguage( $title, $content );
@@ -668,12 +709,19 @@ abstract class ContentHandler {
* typically based on the namespace or some other aspect of the title, such as a special suffix
* (e.g. ".svg" for SVG content).
*
- * @param Title $title the page's title.
+ * @note this calls the ContentHandlerCanBeUsedOn hook which may be used to override which
+ * content model can be used where.
*
- * @return bool true if content of this kind can be used on the given page, false otherwise.
+ * @param Title $title The page's title.
+ *
+ * @return bool True if content of this kind can be used on the given page, false otherwise.
*/
public function canBeUsedOn( Title $title ) {
- return true;
+ $ok = true;
+
+ wfRunHooks( 'ContentModelCanBeUsedOn', array( $this->getModelID(), $title, &$ok ) );
+
+ return $ok;
}
/**
@@ -688,19 +736,18 @@ abstract class ContentHandler {
}
/**
- * Attempts to merge differences between three versions.
- * Returns a new Content object for a clean merge and false for failure or
- * a conflict.
+ * Attempts to merge differences between three versions. Returns a new
+ * Content object for a clean merge and false for failure or a conflict.
*
* This default implementation always returns false.
*
* @since 1.21
*
- * @param $oldContent Content|string String
- * @param $myContent Content|string String
- * @param $yourContent Content|string String
+ * @param Content $oldContent The page's previous content.
+ * @param Content $myContent One of the page's conflicting contents.
+ * @param Content $yourContent One of the page's conflicting contents.
*
- * @return Content|Bool
+ * @return Content|bool Always false.
*/
public function merge3( Content $oldContent, Content $myContent, Content $yourContent ) {
return false;
@@ -711,13 +758,14 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $oldContent Content|null: the previous text of the page.
- * @param $newContent Content|null: The submitted text of the page.
+ * @param Content $oldContent The previous text of the page.
+ * @param Content $newContent The submitted text of the page.
* @param int $flags Bit mask: a bit mask of flags submitted for the edit.
*
* @return string An appropriate auto-summary, or an empty string.
*/
- public function getAutosummary( Content $oldContent = null, Content $newContent = null, $flags ) {
+ public function getAutosummary( Content $oldContent = null, Content $newContent = null,
+ $flags ) {
// Decide what kind of auto-summary is needed.
// Redirect auto-summaries
@@ -733,15 +781,15 @@ abstract class ContentHandler {
if ( is_object( $rt ) ) {
if ( !is_object( $ot )
|| !$rt->equals( $ot )
- || $ot->getFragment() != $rt->getFragment() )
- {
+ || $ot->getFragment() != $rt->getFragment()
+ ) {
$truncatedtext = $newContent->getTextForSummary(
250
- - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
- - strlen( $rt->getFullText() ) );
+ - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
+ - strlen( $rt->getFullText() ) );
return wfMessage( 'autoredircomment', $rt->getFullText() )
- ->rawParams( $truncatedtext )->inContentLanguage()->text();
+ ->rawParams( $truncatedtext )->inContentLanguage()->text();
}
}
@@ -754,7 +802,7 @@ abstract class ContentHandler {
200 - strlen( wfMessage( 'autosumm-new' )->inContentLanguage()->text() ) );
return wfMessage( 'autosumm-new' )->rawParams( $truncatedtext )
- ->inContentLanguage()->text();
+ ->inContentLanguage()->text();
}
// Blanking auto-summaries
@@ -762,15 +810,20 @@ abstract class ContentHandler {
return wfMessage( 'autosumm-blank' )->inContentLanguage()->text();
} elseif ( !empty( $oldContent )
&& $oldContent->getSize() > 10 * $newContent->getSize()
- && $newContent->getSize() < 500 )
- {
+ && $newContent->getSize() < 500
+ ) {
// Removing more than 90% of the article
$truncatedtext = $newContent->getTextForSummary(
200 - strlen( wfMessage( 'autosumm-replace' )->inContentLanguage()->text() ) );
return wfMessage( 'autosumm-replace' )->rawParams( $truncatedtext )
- ->inContentLanguage()->text();
+ ->inContentLanguage()->text();
+ }
+
+ // New blank article auto-summary
+ if ( $flags & EDIT_NEW && $newContent->isEmpty() ) {
+ return wfMessage( 'autosumm-newblank' )->inContentLanguage()->text();
}
// If we reach this point, there's no applicable auto-summary for our
@@ -783,12 +836,13 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $title Title: the page's title
- * @param &$hasHistory Boolean: whether the page has a history
+ * @param Title $title The page's title
+ * @param bool &$hasHistory Whether the page has a history
+ *
* @return mixed String containing deletion reason or empty string, or
* boolean false if no revision occurred
*
- * @XXX &$hasHistory is extremely ugly, it's here because
+ * @todo &$hasHistory is extremely ugly, it's here because
* WikiPage::getAutoDeleteReason() and Article::generateReason()
* have it / want it.
*/
@@ -891,9 +945,9 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $current Revision The current text
- * @param $undo Revision The revision to undo
- * @param $undoafter Revision Must be an earlier revision than $undo
+ * @param Revision $current The current text
+ * @param Revision $undo The revision to undo
+ * @param Revision $undoafter Must be an earlier revision than $undo
*
* @return mixed String on success, false on failure
*/
@@ -907,6 +961,10 @@ abstract class ContentHandler {
$undo_content = $undo->getContent();
$undoafter_content = $undoafter->getContent();
+ if ( !$undo_content || !$undoafter_content ) {
+ return false; // no content to undo
+ }
+
$this->checkModelID( $cur_content->getModel() );
$this->checkModelID( $undo_content->getModel() );
$this->checkModelID( $undoafter_content->getModel() );
@@ -922,7 +980,7 @@ abstract class ContentHandler {
}
/**
- * Get parser options suitable for rendering the primary article wikitext
+ * Get parser options suitable for rendering and caching the article
*
* @param IContextSource|User|string $context One of the following:
* - IContextSource: Use the User and the Language of the provided
@@ -932,8 +990,6 @@ abstract class ContentHandler {
* - 'canonical': Canonical options (anonymous user with default
* preferences and content language).
*
- * @param IContextSource|User|string $context
- *
* @throws MWException
* @return ParserOptions
*/
@@ -962,7 +1018,7 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return bool
+ * @return bool Always false.
*/
public function isParserCacheSupported() {
return false;
@@ -975,7 +1031,7 @@ abstract class ContentHandler {
* Content models that return true here should also implement
* Content::getSection, Content::replaceSection, etc. to handle sections..
*
- * @return boolean whether sections are supported.
+ * @return bool Always false.
*/
public function supportsSections() {
return false;
@@ -988,7 +1044,7 @@ abstract class ContentHandler {
* Content models that return true here should also implement
* ContentHandler::makeRedirectContent to return a Content object.
*
- * @return boolean whether redirects are supported.
+ * @return bool Always false.
*/
public function supportsRedirects() {
return false;
@@ -998,11 +1054,11 @@ abstract class ContentHandler {
* Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if
* self::$enableDeprecationWarnings is set to true.
*
- * @param string $func The name of the deprecated function
- * @param string $version The version since the method is deprecated. Usually 1.21
- * for ContentHandler related stuff.
- * @param string|bool $component: Component to which the function belongs.
- * If false, it is assumed the function is in MediaWiki core.
+ * @param string $func The name of the deprecated function
+ * @param string $version The version since the method is deprecated. Usually 1.21
+ * for ContentHandler related stuff.
+ * @param string|bool $component : Component to which the function belongs.
+ * If false, it is assumed the function is in MediaWiki core.
*
* @see ContentHandler::$enableDeprecationWarnings
* @see wfDeprecated
@@ -1020,18 +1076,19 @@ abstract class ContentHandler {
* hook function, a new Content object is constructed from the new
* text.
*
- * @param string $event event name
- * @param array $args parameters passed to hook functions
- * @param bool $warn whether to log a warning.
+ * @param string $event Event name
+ * @param array $args Parameters passed to hook functions
+ * @param bool $warn Whether to log a warning.
* Default to self::$enableDeprecationWarnings.
* May be set to false for testing.
*
- * @return Boolean True if no handler aborted the hook
+ * @return bool True if no handler aborted the hook
*
* @see ContentHandler::$enableDeprecationWarnings
*/
public static function runLegacyHooks( $event, $args = array(),
- $warn = null ) {
+ $warn = null
+ ) {
if ( $warn === null ) {
$warn = self::$enableDeprecationWarnings;
@@ -1073,7 +1130,8 @@ abstract class ContentHandler {
wfRestoreWarnings();
- wfWarn( "Using obsolete hook $event via ContentHandler::runLegacyHooks()! Handlers: " . implode( ', ', $handlerInfo ), 2 );
+ wfWarn( "Using obsolete hook $event via ContentHandler::runLegacyHooks()! Handlers: " .
+ implode( ', ', $handlerInfo ), 2 );
}
// convert Content objects to text
diff --git a/includes/content/CssContent.php b/includes/content/CssContent.php
index 03cc2d00..8290603c 100644
--- a/includes/content/CssContent.php
+++ b/includes/content/CssContent.php
@@ -31,18 +31,26 @@
* @ingroup Content
*/
class CssContent extends TextContent {
- public function __construct( $text ) {
- parent::__construct( $text, CONTENT_MODEL_CSS );
+
+ /**
+ * @param string $text CSS code.
+ * @param string $modelId the content content model
+ */
+ public function __construct( $text, $modelId = CONTENT_MODEL_CSS ) {
+ parent::__construct( $text, $modelId );
}
/**
* Returns a Content object with pre-save transformations applied using
* Parser::preSaveTransform().
*
- * @param $title Title
- * @param $user User
- * @param $popts ParserOptions
- * @return Content
+ * @param Title $title
+ * @param User $user
+ * @param ParserOptions $popts
+ *
+ * @return CssContent
+ *
+ * @see TextContent::preSaveTransform
*/
public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
global $wgParser;
@@ -51,15 +59,19 @@ class CssContent extends TextContent {
$text = $this->getNativeData();
$pst = $wgParser->preSaveTransform( $text, $title, $user, $popts );
- return new CssContent( $pst );
+ return new static( $pst );
}
+ /**
+ * @return string CSS wrapped in a <pre> tag.
+ */
protected function getHtml() {
$html = "";
$html .= "<pre class=\"mw-code mw-css\" dir=\"ltr\">\n";
- $html .= $this->getHighlightHtml();
+ $html .= htmlspecialchars( $this->getNativeData() );
$html .= "\n</pre>\n";
return $html;
}
+
}
diff --git a/includes/content/CssContentHandler.php b/includes/content/CssContentHandler.php
index cb5a349d..b2a8676b 100644
--- a/includes/content/CssContentHandler.php
+++ b/includes/content/CssContentHandler.php
@@ -27,41 +27,16 @@
* @since 1.21
* @ingroup Content
*/
-class CssContentHandler extends TextContentHandler {
-
- public function __construct( $modelId = CONTENT_MODEL_CSS ) {
- parent::__construct( $modelId, array( CONTENT_FORMAT_CSS ) );
- }
-
- public function unserializeContent( $text, $format = null ) {
- $this->checkFormat( $format );
-
- return new CssContent( $text );
- }
-
- public function makeEmptyContent() {
- return new CssContent( '' );
- }
+class CssContentHandler extends CodeContentHandler {
/**
- * Returns the english language, because CSS is english, and should be handled as such.
- *
- * @return Language wfGetLangObj( 'en' )
- *
- * @see ContentHandler::getPageLanguage()
+ * @param string $modelId
*/
- public function getPageLanguage( Title $title, Content $content = null ) {
- return wfGetLangObj( 'en' );
+ public function __construct( $modelId = CONTENT_MODEL_CSS ) {
+ parent::__construct( $modelId, array( CONTENT_FORMAT_CSS ) );
}
- /**
- * Returns the english language, because CSS is english, and should be handled as such.
- *
- * @return Language wfGetLangObj( 'en' )
- *
- * @see ContentHandler::getPageViewLanguage()
- */
- public function getPageViewLanguage( Title $title, Content $content = null ) {
- return wfGetLangObj( 'en' );
+ protected function getContentClass() {
+ return 'CssContent';
}
}
diff --git a/includes/content/JavaScriptContent.php b/includes/content/JavaScriptContent.php
index 2ae572be..c0194c2e 100644
--- a/includes/content/JavaScriptContent.php
+++ b/includes/content/JavaScriptContent.php
@@ -31,8 +31,13 @@
* @ingroup Content
*/
class JavaScriptContent extends TextContent {
- public function __construct( $text ) {
- parent::__construct( $text, CONTENT_MODEL_JAVASCRIPT );
+
+ /**
+ * @param string $text JavaScript code.
+ * @param string $modelId the content model name
+ */
+ public function __construct( $text, $modelId = CONTENT_MODEL_JAVASCRIPT ) {
+ parent::__construct( $text, $modelId );
}
/**
@@ -42,7 +47,8 @@ class JavaScriptContent extends TextContent {
* @param Title $title
* @param User $user
* @param ParserOptions $popts
- * @return Content
+ *
+ * @return JavaScriptContent
*/
public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
global $wgParser;
@@ -52,15 +58,19 @@ class JavaScriptContent extends TextContent {
$text = $this->getNativeData();
$pst = $wgParser->preSaveTransform( $text, $title, $user, $popts );
- return new JavaScriptContent( $pst );
+ return new static( $pst );
}
+ /**
+ * @return string JavaScript wrapped in a <pre> tag.
+ */
protected function getHtml() {
$html = "";
$html .= "<pre class=\"mw-code mw-js\" dir=\"ltr\">\n";
- $html .= $this->getHighlightHtml();
+ $html .= htmlspecialchars( $this->getNativeData() );
$html .= "\n</pre>\n";
return $html;
}
+
}
diff --git a/includes/content/JavaScriptContentHandler.php b/includes/content/JavaScriptContentHandler.php
index 33fa9172..457b83d7 100644
--- a/includes/content/JavaScriptContentHandler.php
+++ b/includes/content/JavaScriptContentHandler.php
@@ -27,41 +27,16 @@
* @ingroup Content
* @todo make ScriptContentHandler base class, do highlighting stuff there?
*/
-class JavaScriptContentHandler extends TextContentHandler {
-
- public function __construct( $modelId = CONTENT_MODEL_JAVASCRIPT ) {
- parent::__construct( $modelId, array( CONTENT_FORMAT_JAVASCRIPT ) );
- }
-
- public function unserializeContent( $text, $format = null ) {
- $this->checkFormat( $format );
-
- return new JavaScriptContent( $text );
- }
-
- public function makeEmptyContent() {
- return new JavaScriptContent( '' );
- }
+class JavaScriptContentHandler extends CodeContentHandler {
/**
- * Returns the english language, because JS is english, and should be handled as such.
- *
- * @return Language wfGetLangObj( 'en' )
- *
- * @see ContentHandler::getPageLanguage()
+ * @param string $modelId
*/
- public function getPageLanguage( Title $title, Content $content = null ) {
- return wfGetLangObj( 'en' );
+ public function __construct( $modelId = CONTENT_MODEL_JAVASCRIPT ) {
+ parent::__construct( $modelId, array( CONTENT_FORMAT_JAVASCRIPT ) );
}
- /**
- * Returns the english language, because JS is english, and should be handled as such.
- *
- * @return Language wfGetLangObj( 'en' )
- *
- * @see ContentHandler::getPageViewLanguage()
- */
- public function getPageViewLanguage( Title $title, Content $content = null ) {
- return wfGetLangObj( 'en' );
+ protected function getContentClass() {
+ return 'JavaScriptContent';
}
}
diff --git a/includes/content/JsonContent.php b/includes/content/JsonContent.php
new file mode 100644
index 00000000..b36827c5
--- /dev/null
+++ b/includes/content/JsonContent.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * JSON Content Model
+ *
+ * @file
+ *
+ * @author Ori Livneh <ori@wikimedia.org>
+ * @author Kunal Mehta <legoktm@gmail.com>
+ */
+
+/**
+ * Represents the content of a JSON content.
+ * @since 1.24
+ */
+class JsonContent extends TextContent {
+
+ public function __construct( $text, $modelId = CONTENT_MODEL_JSON ) {
+ parent::__construct( $text, $modelId );
+ }
+
+ /**
+ * Decodes the JSON into a PHP associative array.
+ * @return array
+ */
+ public function getJsonData() {
+ return FormatJson::decode( $this->getNativeData(), true );
+ }
+
+ /**
+ * @return bool Whether content is valid JSON.
+ */
+ public function isValid() {
+ return $this->getJsonData() !== null;
+ }
+
+ /**
+ * Pretty-print JSON
+ *
+ * @return bool|null|string
+ */
+ public function beautifyJSON() {
+ $decoded = FormatJson::decode( $this->getNativeData(), true );
+ if ( !is_array( $decoded ) ) {
+ return null;
+ }
+ return FormatJson::encode( $decoded, true );
+
+ }
+
+ /**
+ * Beautifies JSON prior to save.
+ * @param Title $title Title
+ * @param User $user User
+ * @param ParserOptions $popts
+ * @return JsonContent
+ */
+ public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
+ return new static( $this->beautifyJSON() );
+ }
+
+ /**
+ * Set the HTML and add the appropriate styles
+ *
+ *
+ * @param Title $title
+ * @param int $revId
+ * @param ParserOptions $options
+ * @param bool $generateHtml
+ * @param ParserOutput $output
+ */
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output
+ ) {
+ if ( $generateHtml ) {
+ $output->setText( $this->objectTable( $this->getJsonData() ) );
+ $output->addModuleStyles( 'mediawiki.content.json' );
+ } else {
+ $output->setText( '' );
+ }
+ }
+ /**
+ * Constructs an HTML representation of a JSON object.
+ * @param array $mapping
+ * @return string HTML
+ */
+ protected function objectTable( $mapping ) {
+ $rows = array();
+
+ foreach ( $mapping as $key => $val ) {
+ $rows[] = $this->objectRow( $key, $val );
+ }
+ return Xml::tags( 'table', array( 'class' => 'mw-json' ),
+ Xml::tags( 'tbody', array(), join( "\n", $rows ) )
+ );
+ }
+
+ /**
+ * Constructs HTML representation of a single key-value pair.
+ * @param string $key
+ * @param mixed $val
+ * @return string HTML.
+ */
+ protected function objectRow( $key, $val ) {
+ $th = Xml::elementClean( 'th', array(), $key );
+ if ( is_array( $val ) ) {
+ $td = Xml::tags( 'td', array(), self::objectTable( $val ) );
+ } else {
+ if ( is_string( $val ) ) {
+ $val = '"' . $val . '"';
+ } else {
+ $val = FormatJson::encode( $val );
+ }
+
+ $td = Xml::elementClean( 'td', array( 'class' => 'value' ), $val );
+ }
+
+ return Xml::tags( 'tr', array(), $th . $td );
+ }
+
+}
diff --git a/includes/content/JsonContentHandler.php b/includes/content/JsonContentHandler.php
new file mode 100644
index 00000000..392ce37b
--- /dev/null
+++ b/includes/content/JsonContentHandler.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * JSON Schema Content Handler
+ *
+ * @file
+ *
+ * @author Ori Livneh <ori@wikimedia.org>
+ * @author Kunal Mehta <legoktm@gmail.com>
+ */
+
+/**
+ * @since 1.24
+ */
+class JsonContentHandler extends CodeContentHandler {
+
+ public function __construct( $modelId = CONTENT_MODEL_JSON ) {
+ parent::__construct( $modelId, array( CONTENT_FORMAT_JSON ) );
+ }
+
+ /**
+ * @return string
+ */
+ protected function getContentClass() {
+ return 'JsonContent';
+ }
+}
diff --git a/includes/content/MessageContent.php b/includes/content/MessageContent.php
index b36b670c..5b84657f 100644
--- a/includes/content/MessageContent.php
+++ b/includes/content/MessageContent.php
@@ -29,7 +29,7 @@
* Wrapper allowing us to handle a system message as a Content object.
* Note that this is generally *not* used to represent content from the
* MediaWiki namespace, and that there is no MessageContentHandler.
- * MessageContent is just intended as glue for wrapping a message programatically.
+ * MessageContent is just intended as glue for wrapping a message programmatically.
*
* @ingroup Content
*/
@@ -41,8 +41,8 @@ class MessageContent extends AbstractContent {
protected $mMessage;
/**
- * @param Message|String $msg A Message object, or a message key
- * @param array|null $params An optional array of message parameters
+ * @param Message|string $msg A Message object, or a message key.
+ * @param string[] $params An optional array of message parameters.
*/
public function __construct( $msg, $params = null ) {
# XXX: messages may be wikitext, html or plain text! and maybe even something else entirely.
@@ -60,18 +60,18 @@ class MessageContent extends AbstractContent {
}
/**
- * Returns the message as rendered HTML
+ * Fully parse the text from wikitext to HTML.
*
- * @return string The message text, parsed into html
+ * @return string Parsed HTML.
*/
public function getHtml() {
return $this->mMessage->parse();
}
/**
- * Returns the message as rendered HTML
+ * Returns the message text. {{-transformation is done.
*
- * @return string The message text, parsed into html
+ * @return string Unescaped message text.
*/
public function getWikitext() {
return $this->mMessage->text();
@@ -88,6 +88,8 @@ class MessageContent extends AbstractContent {
}
/**
+ * @return string
+ *
* @see Content::getTextForSearchIndex
*/
public function getTextForSearchIndex() {
@@ -95,6 +97,8 @@ class MessageContent extends AbstractContent {
}
/**
+ * @return string
+ *
* @see Content::getWikitextForTransclusion
*/
public function getWikitextForTransclusion() {
@@ -102,6 +106,10 @@ class MessageContent extends AbstractContent {
}
/**
+ * @param int $maxlength Maximum length of the summary text, defaults to 250.
+ *
+ * @return string The summary text.
+ *
* @see Content::getTextForSummary
*/
public function getTextForSummary( $maxlength = 250 ) {
@@ -109,18 +117,18 @@ class MessageContent extends AbstractContent {
}
/**
- * @see Content::getSize
- *
* @return int
+ *
+ * @see Content::getSize
*/
public function getSize() {
return strlen( $this->mMessage->plain() );
}
/**
- * @see Content::copy
+ * @return Content A copy of this object
*
- * @return Content. A copy of this object
+ * @see Content::copy
*/
public function copy() {
// MessageContent is immutable (because getNativeData() returns a clone of the Message object)
@@ -128,24 +136,28 @@ class MessageContent extends AbstractContent {
}
/**
- * @see Content::isCountable
+ * @param bool $hasLinks
+ *
+ * @return bool Always false.
*
- * @return bool false
+ * @see Content::isCountable
*/
public function isCountable( $hasLinks = null ) {
return false;
}
/**
- * @see Content::getParserOutput
+ * @param Title $title Unused.
+ * @param int $revId Unused.
+ * @param ParserOptions $options Unused.
+ * @param bool $generateHtml Whether to generate HTML (default: true).
*
* @return ParserOutput
+ *
+ * @see Content::getParserOutput
*/
- public function getParserOutput(
- Title $title, $revId = null,
- ParserOptions $options = null, $generateHtml = true
- ) {
-
+ public function getParserOutput( Title $title, $revId = null,
+ ParserOptions $options = null, $generateHtml = true ) {
if ( $generateHtml ) {
$html = $this->getHtml();
} else {
@@ -153,6 +165,10 @@ class MessageContent extends AbstractContent {
}
$po = new ParserOutput( $html );
+ // Message objects are in the user language.
+ $po->recordOption( 'userlang' );
+
return $po;
}
+
}
diff --git a/includes/content/TextContent.php b/includes/content/TextContent.php
index f66dacd7..c479f202 100644
--- a/includes/content/TextContent.php
+++ b/includes/content/TextContent.php
@@ -34,12 +34,16 @@
*/
class TextContent extends AbstractContent {
+ /**
+ * @param string $text
+ * @param string $model_id
+ */
public function __construct( $text, $model_id = CONTENT_MODEL_TEXT ) {
parent::__construct( $model_id );
if ( $text === null || $text === false ) {
wfWarn( "TextContent constructed with \$text = " . var_export( $text, true ) . "! "
- . "This may indicate an error in the caller's scope." );
+ . "This may indicate an error in the caller's scope.", 2 );
$text = '';
}
@@ -51,6 +55,11 @@ class TextContent extends AbstractContent {
$this->mText = $text;
}
+ /**
+ * @note Mutable subclasses MUST override this to return a copy!
+ *
+ * @return Content $this
+ */
public function copy() {
return $this; # NOTE: this is ok since TextContent are immutable.
}
@@ -68,12 +77,13 @@ class TextContent extends AbstractContent {
}
/**
- * returns the text's size in bytes.
+ * Returns the text's size in bytes.
*
- * @return int The size
+ * @return int
*/
public function getSize() {
$text = $this->getNativeData();
+
return strlen( $text );
}
@@ -81,10 +91,10 @@ class TextContent extends AbstractContent {
* Returns true if this content is not a redirect, and $wgArticleCountMethod
* is "any".
*
- * @param bool $hasLinks if it is known whether this content contains links,
+ * @param bool $hasLinks If it is known whether this content contains links,
* provide this information here, to avoid redundant parsing to find out.
*
- * @return bool True if the content is countable
+ * @return bool
*/
public function isCountable( $hasLinks = null ) {
global $wgArticleCountMethod;
@@ -103,17 +113,16 @@ class TextContent extends AbstractContent {
/**
* Returns the text represented by this Content object, as a string.
*
- * @return string: the raw text
+ * @return string The raw text.
*/
public function getNativeData() {
- $text = $this->mText;
- return $text;
+ return $this->mText;
}
/**
* Returns the text represented by this Content object, as a string.
*
- * @return string: the raw text
+ * @return string The raw text.
*/
public function getTextForSearchIndex() {
return $this->getNativeData();
@@ -123,9 +132,9 @@ class TextContent extends AbstractContent {
* Returns attempts to convert this content object to wikitext,
* and then returns the text string. The conversion may be lossy.
*
- * @note: this allows any text-based content to be transcluded as if it was wikitext.
+ * @note this allows any text-based content to be transcluded as if it was wikitext.
*
- * @return string|false: the raw text, or null if the conversion failed
+ * @return string|bool The raw text, or false if the conversion failed.
*/
public function getWikitextForTransclusion() {
$wikitext = $this->convert( CONTENT_MODEL_WIKITEXT, 'lossy' );
@@ -141,29 +150,29 @@ class TextContent extends AbstractContent {
* Returns a Content object with pre-save transformations applied.
* This implementation just trims trailing whitespace.
*
- * @param $title Title
- * @param $user User
- * @param $popts ParserOptions
+ * @param Title $title
+ * @param User $user
+ * @param ParserOptions $popts
+ *
* @return Content
*/
public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
$text = $this->getNativeData();
$pst = rtrim( $text );
- return ( $text === $pst ) ? $this : new WikitextContent( $pst );
+ return ( $text === $pst ) ? $this : new static( $pst, $this->getModel() );
}
/**
* Diff this content object with another content object.
*
- * @since 1.21diff
+ * @since 1.21
*
- * @param $that Content: The other content object to compare this content
- * object to.
- * @param $lang Language: The language object to use for text segmentation.
+ * @param Content $that The other content object to compare this content object to.
+ * @param Language $lang The language object to use for text segmentation.
* If not given, $wgContentLang is used.
*
- * @return DiffResult: A diff representing the changes that would have to be
+ * @return Diff A diff representing the changes that would have to be
* made to this content object to make it equal to $that.
*/
public function diff( Content $that, Language $lang = null ) {
@@ -178,43 +187,42 @@ class TextContent extends AbstractContent {
}
$otext = $this->getNativeData();
- $ntext = $this->getNativeData();
+ $ntext = $that->getNativeData();
# Note: Use native PHP diff, external engines don't give us abstract output
$ota = explode( "\n", $lang->segmentForDiff( $otext ) );
$nta = explode( "\n", $lang->segmentForDiff( $ntext ) );
$diff = new Diff( $ota, $nta );
+
return $diff;
}
/**
- * Returns a generic ParserOutput object, wrapping the HTML returned by
- * getHtml().
+ * Fills the provided ParserOutput object with information derived from the content.
+ * Unless $generateHtml was false, this includes an HTML representation of the content
+ * provided by getHtml().
*
- * @param $title Title Context title for parsing
- * @param int|null $revId Revision ID (for {{REVISIONID}})
- * @param $options ParserOptions|null Parser options
- * @param bool $generateHtml Whether or not to generate HTML
+ * For content models listed in $wgTextModelsToParse, this method will call the MediaWiki
+ * wikitext parser on the text to extract any (wikitext) links, magic words, etc.
*
- * @return ParserOutput representing the HTML form of the text
+ * Subclasses may override this to provide custom content processing.
+ * For custom HTML generation alone, it is sufficient to override getHtml().
+ *
+ * @param Title $title Context title for parsing
+ * @param int $revId Revision ID (for {{REVISIONID}})
+ * @param ParserOptions $options Parser options
+ * @param bool $generateHtml Whether or not to generate HTML
+ * @param ParserOutput $output The output object to fill (reference).
*/
- public function getParserOutput( Title $title,
- $revId = null,
- ParserOptions $options = null, $generateHtml = true
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output
) {
global $wgParser, $wgTextModelsToParse;
- if ( !$options ) {
- //NOTE: use canonical options per default to produce cacheable output
- $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
- }
-
if ( in_array( $this->getModel(), $wgTextModelsToParse ) ) {
- // parse just to get links etc into the database
- $po = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
- } else {
- $po = new ParserOutput();
+ // parse just to get links etc into the database, HTML is replaced below.
+ $output = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
}
if ( $generateHtml ) {
@@ -223,18 +231,19 @@ class TextContent extends AbstractContent {
$html = '';
}
- $po->setText( $html );
- return $po;
+ $output->setText( $html );
}
/**
* Generates an HTML version of the content, for display. Used by
- * getParserOutput() to construct a ParserOutput object.
+ * fillParserOutput() to provide HTML for the ParserOutput object.
+ *
+ * Subclasses may override this to provide a custom HTML rendering.
+ * If further information is to be derived from the content (such as
+ * categories), the fillParserOutput() method can be overridden instead.
*
- * This default implementation just calls getHighlightHtml(). Content
- * models that have another mapping to HTML (as is the case for markup
- * languages like wikitext) should override this method to generate the
- * appropriate HTML.
+ * For backwards-compatibility, this default implementation just calls
+ * getHighlightHtml().
*
* @return string An HTML representation of the content
*/
@@ -243,28 +252,37 @@ class TextContent extends AbstractContent {
}
/**
- * Generates a syntax-highlighted version of the content, as HTML.
- * Used by the default implementation of getHtml().
+ * Generates an HTML version of the content, for display.
+ *
+ * This default implementation returns an HTML-escaped version
+ * of the raw text content.
+ *
+ * @note The functionality of this method should really be implemented
+ * in getHtml(), and subclasses should override getHtml() if needed.
+ * getHighlightHtml() is kept around for backward compatibility with
+ * extensions that already override it.
+ *
+ * @deprecated since 1.24. Use getHtml() instead. In particular, subclasses overriding
+ * getHighlightHtml() should override getHtml() instead.
*
- * @return string an HTML representation of the content's markup
+ * @return string An HTML representation of the content
*/
protected function getHighlightHtml() {
- # TODO: make Highlighter interface, use highlighter here, if available
return htmlspecialchars( $this->getNativeData() );
}
/**
- * @see Content::convert()
- *
* This implementation provides lossless conversion between content models based
* on TextContent.
*
- * @param string $toModel the desired content model, use the CONTENT_MODEL_XXX flags.
- * @param string $lossy flag, set to "lossy" to allow lossy conversion. If lossy conversion is
- * not allowed, full round-trip conversion is expected to work without losing information.
+ * @param string $toModel The desired content model, use the CONTENT_MODEL_XXX flags.
+ * @param string $lossy Flag, set to "lossy" to allow lossy conversion. If lossy conversion is not
+ * allowed, full round-trip conversion is expected to work without losing information.
*
- * @return Content|bool A content object with the content model $toModel, or false if
- * that conversion is not supported.
+ * @return Content|bool A content object with the content model $toModel, or false if that
+ * conversion is not supported.
+ *
+ * @see Content::convert()
*/
public function convert( $toModel, $lossy = '' ) {
$converted = parent::convert( $toModel, $lossy );
@@ -276,11 +294,12 @@ class TextContent extends AbstractContent {
$toHandler = ContentHandler::getForModelID( $toModel );
if ( $toHandler instanceof TextContentHandler ) {
- //NOTE: ignore content serialization format - it's just text anyway.
+ // NOTE: ignore content serialization format - it's just text anyway.
$text = $this->getNativeData();
$converted = $toHandler->unserializeContent( $text );
}
return $converted;
}
+
}
diff --git a/includes/content/TextContentHandler.php b/includes/content/TextContentHandler.php
index e7f41e18..ffe1acbd 100644
--- a/includes/content/TextContentHandler.php
+++ b/includes/content/TextContentHandler.php
@@ -30,19 +30,24 @@
*/
class TextContentHandler extends ContentHandler {
- public function __construct( $modelId = CONTENT_MODEL_TEXT, $formats = array( CONTENT_FORMAT_TEXT ) ) {
+ // @codingStandardsIgnoreStart bug 57585
+ public function __construct( $modelId = CONTENT_MODEL_TEXT,
+ $formats = array( CONTENT_FORMAT_TEXT ) ) {
parent::__construct( $modelId, $formats );
}
+ // @codingStandardsIgnoreEnd
/**
* Returns the content's text as-is.
*
- * @param $content Content
- * @param $format string|null
+ * @param Content $content
+ * @param string $format The serialization format to check
+ *
* @return mixed
*/
public function serializeContent( Content $content, $format = null ) {
$this->checkFormat( $format );
+
return $content->getNativeData();
}
@@ -55,11 +60,11 @@ class TextContentHandler extends ContentHandler {
*
* This text-based implementation uses wfMerge().
*
- * @param $oldContent Content|string String
- * @param $myContent Content|string String
- * @param $yourContent Content|string String
+ * @param Content $oldContent The page's previous content.
+ * @param Content $myContent One of the page's conflicting contents.
+ * @param Content $yourContent One of the page's conflicting contents.
*
- * @return Content|Bool
+ * @return Content|bool
*/
public function merge3( Content $oldContent, Content $myContent, Content $yourContent ) {
$this->checkModelID( $oldContent->getModel() );
@@ -83,23 +88,38 @@ class TextContentHandler extends ContentHandler {
}
$mergedContent = $this->unserializeContent( $result, $format );
+
return $mergedContent;
}
/**
+ * Returns the name of the associated Content class, to
+ * be used when creating new objects. Override expected
+ * by subclasses.
+ *
+ * @since 1.24
+ *
+ * @return string
+ */
+ protected function getContentClass() {
+ return 'TextContent';
+ }
+
+ /**
* Unserializes a Content object of the type supported by this ContentHandler.
*
* @since 1.21
*
- * @param $text string serialized form of the content
- * @param $format null|String the format used for serialization
+ * @param string $text Serialized form of the content
+ * @param string $format The format used for serialization
*
- * @return Content the TextContent object wrapping $text
+ * @return Content The TextContent object wrapping $text
*/
public function unserializeContent( $text, $format = null ) {
$this->checkFormat( $format );
- return new TextContent( $text );
+ $class = $this->getContentClass();
+ return new $class( $text );
}
/**
@@ -107,9 +127,11 @@ class TextContentHandler extends ContentHandler {
*
* @since 1.21
*
- * @return Content
+ * @return Content A new TextContent object with empty text.
*/
public function makeEmptyContent() {
- return new TextContent( '' );
+ $class = $this->getContentClass();
+ return new $class( '' );
}
+
}
diff --git a/includes/content/WikitextContent.php b/includes/content/WikitextContent.php
index 26337db9..9a8ab3a6 100644
--- a/includes/content/WikitextContent.php
+++ b/includes/content/WikitextContent.php
@@ -31,31 +31,43 @@
* @ingroup Content
*/
class WikitextContent extends TextContent {
+ private $redirectTargetAndText = null;
public function __construct( $text ) {
parent::__construct( $text, CONTENT_MODEL_WIKITEXT );
}
/**
+ * @param string|number $sectionId
+ *
+ * @return Content|bool|null
+ *
* @see Content::getSection()
*/
- public function getSection( $section ) {
+ public function getSection( $sectionId ) {
global $wgParser;
$text = $this->getNativeData();
- $sect = $wgParser->getSection( $text, $section, false );
+ $sect = $wgParser->getSection( $text, $sectionId, false );
if ( $sect === false ) {
return false;
} else {
- return new WikitextContent( $sect );
+ return new static( $sect );
}
}
/**
+ * @param string|number|null|bool $sectionId
+ * @param Content $with
+ * @param string $sectionTitle
+ *
+ * @throws MWException
+ * @return Content
+ *
* @see Content::replaceSection()
*/
- public function replaceSection( $section, Content $with, $sectionTitle = '' ) {
+ public function replaceSection( $sectionId, Content $with, $sectionTitle = '' ) {
wfProfileIn( __METHOD__ );
$myModelId = $this->getModel();
@@ -71,13 +83,16 @@ class WikitextContent extends TextContent {
$oldtext = $this->getNativeData();
$text = $with->getNativeData();
- if ( $section === '' ) {
+ if ( strval( $sectionId ) === '' ) {
wfProfileOut( __METHOD__ );
+
return $with; # XXX: copy first?
- } if ( $section == 'new' ) {
+ }
+
+ if ( $sectionId === 'new' ) {
# Inserting a new section
$subject = $sectionTitle ? wfMessage( 'newsectionheaderdefaultlevel' )
- ->rawParams( $sectionTitle )->inContentLanguage()->text() . "\n\n" : '';
+ ->rawParams( $sectionTitle )->inContentLanguage()->text() . "\n\n" : '';
if ( wfRunHooks( 'PlaceNewSection', array( $this, $oldtext, $subject, &$text ) ) ) {
$text = strlen( trim( $oldtext ) ) > 0
? "{$oldtext}\n\n{$subject}{$text}"
@@ -87,12 +102,13 @@ class WikitextContent extends TextContent {
# Replacing an existing section; roll out the big guns
global $wgParser;
- $text = $wgParser->replaceSection( $oldtext, $section, $text );
+ $text = $wgParser->replaceSection( $oldtext, $sectionId, $text );
}
- $newContent = new WikitextContent( $text );
+ $newContent = new static( $text );
wfProfileOut( __METHOD__ );
+
return $newContent;
}
@@ -100,7 +116,8 @@ class WikitextContent extends TextContent {
* Returns a new WikitextContent object with the given section heading
* prepended.
*
- * @param $header string
+ * @param string $header
+ *
* @return Content
*/
public function addSectionHeader( $header ) {
@@ -109,16 +126,17 @@ class WikitextContent extends TextContent {
$text .= "\n\n";
$text .= $this->getNativeData();
- return new WikitextContent( $text );
+ return new static( $text );
}
/**
* Returns a Content object with pre-save transformations applied using
* Parser::preSaveTransform().
*
- * @param $title Title
- * @param $user User
- * @param $popts ParserOptions
+ * @param Title $title
+ * @param User $user
+ * @param ParserOptions $popts
+ *
* @return Content
*/
public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
@@ -128,50 +146,58 @@ class WikitextContent extends TextContent {
$pst = $wgParser->preSaveTransform( $text, $title, $user, $popts );
rtrim( $pst );
- return ( $text === $pst ) ? $this : new WikitextContent( $pst );
+ return ( $text === $pst ) ? $this : new static( $pst );
}
/**
* Returns a Content object with preload transformations applied (or this
* object if no transformations apply).
*
- * @param $title Title
- * @param $popts ParserOptions
+ * @param Title $title
+ * @param ParserOptions $popts
+ * @param array $params
+ *
* @return Content
*/
- public function preloadTransform( Title $title, ParserOptions $popts ) {
+ public function preloadTransform( Title $title, ParserOptions $popts, $params = array() ) {
global $wgParser;
$text = $this->getNativeData();
- $plt = $wgParser->getPreloadText( $text, $title, $popts );
+ $plt = $wgParser->getPreloadText( $text, $title, $popts, $params );
- return new WikitextContent( $plt );
+ return new static( $plt );
}
/**
- * Implement redirect extraction for wikitext.
+ * Extract the redirect target and the remaining text on the page.
*
- * @return null|Title
+ * @note migrated here from Title::newFromRedirectInternal()
*
- * @note: migrated here from Title::newFromRedirectInternal()
+ * @since 1.23
*
- * @see Content::getRedirectTarget
- * @see AbstractContent::getRedirectTarget
+ * @return array List of two elements: Title|null and string.
*/
- public function getRedirectTarget() {
+ protected function getRedirectTargetAndText() {
global $wgMaxRedirects;
+
+ if ( $this->redirectTargetAndText !== null ) {
+ return $this->redirectTargetAndText;
+ }
+
if ( $wgMaxRedirects < 1 ) {
// redirects are disabled, so quit early
- return null;
+ $this->redirectTargetAndText = array( null, $this->getNativeData() );
+ return $this->redirectTargetAndText;
}
+
$redir = MagicWord::get( 'redirect' );
- $text = trim( $this->getNativeData() );
+ $text = ltrim( $this->getNativeData() );
if ( $redir->matchStartAndRemove( $text ) ) {
// Extract the first link and see if it's usable
// Ensure that it really does come directly after #REDIRECT
// Some older redirects included a colon, so don't freak about that!
$m = array();
- if ( preg_match( '!^\s*:?\s*\[{2}(.*?)(?:\|.*?)?\]{2}!', $text, $m ) ) {
+ if ( preg_match( '!^\s*:?\s*\[{2}(.*?)(?:\|.*?)?\]{2}\s*!', $text, $m ) ) {
// Strip preceding colon used to "escape" categories, etc.
// and URL-decode links
if ( strpos( $m[1], '%' ) !== false ) {
@@ -181,17 +207,33 @@ class WikitextContent extends TextContent {
$title = Title::newFromText( $m[1] );
// If the title is a redirect to bad special pages or is invalid, return null
if ( !$title instanceof Title || !$title->isValidRedirectTarget() ) {
- return null;
+ $this->redirectTargetAndText = array( null, $this->getNativeData() );
+ return $this->redirectTargetAndText;
}
- return $title;
+
+ $this->redirectTargetAndText = array( $title, substr( $text, strlen( $m[0] ) ) );
+ return $this->redirectTargetAndText;
}
}
- return null;
+
+ $this->redirectTargetAndText = array( null, $this->getNativeData() );
+ return $this->redirectTargetAndText;
}
/**
- * @see Content::updateRedirect()
+ * Implement redirect extraction for wikitext.
*
+ * @return Title|null
+ *
+ * @see Content::getRedirectTarget
+ */
+ public function getRedirectTarget() {
+ list( $title, ) = $this->getRedirectTargetAndText();
+
+ return $title;
+ }
+
+ /**
* This implementation replaces the first link on the page with the given new target
* if this Content object is a redirect. Otherwise, this method returns $this.
*
@@ -199,7 +241,9 @@ class WikitextContent extends TextContent {
*
* @param Title $target
*
- * @return Content a new Content object with the updated redirect (or $this if this Content object isn't a redirect)
+ * @return Content
+ *
+ * @see Content::updateRedirect()
*/
public function updateRedirect( Title $target ) {
if ( !$this->isRedirect() ) {
@@ -213,21 +257,19 @@ class WikitextContent extends TextContent {
'[[' . $target->getFullText() . ']]',
$this->getNativeData(), 1 );
- return new WikitextContent( $newText );
+ return new static( $newText );
}
/**
* Returns true if this content is not a redirect, and this content's text
* is countable according to the criteria defined by $wgArticleCountMethod.
*
- * @param bool $hasLinks if it is known whether this content contains
+ * @param bool $hasLinks If it is known whether this content contains
* links, provide this information here, to avoid redundant parsing to
* find out (default: null).
- * @param $title Title: (default: null)
- *
- * @internal param \IContextSource $context context for parsing if necessary
+ * @param Title $title Optional title, defaults to the title from the current main request.
*
- * @return bool True if the content is countable
+ * @return bool
*/
public function isCountable( $hasLinks = null, Title $title = null ) {
global $wgArticleCountMethod;
@@ -261,6 +303,10 @@ class WikitextContent extends TextContent {
return false;
}
+ /**
+ * @param int $maxlength
+ * @return string
+ */
public function getTextForSummary( $maxlength = 250 ) {
$truncatedtext = parent::getTextForSummary( $maxlength );
@@ -276,31 +322,39 @@ class WikitextContent extends TextContent {
* Returns a ParserOutput object resulting from parsing the content's text
* using $wgParser.
*
- * @since 1.21
- *
- * @param $title Title
+ * @param Title $title
* @param int $revId Revision to pass to the parser (default: null)
- * @param $options ParserOptions (default: null)
- * @param bool $generateHtml (default: false)
- *
- * @internal param \IContextSource|null $context
- * @return ParserOutput representing the HTML form of the text
+ * @param ParserOptions $options (default: null)
+ * @param bool $generateHtml (default: true)
+ * @param ParserOutput &$output ParserOutput representing the HTML form of the text,
+ * may be manipulated or replaced.
*/
- public function getParserOutput( Title $title,
- $revId = null,
- ParserOptions $options = null, $generateHtml = true
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output
) {
global $wgParser;
- if ( !$options ) {
- //NOTE: use canonical options per default to produce cacheable output
- $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
+ list( $redir, $text ) = $this->getRedirectTargetAndText();
+ $output = $wgParser->parse( $text, $title, $options, true, true, $revId );
+
+ // Add redirect indicator at the top
+ if ( $redir ) {
+ // Make sure to include the redirect link in pagelinks
+ $output->addLink( $redir );
+ if ( $generateHtml ) {
+ $chain = $this->getRedirectChain();
+ $output->setText(
+ Article::getRedirectHeaderHtml( $title->getPageLanguage(), $chain, false ) .
+ $output->getText()
+ );
+ $output->addModuleStyles( 'mediawiki.action.view.redirectPage' );
+ }
}
-
- $po = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
- return $po;
}
+ /**
+ * @throws MWException
+ */
protected function getHtml() {
throw new MWException(
"getHtml() not implemented for wikitext. "
@@ -309,15 +363,16 @@ class WikitextContent extends TextContent {
}
/**
- * @see Content::matchMagicWord()
- *
* This implementation calls $word->match() on the this TextContent object's text.
*
* @param MagicWord $word
*
- * @return bool whether this Content object matches the given magic word.
+ * @return bool
+ *
+ * @see Content::matchMagicWord()
*/
public function matchMagicWord( MagicWord $word ) {
return $word->match( $this->getNativeData() );
}
+
}
diff --git a/includes/content/WikitextContentHandler.php b/includes/content/WikitextContentHandler.php
index b1b461fb..c1db1de6 100644
--- a/includes/content/WikitextContentHandler.php
+++ b/includes/content/WikitextContentHandler.php
@@ -34,30 +34,19 @@ class WikitextContentHandler extends TextContentHandler {
parent::__construct( $modelId, array( CONTENT_FORMAT_WIKITEXT ) );
}
- public function unserializeContent( $text, $format = null ) {
- $this->checkFormat( $format );
-
- return new WikitextContent( $text );
- }
-
- /**
- * @see ContentHandler::makeEmptyContent
- *
- * @return Content
- */
- public function makeEmptyContent() {
- return new WikitextContent( '' );
+ protected function getContentClass() {
+ return 'WikitextContent';
}
/**
* Returns a WikitextContent object representing a redirect to the given destination page.
*
- * @see ContentHandler::makeRedirectContent
- *
- * @param Title $destination the page to redirect to.
- * @param string $text text to include in the redirect, if possible.
+ * @param Title $destination The page to redirect to.
+ * @param string $text Text to include in the redirect, if possible.
*
* @return Content
+ *
+ * @see ContentHandler::makeRedirectContent
*/
public function makeRedirectContent( Title $destination, $text = '' ) {
$optionalColon = '';
@@ -72,20 +61,23 @@ class WikitextContentHandler extends TextContentHandler {
}
$mwRedir = MagicWord::get( 'redirect' );
- $redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $optionalColon . $destination->getFullText() . ']]';
+ $redirectText = $mwRedir->getSynonym( 0 ) .
+ ' [[' . $optionalColon . $destination->getFullText() . ']]';
+
if ( $text != '' ) {
$redirectText .= "\n" . $text;
}
- return new WikitextContent( $redirectText );
+ $class = $this->getContentClass();
+ return new $class( $redirectText );
}
/**
* Returns true because wikitext supports redirects.
*
- * @see ContentHandler::supportsRedirects
+ * @return bool Always true.
*
- * @return boolean whether redirects are supported.
+ * @see ContentHandler::supportsRedirects
*/
public function supportsRedirects() {
return true;
@@ -94,7 +86,9 @@ class WikitextContentHandler extends TextContentHandler {
/**
* Returns true because wikitext supports sections.
*
- * @return boolean whether sections are supported.
+ * @return bool Always true.
+ *
+ * @see ContentHandler::supportsSections
*/
public function supportsSections() {
return true;
@@ -105,9 +99,13 @@ class WikitextContentHandler extends TextContentHandler {
* ParserCache mechanism.
*
* @since 1.21
- * @return bool
+ *
+ * @return bool Always true.
+ *
+ * @see ContentHandler::isParserCacheSupported
*/
public function isParserCacheSupported() {
return true;
}
+
}