summaryrefslogtreecommitdiff
path: root/includes/content
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2015-06-04 07:31:04 +0200
committerPierre Schmitz <pierre@archlinux.de>2015-06-04 07:58:39 +0200
commitf6d65e533c62f6deb21342d4901ece24497b433e (patch)
treef28adf0362d14bcd448f7b65a7aaf38650f923aa /includes/content
parentc27b2e832fe25651ef2410fae85b41072aae7519 (diff)
Update to MediaWiki 1.25.1
Diffstat (limited to 'includes/content')
-rw-r--r--includes/content/AbstractContent.php27
-rw-r--r--includes/content/CodeContentHandler.php1
-rw-r--r--includes/content/Content.php7
-rw-r--r--includes/content/ContentHandler.php23
-rw-r--r--includes/content/JavaScriptContentHandler.php8
-rw-r--r--includes/content/JsonContent.php192
-rw-r--r--includes/content/JsonContentHandler.php24
-rw-r--r--includes/content/TextContent.php1
-rw-r--r--includes/content/WikitextContent.php7
9 files changed, 227 insertions, 63 deletions
diff --git a/includes/content/AbstractContent.php b/includes/content/AbstractContent.php
index 9d257a6a..6542d7df 100644
--- a/includes/content/AbstractContent.php
+++ b/includes/content/AbstractContent.php
@@ -204,13 +204,13 @@ abstract class AbstractContent implements Content {
* Returns a list of DataUpdate objects for recording information about this
* Content in some secondary data store.
*
- * This default implementation calls
- * $this->getParserOutput( $content, $title, null, null, false ),
- * and then calls getSecondaryDataUpdates( $title, $recursive ) on the
- * resulting ParserOutput object.
+ * This default implementation returns a LinksUpdate object and calls the
+ * SecondaryDataUpdates hook.
*
* Subclasses may override this to determine the secondary data updates more
* efficiently, preferably without the need to generate a parser output object.
+ * They should however make sure to call SecondaryDataUpdates to give extensions
+ * a chance to inject additional updates.
*
* @since 1.21
*
@@ -224,12 +224,19 @@ abstract class AbstractContent implements Content {
* @see Content::getSecondaryDataUpdates()
*/
public function getSecondaryDataUpdates( Title $title, Content $old = null,
- $recursive = true, ParserOutput $parserOutput = null ) {
+ $recursive = true, ParserOutput $parserOutput = null
+ ) {
if ( $parserOutput === null ) {
$parserOutput = $this->getParserOutput( $title, null, null, false );
}
- return $parserOutput->getSecondaryDataUpdates( $title, $recursive );
+ $updates = array(
+ new LinksUpdate( $title, $parserOutput, $recursive )
+ );
+
+ Hooks::run( 'SecondaryDataUpdates', array( $title, $old, $recursive, $parserOutput, &$updates ) );
+
+ return $updates;
}
/**
@@ -386,7 +393,7 @@ abstract class AbstractContent implements Content {
*
* @see Content::prepareSave
*/
- public function prepareSave( WikiPage $page, $flags, $baseRevId, User $user ) {
+ public function prepareSave( WikiPage $page, $flags, $parentRevId, User $user ) {
if ( $this->isValid() ) {
return Status::newGood();
} else {
@@ -446,7 +453,7 @@ abstract class AbstractContent implements Content {
$lossy = ( $lossy === 'lossy' ); // string flag, convert to boolean for convenience
$result = false;
- wfRunHooks( 'ConvertContent', array( $this, $toModel, $lossy, &$result ) );
+ Hooks::run( 'ConvertContent', array( $this, $toModel, $lossy, &$result ) );
return $result;
}
@@ -480,7 +487,7 @@ abstract class AbstractContent implements Content {
$po = new ParserOutput();
- if ( wfRunHooks( 'ContentGetParserOutput',
+ if ( Hooks::run( 'ContentGetParserOutput',
array( $this, $title, $revId, $options, $generateHtml, &$po ) ) ) {
// Save and restore the old value, just in case something is reusing
@@ -491,6 +498,8 @@ abstract class AbstractContent implements Content {
$options->setRedirectTarget( $oldRedir );
}
+ Hooks::run( 'ContentAlterParserOutput', array( $this, $title, $po ) );
+
return $po;
}
diff --git a/includes/content/CodeContentHandler.php b/includes/content/CodeContentHandler.php
index 447a2a73..694b633e 100644
--- a/includes/content/CodeContentHandler.php
+++ b/includes/content/CodeContentHandler.php
@@ -58,6 +58,7 @@ abstract class CodeContentHandler extends TextContentHandler {
/**
* @return string
+ * @throws MWException
*/
protected function getContentClass() {
throw new MWException( 'Subclass must override' );
diff --git a/includes/content/Content.php b/includes/content/Content.php
index 61b9254f..8ed761f5 100644
--- a/includes/content/Content.php
+++ b/includes/content/Content.php
@@ -292,6 +292,9 @@ interface Content {
* Subclasses may implement this to determine the necessary updates more
* efficiently, or make use of information about the old content.
*
+ * @note Implementations should call the SecondaryDataUpdates hook, like
+ * AbstractContent does.
+ *
* @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
@@ -461,7 +464,7 @@ interface Content {
*
* @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 int $parentRevId The ID of the current revision
* @param User $user
*
* @return Status A status object indicating whether the content was
@@ -470,7 +473,7 @@ interface Content {
*
* @see WikiPage::doEditContent()
*/
- public function prepareSave( WikiPage $page, $flags, $baseRevId, User $user );
+ public function prepareSave( WikiPage $page, $flags, $parentRevId, User $user );
/**
* Returns a list of updates to perform when this content is deleted.
diff --git a/includes/content/ContentHandler.php b/includes/content/ContentHandler.php
index ac417223..371b267e 100644
--- a/includes/content/ContentHandler.php
+++ b/includes/content/ContentHandler.php
@@ -201,7 +201,7 @@ abstract class ContentHandler {
$model = MWNamespace::getNamespaceContentModel( $ns );
// Hook can determine default model
- if ( !wfRunHooks( 'ContentHandlerDefaultModelFor', array( $title, &$model ) ) ) {
+ if ( !Hooks::run( 'ContentHandlerDefaultModelFor', array( $title, &$model ) ) ) {
if ( !is_null( $model ) ) {
return $model;
}
@@ -214,7 +214,7 @@ abstract class ContentHandler {
}
// Hook can force JS/CSS
- wfRunHooks( 'TitleIsCssOrJsPage', array( $title, &$isCssOrJsPage ) );
+ Hooks::run( 'TitleIsCssOrJsPage', array( $title, &$isCssOrJsPage ), '1.25' );
// Is this a .css subpage of a user page?
$isJsCssSubpage = NS_USER == $ns
@@ -229,7 +229,7 @@ abstract class ContentHandler {
$isWikitext = $isWikitext && !$isCssOrJsPage && !$isJsCssSubpage;
// Hook can override $isWikitext
- wfRunHooks( 'TitleIsWikitextPage', array( $title, &$isWikitext ) );
+ Hooks::run( 'TitleIsWikitextPage', array( $title, &$isWikitext ), '1.25' );
if ( !$isWikitext ) {
switch ( $ext ) {
@@ -318,7 +318,7 @@ abstract class ContentHandler {
if ( empty( $wgContentHandlers[$modelId] ) ) {
$handler = null;
- wfRunHooks( 'ContentHandlerForModelID', array( $modelId, &$handler ) );
+ Hooks::run( 'ContentHandlerForModelID', array( $modelId, &$handler ) );
if ( $handler === null ) {
throw new MWException( "No handler for model '$modelId' registered in \$wgContentHandlers" );
@@ -626,8 +626,15 @@ abstract class ContentHandler {
public function createDifferenceEngine( IContextSource $context, $old = 0, $new = 0,
$rcid = 0, //FIXME: Deprecated, no longer used
$refreshCache = false, $unhide = false ) {
- $diffEngineClass = $this->getDiffEngineClass();
+ // hook: get difference engine
+ $differenceEngine = null;
+ if ( !wfRunHooks( 'GetDifferenceEngine',
+ array( $context, $old, $new, $refreshCache, $unhide, &$differenceEngine )
+ ) ) {
+ return $differenceEngine;
+ }
+ $diffEngineClass = $this->getDiffEngineClass();
return new $diffEngineClass( $context, $old, $new, $rcid, $refreshCache, $unhide );
}
@@ -660,7 +667,7 @@ abstract class ContentHandler {
$pageLang = wfGetLangObj( $lang );
}
- wfRunHooks( 'PageContentLanguage', array( $title, &$pageLang, $wgLang ) );
+ Hooks::run( 'PageContentLanguage', array( $title, &$pageLang, $wgLang ) );
return wfGetLangObj( $pageLang );
}
@@ -719,7 +726,7 @@ abstract class ContentHandler {
public function canBeUsedOn( Title $title ) {
$ok = true;
- wfRunHooks( 'ContentModelCanBeUsedOn', array( $this->getModelID(), $title, &$ok ) );
+ Hooks::run( 'ContentModelCanBeUsedOn', array( $this->getModelID(), $title, &$ok ) );
return $ok;
}
@@ -1151,7 +1158,7 @@ abstract class ContentHandler {
}
// call the hook functions
- $ok = wfRunHooks( $event, $args );
+ $ok = Hooks::run( $event, $args );
// see if the hook changed the text
foreach ( $contentTexts as $k => $orig ) {
diff --git a/includes/content/JavaScriptContentHandler.php b/includes/content/JavaScriptContentHandler.php
index 457b83d7..d2218971 100644
--- a/includes/content/JavaScriptContentHandler.php
+++ b/includes/content/JavaScriptContentHandler.php
@@ -1,7 +1,5 @@
<?php
/**
- * Content handler for JavaScript pages.
- *
* 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
@@ -23,9 +21,10 @@
/**
* Content handler for JavaScript pages.
*
+ * @todo Create a ScriptContentHandler base class, do highlighting stuff there?
+ *
* @since 1.21
* @ingroup Content
- * @todo make ScriptContentHandler base class, do highlighting stuff there?
*/
class JavaScriptContentHandler extends CodeContentHandler {
@@ -36,6 +35,9 @@ class JavaScriptContentHandler extends CodeContentHandler {
parent::__construct( $modelId, array( CONTENT_FORMAT_JAVASCRIPT ) );
}
+ /**
+ * @return string
+ */
protected function getContentClass() {
return 'JavaScriptContent';
}
diff --git a/includes/content/JsonContent.php b/includes/content/JsonContent.php
index b36827c5..bf3a25bf 100644
--- a/includes/content/JsonContent.php
+++ b/includes/content/JsonContent.php
@@ -14,53 +14,83 @@
*/
class JsonContent extends TextContent {
+ /**
+ * @since 1.25
+ * @var Status
+ */
+ protected $jsonParse;
+
+ /**
+ * @param string $text JSON
+ */
public function __construct( $text, $modelId = CONTENT_MODEL_JSON ) {
parent::__construct( $text, $modelId );
}
/**
* Decodes the JSON into a PHP associative array.
- * @return array
+ *
+ * @deprecated since 1.25 Use getData instead.
+ * @return array|null
*/
public function getJsonData() {
+ wfDeprecated( __METHOD__, '1.25' );
return FormatJson::decode( $this->getNativeData(), true );
}
/**
- * @return bool Whether content is valid JSON.
+ * Decodes the JSON string.
+ *
+ * Note that this parses it without casting objects to associative arrays.
+ * Objects and arrays are kept as distinguishable types in the PHP values.
+ *
+ * @return Status
+ */
+ public function getData() {
+ if ( $this->jsonParse === null ) {
+ $this->jsonParse = FormatJson::parse( $this->getNativeData() );
+ }
+ return $this->jsonParse;
+ }
+
+ /**
+ * @return bool Whether content is valid.
*/
public function isValid() {
- return $this->getJsonData() !== null;
+ return $this->getData()->isGood();
}
/**
- * Pretty-print JSON
+ * Pretty-print JSON.
*
- * @return bool|null|string
+ * If called before validation, it may return JSON "null".
+ *
+ * @return string
*/
public function beautifyJSON() {
- $decoded = FormatJson::decode( $this->getNativeData(), true );
- if ( !is_array( $decoded ) ) {
- return null;
- }
- return FormatJson::encode( $decoded, true );
-
+ return FormatJson::encode( $this->getData()->getValue(), true, FormatJson::UTF8_OK );
}
/**
* 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 ) {
+ // FIXME: WikiPage::doEditContent invokes PST before validation. As such, native data
+ // may be invalid (though PST result is discarded later in that case).
+ if ( !$this->isValid() ) {
+ return $this;
+ }
+
return new static( $this->beautifyJSON() );
}
/**
- * Set the HTML and add the appropriate styles
- *
+ * Set the HTML and add the appropriate styles.
*
* @param Title $title
* @param int $revId
@@ -71,50 +101,150 @@ class JsonContent extends TextContent {
protected function fillParserOutput( Title $title, $revId,
ParserOptions $options, $generateHtml, ParserOutput &$output
) {
- if ( $generateHtml ) {
- $output->setText( $this->objectTable( $this->getJsonData() ) );
+ // FIXME: WikiPage::doEditContent generates parser output before validation.
+ // As such, native data may be invalid (though output is discarded later in that case).
+ if ( $generateHtml && $this->isValid() ) {
+ $output->setText( $this->rootValueTable( $this->getData()->getValue() ) );
$output->addModuleStyles( 'mediawiki.content.json' );
} else {
$output->setText( '' );
}
}
+
/**
- * Constructs an HTML representation of a JSON object.
- * @param array $mapping
+ * Construct HTML table representation of any JSON value.
+ *
+ * See also valueCell, which is similar.
+ *
+ * @param mixed $val
+ * @return string HTML.
+ */
+ protected function rootValueTable( $val ) {
+ if ( is_object( $val ) ) {
+ return self::objectTable( $val );
+ }
+
+ if ( is_array( $val ) ) {
+ // Wrap arrays in another array so that they're visually boxed in a container.
+ // Otherwise they are visually indistinguishable from a single value.
+ return self::arrayTable( array( $val ) );
+ }
+
+ return Html::rawElement( 'table', array( 'class' => 'mw-json mw-json-single-value' ),
+ Html::rawElement( 'tbody', array(),
+ Html::rawElement( 'tr', array(),
+ Html::element( 'td', array(), self::primitiveValue( $val ) )
+ )
+ )
+ );
+ }
+
+ /**
+ * Create HTML table representing a JSON object.
+ *
+ * @param stdClass $mapping
* @return string HTML
*/
protected function objectTable( $mapping ) {
$rows = array();
+ $empty = true;
foreach ( $mapping as $key => $val ) {
$rows[] = $this->objectRow( $key, $val );
+ $empty = false;
+ }
+ if ( $empty ) {
+ $rows[] = Html::rawElement( 'tr', array(),
+ Html::element( 'td', array( 'class' => 'mw-json-empty' ),
+ wfMessage( 'content-json-empty-object' )->text()
+ )
+ );
}
- return Xml::tags( 'table', array( 'class' => 'mw-json' ),
- Xml::tags( 'tbody', array(), join( "\n", $rows ) )
+ return Html::rawElement( 'table', array( 'class' => 'mw-json' ),
+ Html::rawElement( 'tbody', array(), join( '', $rows ) )
);
}
/**
- * Constructs HTML representation of a single key-value pair.
+ * Create HTML table row representing one object property.
+ *
* @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 );
- }
+ $th = Html::element( 'th', array(), $key );
+ $td = self::valueCell( $val );
+ return Html::rawElement( 'tr', array(), $th . $td );
+ }
- $td = Xml::elementClean( 'td', array( 'class' => 'value' ), $val );
+ /**
+ * Create HTML table representing a JSON array.
+ *
+ * @param array $mapping
+ * @return string HTML
+ */
+ protected function arrayTable( $mapping ) {
+ $rows = array();
+ $empty = true;
+
+ foreach ( $mapping as $val ) {
+ $rows[] = $this->arrayRow( $val );
+ $empty = false;
+ }
+ if ( $empty ) {
+ $rows[] = Html::rawElement( 'tr', array(),
+ Html::element( 'td', array( 'class' => 'mw-json-empty' ),
+ wfMessage( 'content-json-empty-array' )->text()
+ )
+ );
}
+ return Html::rawElement( 'table', array( 'class' => 'mw-json' ),
+ Html::rawElement( 'tbody', array(), join( "\n", $rows ) )
+ );
+ }
- return Xml::tags( 'tr', array(), $th . $td );
+ /**
+ * Create HTML table row representing the value in an array.
+ *
+ * @param mixed $val
+ * @return string HTML.
+ */
+ protected function arrayRow( $val ) {
+ $td = self::valueCell( $val );
+ return Html::rawElement( 'tr', array(), $td );
}
+ /**
+ * Construct HTML table cell representing any JSON value.
+ *
+ * @param mixed $val
+ * @return string HTML.
+ */
+ protected function valueCell( $val ) {
+ if ( is_object( $val ) ) {
+ return Html::rawElement( 'td', array(), self::objectTable( $val ) );
+ }
+
+ if ( is_array( $val ) ) {
+ return Html::rawElement( 'td', array(), self::arrayTable( $val ) );
+ }
+
+ return Html::element( 'td', array( 'class' => 'value' ), self::primitiveValue( $val ) );
+ }
+
+ /**
+ * Construct text representing a JSON primitive value.
+ *
+ * @param mixed $val
+ * @return string Text.
+ */
+ protected function primitiveValue( $val ) {
+ if ( is_string( $val ) ) {
+ // Don't FormatJson::encode for strings since we want quotes
+ // and new lines to render visually instead of escaped.
+ return '"' . $val . '"';
+ }
+ return FormatJson::encode( $val );
+ }
}
diff --git a/includes/content/JsonContentHandler.php b/includes/content/JsonContentHandler.php
index 392ce37b..b149f528 100644
--- a/includes/content/JsonContentHandler.php
+++ b/includes/content/JsonContentHandler.php
@@ -1,15 +1,31 @@
<?php
/**
- * JSON Schema Content Handler
+ * 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.
*
- * @file
+ * 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.
*
- * @author Ori Livneh <ori@wikimedia.org>
- * @author Kunal Mehta <legoktm@gmail.com>
+ * 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
*/
/**
+ * Content handler for JSON.
+ *
+ * @author Ori Livneh <ori@wikimedia.org>
+ * @author Kunal Mehta <legoktm@gmail.com>
+ *
* @since 1.24
+ * @ingroup Content
*/
class JsonContentHandler extends CodeContentHandler {
diff --git a/includes/content/TextContent.php b/includes/content/TextContent.php
index c479f202..baea8125 100644
--- a/includes/content/TextContent.php
+++ b/includes/content/TextContent.php
@@ -37,6 +37,7 @@ class TextContent extends AbstractContent {
/**
* @param string $text
* @param string $model_id
+ * @throws MWException
*/
public function __construct( $text, $model_id = CONTENT_MODEL_TEXT ) {
parent::__construct( $model_id );
diff --git a/includes/content/WikitextContent.php b/includes/content/WikitextContent.php
index 9a8ab3a6..dbe09f91 100644
--- a/includes/content/WikitextContent.php
+++ b/includes/content/WikitextContent.php
@@ -68,13 +68,11 @@ class WikitextContent extends TextContent {
* @see Content::replaceSection()
*/
public function replaceSection( $sectionId, Content $with, $sectionTitle = '' ) {
- wfProfileIn( __METHOD__ );
$myModelId = $this->getModel();
$sectionModelId = $with->getModel();
if ( $sectionModelId != $myModelId ) {
- wfProfileOut( __METHOD__ );
throw new MWException( "Incompatible content model for section: " .
"document uses $myModelId but " .
"section uses $sectionModelId." );
@@ -84,7 +82,6 @@ class WikitextContent extends TextContent {
$text = $with->getNativeData();
if ( strval( $sectionId ) === '' ) {
- wfProfileOut( __METHOD__ );
return $with; # XXX: copy first?
}
@@ -93,7 +90,7 @@ class WikitextContent extends TextContent {
# Inserting a new section
$subject = $sectionTitle ? wfMessage( 'newsectionheaderdefaultlevel' )
->rawParams( $sectionTitle )->inContentLanguage()->text() . "\n\n" : '';
- if ( wfRunHooks( 'PlaceNewSection', array( $this, $oldtext, $subject, &$text ) ) ) {
+ if ( Hooks::run( 'PlaceNewSection', array( $this, $oldtext, $subject, &$text ) ) ) {
$text = strlen( trim( $oldtext ) ) > 0
? "{$oldtext}\n\n{$subject}{$text}"
: "{$subject}{$text}";
@@ -107,8 +104,6 @@ class WikitextContent extends TextContent {
$newContent = new static( $text );
- wfProfileOut( __METHOD__ );
-
return $newContent;
}