summaryrefslogtreecommitdiff
path: root/www/wiki/includes/content/JsonContent.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/includes/content/JsonContent.php')
-rw-r--r--www/wiki/includes/content/JsonContent.php251
1 files changed, 251 insertions, 0 deletions
diff --git a/www/wiki/includes/content/JsonContent.php b/www/wiki/includes/content/JsonContent.php
new file mode 100644
index 00000000..7d8f67ce
--- /dev/null
+++ b/www/wiki/includes/content/JsonContent.php
@@ -0,0 +1,251 @@
+<?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 {
+
+ /**
+ * @since 1.25
+ * @var Status
+ */
+ protected $jsonParse;
+
+ /**
+ * @param string $text JSON
+ * @param string $modelId
+ */
+ public function __construct( $text, $modelId = CONTENT_MODEL_JSON ) {
+ parent::__construct( $text, $modelId );
+ }
+
+ /**
+ * Decodes the JSON into a PHP associative array.
+ *
+ * @deprecated since 1.25 Use getData instead.
+ * @return array|null
+ */
+ public function getJsonData() {
+ wfDeprecated( __METHOD__, '1.25' );
+ return FormatJson::decode( $this->getNativeData(), true );
+ }
+
+ /**
+ * 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->getData()->isGood();
+ }
+
+ /**
+ * Pretty-print JSON.
+ *
+ * If called before validation, it may return JSON "null".
+ *
+ * @return string
+ */
+ public function beautifyJSON() {
+ return FormatJson::encode( $this->getData()->getValue(), true, FormatJson::UTF8_OK );
+ }
+
+ /**
+ * Beautifies JSON prior to save.
+ *
+ * @param Title $title
+ * @param 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( self::normalizeLineEndings( $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
+ ) {
+ // 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( '' );
+ }
+ }
+
+ /**
+ * 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 $this->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 $this->arrayTable( [ $val ] );
+ }
+
+ return Html::rawElement( 'table', [ 'class' => 'mw-json mw-json-single-value' ],
+ Html::rawElement( 'tbody', [],
+ Html::rawElement( 'tr', [],
+ Html::element( 'td', [], $this->primitiveValue( $val ) )
+ )
+ )
+ );
+ }
+
+ /**
+ * Create HTML table representing a JSON object.
+ *
+ * @param stdClass $mapping
+ * @return string HTML
+ */
+ protected function objectTable( $mapping ) {
+ $rows = [];
+ $empty = true;
+
+ foreach ( $mapping as $key => $val ) {
+ $rows[] = $this->objectRow( $key, $val );
+ $empty = false;
+ }
+ if ( $empty ) {
+ $rows[] = Html::rawElement( 'tr', [],
+ Html::element( 'td', [ 'class' => 'mw-json-empty' ],
+ wfMessage( 'content-json-empty-object' )->text()
+ )
+ );
+ }
+ return Html::rawElement( 'table', [ 'class' => 'mw-json' ],
+ Html::rawElement( 'tbody', [], implode( '', $rows ) )
+ );
+ }
+
+ /**
+ * Create HTML table row representing one object property.
+ *
+ * @param string $key
+ * @param mixed $val
+ * @return string HTML.
+ */
+ protected function objectRow( $key, $val ) {
+ $th = Html::element( 'th', [], $key );
+ $td = $this->valueCell( $val );
+ return Html::rawElement( 'tr', [], $th . $td );
+ }
+
+ /**
+ * Create HTML table representing a JSON array.
+ *
+ * @param array $mapping
+ * @return string HTML
+ */
+ protected function arrayTable( $mapping ) {
+ $rows = [];
+ $empty = true;
+
+ foreach ( $mapping as $val ) {
+ $rows[] = $this->arrayRow( $val );
+ $empty = false;
+ }
+ if ( $empty ) {
+ $rows[] = Html::rawElement( 'tr', [],
+ Html::element( 'td', [ 'class' => 'mw-json-empty' ],
+ wfMessage( 'content-json-empty-array' )->text()
+ )
+ );
+ }
+ return Html::rawElement( 'table', [ 'class' => 'mw-json' ],
+ Html::rawElement( 'tbody', [], implode( "\n", $rows ) )
+ );
+ }
+
+ /**
+ * Create HTML table row representing the value in an array.
+ *
+ * @param mixed $val
+ * @return string HTML.
+ */
+ protected function arrayRow( $val ) {
+ $td = $this->valueCell( $val );
+ return Html::rawElement( 'tr', [], $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', [], $this->objectTable( $val ) );
+ }
+
+ if ( is_array( $val ) ) {
+ return Html::rawElement( 'td', [], $this->arrayTable( $val ) );
+ }
+
+ return Html::element( 'td', [ 'class' => 'value' ], $this->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 );
+ }
+}