Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
85.37% |
70 / 82 |
|
42.86% |
3 / 7 |
CRAP | |
0.00% |
0 / 1 |
MapFrameAttributeGenerator | |
85.37% |
70 / 82 |
|
42.86% |
3 / 7 |
24.66 | |
0.00% |
0 / 1 |
__construct | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
getContainerClasses | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getThumbClasses | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
prepareAttrs | |
88.89% |
24 / 27 |
|
0.00% |
0 / 1 |
6.05 | |||
prepareImgAttrs | |
92.00% |
23 / 25 |
|
0.00% |
0 / 1 |
6.02 | |||
getUrlAttrs | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getSrcSet | |
33.33% |
3 / 9 |
|
0.00% |
0 / 1 |
12.41 |
1 | <?php |
2 | |
3 | namespace Kartographer\Tag; |
4 | |
5 | use Kartographer\Special\SpecialMap; |
6 | use MediaWiki\Config\Config; |
7 | use MediaWiki\Json\FormatJson; |
8 | use MediaWiki\MainConfigNames; |
9 | |
10 | /** |
11 | * @license MIT |
12 | */ |
13 | class MapFrameAttributeGenerator { |
14 | |
15 | private const ALIGN_CLASSES = [ |
16 | 'left' => 'floatleft', |
17 | 'center' => 'center', |
18 | 'right' => 'floatright', |
19 | ]; |
20 | |
21 | private const THUMB_ALIGN_CLASSES = [ |
22 | 'left' => 'tleft', |
23 | 'center' => 'tnone center', |
24 | 'right' => 'tright', |
25 | 'none' => 'tnone', |
26 | ]; |
27 | |
28 | private MapTagArgumentValidator $args; |
29 | private Config $config; |
30 | public string $cssWidth; |
31 | |
32 | public function __construct( MapTagArgumentValidator $args, Config $config ) { |
33 | $this->args = $args; |
34 | $this->config = $config; |
35 | |
36 | if ( $this->args->width === 'full' ) { |
37 | $this->cssWidth = '100%'; |
38 | } else { |
39 | $this->cssWidth = $this->args->width . 'px'; |
40 | } |
41 | } |
42 | |
43 | /** |
44 | * @return string[] |
45 | */ |
46 | private function getContainerClasses(): array { |
47 | return [ |
48 | 'mw-kartographer-container', |
49 | ...( $this->args->width === 'full' ? [ 'mw-kartographer-full' ] : [] ), |
50 | ]; |
51 | } |
52 | |
53 | /** |
54 | * @return string[] |
55 | */ |
56 | public function getThumbClasses(): array { |
57 | return [ |
58 | ...$this->getContainerClasses(), |
59 | 'thumb', |
60 | self::THUMB_ALIGN_CLASSES[$this->args->align], |
61 | ]; |
62 | } |
63 | |
64 | public function prepareAttrs(): array { |
65 | $attrs = [ |
66 | // T359082: Temporarily disable dark mode unless we have a better idea |
67 | 'class' => [ 'mw-kartographer-map', 'notheme' ], |
68 | // We need dimensions for when there is no img (editpreview or no staticmap) |
69 | // because an <img> element with permanent failing src has either: |
70 | // - intrinsic dimensions of 0x0, when alt='' |
71 | // - intrinsic dimensions of alt size |
72 | 'style' => "width: $this->cssWidth; height: {$this->args->height}px;", |
73 | // Attributes starting with "data-mw" are banned from user content in Sanitizer; |
74 | // we add such an attribute here (by default empty) so that its presence can be |
75 | // checked later to guarantee that they were generated by Kartographer |
76 | // Warning: This attribute is also checked in Wikibase and should be modified there as |
77 | // well if necessary! |
78 | 'data-mw-kartographer' => 'mapframe', |
79 | 'data-style' => $this->args->mapStyle, |
80 | 'data-width' => $this->args->width, |
81 | 'data-height' => $this->args->height, |
82 | ]; |
83 | |
84 | if ( $this->args->zoom !== null ) { |
85 | $attrs['data-zoom'] = $this->args->zoom; |
86 | } |
87 | |
88 | if ( $this->args->hasCoordinates() ) { |
89 | $attrs['data-lat'] = $this->args->lat; |
90 | $attrs['data-lon'] = $this->args->lon; |
91 | } |
92 | |
93 | if ( $this->args->specifiedLangCode !== null ) { |
94 | $attrs['data-lang'] = $this->args->specifiedLangCode; |
95 | } |
96 | |
97 | if ( $this->args->showGroups ) { |
98 | $attrs['data-overlays'] = FormatJson::encode( $this->args->showGroups, false, |
99 | FormatJson::ALL_OK ); |
100 | } |
101 | |
102 | $attrs['href'] = SpecialMap::link( $this->args->lat, $this->args->lon, $this->args->zoom, |
103 | $this->args->getLanguageCodeWithDefaultFallback() ); |
104 | |
105 | if ( $this->args->frameless ) { |
106 | $attrs['class'] = [ |
107 | ...$attrs['class'], |
108 | ...$this->getContainerClasses(), |
109 | ...(array)( self::ALIGN_CLASSES[$this->args->align] ?? [] ), |
110 | ]; |
111 | } |
112 | |
113 | return $attrs; |
114 | } |
115 | |
116 | /** |
117 | * @param bool $serverMayRenderOverlays If the map server should attempt to render GeoJSON |
118 | * overlays via their group id |
119 | * @param string $pagetitle |
120 | * @param ?int $revisionId |
121 | * @return array |
122 | */ |
123 | public function prepareImgAttrs( bool $serverMayRenderOverlays, string $pagetitle, ?int $revisionId ): array { |
124 | $mapServer = $this->config->get( 'KartographerMapServer' ); |
125 | $fullWidth = $this->config->get( 'KartographerStaticFullWidth' ); |
126 | $staticWidth = $this->args->width === 'full' ? $fullWidth : (int)$this->args->width; |
127 | |
128 | $imgUrlParams = [ |
129 | 'lang' => $this->args->getLanguageCodeWithDefaultFallback(), |
130 | ]; |
131 | if ( $this->args->showGroups && $serverMayRenderOverlays ) { |
132 | // Groups are not available to the static map renderer |
133 | // before the page was saved, can only be applied via JS |
134 | $imgUrlParams += self::getUrlAttrs( $this->config, $pagetitle, $revisionId, $this->args->showGroups ); |
135 | } |
136 | |
137 | $imgUrl = "$mapServer/img/{$this->args->mapStyle}," . |
138 | ( $this->args->zoom ?? 'a' ) . ',' . |
139 | ( $this->args->lat ?? 'a' ) . ',' . |
140 | ( $this->args->lon ?? 'a' ) . |
141 | ",{$staticWidth}x{$this->args->height}"; |
142 | |
143 | $imgAttrs = [ |
144 | 'src' => "$imgUrl.png?" . wfArrayToCgi( $imgUrlParams ), |
145 | 'width' => $staticWidth, |
146 | 'height' => $this->args->height, |
147 | 'decoding' => 'async', |
148 | ]; |
149 | |
150 | $srcSet = $this->getSrcSet( $imgUrl, $imgUrlParams ); |
151 | if ( $srcSet ) { |
152 | $imgAttrs['srcset'] = $srcSet; |
153 | } |
154 | |
155 | if ( $this->args->alt !== '' ) { |
156 | $imgAttrs['alt'] = $this->args->alt; |
157 | } |
158 | |
159 | return $imgAttrs; |
160 | } |
161 | |
162 | /** |
163 | * @param Config $config |
164 | * @param string $title |
165 | * @param int|null $revId |
166 | * @param array $groupId |
167 | * @return array |
168 | */ |
169 | public static function getUrlAttrs( Config $config, string $title, ?int $revId, array $groupId ): array { |
170 | return [ |
171 | 'domain' => $config->get( 'KartographerMediaWikiInternalUrl' ) ?? |
172 | $config->get( MainConfigNames::ServerName ), |
173 | 'title' => $title, |
174 | 'revid' => $revId, |
175 | 'groups' => implode( ',', $groupId ) |
176 | ]; |
177 | } |
178 | |
179 | private function getSrcSet( string $imgUrl, array $imgUrlParams = [] ): ?string { |
180 | $scales = $this->config->get( 'KartographerSrcsetScales' ); |
181 | if ( !$scales || !$this->config->get( MainConfigNames::ResponsiveImages ) ) { |
182 | return null; |
183 | } |
184 | |
185 | // For now only support 2x, not 1.5. Saves some bytes... |
186 | $scales = array_intersect( $scales, [ 2 ] ); |
187 | $srcSets = []; |
188 | foreach ( $scales as $scale ) { |
189 | $scaledImgUrl = "$imgUrl@{$scale}x.png?" . wfArrayToCgi( $imgUrlParams ); |
190 | $srcSets[] = "$scaledImgUrl {$scale}x"; |
191 | } |
192 | return implode( ', ', $srcSets ) ?: null; |
193 | } |
194 | } |