Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
71.67% |
172 / 240 |
|
72.22% |
13 / 18 |
CRAP | |
0.00% |
0 / 1 |
ZObjectContentHandler | |
71.67% |
172 / 240 |
|
72.22% |
13 / 18 |
76.39 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
canBeUsedOn | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
makeEmptyContent | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
1 | |||
makeContent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getContentClass | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
serializeContent | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
unserializeContent | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getExternalRepresentation | |
85.00% |
34 / 40 |
|
0.00% |
0 / 1 |
6.12 | |||
getSecondaryDataUpdates | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getDeletionUpdates | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
supportsDirectEditing | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getActionOverrides | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
generateHTMLOnEdit | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
fillParserOutput | |
5.00% |
3 / 60 |
|
0.00% |
0 / 1 |
26.43 | |||
createZObjectViewHeader | |
98.67% |
74 / 75 |
|
0.00% |
0 / 1 |
9 | |||
validateSave | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
preSaveTransform | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
createDifferenceEngine | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * WikiLambda content handler for ZObjects |
4 | * |
5 | * @file |
6 | * @ingroup Extensions |
7 | * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt |
8 | * @license MIT |
9 | */ |
10 | |
11 | namespace MediaWiki\Extension\WikiLambda; |
12 | |
13 | use Content; |
14 | use ContentHandler; |
15 | use FormatJson; |
16 | use InvalidArgumentException; |
17 | use Language; |
18 | use MediaWiki\Content\Renderer\ContentParseParams; |
19 | use MediaWiki\Content\Transform\PreSaveTransformParams; |
20 | use MediaWiki\Content\ValidationParams; |
21 | use MediaWiki\Context\IContextSource; |
22 | use MediaWiki\Context\RequestContext; |
23 | use MediaWiki\Extension\WikiLambda\Diff\ZObjectContentDifferenceEngine; |
24 | use MediaWiki\Extension\WikiLambda\Registry\ZErrorTypeRegistry; |
25 | use MediaWiki\Extension\WikiLambda\Registry\ZLangRegistry; |
26 | use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry; |
27 | use MediaWiki\Html\Html; |
28 | use MediaWiki\MediaWikiServices; |
29 | use MediaWiki\Revision\SlotRenderingProvider; |
30 | use MediaWiki\Title\Title; |
31 | use ParserOutput; |
32 | use StatusValue; |
33 | |
34 | class ZObjectContentHandler extends ContentHandler { |
35 | use ZObjectEditingPageTrait; |
36 | |
37 | /** |
38 | * @param string $modelId |
39 | */ |
40 | public function __construct( $modelId ) { |
41 | if ( $modelId !== CONTENT_MODEL_ZOBJECT ) { |
42 | throw new InvalidArgumentException( __CLASS__ . " initialised for invalid content model" ); |
43 | } |
44 | |
45 | // Triggers use of message content-model-zobject |
46 | parent::__construct( CONTENT_MODEL_ZOBJECT, [ CONTENT_FORMAT_TEXT ] ); |
47 | } |
48 | |
49 | /** |
50 | * @param Title $title Page to check |
51 | * @return bool |
52 | */ |
53 | public function canBeUsedOn( Title $title ) { |
54 | return $title->inNamespace( NS_MAIN ); |
55 | } |
56 | |
57 | /** |
58 | * @return ZObjectContent |
59 | */ |
60 | public function makeEmptyContent() { |
61 | $class = $this->getContentClass(); |
62 | return new $class( |
63 | '{' . "\n" |
64 | . '"' . ZTypeRegistry::Z_OBJECT_TYPE . '": "' . ZTypeRegistry::Z_PERSISTENTOBJECT . '",' . "\n" |
65 | . '"' . ZTypeRegistry::Z_PERSISTENTOBJECT_ID . '": "' . ZTypeRegistry::Z_NULL_REFERENCE . '",' . "\n" |
66 | . '"' . ZTypeRegistry::Z_PERSISTENTOBJECT_VALUE . '": "",' . "\n" |
67 | . '"' . ZTypeRegistry::Z_PERSISTENTOBJECT_LABEL . '": {' . "\n" |
68 | . '"' . ZTypeRegistry::Z_OBJECT_TYPE . '": "' . ZTypeRegistry::Z_MULTILINGUALSTRING . '",' . "\n" |
69 | . '"' . ZTypeRegistry::Z_MULTILINGUALSTRING_VALUE . '":' |
70 | . '["' . ZTypeRegistry::Z_MONOLINGUALSTRING . '"]' . "\n" |
71 | . '},' . "\n" |
72 | . '"' . ZTypeRegistry::Z_PERSISTENTOBJECT_ALIASES . '": {' . "\n" |
73 | . '"' . ZTypeRegistry::Z_OBJECT_TYPE . '": "' . ZTypeRegistry::Z_MULTILINGUALSTRINGSET . '",' . "\n" |
74 | . '"' . ZTypeRegistry::Z_MULTILINGUALSTRINGSET_VALUE . '":' |
75 | . '["' . ZTypeRegistry::Z_MONOLINGUALSTRINGSET . '"]' . "\n" |
76 | . '},' . "\n" |
77 | . '"' . ZTypeRegistry::Z_PERSISTENTOBJECT_DESCRIPTION . '": {' . "\n" |
78 | . '"' . ZTypeRegistry::Z_OBJECT_TYPE . '": "' . ZTypeRegistry::Z_MULTILINGUALSTRING . '",' . "\n" |
79 | . '"' . ZTypeRegistry::Z_MULTILINGUALSTRING_VALUE . '":' |
80 | . '["' . ZTypeRegistry::Z_MONOLINGUALSTRING . '"]' . "\n" |
81 | . '}' . "\n" |
82 | . '}' |
83 | ); |
84 | } |
85 | |
86 | /** |
87 | * @param string $data |
88 | * @param Title|null $title |
89 | * @param string|null $modelId |
90 | * @param string|null $format |
91 | * @return ZObjectContent |
92 | */ |
93 | public static function makeContent( $data, Title $title = null, $modelId = null, $format = null ) { |
94 | // @phan-suppress-next-line PhanTypeMismatchReturnSuperType |
95 | return parent::makeContent( $data, $title, $modelId, $format ); |
96 | } |
97 | |
98 | /** |
99 | * @return string |
100 | */ |
101 | protected function getContentClass() { |
102 | return ZObjectContent::class; |
103 | } |
104 | |
105 | /** |
106 | * @param Content $content |
107 | * @param string|null $format |
108 | * @return string |
109 | */ |
110 | public function serializeContent( Content $content, $format = null ) { |
111 | $this->checkFormat( $format ); |
112 | |
113 | if ( !( $content instanceof ZObjectContent ) ) { |
114 | // Throw? |
115 | return ''; |
116 | } |
117 | |
118 | return $content->getText(); |
119 | } |
120 | |
121 | /** |
122 | * @param string $text |
123 | * @param string|null $format |
124 | * @return ZObjectContent |
125 | */ |
126 | public function unserializeContent( $text, $format = null ) { |
127 | $this->checkFormat( $format ); |
128 | |
129 | $class = $this->getContentClass(); |
130 | return new $class( $text ); |
131 | } |
132 | |
133 | /** |
134 | * @param Title $zObjectTitle The page to fetch. |
135 | * @param string|null $languageCode The language in which to return results. If unset, all results are returned. |
136 | * @param int|null $revision The revision ID of the page to fetch. If unset, the latest is returned. |
137 | * @return string The external JSON form of the given title. |
138 | * @throws ZErrorException |
139 | */ |
140 | public static function getExternalRepresentation( |
141 | Title $zObjectTitle, ?string $languageCode = null, ?int $revision = null |
142 | ): string { |
143 | if ( $zObjectTitle->getNamespace() !== NS_MAIN ) { |
144 | throw new ZErrorException( |
145 | ZErrorFactory::createZErrorInstance( |
146 | ZErrorTypeRegistry::Z_ERROR_WRONG_NAMESPACE, |
147 | [ 'title' => (string)$zObjectTitle ] |
148 | ) |
149 | ); |
150 | } |
151 | |
152 | if ( $zObjectTitle->getContentModel() !== CONTENT_MODEL_ZOBJECT ) { |
153 | throw new ZErrorException( |
154 | ZErrorFactory::createZErrorInstance( |
155 | ZErrorTypeRegistry::Z_ERROR_WRONG_CONTENT_TYPE, |
156 | [ 'title' => (string)$zObjectTitle ] |
157 | ) |
158 | ); |
159 | } |
160 | |
161 | $zObjectStore = WikiLambdaServices::getZObjectStore(); |
162 | $zObject = $zObjectStore->fetchZObjectByTitle( $zObjectTitle, $revision ); |
163 | |
164 | if ( $zObject === false ) { |
165 | throw new ZErrorException( |
166 | ZErrorFactory::createZErrorInstance( |
167 | ZErrorTypeRegistry::Z_ERROR_ZID_NOT_FOUND, |
168 | [ 'data' => (string)$zObjectTitle ] |
169 | ) |
170 | ); |
171 | } |
172 | |
173 | $object = ZObjectUtils::canonicalize( $zObject->getObject() ); |
174 | |
175 | if ( $languageCode ) { |
176 | // TODO (T362246): Dependency-inject |
177 | $services = MediaWikiServices::getInstance(); |
178 | |
179 | // If language code is not valid, throws ZErrorException of Z540/Invalid language code |
180 | if ( !$services->getLanguageNameUtils()->isValidCode( $languageCode ) ) { |
181 | throw new ZErrorException( |
182 | ZErrorFactory::createZErrorInstance( |
183 | ZErrorTypeRegistry::Z_ERROR_INVALID_LANG_CODE, |
184 | [ 'lang' => $languageCode ] |
185 | ) |
186 | ); |
187 | } |
188 | |
189 | // If language doesn't have a Zid, throws ZErrorException of Z541/Language code not found |
190 | $languageZid = ZLangRegistry::singleton()->getLanguageZidFromCode( $languageCode ); |
191 | |
192 | // Filter all Multilingual Strings and Stringsets if language is present and valid |
193 | $object = ZObjectUtils::filterZMultilingualStringsToLanguage( $object, [ $languageZid ] ); |
194 | } |
195 | |
196 | // Replace Z2K1: Z0 with the actual page ID. |
197 | $object->{ ZTypeRegistry::Z_PERSISTENTOBJECT_ID } = [ |
198 | ZTypeRegistry::Z_OBJECT_TYPE => ZTypeRegistry::Z_STRING, |
199 | ZTypeRegistry::Z_STRING_VALUE => $zObjectTitle->getDBkey() |
200 | ]; |
201 | |
202 | return FormatJson::encode( $object, true, FormatJson::UTF8_OK ); |
203 | } |
204 | |
205 | /** |
206 | * @inheritDoc |
207 | */ |
208 | public function getSecondaryDataUpdates( |
209 | Title $title, |
210 | Content $content, |
211 | $role, |
212 | SlotRenderingProvider $slotOutput |
213 | ) { |
214 | return array_merge( |
215 | parent::getSecondaryDataUpdates( $title, $content, $role, $slotOutput ), |
216 | [ new ZObjectSecondaryDataUpdate( $title, $content ) ] |
217 | ); |
218 | } |
219 | |
220 | /** |
221 | * @inheritDoc |
222 | */ |
223 | public function getDeletionUpdates( Title $title, $role ) { |
224 | return array_merge( |
225 | parent::getDeletionUpdates( $title, $role ), |
226 | [ new ZObjectSecondaryDataRemoval( $title ) ] |
227 | ); |
228 | } |
229 | |
230 | /** |
231 | * @inheritDoc |
232 | */ |
233 | public function supportsDirectEditing() { |
234 | return false; |
235 | } |
236 | |
237 | /** |
238 | * @inheritDoc |
239 | */ |
240 | public function getActionOverrides() { |
241 | return [ |
242 | 'edit' => ZObjectEditAction::class, |
243 | 'history' => ZObjectHistoryAction::class |
244 | ]; |
245 | } |
246 | |
247 | /** |
248 | * Do not render HTML on edit (T285987) |
249 | * |
250 | * @return bool |
251 | */ |
252 | public function generateHTMLOnEdit(): bool { |
253 | return false; |
254 | } |
255 | |
256 | /** |
257 | * Set the HTML and add the appropriate styles. |
258 | * |
259 | * @inheritDoc |
260 | * @param Content $content |
261 | * @param ContentParseParams $cpoParams |
262 | * @param ParserOutput &$parserOutput The output object to fill (reference). |
263 | */ |
264 | protected function fillParserOutput( |
265 | Content $content, |
266 | ContentParseParams $cpoParams, |
267 | ParserOutput &$parserOutput |
268 | ) { |
269 | if ( !$cpoParams->getGenerateHtml() ) { |
270 | $parserOutput->setText( '' ); |
271 | return; |
272 | } |
273 | |
274 | $userLang = RequestContext::getMain()->getLanguage(); |
275 | |
276 | // Ensure the stored content is a valid ZObject; this also populates $this->getZObject() for us |
277 | if ( !( $content instanceof ZObjectContent ) || !$content->isValid() ) { |
278 | $parserOutput->setText( |
279 | Html::element( |
280 | 'div', |
281 | [ |
282 | 'class' => [ 'ext-wikilambda-view-invalidcontent', 'warning' ], |
283 | ], |
284 | wfMessage( 'wikilambda-invalidzobject' )->inLanguage( $userLang )->text() |
285 | ) |
286 | ); |
287 | // Exit early, as the rest of the code relies on the stored content being well-formed and valid. |
288 | return; |
289 | } |
290 | |
291 | $pageIdentity = $cpoParams->getPage(); |
292 | |
293 | // TODO (T362245): Re-work our code to use PageReferences rather than Titles |
294 | $title = Title::castFromPageReference( $pageIdentity ); |
295 | '@phan-var Title $title'; |
296 | |
297 | $header = static::createZObjectViewHeader( $content, $title, $userLang ); |
298 | $parserOutput->setTitleText( $header ); |
299 | |
300 | $parserOutput->addModuleStyles( [ 'ext.wikilambda.viewpage.styles' ] ); |
301 | |
302 | $parserOutput->addModules( [ 'ext.wikilambda.edit' ] ); |
303 | |
304 | // $zObjectStore = WikiLambdaServices::getZObjectStore(); |
305 | // $zObject = $zObjectStore->fetchZObjectByTitle( $title ); |
306 | |
307 | $zLangRegistry = ZLangRegistry::singleton(); |
308 | $userLangCode = $userLang->getCode(); |
309 | |
310 | // If the userLang isn't recognised (e.g. it's qqx, or a language we don't support yet, or it's |
311 | // nonsense), then fall back to English. |
312 | $userLangZid = $zLangRegistry->getLanguageZidFromCode( $userLangCode, true ); |
313 | // Normalise our used language code from what the Language object says |
314 | $userLangCode = $zLangRegistry->getLanguageCodeFromZid( $userLangZid ); |
315 | |
316 | // Add the canonical page link to /view/<lang>/<zid> |
317 | $output = RequestContext::getMain()->getOutput(); |
318 | $output->addLink( [ |
319 | 'rel' => 'canonical', |
320 | 'hreflang' => $userLangCode, |
321 | 'href' => "/view/$userLangCode/" . $title->getDBkey(), |
322 | ] ); |
323 | |
324 | $editingData = [ |
325 | // The following paramether may be the same now, |
326 | // but will surely change in the future as we remove the Zds from the UI |
327 | 'title' => $title->getBaseText(), |
328 | 'zId' => $title->getBaseText(), |
329 | 'page' => $title->getPrefixedDBkey(), |
330 | 'zlang' => $userLangCode, |
331 | 'zlangZid' => $userLangZid, |
332 | 'createNewPage' => false, |
333 | 'viewmode' => true |
334 | ]; |
335 | |
336 | $parserOutput->setJsConfigVar( 'wgWikiLambda', $editingData ); |
337 | |
338 | $parserOutput->setText( |
339 | // Placeholder div for the Vue template. |
340 | Html::element( 'div', [ 'id' => 'ext-wikilambda-app' ] ) |
341 | // Fallback div for the warning. |
342 | . Html::rawElement( |
343 | 'div', |
344 | [ |
345 | 'class' => [ 'ext-wikilambda-view-nojsfallback', 'client-nojs' ], |
346 | ], |
347 | Html::element( |
348 | 'div', |
349 | [ |
350 | 'class' => [ 'ext-wikilambda-view-nojswarning', 'warning' ], |
351 | ], |
352 | wfMessage( 'wikilambda-viewmode-nojs' )->inLanguage( $userLang )->text() |
353 | ) |
354 | ) |
355 | ); |
356 | |
357 | // Add links to other ZObjects |
358 | foreach ( $content->getInnerZObject()->getLinkedZObjects() as $link ) { |
359 | $parserOutput->addLink( Title::newFromText( $link, NS_MAIN ) ); |
360 | } |
361 | } |
362 | |
363 | /** |
364 | * Generate the special "title" shown on view pages |
365 | * |
366 | * <span class="ext-wikilambda-viewpage-header" lang="es"> |
367 | * <span class="ext-wikilambda-viewpage-header--bcp47-code">en</span> |
368 | *  |
369 | * <span class="ext-wikilambda-viewpage-header-label">multiply</span> |
370 | * <span class="ext-wikilambda-viewpage-header-zid">Z12345</span> |
371 | * <div class="ext-wikilambda-viewpage-header-type"> |
372 | * <span class="ext-wikilambda-viewpage-header--bcp47-code">en</span> |
373 | *  |
374 | * <span class="ext-wikilambda-viewpage-header-type-label">Function</span> |
375 | * </div> |
376 | * </span> |
377 | * |
378 | * @param ZObjectContent $content |
379 | * @param Title $title |
380 | * @param Language $userLang |
381 | * @return string |
382 | */ |
383 | public static function createZObjectViewHeader( |
384 | ZObjectContent $content, Title $title, Language $userLang |
385 | ): string { |
386 | // TODO (T362246): Dependency-inject |
387 | $services = MediaWikiServices::getInstance(); |
388 | |
389 | $zobject = $content->getZObject(); |
390 | |
391 | if ( !$zobject || !$zobject->isValid() ) { |
392 | // Soemthing's bad, let's give up. |
393 | return ''; |
394 | } |
395 | |
396 | // Get best-available label (and its language code) for the target object's type, given the request language. |
397 | [ |
398 | 'title' => $targetTypeLabel, |
399 | 'languageCode' => $targetTypeLabelLanguage |
400 | ] = $content->getTypeStringAndLanguage( $userLang ); |
401 | |
402 | // OBJECT TYPE Language code, which is usually a BCP47 code (e.g. 'en') but sometimes tests inject it as a |
403 | // Language object(!) |
404 | $targetTypeDisplayCode = gettype( $targetTypeLabelLanguage ) === 'string' |
405 | ? $targetTypeLabelLanguage : $targetTypeLabelLanguage->getCode(); |
406 | // OBJECT TYPE language label (e.g. 'Function') of the language currently being rendered |
407 | $targetTypeDisplayLabelLanguageName = $services->getLanguageNameUtils()->getLanguageName( |
408 | $targetTypeDisplayCode |
409 | ); |
410 | |
411 | // Get best-available label (and its language code) for the target object's name, given the request language. |
412 | |
413 | // OBJECT NAME label (e.g. 'My function' or 'Unknown') and language code (e.g. 'en') |
414 | [ |
415 | 'title' => $targetLabel, |
416 | 'languageCode' => $targetLabelLanguageCode |
417 | ] = $zobject->getLabels()->buildStringForLanguage( $userLang ) |
418 | ->fallbackWithEnglish() |
419 | ->placeholderForTitle() |
420 | ->getStringAndLanguageCode(); |
421 | |
422 | // OBJECT NAME language label (e.g. 'English') of the language currently being rendered |
423 | $targetDisplayLabelLanguageName = $services->getLanguageNameUtils()->getLanguageName( |
424 | $targetLabelLanguageCode |
425 | ); |
426 | |
427 | $bcp47CodeClassName = 'ext-wikilambda-viewpage-header--bcp47-code'; |
428 | |
429 | $targetDisplayLabelWidget = ''; |
430 | // If the object type label (e.g. 'Function') is not in the user's language, show a BCP47 code widget |
431 | // for the language used instead |
432 | if ( $targetLabelLanguageCode !== $userLang->getCode() ) { |
433 | $targetDisplayLabelWidget = ZObjectUtils::wrapBCP47CodeInFakeCodexChip( |
434 | $targetLabelLanguageCode, $targetDisplayLabelLanguageName, $bcp47CodeClassName |
435 | ); |
436 | } |
437 | |
438 | $targetDisplayTypeWidget = ''; |
439 | // If the object label (e.g. 'Echo') is not in the user's language, show a BCP47 code widget |
440 | // for the language used instead |
441 | if ( $targetTypeDisplayCode !== $userLang->getCode() ) { |
442 | $targetDisplayTypeWidget = ZObjectUtils::wrapBCP47CodeInFakeCodexChip( |
443 | $targetTypeDisplayCode, $targetTypeDisplayLabelLanguageName, $bcp47CodeClassName |
444 | ); |
445 | } |
446 | |
447 | $untitledStyle = $targetLabel === wfMessage( 'wikilambda-editor-default-name' )->text() ? |
448 | 'ext-wikilambda-viewpage-header--title-untitled' : null; |
449 | |
450 | $labelSpan = Html::element( |
451 | 'span', |
452 | [ |
453 | 'class' => [ |
454 | 'ext-wikilambda-viewpage-header-title', |
455 | 'ext-wikilambda-viewpage-header-title--function-name', |
456 | $untitledStyle |
457 | ] |
458 | ], |
459 | $targetLabel |
460 | ); |
461 | |
462 | $zidSpan = Html::element( |
463 | 'span', |
464 | [ |
465 | 'class' => 'ext-wikilambda-viewpage-header-zid' |
466 | ], |
467 | $title->getText() |
468 | ); |
469 | |
470 | $labelTitle = |
471 | // (T356731) When $targetDisplayLabelWidget is an empty string, colon-separator already |
472 | // adds/removes the needed/unneeded whitespace for languages. Always adding a |
473 | // space would unexpectedly add unneeded extra whitespace for languages including |
474 | // zh-hans, zh-hant, etc. |
475 | ( $targetDisplayLabelWidget === '' ? '' : $targetDisplayLabelWidget . ' ' ) |
476 | . $labelSpan . ' ' . $zidSpan; |
477 | |
478 | $typeSubtitle = Html::rawElement( |
479 | 'div', [ 'class' => 'ext-wikilambda-viewpage-header-type' ], |
480 | $targetDisplayTypeWidget . ' ' . Html::element( |
481 | 'span', |
482 | [ |
483 | 'class' => 'ext-wikilambda-viewpage-header-type-label' |
484 | ], |
485 | $targetTypeLabel |
486 | ) |
487 | ); |
488 | |
489 | return Html::rawElement( |
490 | 'span', |
491 | [ |
492 | // Mark the header in the correct language, regardless of the rest of the page |
493 | // … but mark it back into their requested language if it's actually untitled |
494 | 'lang' => ( $untitledStyle === null ? $userLang->getCode() : $targetTypeDisplayCode ), |
495 | 'class' => 'ext-wikilambda-viewpage-header' |
496 | ], |
497 | $labelTitle . $typeSubtitle |
498 | ); |
499 | } |
500 | |
501 | /** |
502 | * @param Content $content |
503 | * @param ValidationParams $validationParams |
504 | * @return StatusValue |
505 | */ |
506 | public function validateSave( $content, $validationParams ) { |
507 | if ( $content->isValid() ) { |
508 | return StatusValue::newGood(); |
509 | } |
510 | return StatusValue::newFatal( "wikilambda-invalidzobject" ); |
511 | } |
512 | |
513 | /** |
514 | * @inheritDoc |
515 | */ |
516 | public function preSaveTransform( Content $content, PreSaveTransformParams $pstParams ): Content { |
517 | '@phan-var ZObjectContent $content'; |
518 | |
519 | if ( !$content->isValid() ) { |
520 | return $content; |
521 | } |
522 | |
523 | $json = ZObjectUtils::canonicalize( $content->getObject() ); |
524 | $encoded = FormatJson::encode( $json, true, FormatJson::UTF8_OK ); |
525 | $encodedCleanedWhitespace = str_replace( [ "\r\n", "\r" ], "\n", rtrim( $encoded ) ); |
526 | |
527 | if ( $content->getText() !== $encodedCleanedWhitespace ) { |
528 | $contentClass = $this->getContentClass(); |
529 | return new $contentClass( $encodedCleanedWhitespace ); |
530 | } |
531 | |
532 | return $content; |
533 | } |
534 | |
535 | /** |
536 | * @inheritDoc |
537 | */ |
538 | public function createDifferenceEngine( |
539 | IContextSource $context, |
540 | $oldContentRevisionId = 0, |
541 | $newContentRevisionId = 0, |
542 | $recentChangesId = 0, |
543 | $refreshCache = false, |
544 | $unhide = false |
545 | ) { |
546 | return new ZObjectContentDifferenceEngine( |
547 | $context, $oldContentRevisionId, $newContentRevisionId, $recentChangesId, $refreshCache, $unhide |
548 | ); |
549 | } |
550 | } |