MediaWiki REL1_30
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 $variants = [];
43 protected $prefix = null;
44 protected $selectorWithoutVariant = '.{prefix}-{name}';
45 protected $selectorWithVariant = '.{prefix}-{name}-{variant}';
46 protected $targets = [ 'desktop', 'mobile' ];
47
104 public function __construct( $options = [], $localBasePath = null ) {
105 $this->localBasePath = self::extractLocalBasePath( $options, $localBasePath );
106
107 $this->definition = $options;
108 }
109
113 protected function loadFromDefinition() {
114 if ( $this->definition === null ) {
115 return;
116 }
117
119 $this->definition = null;
120
121 if ( isset( $options['data'] ) ) {
122 $dataPath = $this->localBasePath . '/' . $options['data'];
123 $data = json_decode( file_get_contents( $dataPath ), true );
124 $options = array_merge( $data, $options );
125 }
126
127 // Accepted combinations:
128 // * prefix
129 // * selector
130 // * selectorWithoutVariant + selectorWithVariant
131 // * prefix + selector
132 // * prefix + selectorWithoutVariant + selectorWithVariant
133
134 $prefix = isset( $options['prefix'] ) && $options['prefix'];
135 $selector = isset( $options['selector'] ) && $options['selector'];
136 $selectorWithoutVariant = isset( $options['selectorWithoutVariant'] )
137 && $options['selectorWithoutVariant'];
138 $selectorWithVariant = isset( $options['selectorWithVariant'] )
139 && $options['selectorWithVariant'];
140
142 throw new InvalidArgumentException(
143 "Given 'selectorWithoutVariant' but no 'selectorWithVariant'."
144 );
145 }
147 throw new InvalidArgumentException(
148 "Given 'selectorWithVariant' but no 'selectorWithoutVariant'."
149 );
150 }
152 throw new InvalidArgumentException(
153 "Incompatible 'selector' and 'selectorWithVariant'+'selectorWithoutVariant' given."
154 );
155 }
156 if ( !$prefix && !$selector && !$selectorWithVariant ) {
157 throw new InvalidArgumentException(
158 "None of 'prefix', 'selector' or 'selectorWithVariant'+'selectorWithoutVariant' given."
159 );
160 }
161
162 foreach ( $options as $member => $option ) {
163 switch ( $member ) {
164 case 'images':
165 case 'variants':
166 if ( !is_array( $option ) ) {
167 throw new InvalidArgumentException(
168 "Invalid list error. '$option' given, array expected."
169 );
170 }
171 if ( !isset( $option['default'] ) ) {
172 // Backwards compatibility
173 $option = [ 'default' => $option ];
174 }
175 foreach ( $option as $skin => $data ) {
176 if ( !is_array( $option ) ) {
177 throw new InvalidArgumentException(
178 "Invalid list error. '$option' given, array expected."
179 );
180 }
181 }
182 $this->{$member} = $option;
183 break;
184
185 case 'prefix':
186 case 'selectorWithoutVariant':
187 case 'selectorWithVariant':
188 $this->{$member} = (string)$option;
189 break;
190
191 case 'selector':
192 $this->selectorWithoutVariant = $this->selectorWithVariant = (string)$option;
193 }
194 }
195 }
196
201 public function getPrefix() {
202 $this->loadFromDefinition();
203 return $this->prefix;
204 }
205
210 public function getSelectors() {
211 $this->loadFromDefinition();
212 return [
213 'selectorWithoutVariant' => $this->selectorWithoutVariant,
214 'selectorWithVariant' => $this->selectorWithVariant,
215 ];
216 }
217
224 public function getImage( $name, ResourceLoaderContext $context ) {
225 $this->loadFromDefinition();
226 $images = $this->getImages( $context );
227 return isset( $images[$name] ) ? $images[$name] : null;
228 }
229
236 $skin = $context->getSkin();
237 if ( !isset( $this->imageObjects ) ) {
238 $this->loadFromDefinition();
239 $this->imageObjects = [];
240 }
241 if ( !isset( $this->imageObjects[$skin] ) ) {
242 $this->imageObjects[$skin] = [];
243 if ( !isset( $this->images[$skin] ) ) {
244 $this->images[$skin] = isset( $this->images['default'] ) ?
245 $this->images['default'] :
246 [];
247 }
248 foreach ( $this->images[$skin] as $name => $options ) {
249 $fileDescriptor = is_string( $options ) ? $options : $options['file'];
250
251 $allowedVariants = array_merge(
252 is_array( $options ) && isset( $options['variants'] ) ? $options['variants'] : [],
253 $this->getGlobalVariants( $context )
254 );
255 if ( isset( $this->variants[$skin] ) ) {
256 $variantConfig = array_intersect_key(
257 $this->variants[$skin],
258 array_fill_keys( $allowedVariants, true )
259 );
260 } else {
261 $variantConfig = [];
262 }
263
265 $name,
266 $this->getName(),
267 $fileDescriptor,
268 $this->localBasePath,
269 $variantConfig
270 );
271 $this->imageObjects[$skin][$image->getName()] = $image;
272 }
273 }
274
275 return $this->imageObjects[$skin];
276 }
277
285 $skin = $context->getSkin();
286 if ( !isset( $this->globalVariants ) ) {
287 $this->loadFromDefinition();
288 $this->globalVariants = [];
289 }
290 if ( !isset( $this->globalVariants[$skin] ) ) {
291 $this->globalVariants[$skin] = [];
292 if ( !isset( $this->variants[$skin] ) ) {
293 $this->variants[$skin] = isset( $this->variants['default'] ) ?
294 $this->variants['default'] :
295 [];
296 }
297 foreach ( $this->variants[$skin] as $name => $config ) {
298 if ( isset( $config['global'] ) && $config['global'] ) {
299 $this->globalVariants[$skin][] = $name;
300 }
301 }
302 }
303
304 return $this->globalVariants[$skin];
305 }
306
312 $this->loadFromDefinition();
313
314 // Build CSS rules
315 $rules = [];
316 $script = $context->getResourceLoader()->getLoadScript( $this->getSource() );
317 $selectors = $this->getSelectors();
318
319 foreach ( $this->getImages( $context ) as $name => $image ) {
320 $declarations = $this->getStyleDeclarations( $context, $image, $script );
321 $selector = strtr(
322 $selectors['selectorWithoutVariant'],
323 [
324 '{prefix}' => $this->getPrefix(),
325 '{name}' => $name,
326 '{variant}' => '',
327 ]
328 );
329 $rules[] = "$selector {\n\t$declarations\n}";
330
331 foreach ( $image->getVariants() as $variant ) {
332 $declarations = $this->getStyleDeclarations( $context, $image, $script, $variant );
333 $selector = strtr(
334 $selectors['selectorWithVariant'],
335 [
336 '{prefix}' => $this->getPrefix(),
337 '{name}' => $name,
338 '{variant}' => $variant,
339 ]
340 );
341 $rules[] = "$selector {\n\t$declarations\n}";
342 }
343 }
344
345 $style = implode( "\n", $rules );
346 return [ 'all' => $style ];
347 }
348
356 private function getStyleDeclarations(
359 $script,
360 $variant = null
361 ) {
362 $imageDataUri = $image->getDataUri( $context, $variant, 'original' );
363 $primaryUrl = $imageDataUri ?: $image->getUrl( $context, $script, $variant, 'original' );
364 $declarations = $this->getCssDeclarations(
365 $primaryUrl,
366 $image->getUrl( $context, $script, $variant, 'rasterized' )
367 );
368 return implode( "\n\t", $declarations );
369 }
370
383 protected function getCssDeclarations( $primary, $fallback ) {
384 $primaryUrl = CSSMin::buildUrlValue( $primary );
385 $fallbackUrl = CSSMin::buildUrlValue( $fallback );
386 return [
387 "background-image: $fallbackUrl;",
388 "background-image: linear-gradient(transparent, transparent), $primaryUrl;",
389 // Do not serve SVG to Opera 12, bad rendering with border-radius or background-size (T87504)
390 "background-image: -o-linear-gradient(transparent, transparent), $fallbackUrl;",
391 ];
392 }
393
397 public function supportsURLLoading() {
398 return false;
399 }
400
408 $this->loadFromDefinition();
409 $summary = parent::getDefinitionSummary( $context );
410
411 $options = [];
412 foreach ( [
413 'localBasePath',
414 'images',
415 'variants',
416 'prefix',
417 'selectorWithoutVariant',
418 'selectorWithVariant',
419 ] as $member ) {
420 $options[$member] = $this->{$member};
421 };
422
423 $summary[] = [
424 'options' => $options,
425 'fileHashes' => $this->getFileHashes( $context ),
426 ];
427 return $summary;
428 }
429
436 $this->loadFromDefinition();
437 $files = [];
438 foreach ( $this->getImages( $context ) as $name => $image ) {
439 $files[] = $image->getPath( $context );
440 }
441 $files = array_values( array_unique( $files ) );
442 return array_map( [ __CLASS__, 'safeFileHash' ], $files );
443 }
444
453 public static function extractLocalBasePath( $options, $localBasePath = null ) {
454 global $IP;
455
456 if ( $localBasePath === null ) {
458 }
459
460 if ( array_key_exists( 'localBasePath', $options ) ) {
461 $localBasePath = (string)$options['localBasePath'];
462 }
463
464 return $localBasePath;
465 }
466
470 public function getType() {
471 return self::LOAD_STYLES;
472 }
473}
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 origin 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:893
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:1971
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:2780
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:1981
$IP
Definition update.php:3
$selector