MediaWiki REL1_32
ResourceLoaderImageModule.php
Go to the documentation of this file.
1<?php
30
31 protected $definition = null;
32
37 protected $localBasePath = '';
38
40
41 protected $images = [];
42 protected $defaultColor = null;
43 protected $useDataURI = true;
44 protected $variants = [];
45 protected $prefix = null;
46 protected $selectorWithoutVariant = '.{prefix}-{name}';
47 protected $selectorWithVariant = '.{prefix}-{name}-{variant}';
48 protected $targets = [ 'desktop', 'mobile' ];
49
106 public function __construct( $options = [], $localBasePath = null ) {
107 $this->localBasePath = self::extractLocalBasePath( $options, $localBasePath );
108
109 $this->definition = $options;
110 }
111
115 protected function loadFromDefinition() {
116 if ( $this->definition === null ) {
117 return;
118 }
119
121 $this->definition = null;
122
123 if ( isset( $options['data'] ) ) {
124 $dataPath = $this->localBasePath . '/' . $options['data'];
125 $data = json_decode( file_get_contents( $dataPath ), true );
126 $options = array_merge( $data, $options );
127 }
128
129 // Accepted combinations:
130 // * prefix
131 // * selector
132 // * selectorWithoutVariant + selectorWithVariant
133 // * prefix + selector
134 // * prefix + selectorWithoutVariant + selectorWithVariant
135
136 $prefix = isset( $options['prefix'] ) && $options['prefix'];
137 $selector = isset( $options['selector'] ) && $options['selector'];
138 $selectorWithoutVariant = isset( $options['selectorWithoutVariant'] )
139 && $options['selectorWithoutVariant'];
140 $selectorWithVariant = isset( $options['selectorWithVariant'] )
141 && $options['selectorWithVariant'];
142
144 throw new InvalidArgumentException(
145 "Given 'selectorWithoutVariant' but no 'selectorWithVariant'."
146 );
147 }
149 throw new InvalidArgumentException(
150 "Given 'selectorWithVariant' but no 'selectorWithoutVariant'."
151 );
152 }
154 throw new InvalidArgumentException(
155 "Incompatible 'selector' and 'selectorWithVariant'+'selectorWithoutVariant' given."
156 );
157 }
158 if ( !$prefix && !$selector && !$selectorWithVariant ) {
159 throw new InvalidArgumentException(
160 "None of 'prefix', 'selector' or 'selectorWithVariant'+'selectorWithoutVariant' given."
161 );
162 }
163
164 foreach ( $options as $member => $option ) {
165 switch ( $member ) {
166 case 'images':
167 case 'variants':
168 if ( !is_array( $option ) ) {
169 throw new InvalidArgumentException(
170 "Invalid list error. '$option' given, array expected."
171 );
172 }
173 if ( !isset( $option['default'] ) ) {
174 // Backwards compatibility
175 $option = [ 'default' => $option ];
176 }
177 foreach ( $option as $skin => $data ) {
178 if ( !is_array( $option ) ) {
179 throw new InvalidArgumentException(
180 "Invalid list error. '$option' given, array expected."
181 );
182 }
183 }
184 $this->{$member} = $option;
185 break;
186
187 case 'useDataURI':
188 $this->{$member} = (bool)$option;
189 break;
190 case 'defaultColor':
191 case 'prefix':
192 case 'selectorWithoutVariant':
193 case 'selectorWithVariant':
194 $this->{$member} = (string)$option;
195 break;
196
197 case 'selector':
198 $this->selectorWithoutVariant = $this->selectorWithVariant = (string)$option;
199 }
200 }
201 }
202
207 public function getPrefix() {
208 $this->loadFromDefinition();
209 return $this->prefix;
210 }
211
216 public function getSelectors() {
217 $this->loadFromDefinition();
218 return [
219 'selectorWithoutVariant' => $this->selectorWithoutVariant,
220 'selectorWithVariant' => $this->selectorWithVariant,
221 ];
222 }
223
230 public function getImage( $name, ResourceLoaderContext $context ) {
231 $this->loadFromDefinition();
232 $images = $this->getImages( $context );
233 return $images[$name] ?? null;
234 }
235
242 $skin = $context->getSkin();
243 if ( !isset( $this->imageObjects ) ) {
244 $this->loadFromDefinition();
245 $this->imageObjects = [];
246 }
247 if ( !isset( $this->imageObjects[$skin] ) ) {
248 $this->imageObjects[$skin] = [];
249 if ( !isset( $this->images[$skin] ) ) {
250 $this->images[$skin] = $this->images['default'] ?? [];
251 }
252 foreach ( $this->images[$skin] as $name => $options ) {
253 $fileDescriptor = is_string( $options ) ? $options : $options['file'];
254
255 $allowedVariants = array_merge(
256 ( is_array( $options ) && isset( $options['variants'] ) ) ? $options['variants'] : [],
257 $this->getGlobalVariants( $context )
258 );
259 if ( isset( $this->variants[$skin] ) ) {
260 $variantConfig = array_intersect_key(
261 $this->variants[$skin],
262 array_fill_keys( $allowedVariants, true )
263 );
264 } else {
265 $variantConfig = [];
266 }
267
269 $name,
270 $this->getName(),
271 $fileDescriptor,
272 $this->localBasePath,
273 $variantConfig,
274 $this->defaultColor
275 );
276 $this->imageObjects[$skin][$image->getName()] = $image;
277 }
278 }
279
280 return $this->imageObjects[$skin];
281 }
282
290 $skin = $context->getSkin();
291 if ( !isset( $this->globalVariants ) ) {
292 $this->loadFromDefinition();
293 $this->globalVariants = [];
294 }
295 if ( !isset( $this->globalVariants[$skin] ) ) {
296 $this->globalVariants[$skin] = [];
297 if ( !isset( $this->variants[$skin] ) ) {
298 $this->variants[$skin] = $this->variants['default'] ?? [];
299 }
300 foreach ( $this->variants[$skin] as $name => $config ) {
301 if ( isset( $config['global'] ) && $config['global'] ) {
302 $this->globalVariants[$skin][] = $name;
303 }
304 }
305 }
306
307 return $this->globalVariants[$skin];
308 }
309
315 $this->loadFromDefinition();
316
317 // Build CSS rules
318 $rules = [];
319 $script = $context->getResourceLoader()->getLoadScript( $this->getSource() );
320 $selectors = $this->getSelectors();
321
322 foreach ( $this->getImages( $context ) as $name => $image ) {
323 $declarations = $this->getStyleDeclarations( $context, $image, $script );
324 $selector = strtr(
325 $selectors['selectorWithoutVariant'],
326 [
327 '{prefix}' => $this->getPrefix(),
328 '{name}' => $name,
329 '{variant}' => '',
330 ]
331 );
332 $rules[] = "$selector {\n\t$declarations\n}";
333
334 foreach ( $image->getVariants() as $variant ) {
335 $declarations = $this->getStyleDeclarations( $context, $image, $script, $variant );
336 $selector = strtr(
337 $selectors['selectorWithVariant'],
338 [
339 '{prefix}' => $this->getPrefix(),
340 '{name}' => $name,
341 '{variant}' => $variant,
342 ]
343 );
344 $rules[] = "$selector {\n\t$declarations\n}";
345 }
346 }
347
348 $style = implode( "\n", $rules );
349 return [ 'all' => $style ];
350 }
351
359 private function getStyleDeclarations(
362 $script,
363 $variant = null
364 ) {
365 $imageDataUri = $this->useDataURI ? $image->getDataUri( $context, $variant, 'original' ) : false;
366 $primaryUrl = $imageDataUri ?: $image->getUrl( $context, $script, $variant, 'original' );
367 $declarations = $this->getCssDeclarations(
368 $primaryUrl,
369 $image->getUrl( $context, $script, $variant, 'rasterized' )
370 );
371 return implode( "\n\t", $declarations );
372 }
373
386 protected function getCssDeclarations( $primary, $fallback ) {
387 $primaryUrl = CSSMin::buildUrlValue( $primary );
388 $fallbackUrl = CSSMin::buildUrlValue( $fallback );
389 return [
390 "background-image: $fallbackUrl;",
391 "background-image: linear-gradient(transparent, transparent), $primaryUrl;",
392 ];
393 }
394
398 public function supportsURLLoading() {
399 return false;
400 }
401
409 $this->loadFromDefinition();
410 $summary = parent::getDefinitionSummary( $context );
411
412 $options = [];
413 foreach ( [
414 'localBasePath',
415 'images',
416 'variants',
417 'prefix',
418 'selectorWithoutVariant',
419 'selectorWithVariant',
420 ] as $member ) {
421 $options[$member] = $this->{$member};
422 };
423
424 $summary[] = [
425 'options' => $options,
426 'fileHashes' => $this->getFileHashes( $context ),
427 ];
428 return $summary;
429 }
430
437 $this->loadFromDefinition();
438 $files = [];
439 foreach ( $this->getImages( $context ) as $name => $image ) {
440 $files[] = $image->getPath( $context );
441 }
442 $files = array_values( array_unique( $files ) );
443 return array_map( [ __CLASS__, 'safeFileHash' ], $files );
444 }
445
454 public static function extractLocalBasePath( $options, $localBasePath = null ) {
455 global $IP;
456
457 if ( $localBasePath === null ) {
459 }
460
461 if ( array_key_exists( 'localBasePath', $options ) ) {
462 $localBasePath = (string)$options['localBasePath'];
463 }
464
465 return $localBasePath;
466 }
467
471 public function getType() {
472 return self::LOAD_STYLES;
473 }
474}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable or merely the Work and Derivative Works thereof Contribution shall mean any work of including the original version of the Work and any modifications or additions to that Work or Derivative Works that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner For the purposes of this definition
$fallback
Object passed around to modules which contains information about the state of a specific loader reque...
ResourceLoader module for generated and embedded images.
getImage( $name, ResourceLoaderContext $context)
Get a ResourceLoaderImage object for given image.
loadFromDefinition()
Parse definition and external JSON data, if referenced.
getStyles(ResourceLoaderContext $context)
string $localBasePath
Local base path, see __construct()
__construct( $options=[], $localBasePath=null)
Constructs a new module from an options array.
getDefinitionSummary(ResourceLoaderContext $context)
Get the definition summary for this module.
static extractLocalBasePath( $options, $localBasePath=null)
Extract a local base path from module definition information.
getSelectors()
Get CSS selector templates used by this module.
getCssDeclarations( $primary, $fallback)
SVG support using a transparent gradient to guarantee cross-browser compatibility (browsers able to u...
getFileHashes(ResourceLoaderContext $context)
Helper method for getDefinitionSummary.
getPrefix()
Get CSS class prefix used by this module.
getStyleDeclarations(ResourceLoaderContext $context, ResourceLoaderImage $image, $script, $variant=null)
getGlobalVariants(ResourceLoaderContext $context)
Get list of variants in this module that are 'global', i.e., available for every image regardless of ...
getImages(ResourceLoaderContext $context)
Get ResourceLoaderImage objects for all images.
Class encapsulating an image used in a ResourceLoaderImageModule.
Abstraction for ResourceLoader modules, with name registration and maxage functionality.
getSource()
Get the source of this module.
getName()
Get this module's name.
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition hooks.txt:181
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check $image
Definition hooks.txt:925
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition hooks.txt:2050
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition hooks.txt:2885
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned $skin
Definition hooks.txt:2060
$IP
Definition update.php:3
$selector