Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 186 |
|
0.00% |
0 / 22 |
CRAP | |
0.00% |
0 / 1 |
JCHooks | |
0.00% |
0 / 186 |
|
0.00% |
0 / 22 |
6972 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
onCanonicalNamespaces | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
56 | |||
onContentHandlerDefaultModelFor | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
onGetContentModels | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
onContentHandlerForModelID | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
onAlternateEdit | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
onEditPage__showEditForm_initial | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
onEditFilterMergedContent | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
30 | |||
getTitleLicenseCode | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
onEditPageCopyrightWarning | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
30 | |||
onTitleGetEditNotices | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
56 | |||
onSkinCopyrightFooter | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
onBeforePageDisplay | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
onMovePageIsValidMove | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
onApiMain__moduleManager | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
onPageSaveComplete | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onArticleDeleteComplete | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onArticleUndelete | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onPageMoveComplete | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
onGetUserPermissionsErrors | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
onArticleChangeComplete | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
110 | |||
jsonConfigIsStorage | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
42 |
1 | <?php |
2 | |
3 | // phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName |
4 | |
5 | namespace JsonConfig; |
6 | |
7 | use ApiModuleManager; |
8 | use Content; |
9 | use IContextSource; |
10 | use MediaWiki\Api\Hook\ApiMain__moduleManagerHook; |
11 | use MediaWiki\Config\Config; |
12 | use MediaWiki\Content\Hook\ContentHandlerForModelIDHook; |
13 | use MediaWiki\Content\Hook\GetContentModelsHook; |
14 | use MediaWiki\Content\IContentHandlerFactory; |
15 | use MediaWiki\EditPage\EditPage; |
16 | use MediaWiki\Hook\AlternateEditHook; |
17 | use MediaWiki\Hook\BeforePageDisplayHook; |
18 | use MediaWiki\Hook\CanonicalNamespacesHook; |
19 | use MediaWiki\Hook\EditFilterMergedContentHook; |
20 | use MediaWiki\Hook\EditPage__showEditForm_initialHook; |
21 | use MediaWiki\Hook\EditPageCopyrightWarningHook; |
22 | use MediaWiki\Hook\MovePageIsValidMoveHook; |
23 | use MediaWiki\Hook\PageMoveCompleteHook; |
24 | use MediaWiki\Hook\SkinCopyrightFooterHook; |
25 | use MediaWiki\Hook\TitleGetEditNoticesHook; |
26 | use MediaWiki\Html\Html; |
27 | use MediaWiki\Output\OutputPage; |
28 | use MediaWiki\Page\Hook\ArticleDeleteCompleteHook; |
29 | use MediaWiki\Page\Hook\ArticleUndeleteHook; |
30 | use MediaWiki\Permissions\Hook\GetUserPermissionsErrorsHook; |
31 | use MediaWiki\Revision\Hook\ContentHandlerDefaultModelForHook; |
32 | use MediaWiki\Status\Status; |
33 | use MediaWiki\Storage\Hook\PageSaveCompleteHook; |
34 | use MediaWiki\Title\Title; |
35 | use MediaWiki\User\User; |
36 | use MessageSpecifier; |
37 | |
38 | /** |
39 | * Hook handlers for JsonConfig extension. |
40 | * |
41 | * @file |
42 | * @ingroup Extensions |
43 | * @ingroup JsonConfig |
44 | * @license GPL-2.0-or-later |
45 | */ |
46 | class JCHooks implements |
47 | ApiMain__moduleManagerHook, |
48 | ArticleDeleteCompleteHook, |
49 | ArticleUndeleteHook, |
50 | BeforePageDisplayHook, |
51 | CanonicalNamespacesHook, |
52 | ContentHandlerDefaultModelForHook, |
53 | ContentHandlerForModelIDHook, |
54 | GetContentModelsHook, |
55 | AlternateEditHook, |
56 | EditPage__showEditForm_initialHook, |
57 | EditFilterMergedContentHook, |
58 | EditPageCopyrightWarningHook, |
59 | MovePageIsValidMoveHook, |
60 | PageSaveCompleteHook, |
61 | SkinCopyrightFooterHook, |
62 | TitleGetEditNoticesHook, |
63 | PageMoveCompleteHook, |
64 | GetUserPermissionsErrorsHook |
65 | { |
66 | private Config $config; |
67 | private IContentHandlerFactory $contentHandlerFactory; |
68 | |
69 | public function __construct( |
70 | Config $config, |
71 | IContentHandlerFactory $contentHandlerFactory |
72 | ) { |
73 | $this->config = $config; |
74 | $this->contentHandlerFactory = $contentHandlerFactory; |
75 | } |
76 | |
77 | /** |
78 | * Only register NS_CONFIG if running on the MediaWiki instance which houses |
79 | * the JSON configs (i.e. META) |
80 | * @param array &$namespaces |
81 | */ |
82 | public function onCanonicalNamespaces( &$namespaces ) { |
83 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
84 | return; |
85 | } |
86 | |
87 | JCSingleton::init(); |
88 | foreach ( JCSingleton::$namespaces as $ns => $name ) { |
89 | if ( $name === false ) { // must be already declared |
90 | if ( !array_key_exists( $ns, $namespaces ) ) { |
91 | wfLogWarning( "JsonConfig: Invalid \$wgJsonConfigs: Namespace $ns " . |
92 | "has not been declared by core or other extensions" ); |
93 | } |
94 | } elseif ( array_key_exists( $ns, $namespaces ) ) { |
95 | wfLogWarning( "JsonConfig: Invalid \$wgJsonConfigs: Namespace $ns => '$name' " . |
96 | "is already declared as '$namespaces[$ns]'" ); |
97 | } else { |
98 | $key = array_search( $name, $namespaces ); |
99 | if ( $key !== false ) { |
100 | wfLogWarning( "JsonConfig: Invalid \$wgJsonConfigs: Namespace $ns => '$name' " . |
101 | "has identical name with the namespace #$key" ); |
102 | } else { |
103 | $namespaces[$ns] = $name; |
104 | } |
105 | } |
106 | } |
107 | } |
108 | |
109 | /** |
110 | * Initialize state |
111 | * @param Title $title |
112 | * @param string &$modelId |
113 | * @return bool |
114 | */ |
115 | public function onContentHandlerDefaultModelFor( $title, &$modelId ) { |
116 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
117 | return true; |
118 | } |
119 | |
120 | $jct = JCSingleton::parseTitle( $title ); |
121 | if ( $jct ) { |
122 | $modelId = $jct->getConfig()->model; |
123 | return false; |
124 | } |
125 | return true; |
126 | } |
127 | |
128 | /** |
129 | * Ensure that ContentHandler knows about our dynamic models (T259126) |
130 | * @param string[] &$models |
131 | */ |
132 | public function onGetContentModels( &$models ) { |
133 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
134 | return; |
135 | } |
136 | |
137 | JCSingleton::init(); |
138 | // TODO: this is copied from onContentHandlerForModelID() |
139 | $ourModels = array_replace_recursive( |
140 | \ExtensionRegistry::getInstance()->getAttribute( 'JsonConfigModels' ), |
141 | $this->config->get( 'JsonConfigModels' ) |
142 | ); |
143 | $models = array_merge( $models, array_keys( $ourModels ) ); |
144 | } |
145 | |
146 | /** |
147 | * Instantiate JCContentHandler if we can handle this modelId |
148 | * @param string $modelId |
149 | * @param \ContentHandler &$handler |
150 | * @return bool |
151 | */ |
152 | public function onContentHandlerForModelID( $modelId, &$handler ) { |
153 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
154 | return true; |
155 | } |
156 | |
157 | JCSingleton::init(); |
158 | $models = array_replace_recursive( |
159 | \ExtensionRegistry::getInstance()->getAttribute( 'JsonConfigModels' ), |
160 | $this->config->get( 'JsonConfigModels' ) |
161 | ); |
162 | if ( array_key_exists( $modelId, $models ) ) { |
163 | // This is one of our model IDs |
164 | $handler = new JCContentHandler( $modelId ); |
165 | return false; |
166 | } |
167 | return true; |
168 | } |
169 | |
170 | /** |
171 | * AlternateEdit hook handler |
172 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/AlternateEdit |
173 | * @param EditPage $editpage |
174 | */ |
175 | public function onAlternateEdit( $editpage ) { |
176 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
177 | return; |
178 | } |
179 | $jct = JCSingleton::parseTitle( $editpage->getTitle() ); |
180 | if ( $jct ) { |
181 | $editpage->contentFormat = JCContentHandler::CONTENT_FORMAT_JSON_PRETTY; |
182 | } |
183 | } |
184 | |
185 | /** |
186 | * @param EditPage $editPage |
187 | * @param OutputPage $output |
188 | */ |
189 | public function onEditPage__showEditForm_initial( $editPage, $output ) { |
190 | if ( |
191 | $output->getConfig()->get( 'JsonConfigUseGUI' ) && |
192 | $editPage->getTitle()->getContentModel() === 'Tabular.JsonConfig' |
193 | ) { |
194 | $output->addModules( 'ext.jsonConfig.edit' ); |
195 | } |
196 | } |
197 | |
198 | /** |
199 | * Validates that the revised contents are valid JSON. |
200 | * If not valid, rejects edit with error message. |
201 | * @param IContextSource $context |
202 | * @param Content $content |
203 | * @param Status $status |
204 | * @param string $summary Edit summary provided for edit. |
205 | * @param User $user |
206 | * @param bool $minoredit |
207 | * @return bool |
208 | */ |
209 | public function onEditFilterMergedContent( |
210 | /** @noinspection PhpUnusedParameterInspection */ |
211 | IContextSource $context, Content $content, Status $status, $summary, User $user, $minoredit |
212 | ) { |
213 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
214 | return true; |
215 | } |
216 | |
217 | if ( $content instanceof JCContent ) { |
218 | $status->merge( $content->getStatus() ); |
219 | if ( !$status->isGood() ) { |
220 | // @todo Use $status->setOK() instead after this extension |
221 | // do not support mediawiki version 1.36 and before |
222 | $status->setResult( false, $status->getValue() ?: EditPage::AS_HOOK_ERROR_EXPECTED ); |
223 | return false; |
224 | } |
225 | } |
226 | return true; |
227 | } |
228 | |
229 | /** |
230 | * Get the license code for the title or false otherwise. |
231 | * license code is identifier from https://spdx.org/licenses/ |
232 | * |
233 | * @param JCTitle $jct |
234 | * @return bool|string Returns licence code string, or false if license is unknown |
235 | */ |
236 | private static function getTitleLicenseCode( JCTitle $jct ) { |
237 | $jctContent = JCSingleton::getContent( $jct ); |
238 | if ( $jctContent && $jctContent instanceof JCDataContent ) { |
239 | $license = $jctContent->getLicenseObject(); |
240 | if ( $license ) { |
241 | return $license['code']; |
242 | } |
243 | } |
244 | return false; |
245 | } |
246 | |
247 | /** |
248 | * Override a per-page specific edit page copyright warning |
249 | * |
250 | * @param Title $title |
251 | * @param string[] &$msg |
252 | * |
253 | * @return bool |
254 | */ |
255 | public function onEditPageCopyrightWarning( $title, &$msg ) { |
256 | if ( self::jsonConfigIsStorage( $this->config ) ) { |
257 | $jct = JCSingleton::parseTitle( $title ); |
258 | if ( $jct ) { |
259 | $code = self::getTitleLicenseCode( $jct ); |
260 | if ( $code ) { |
261 | $msg = [ 'jsonconfig-license-copyrightwarning', $code ]; |
262 | } else { |
263 | $requireLicense = $jct->getConfig()->license ?? false; |
264 | // Check if page has license field to apply only if it is required |
265 | // https://phabricator.wikimedia.org/T203173 |
266 | if ( $requireLicense ) { |
267 | $msg = [ 'jsonconfig-license-copyrightwarning-license-unset' ]; |
268 | } |
269 | } |
270 | return false; // Do not allow any other hook handler to override this |
271 | } |
272 | } |
273 | return true; |
274 | } |
275 | |
276 | /** |
277 | * Display a page-specific edit notice |
278 | * |
279 | * @param Title $title |
280 | * @param int $oldid |
281 | * @param array &$notices |
282 | */ |
283 | public function onTitleGetEditNotices( $title, $oldid, &$notices ) { |
284 | if ( self::jsonConfigIsStorage( $this->config ) ) { |
285 | $jct = JCSingleton::parseTitle( $title ); |
286 | if ( $jct ) { |
287 | $code = self::getTitleLicenseCode( $jct ); |
288 | if ( $code ) { |
289 | $noticeText = wfMessage( 'jsonconfig-license-notice', $code )->parse(); |
290 | $iconCodes = ''; |
291 | if ( preg_match_all( "/[a-z][a-z0-9]+/i", $code, $subcodes ) ) { |
292 | // Flip order due to dom ordering of the floating elements |
293 | foreach ( array_reverse( $subcodes[0] ) as $c => $match ) { |
294 | // Used classes: |
295 | // * mw-jsonconfig-editnotice-icon-BY |
296 | // * mw-jsonconfig-editnotice-icon-CC |
297 | // * mw-jsonconfig-editnotice-icon-CC0 |
298 | // * mw-jsonconfig-editnotice-icon-ODbL |
299 | // * mw-jsonconfig-editnotice-icon-SA |
300 | $iconCodes .= Html::rawElement( |
301 | 'span', [ 'class' => 'mw-jsonconfig-editnotice-icon-' . $match ], '' |
302 | ); |
303 | } |
304 | $iconCodes = Html::rawElement( |
305 | 'div', [ 'class' => 'mw-jsonconfig-editnotice-icons' ], $iconCodes |
306 | ); |
307 | } |
308 | |
309 | $noticeFooter = Html::rawElement( |
310 | 'div', [ 'class' => 'mw-jsonconfig-editnotice-footer' ], '' |
311 | ); |
312 | |
313 | $notices['jsonconfig'] = Html::rawElement( |
314 | 'div', |
315 | [ 'class' => 'mw-jsonconfig-editnotice' ], |
316 | $iconCodes . $noticeText . $noticeFooter |
317 | ); |
318 | } else { |
319 | // Check if page has license field to apply notice msgs only when license is required |
320 | // https://phabricator.wikimedia.org/T203173 |
321 | $requireLicense = $jct->getConfig()->license ?? false; |
322 | if ( $requireLicense ) { |
323 | $notices['jsonconfig'] = wfMessage( 'jsonconfig-license-notice-license-unset' )->parse(); |
324 | } |
325 | } |
326 | } |
327 | } |
328 | } |
329 | |
330 | /** |
331 | * Override with per-page specific copyright message |
332 | * |
333 | * @param Title $title |
334 | * @param string $type |
335 | * @param string &$msg |
336 | * @param string &$link |
337 | * |
338 | * @return bool |
339 | */ |
340 | public function onSkinCopyrightFooter( $title, $type, &$msg, &$link ) { |
341 | if ( self::jsonConfigIsStorage( $this->config ) ) { |
342 | $jct = JCSingleton::parseTitle( $title ); |
343 | if ( $jct ) { |
344 | $code = self::getTitleLicenseCode( $jct ); |
345 | if ( $code ) { |
346 | $msg = 'jsonconfig-license'; |
347 | $link = Html::element( 'a', [ |
348 | 'href' => wfMessage( 'jsonconfig-license-url-' . $code )->plain() |
349 | ], wfMessage( 'jsonconfig-license-name-' . $code )->plain() ); |
350 | return false; |
351 | } |
352 | } |
353 | } |
354 | return true; |
355 | } |
356 | |
357 | /** |
358 | * Adds CSS for pretty-printing configuration on NS_CONFIG pages. |
359 | * @param OutputPage $out |
360 | * @param \Skin $skin |
361 | */ |
362 | public function onBeforePageDisplay( |
363 | /** @noinspection PhpUnusedParameterInspection */ $out, $skin |
364 | ): void { |
365 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
366 | return; |
367 | } |
368 | |
369 | $title = $out->getTitle(); |
370 | // todo/fixme? We should probably add ext.jsonConfig style to only those pages |
371 | // that pass parseTitle() |
372 | $handler = $this->contentHandlerFactory |
373 | ->getContentHandler( $title->getContentModel() ); |
374 | if ( $handler->getDefaultFormat() === CONTENT_FORMAT_JSON || |
375 | JCSingleton::parseTitle( $title ) |
376 | ) { |
377 | $out->addModuleStyles( 'ext.jsonConfig' ); |
378 | } |
379 | } |
380 | |
381 | public function onMovePageIsValidMove( |
382 | $oldTitle, $newTitle, $status |
383 | ) { |
384 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
385 | return true; |
386 | } |
387 | |
388 | $jctOld = JCSingleton::parseTitle( $oldTitle ); |
389 | if ( $jctOld ) { |
390 | $jctNew = JCSingleton::parseTitle( $newTitle ); |
391 | if ( !$jctNew ) { |
392 | $status->fatal( 'jsonconfig-move-aborted-ns' ); |
393 | return false; |
394 | } elseif ( $jctOld->getConfig()->model !== $jctNew->getConfig()->model ) { |
395 | $status->fatal( 'jsonconfig-move-aborted-model', $jctOld->getConfig()->model, |
396 | $jctNew->getConfig()->model ); |
397 | return false; |
398 | } |
399 | } |
400 | |
401 | return true; |
402 | } |
403 | |
404 | /** |
405 | * Conditionally load API module 'jsondata' depending on whether or not |
406 | * this wiki stores any jsonconfig data |
407 | * |
408 | * @param ApiModuleManager $moduleManager Module manager instance |
409 | */ |
410 | public function onApiMain__moduleManager( $moduleManager ) { |
411 | if ( $moduleManager->getConfig()->get( 'JsonConfigEnableLuaSupport' ) ) { |
412 | $moduleManager->addModule( 'jsondata', 'action', JCDataApi::class ); |
413 | } |
414 | } |
415 | |
416 | public function onPageSaveComplete( |
417 | /** @noinspection PhpUnusedParameterInspection */ |
418 | $wikiPage, $user, $summary, $flags, $revisionRecord, $editResult |
419 | ) { |
420 | return $this->onArticleChangeComplete( $wikiPage ); |
421 | } |
422 | |
423 | public function onArticleDeleteComplete( |
424 | /** @noinspection PhpUnusedParameterInspection */ |
425 | $article, $user, $reason, $id, $content, $logEntry, $archivedRevisionCount |
426 | ) { |
427 | return $this->onArticleChangeComplete( $article ); |
428 | } |
429 | |
430 | public function onArticleUndelete( |
431 | /** @noinspection PhpUnusedParameterInspection */ |
432 | $title, $created, $comment, $oldPageId, $restoredPages |
433 | ) { |
434 | return $this->onArticleChangeComplete( $title ); |
435 | } |
436 | |
437 | public function onPageMoveComplete( |
438 | /** @noinspection PhpUnusedParameterInspection */ |
439 | $title, $newTitle, $user, $pageid, $redirid, $reason, $revisionRecord |
440 | ) { |
441 | $title = Title::newFromLinkTarget( $title ); |
442 | $newTitle = Title::newFromLinkTarget( $newTitle ); |
443 | return $this->onArticleChangeComplete( $title ) || |
444 | $this->onArticleChangeComplete( $newTitle ); |
445 | } |
446 | |
447 | /** |
448 | * Prohibit creation of the pages that are part of our namespaces but have not been explicitly |
449 | * allowed. |
450 | * @param Title $title |
451 | * @param User $user |
452 | * @param string $action |
453 | * @param array|string|MessageSpecifier &$result |
454 | * @return bool |
455 | */ |
456 | public function onGetUserPermissionsErrors( |
457 | /** @noinspection PhpUnusedParameterInspection */ |
458 | $title, $user, $action, &$result |
459 | ) { |
460 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
461 | return true; |
462 | } |
463 | |
464 | if ( $action === 'create' && JCSingleton::parseTitle( $title ) === null ) { |
465 | // prohibit creation of the pages for the namespace that we handle, |
466 | // if the title is not matching declared rules |
467 | $result = 'jsonconfig-blocked-page-creation'; |
468 | return false; |
469 | } |
470 | return true; |
471 | } |
472 | |
473 | /** |
474 | * @param \WikiPage|Title $value |
475 | * @param JCContent|null $content |
476 | * @return bool |
477 | */ |
478 | private function onArticleChangeComplete( $value, $content = null ) { |
479 | if ( !self::jsonConfigIsStorage( $this->config ) ) { |
480 | return true; |
481 | } |
482 | |
483 | if ( $value && ( !$content || $content instanceof JCContent ) ) { |
484 | if ( method_exists( $value, 'getTitle' ) ) { |
485 | $value = $value->getTitle(); |
486 | } |
487 | $jct = JCSingleton::parseTitle( $value ); |
488 | if ( $jct && $jct->getConfig()->store ) { |
489 | $store = new JCCache( $jct, $content ); |
490 | $store->resetCache(); |
491 | |
492 | // Handle remote site notification |
493 | $store = $jct->getConfig()->store; |
494 | // @phan-suppress-next-line PhanTypeExpectedObjectPropAccess |
495 | if ( $store->notifyUrl ) { |
496 | $req = |
497 | // @phan-suppress-next-line PhanTypeExpectedObjectPropAccess |
498 | JCUtils::initApiRequestObj( $store->notifyUrl, $store->notifyUsername, |
499 | // @phan-suppress-next-line PhanTypeExpectedObjectPropAccess |
500 | $store->notifyPassword ); |
501 | if ( $req ) { |
502 | $query = [ |
503 | 'format' => 'json', |
504 | 'action' => 'jsonconfig', |
505 | 'command' => 'reload', |
506 | 'title' => $jct->getNamespace() . ':' . $jct->getDBkey(), |
507 | ]; |
508 | JCUtils::callApi( $req, $query, 'notify remote JsonConfig client' ); |
509 | } |
510 | } |
511 | } |
512 | } |
513 | return true; |
514 | } |
515 | |
516 | /** |
517 | * Quick check if the current wiki will store any configurations. |
518 | * Faster than doing a full parsing of the $wgJsonConfigs in the JCSingleton::init() |
519 | * @param Config $config |
520 | * @return bool |
521 | */ |
522 | public static function jsonConfigIsStorage( Config $config ) { |
523 | static $isStorage = null; |
524 | if ( $isStorage === null ) { |
525 | $isStorage = false; |
526 | $configs = array_replace_recursive( |
527 | \ExtensionRegistry::getInstance()->getAttribute( 'JsonConfigs' ), |
528 | $config->get( 'JsonConfigs' ) |
529 | ); |
530 | foreach ( $configs as $jc ) { |
531 | if ( ( !array_key_exists( 'isLocal', $jc ) || $jc['isLocal'] ) || |
532 | ( array_key_exists( 'store', $jc ) ) |
533 | ) { |
534 | $isStorage = true; |
535 | break; |
536 | } |
537 | } |
538 | } |
539 | return $isStorage; |
540 | } |
541 | } |