Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.74% |
54 / 57 |
|
57.14% |
4 / 7 |
CRAP | |
0.00% |
0 / 1 |
JCMapDataContent | |
94.74% |
54 / 57 |
|
57.14% |
4 / 7 |
29.12 | |
0.00% |
0 / 1 |
validateContent | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
2.01 | |||
getSafeData | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
isValidData | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
recursiveWalk | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
11.06 | |||
isValidStringOrLocalized | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
5 | |||
localizeData | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
createDefaultView | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | namespace JsonConfig; |
3 | |
4 | use Kartographer\SimpleStyleParser; |
5 | use MediaWiki\Json\FormatJson; |
6 | use MediaWiki\Language\Language; |
7 | use MediaWiki\MediaWikiServices; |
8 | use MediaWiki\Parser\ParserOptions; |
9 | use MediaWiki\User\User; |
10 | |
11 | class JCMapDataContent extends JCDataContent { |
12 | |
13 | public function validateContent() { |
14 | parent::validateContent(); |
15 | |
16 | if ( !$this->thorough() ) { |
17 | // We are not doing any modifications to the original, so no need to validate it |
18 | return; |
19 | } |
20 | |
21 | $this->testOptional( 'zoom', 3, JCValidators::isInt() ); |
22 | $this->testOptional( 'latitude', 0, JCValidators::isNumber() ); |
23 | $this->testOptional( 'longitude', 0, JCValidators::isNumber() ); |
24 | |
25 | $this->test( 'data', self::isValidData() ); |
26 | |
27 | $this->test( [], JCValidators::noExtraValues() ); |
28 | } |
29 | |
30 | /** |
31 | * @inheritDoc |
32 | */ |
33 | public function getSafeData( $data ) { |
34 | $parser = MediaWikiServices::getInstance()->getParser(); |
35 | |
36 | // In case the parser hasn't been used yet |
37 | if ( !$parser->getOptions() ) { |
38 | $options = new ParserOptions( new User() ); |
39 | $parser->startExternalParse( null, $options, OT_HTML ); |
40 | } |
41 | |
42 | $data = parent::getSafeData( $data ); |
43 | |
44 | $ssp = SimpleStyleParser::newFromParser( $parser ); |
45 | $ssp->normalizeAndSanitize( $data->data ); |
46 | |
47 | return $data; |
48 | } |
49 | |
50 | /** |
51 | * @return callable |
52 | */ |
53 | private static function isValidData() { |
54 | return static function ( JCValue $v, array $path ) { |
55 | $value = $v->getValue(); |
56 | |
57 | if ( ( !is_object( $value ) && !is_array( $value ) ) || |
58 | !JCMapDataContent::recursiveWalk( $value, false ) |
59 | ) { |
60 | $v->error( 'jsonconfig-err-bad-geojson', $path ); |
61 | return false; |
62 | } |
63 | |
64 | // TODO: decide if this is needed. We would have to alter the above code to localize props |
65 | // // Use SimpleStyleParser to verify the data's validity |
66 | // $ssp = new \Kartographer\SimpleStyleParser( MediaWikiServices::getInstance()->getParser() ); |
67 | // $status = $ssp->parseObject( $value ); |
68 | // if ( !$status->isOK() ) { |
69 | // $v->status( $status ); |
70 | // } |
71 | // return $status->isOK(); |
72 | return true; |
73 | }; |
74 | } |
75 | |
76 | /** |
77 | * Recursively walk the geojson to replace localized "title" and "description" values |
78 | * with the single string corresponding to the $lang language, or if $lang is not set, |
79 | * validates those values and returns true/false if valid |
80 | * @param \stdClass|array &$json |
81 | * @param Language|false $lang |
82 | * @return bool |
83 | */ |
84 | public static function recursiveWalk( &$json, $lang = false ) { |
85 | if ( is_array( $json ) ) { |
86 | foreach ( $json as &$element ) { |
87 | if ( !self::recursiveWalk( $element, $lang ) ) { |
88 | return false; |
89 | } |
90 | } |
91 | } elseif ( is_object( $json ) ) { |
92 | foreach ( array_keys( get_object_vars( $json ) ) as $prop ) { |
93 | if ( $prop === 'properties' && is_object( $json->properties ) ) { |
94 | if ( !self::isValidStringOrLocalized( $json->properties, 'title', $lang ) || |
95 | !self::isValidStringOrLocalized( $json->properties, 'description', $lang ) |
96 | ) { |
97 | return false; |
98 | } |
99 | } elseif ( !self::recursiveWalk( $json->$prop, $lang ) ) { |
100 | return false; |
101 | } |
102 | } |
103 | } |
104 | return true; |
105 | } |
106 | |
107 | /** |
108 | * @param \stdClass $obj |
109 | * @param string $property |
110 | * @param Language|false $lang |
111 | * @param int $maxlength |
112 | * @return bool |
113 | */ |
114 | private static function isValidStringOrLocalized( $obj, $property, $lang, $maxlength = 400 ) { |
115 | if ( property_exists( $obj, $property ) ) { |
116 | $value = $obj->$property; |
117 | if ( !$lang ) { |
118 | return is_object( $value ) ? JCUtils::isLocalizedArray( (array)$value, $maxlength ) |
119 | : JCUtils::isValidLineString( $value, $maxlength ); |
120 | } elseif ( is_object( $value ) ) { |
121 | $obj->$property = JCUtils::pickLocalizedString( $value, $lang ); |
122 | } |
123 | } |
124 | return true; |
125 | } |
126 | |
127 | protected function localizeData( $result, Language $lang ) { |
128 | parent::localizeData( $result, $lang ); |
129 | |
130 | $data = $this->getData(); |
131 | |
132 | if ( isset( $data->zoom ) ) { |
133 | $result->zoom = $data->zoom; |
134 | } |
135 | if ( isset( $data->latitude ) ) { |
136 | $result->latitude = $data->latitude; |
137 | } |
138 | if ( isset( $data->longitude ) ) { |
139 | $result->longitude = $data->longitude; |
140 | } |
141 | |
142 | $geojson = FormatJson::decode( FormatJson::encode( $data->data, false, FormatJson::ALL_OK ) ); |
143 | self::recursiveWalk( $geojson, $lang ); |
144 | |
145 | $result->data = $geojson; |
146 | } |
147 | |
148 | protected function createDefaultView() { |
149 | return new JCMapDataContentView(); |
150 | } |
151 | } |