2015-08-03 06:37:32 +00:00
|
|
|
<?php
|
|
|
|
|
2022-02-06 18:54:47 +00:00
|
|
|
namespace MediaWiki\Extension\Gadgets\Content;
|
|
|
|
|
2024-01-06 16:45:07 +00:00
|
|
|
use MediaWiki\Status\Status;
|
2022-02-06 18:54:47 +00:00
|
|
|
|
2015-08-03 06:37:32 +00:00
|
|
|
/**
|
|
|
|
* Class responsible for validating Gadget definition contents
|
|
|
|
*
|
|
|
|
* @todo maybe this should use a formal JSON schema validator or something
|
|
|
|
*/
|
|
|
|
class GadgetDefinitionValidator {
|
|
|
|
/**
|
2020-12-17 19:07:37 +00:00
|
|
|
* @var array Validation metadata.
|
2017-05-30 18:38:47 +00:00
|
|
|
* 'foo.bar.baz' => [ 'type check callback',
|
|
|
|
* 'type name' [, 'member type check callback', 'member type name'] ]
|
2015-08-03 06:37:32 +00:00
|
|
|
*/
|
2016-12-28 10:25:47 +00:00
|
|
|
protected static $propertyValidation = [
|
|
|
|
'settings' => [ 'is_array', 'array' ],
|
Improve localisation on Special:Gadgets, minor code cleanups
For each item, either display human-readable and translated text,
or display a technical non-translatable identifier as `<code>`,
with optional localised text in the title attribute.
* Re-format "rights" as a sentence instead of a bullet list. It was
the only one using a bullet list, which made it feel a bit long.
* Re-format "actions" as `<code>` since they are not localised.
* Re-format "contentModels" as `<code>`, and add the localised display
name in a title attribute, same as we do with "rights" already.
ContentHandler::getLocalizedName() is also used already on
Special:ChangeContentModel.
* Fix "contentModels" to set `needLineBreakAfter = true`, otherwise
if a gadget also sets "supportsUrlLoad", then that sentence is
appended to the previous line.
Update phrasing and sorting to be consistent everywhere, and adopt
native PHP types where possible.
In most cases, I made things alphabetical, with the exception of
Special:Gadgets user interface output, and Gadget class methods,
which both follow the order of most recently added feature last
(rights, skins, actions, namespaces, contentmodels, categories).
Highlights:
* Fix namespace IDs type. These can be strings when they are parsed
from the gadget definition text, not always integers.
* Add explicit default for 'category'. In theory not needed because
MediaWikiGadgetsDefinitionRepo has a `$section = '';` default, and
MediaWikiGadgetsJsonRepo uses GadgetDefinitionContentHandler where
`category: ''` is part of both the initial page content, as well
as merged via getDefaultMetadata.
This default benefits simpler test cases, and static analysis,
since the Gadget class constructor does not (yet) require it.
Without this, getCategory() could TypeError due to returning null.
Bug: T63007
Change-Id: I3b2c72a6424d520431d03761d23a5a222518ce3d
2024-03-16 23:32:44 +00:00
|
|
|
'settings.actions' => [ 'is_array', 'array', 'is_string', 'string' ],
|
|
|
|
'settings.categories' => [ 'is_array', 'array', 'is_string', 'string' ],
|
|
|
|
'settings.category' => [ 'is_string', 'string' ],
|
|
|
|
'settings.contentModels' => [ 'is_array', 'array', 'is_string', 'string' ],
|
2016-12-28 10:25:47 +00:00
|
|
|
'settings.default' => [ 'is_bool', 'boolean' ],
|
|
|
|
'settings.hidden' => [ 'is_bool', 'boolean' ],
|
Improve localisation on Special:Gadgets, minor code cleanups
For each item, either display human-readable and translated text,
or display a technical non-translatable identifier as `<code>`,
with optional localised text in the title attribute.
* Re-format "rights" as a sentence instead of a bullet list. It was
the only one using a bullet list, which made it feel a bit long.
* Re-format "actions" as `<code>` since they are not localised.
* Re-format "contentModels" as `<code>`, and add the localised display
name in a title attribute, same as we do with "rights" already.
ContentHandler::getLocalizedName() is also used already on
Special:ChangeContentModel.
* Fix "contentModels" to set `needLineBreakAfter = true`, otherwise
if a gadget also sets "supportsUrlLoad", then that sentence is
appended to the previous line.
Update phrasing and sorting to be consistent everywhere, and adopt
native PHP types where possible.
In most cases, I made things alphabetical, with the exception of
Special:Gadgets user interface output, and Gadget class methods,
which both follow the order of most recently added feature last
(rights, skins, actions, namespaces, contentmodels, categories).
Highlights:
* Fix namespace IDs type. These can be strings when they are parsed
from the gadget definition text, not always integers.
* Add explicit default for 'category'. In theory not needed because
MediaWikiGadgetsDefinitionRepo has a `$section = '';` default, and
MediaWikiGadgetsJsonRepo uses GadgetDefinitionContentHandler where
`category: ''` is part of both the initial page content, as well
as merged via getDefaultMetadata.
This default benefits simpler test cases, and static analysis,
since the Gadget class constructor does not (yet) require it.
Without this, getCategory() could TypeError due to returning null.
Bug: T63007
Change-Id: I3b2c72a6424d520431d03761d23a5a222518ce3d
2024-03-16 23:32:44 +00:00
|
|
|
'settings.namespaces' => [ 'is_array', 'array', 'is_int', 'integer' ],
|
2021-10-17 13:05:15 +00:00
|
|
|
'settings.package' => [ 'is_bool', 'boolean' ],
|
Improve localisation on Special:Gadgets, minor code cleanups
For each item, either display human-readable and translated text,
or display a technical non-translatable identifier as `<code>`,
with optional localised text in the title attribute.
* Re-format "rights" as a sentence instead of a bullet list. It was
the only one using a bullet list, which made it feel a bit long.
* Re-format "actions" as `<code>` since they are not localised.
* Re-format "contentModels" as `<code>`, and add the localised display
name in a title attribute, same as we do with "rights" already.
ContentHandler::getLocalizedName() is also used already on
Special:ChangeContentModel.
* Fix "contentModels" to set `needLineBreakAfter = true`, otherwise
if a gadget also sets "supportsUrlLoad", then that sentence is
appended to the previous line.
Update phrasing and sorting to be consistent everywhere, and adopt
native PHP types where possible.
In most cases, I made things alphabetical, with the exception of
Special:Gadgets user interface output, and Gadget class methods,
which both follow the order of most recently added feature last
(rights, skins, actions, namespaces, contentmodels, categories).
Highlights:
* Fix namespace IDs type. These can be strings when they are parsed
from the gadget definition text, not always integers.
* Add explicit default for 'category'. In theory not needed because
MediaWikiGadgetsDefinitionRepo has a `$section = '';` default, and
MediaWikiGadgetsJsonRepo uses GadgetDefinitionContentHandler where
`category: ''` is part of both the initial page content, as well
as merged via getDefaultMetadata.
This default benefits simpler test cases, and static analysis,
since the Gadget class constructor does not (yet) require it.
Without this, getCategory() could TypeError due to returning null.
Bug: T63007
Change-Id: I3b2c72a6424d520431d03761d23a5a222518ce3d
2024-03-16 23:32:44 +00:00
|
|
|
'settings.requiresES6' => [ 'is_bool', 'boolean' ],
|
|
|
|
'settings.rights' => [ 'is_array', 'array', 'is_string', 'string' ],
|
2023-12-16 08:44:06 +00:00
|
|
|
'settings.skins' => [ 'is_array', 'array', 'is_string', 'string' ],
|
2022-01-11 09:03:44 +00:00
|
|
|
'settings.supportsUrlLoad' => [ 'is_bool', 'boolean' ],
|
Improve localisation on Special:Gadgets, minor code cleanups
For each item, either display human-readable and translated text,
or display a technical non-translatable identifier as `<code>`,
with optional localised text in the title attribute.
* Re-format "rights" as a sentence instead of a bullet list. It was
the only one using a bullet list, which made it feel a bit long.
* Re-format "actions" as `<code>` since they are not localised.
* Re-format "contentModels" as `<code>`, and add the localised display
name in a title attribute, same as we do with "rights" already.
ContentHandler::getLocalizedName() is also used already on
Special:ChangeContentModel.
* Fix "contentModels" to set `needLineBreakAfter = true`, otherwise
if a gadget also sets "supportsUrlLoad", then that sentence is
appended to the previous line.
Update phrasing and sorting to be consistent everywhere, and adopt
native PHP types where possible.
In most cases, I made things alphabetical, with the exception of
Special:Gadgets user interface output, and Gadget class methods,
which both follow the order of most recently added feature last
(rights, skins, actions, namespaces, contentmodels, categories).
Highlights:
* Fix namespace IDs type. These can be strings when they are parsed
from the gadget definition text, not always integers.
* Add explicit default for 'category'. In theory not needed because
MediaWikiGadgetsDefinitionRepo has a `$section = '';` default, and
MediaWikiGadgetsJsonRepo uses GadgetDefinitionContentHandler where
`category: ''` is part of both the initial page content, as well
as merged via getDefaultMetadata.
This default benefits simpler test cases, and static analysis,
since the Gadget class constructor does not (yet) require it.
Without this, getCategory() could TypeError due to returning null.
Bug: T63007
Change-Id: I3b2c72a6424d520431d03761d23a5a222518ce3d
2024-03-16 23:32:44 +00:00
|
|
|
|
2016-12-28 10:25:47 +00:00
|
|
|
'module' => [ 'is_array', 'array' ],
|
|
|
|
'module.dependencies' => [ 'is_array', 'array', 'is_string', 'string' ],
|
|
|
|
'module.messages' => [ 'is_array', 'array', 'is_string', 'string' ],
|
Improve localisation on Special:Gadgets, minor code cleanups
For each item, either display human-readable and translated text,
or display a technical non-translatable identifier as `<code>`,
with optional localised text in the title attribute.
* Re-format "rights" as a sentence instead of a bullet list. It was
the only one using a bullet list, which made it feel a bit long.
* Re-format "actions" as `<code>` since they are not localised.
* Re-format "contentModels" as `<code>`, and add the localised display
name in a title attribute, same as we do with "rights" already.
ContentHandler::getLocalizedName() is also used already on
Special:ChangeContentModel.
* Fix "contentModels" to set `needLineBreakAfter = true`, otherwise
if a gadget also sets "supportsUrlLoad", then that sentence is
appended to the previous line.
Update phrasing and sorting to be consistent everywhere, and adopt
native PHP types where possible.
In most cases, I made things alphabetical, with the exception of
Special:Gadgets user interface output, and Gadget class methods,
which both follow the order of most recently added feature last
(rights, skins, actions, namespaces, contentmodels, categories).
Highlights:
* Fix namespace IDs type. These can be strings when they are parsed
from the gadget definition text, not always integers.
* Add explicit default for 'category'. In theory not needed because
MediaWikiGadgetsDefinitionRepo has a `$section = '';` default, and
MediaWikiGadgetsJsonRepo uses GadgetDefinitionContentHandler where
`category: ''` is part of both the initial page content, as well
as merged via getDefaultMetadata.
This default benefits simpler test cases, and static analysis,
since the Gadget class constructor does not (yet) require it.
Without this, getCategory() could TypeError due to returning null.
Bug: T63007
Change-Id: I3b2c72a6424d520431d03761d23a5a222518ce3d
2024-03-16 23:32:44 +00:00
|
|
|
'module.pages' => [ 'is_array', 'array', [ __CLASS__, 'isValidTitleSuffix' ], '.js, .css or .json page' ],
|
|
|
|
'module.peers' => [ 'is_array', 'array', 'is_string', 'string' ],
|
2023-12-16 08:44:06 +00:00
|
|
|
'module.type' => [ [ __CLASS__, 'isValidType' ], 'general or styles' ],
|
2016-12-28 10:25:47 +00:00
|
|
|
];
|
2015-08-03 06:37:32 +00:00
|
|
|
|
2023-12-16 08:44:06 +00:00
|
|
|
public static function isValidTitleSuffix( string $title ): bool {
|
|
|
|
return str_ends_with( $title, '.js' ) || str_ends_with( $title, '.css' ) || str_ends_with( $title, '.json' );
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function isValidType( string $type ): bool {
|
|
|
|
return $type === '' || $type === 'general' || $type === 'styles';
|
2015-08-03 06:37:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check the validity of the given properties array
|
|
|
|
* @param array $properties Return value of FormatJson::decode( $blob, true )
|
|
|
|
* @param bool $tolerateMissing If true, don't complain about missing keys
|
|
|
|
* @return Status object with error message if applicable
|
|
|
|
*/
|
|
|
|
public function validate( array $properties, $tolerateMissing = false ) {
|
|
|
|
foreach ( self::$propertyValidation as $property => $validation ) {
|
|
|
|
$path = explode( '.', $property );
|
|
|
|
$val = $properties;
|
|
|
|
|
|
|
|
// Walk down and verify that the path from the root to this property exists
|
|
|
|
foreach ( $path as $p ) {
|
|
|
|
if ( !array_key_exists( $p, $val ) ) {
|
|
|
|
if ( $tolerateMissing ) {
|
|
|
|
// Skip validation of this property altogether
|
|
|
|
continue 2;
|
|
|
|
}
|
2021-10-09 00:34:45 +00:00
|
|
|
|
|
|
|
return Status::newFatal( 'gadgets-validate-notset', $property );
|
2015-08-03 06:37:32 +00:00
|
|
|
}
|
|
|
|
$val = $val[$p];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do the actual validation of this property
|
|
|
|
$func = $validation[0];
|
|
|
|
if ( !call_user_func( $func, $val ) ) {
|
|
|
|
return Status::newFatal(
|
2021-10-09 00:34:45 +00:00
|
|
|
'gadgets-validate-wrongtype',
|
|
|
|
$property,
|
|
|
|
$validation[1],
|
|
|
|
gettype( $val )
|
2015-08-03 06:37:32 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-12-21 08:40:05 +00:00
|
|
|
if ( isset( $validation[2] ) && isset( $validation[3] ) && is_array( $val ) ) {
|
2015-08-03 06:37:32 +00:00
|
|
|
// Descend into the array and check the type of each element
|
|
|
|
$func = $validation[2];
|
|
|
|
foreach ( $val as $i => $v ) {
|
|
|
|
if ( !call_user_func( $func, $v ) ) {
|
|
|
|
return Status::newFatal(
|
2021-10-09 00:34:45 +00:00
|
|
|
'gadgets-validate-wrongtype',
|
|
|
|
"{$property}[{$i}]",
|
|
|
|
$validation[3],
|
|
|
|
gettype( $v )
|
2015-08-03 06:37:32 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status::newGood();
|
|
|
|
}
|
|
|
|
}
|