Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 56 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
JCContentHandler | |
0.00% |
0 / 56 |
|
0.00% |
0 / 9 |
342 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
serializeContent | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
merge3 | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
getSlotDiffRendererWithOptions | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
unserializeContent | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getContentClass | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
makeEmptyContent | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
preSaveTransform | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
fillParserOutput | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | |
3 | namespace JsonConfig; |
4 | |
5 | use Content; |
6 | use FormatJson; |
7 | use IContextSource; |
8 | use MediaWiki\Content\Renderer\ContentParseParams; |
9 | use MediaWiki\Content\Transform\PreSaveTransformParams; |
10 | use MediaWiki\Parser\ParserOutput; |
11 | use TextContentHandler; |
12 | |
13 | /** |
14 | * JSON Json Config content handler |
15 | * |
16 | * @file |
17 | * @ingroup Extensions |
18 | * @ingroup JsonConfig |
19 | * |
20 | * @author Yuri Astrakhan <yurik@wikimedia.org> |
21 | */ |
22 | class JCContentHandler extends TextContentHandler { |
23 | |
24 | /** |
25 | * Internal format to force pretty-printed json serialization |
26 | */ |
27 | public const CONTENT_FORMAT_JSON_PRETTY = 'application/json+pretty'; |
28 | |
29 | /** |
30 | * @param string $modelId |
31 | */ |
32 | public function __construct( $modelId ) { |
33 | parent::__construct( $modelId, [ CONTENT_FORMAT_JSON, self::CONTENT_FORMAT_JSON_PRETTY ] ); |
34 | } |
35 | |
36 | /** |
37 | * Returns the content's text as-is. |
38 | * |
39 | * @param \Content|JCContent $content This is actually a Content object |
40 | * @param string|null $format |
41 | * @return mixed |
42 | */ |
43 | public function serializeContent( \Content $content, $format = null ) { |
44 | $this->checkFormat( $format ); |
45 | $status = $content->getStatus(); |
46 | if ( $status->isGood() ) { |
47 | $data = $content->getData(); // There are no errors, normalize data |
48 | } elseif ( $status->isOK() ) { |
49 | $data = $content->getRawData(); // JSON is valid, but the data has errors |
50 | } else { |
51 | return $content->getNativeData(); // Invalid JSON - can't do anything with it |
52 | } |
53 | |
54 | return FormatJson::encode( $data, $format === self::CONTENT_FORMAT_JSON_PRETTY, |
55 | FormatJson::ALL_OK ); |
56 | } |
57 | |
58 | /** |
59 | * @param \Content|JCContent $oldContent |
60 | * @param \Content|JCContent $myContent |
61 | * @param \Content|JCContent $yourContent |
62 | * @return bool|JCContent |
63 | */ |
64 | public function merge3( \Content $oldContent, \Content $myContent, \Content $yourContent ) { |
65 | // Almost identical clone of the parent's merge3, except that we use pretty-printed merge, |
66 | // thus allowing much more lenient line-based merging. |
67 | |
68 | $this->checkModelID( $oldContent->getModel() ); |
69 | $this->checkModelID( $myContent->getModel() ); |
70 | $this->checkModelID( $yourContent->getModel() ); |
71 | |
72 | $format = self::CONTENT_FORMAT_JSON_PRETTY; |
73 | |
74 | $old = $this->serializeContent( $oldContent, $format ); |
75 | $mine = $this->serializeContent( $myContent, $format ); |
76 | $yours = $this->serializeContent( $yourContent, $format ); |
77 | |
78 | $ok = wfMerge( $old, $mine, $yours, $result ); |
79 | |
80 | if ( !$ok ) { |
81 | return false; |
82 | } |
83 | |
84 | if ( !$result ) { |
85 | return $this->makeEmptyContent(); |
86 | } |
87 | |
88 | return $this->unserializeContent( $result, $format ); |
89 | } |
90 | |
91 | protected function getSlotDiffRendererWithOptions( IContextSource $context, $options = [] ) { |
92 | return new JCSlotDiffRenderer( $this->createTextSlotDiffRenderer( $options ) ); |
93 | } |
94 | |
95 | /** |
96 | * Unserializes a JsonSchemaContent object. |
97 | * |
98 | * @param string|null $text Serialized form of the content |
99 | * @param null|string $format The format used for serialization |
100 | * @param bool $isSaving Perform extra validation |
101 | * @return JCContent the JsonSchemaContent object wrapping $text |
102 | */ |
103 | public function unserializeContent( $text, $format = null, $isSaving = true ) { |
104 | $this->checkFormat( $format ); |
105 | $modelId = $this->getModelID(); |
106 | $class = JCSingleton::getContentClass( $modelId ); |
107 | return new $class( $text, $modelId, $isSaving ); |
108 | } |
109 | |
110 | /** @inheritDoc */ |
111 | protected function getContentClass() { |
112 | return JCSingleton::getContentClass( $this->getModelID() ); |
113 | } |
114 | |
115 | /** |
116 | * Creates an empty JsonSchemaContent object. |
117 | * |
118 | * @return JCContent |
119 | */ |
120 | public function makeEmptyContent() { |
121 | // Each model could have its own default JSON value |
122 | // null notifies that default should be used |
123 | return $this->unserializeContent( null ); |
124 | } |
125 | |
126 | /** |
127 | * @inheritDoc |
128 | */ |
129 | public function preSaveTransform( |
130 | Content $content, |
131 | PreSaveTransformParams $pstParams |
132 | ): Content { |
133 | '@phan-var JCContent $content'; |
134 | |
135 | $contentClass = $this->getContentClass(); |
136 | if ( !$content->isValidJson() ) { |
137 | return $content; // Invalid JSON - can't do anything with it |
138 | } |
139 | $formatted = FormatJson::encode( $content->getData(), false, FormatJson::ALL_OK ); |
140 | if ( $content->getText() !== $formatted ) { |
141 | return new $contentClass( $formatted, $content->getModel(), $content->thorough() ); |
142 | } |
143 | return $content; |
144 | } |
145 | |
146 | /** |
147 | * @inheritDoc |
148 | */ |
149 | protected function fillParserOutput( |
150 | Content $content, |
151 | ContentParseParams $cpoParams, |
152 | ParserOutput &$output |
153 | ) { |
154 | '@phan-var JCContent $content'; |
155 | $page = $cpoParams->getPage(); |
156 | $generateHtml = $cpoParams->getGenerateHtml(); |
157 | $revId = $cpoParams->getRevId(); |
158 | $parserOptions = $cpoParams->getParserOptions(); |
159 | if ( !$generateHtml ) { |
160 | return; |
161 | } |
162 | |
163 | $status = $content->getStatus(); |
164 | if ( !$status->isGood() ) { |
165 | // Use user's language, and split parser cache. This should not have a big |
166 | // impact because data namespace is rarely viewed, but viewing it localized |
167 | // will be valuable |
168 | $lang = $parserOptions->getUserLangObj(); |
169 | $html = $status->getHTML( false, false, $lang ); |
170 | } else { |
171 | $html = ''; |
172 | } |
173 | |
174 | if ( $status->isOK() ) { |
175 | $html .= $content |
176 | ->getView( $content->getModel() ) |
177 | ->valueToHtml( $content, $page, $revId, $parserOptions, $generateHtml, $output ); |
178 | } |
179 | |
180 | $output->setText( $html ); |
181 | } |
182 | } |