Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 91 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
ParsoidTagHandler | |
0.00% |
0 / 91 |
|
0.00% |
0 / 5 |
930 | |
0.00% |
0 / 1 |
parseTag | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
56 | |||
processParsoidExtensionArguments | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
30 | |||
reportErrors | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
30 | |||
getJSONValidatorLog | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
12 | |||
processAttributeEmbeddedHTML | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
110 |
1 | <?php |
2 | |
3 | namespace Kartographer\Tag; |
4 | |
5 | use Closure; |
6 | use Kartographer\ParsoidWikitextParser; |
7 | use Kartographer\SimpleStyleParser; |
8 | use LogicException; |
9 | use MediaWiki\MediaWikiServices; |
10 | use StatusValue; |
11 | use stdClass; |
12 | use Wikimedia\Parsoid\DOM\DocumentFragment; |
13 | use Wikimedia\Parsoid\DOM\Element; |
14 | use Wikimedia\Parsoid\Ext\ExtensionTagHandler; |
15 | use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI; |
16 | use Wikimedia\Parsoid\Ext\Utils; |
17 | use Wikimedia\Parsoid\Tokens\KVSourceRange; |
18 | use Wikimedia\Parsoid\Utils\DOMCompat; |
19 | |
20 | /** |
21 | * @license MIT |
22 | */ |
23 | class ParsoidTagHandler extends ExtensionTagHandler { |
24 | public const TAG = ''; |
25 | |
26 | /** |
27 | * @param ParsoidExtensionAPI $extApi |
28 | * @param string $input |
29 | * @param stdClass[] $extArgs |
30 | * @return array{StatusValue,MapTagArgumentValidator,stdClass[],KVSourceRange[]} |
31 | */ |
32 | protected function parseTag( ParsoidExtensionAPI $extApi, string $input, array $extArgs ): array { |
33 | $args = $this->processParsoidExtensionArguments( $extArgs ); |
34 | $status = $args->status; |
35 | $srcOffsets = []; |
36 | foreach ( $extArgs as $extArg ) { |
37 | if ( is_string( $extArg->k ) ) { |
38 | $srcOffsets[$extArg->k] = $extArg->srcOffsets ?? null; |
39 | } |
40 | } |
41 | |
42 | $geometries = []; |
43 | if ( $status->isOK() ) { |
44 | $wp = new ParsoidWikitextParser( $extApi ); |
45 | $status = ( new SimpleStyleParser( $wp ) )->parse( $input ); |
46 | if ( $status->isOk() ) { |
47 | $geometries = $status->getValue()['data']; |
48 | } |
49 | } |
50 | |
51 | if ( $geometries ) { |
52 | $marker = SimpleStyleParser::findFirstMarkerSymbol( $geometries ); |
53 | if ( $marker ) { |
54 | $args->setFirstMarkerProperties( ...$marker ); |
55 | } |
56 | } |
57 | |
58 | return [ $status, $args, $geometries, $srcOffsets ]; |
59 | } |
60 | |
61 | /** |
62 | * @param stdClass[] $extArgs |
63 | * @return MapTagArgumentValidator |
64 | */ |
65 | private function processParsoidExtensionArguments( array $extArgs ): MapTagArgumentValidator { |
66 | $services = MediaWikiServices::getInstance(); |
67 | |
68 | $args = []; |
69 | foreach ( $extArgs as $extArg ) { |
70 | // Might be an array or Token object when wikitext like <maplink {{1x|text}}=… /> is |
71 | // used. The old parser doesn't resolve this either. |
72 | if ( is_string( $extArg->k ) ) { |
73 | if ( $extArg->k === 'text' && $extArg->vsrc ) { |
74 | // preserve newlines and spaces for 'text' attribute |
75 | $args[$extArg->k] = Utils::decodeWtEntities( $extArg->vsrc ); |
76 | } else { |
77 | $args[$extArg->k] = $extArg->v; |
78 | } |
79 | } |
80 | } |
81 | |
82 | return new MapTagArgumentValidator( static::TAG, $args, |
83 | $services->getMainConfig(), |
84 | // FIXME setting the display language to English for the first version, needs to be fixed when we |
85 | // have a localization solution for Parsoid |
86 | $services->getLanguageFactory()->getLanguage( 'en' ), |
87 | $services->getLanguageNameUtils() |
88 | ); |
89 | } |
90 | |
91 | /** |
92 | * @param ParsoidExtensionAPI $extApi |
93 | * @param string $tag |
94 | * @param StatusValue $status |
95 | * @return DocumentFragment |
96 | */ |
97 | public function reportErrors( |
98 | ParsoidExtensionAPI $extApi, string $tag, StatusValue $status |
99 | ): DocumentFragment { |
100 | $errors = $status->getErrors(); |
101 | if ( !$errors ) { |
102 | throw new LogicException( __METHOD__ . '(): attempt to report error when none took place' ); |
103 | } |
104 | $dom = $extApi->getTopLevelDoc()->createDocumentFragment(); |
105 | $div = $extApi->getTopLevelDoc()->createElement( 'div' ); |
106 | $div->setAttribute( 'class', 'mw-kartographer-error' ); |
107 | $div->setAttribute( 'data-mw-kartographer', $tag ); |
108 | $div->setAttribute( 'data-kart', 'error' ); |
109 | $dom->appendChild( $div ); |
110 | if ( count( $errors ) > 1 ) { |
111 | // kartographer-error-context-multi takes two parameters: the tag name and the |
112 | // (formatted, localized) list of errors. We pass '' as second parameter and add the |
113 | // individual errors to the fragment instead. |
114 | $errContainer = $extApi->createInterfaceI18nFragment( 'kartographer-error-context-multi', |
115 | [ static::TAG, '' ] ); |
116 | $div->appendChild( $errContainer ); |
117 | $ul = $extApi->getTopLevelDoc()->createElement( 'ul' ); |
118 | $div->appendChild( $ul ); |
119 | foreach ( $errors as $err ) { |
120 | $li = $extApi->getTopLevelDoc()->createElement( 'li' ); |
121 | $ul->appendChild( $li ); |
122 | $err = $extApi->createInterfaceI18nFragment( $err['message'], $err['params'] ); |
123 | $li->appendChild( $err ); |
124 | } |
125 | } else { |
126 | // kartographer-error-context takes two parameters: the tag name and the |
127 | // localized error. We pass '' as second parameter and add the |
128 | // individual errors to the fragment instead. |
129 | $errContainer = $extApi->createInterfaceI18nFragment( 'kartographer-error-context', [ static::TAG, '' ] ); |
130 | $div->appendChild( $errContainer ); |
131 | $err = $extApi->createInterfaceI18nFragment( $errors[0]['message'], $errors[0]['params'] ); |
132 | $div->appendChild( $err ); |
133 | } |
134 | |
135 | $jsonErrors = $this->getJSONValidatorLog( $extApi, $status ); |
136 | |
137 | if ( $jsonErrors ) { |
138 | $div->appendChild( $jsonErrors ); |
139 | } |
140 | return $dom; |
141 | } |
142 | |
143 | /** |
144 | * @param ParsoidExtensionAPI $extApi |
145 | * @param StatusValue $status |
146 | * @return ?DocumentFragment |
147 | */ |
148 | private function getJSONValidatorLog( ParsoidExtensionAPI $extApi, StatusValue $status ): ?DocumentFragment { |
149 | $errors = $status->getValue()['schema-errors'] ?? []; |
150 | if ( !$errors ) { |
151 | return null; |
152 | } |
153 | |
154 | $dom = $extApi->getTopLevelDoc()->createDocumentFragment(); |
155 | $ul = $extApi->getTopLevelDoc()->createElement( 'ul' ); |
156 | $ul->setAttribute( 'class', 'mw-kartographer-error-log mw-collapsible mw-collapsed' ); |
157 | $dom->appendChild( $ul ); |
158 | /** These errors come from {@see \JsonSchema\Constraints\BaseConstraint::addError} */ |
159 | foreach ( $errors as $error ) { |
160 | $li = $extApi->getTopLevelDoc()->createElement( 'li' ); |
161 | $pointer = $extApi->getTopLevelDoc()->createTextNode( $error['pointer'] ); |
162 | $sep = $extApi->createInterfaceI18nFragment( 'colon-separator', [] ); |
163 | $msg = $extApi->getTopLevelDoc()->createTextNode( $error['message'] ); |
164 | $li->appendChild( $pointer ); |
165 | $li->appendChild( $sep ); |
166 | $li->appendChild( $msg ); |
167 | $ul->appendChild( $li ); |
168 | } |
169 | return $dom; |
170 | } |
171 | |
172 | public function processAttributeEmbeddedHTML( ParsoidExtensionAPI $extApi, Element $elt, Closure $proc ): void { |
173 | if ( $elt->hasAttribute( 'data-mw-kartographer' ) ) { |
174 | $node = $elt; |
175 | } else { |
176 | $node = DOMCompat::querySelector( $elt, '*[data-mw-kartographer]' ); |
177 | } |
178 | if ( !$node ) { |
179 | return; |
180 | } |
181 | $exttagname = $node->getAttribute( 'data-mw-kartographer' ); |
182 | if ( !$exttagname ) { |
183 | return; |
184 | } |
185 | $marker = json_decode( $node->getAttribute( 'data-kart' ) ?? '', false ); |
186 | if ( $marker instanceof stdClass && $marker->geometries ) { |
187 | foreach ( $marker->geometries as $geom ) { |
188 | if ( !isset( $geom->properties ) ) { |
189 | continue; |
190 | } |
191 | foreach ( $geom->properties as $key => $prop ) { |
192 | if ( in_array( $key, SimpleStyleParser::WIKITEXT_PROPERTIES ) ) { |
193 | $geom->properties->{$key} = $proc( $prop ); |
194 | } |
195 | } |
196 | } |
197 | $node->setAttribute( 'data-kart', json_encode( $marker ) ); |
198 | } |
199 | } |
200 | } |