MediaWiki  1.23.1
FormatMetadata.php
Go to the documentation of this file.
1 <?php
55  protected $singleLang = false;
56 
63  public function setSingleLanguage( $val ) {
64  $this->singleLang = $val;
65  }
66 
80  public static function getFormattedData( $tags, $context = false ) {
81  $obj = new FormatMetadata;
82  if ( $context ) {
83  $obj->setContext( $context );
84  }
85 
86  return $obj->makeFormattedData( $tags );
87  }
88 
100  public function makeFormattedData( $tags ) {
101  $resolutionunit = !isset( $tags['ResolutionUnit'] ) || $tags['ResolutionUnit'] == 2 ? 2 : 3;
102  unset( $tags['ResolutionUnit'] );
103 
104  foreach ( $tags as $tag => &$vals ) {
105 
106  // This seems ugly to wrap non-array's in an array just to unwrap again,
107  // especially when most of the time it is not an array
108  if ( !is_array( $tags[$tag] ) ) {
109  $vals = array( $vals );
110  }
111 
112  // _type is a special value to say what array type
113  if ( isset( $tags[$tag]['_type'] ) ) {
114  $type = $tags[$tag]['_type'];
115  unset( $vals['_type'] );
116  } else {
117  $type = 'ul'; // default unordered list.
118  }
119 
120  //This is done differently as the tag is an array.
121  if ( $tag == 'GPSTimeStamp' && count( $vals ) === 3 ) {
122  //hour min sec array
123 
124  $h = explode( '/', $vals[0] );
125  $m = explode( '/', $vals[1] );
126  $s = explode( '/', $vals[2] );
127 
128  // this should already be validated
129  // when loaded from file, but it could
130  // come from a foreign repo, so be
131  // paranoid.
132  if ( !isset( $h[1] )
133  || !isset( $m[1] )
134  || !isset( $s[1] )
135  || $h[1] == 0
136  || $m[1] == 0
137  || $s[1] == 0
138  ) {
139  continue;
140  }
141  $tags[$tag] = str_pad( intval( $h[0] / $h[1] ), 2, '0', STR_PAD_LEFT )
142  . ':' . str_pad( intval( $m[0] / $m[1] ), 2, '0', STR_PAD_LEFT )
143  . ':' . str_pad( intval( $s[0] / $s[1] ), 2, '0', STR_PAD_LEFT );
144 
145  try {
146  $time = wfTimestamp( TS_MW, '1971:01:01 ' . $tags[$tag] );
147  // the 1971:01:01 is just a placeholder, and not shown to user.
148  if ( $time && intval( $time ) > 0 ) {
149  $tags[$tag] = $this->getLanguage()->time( $time );
150  }
151  } catch ( TimestampException $e ) {
152  // This shouldn't happen, but we've seen bad formats
153  // such as 4-digit seconds in the wild.
154  // leave $tags[$tag] as-is
155  }
156  continue;
157  }
158 
159  // The contact info is a multi-valued field
160  // instead of the other props which are single
161  // valued (mostly) so handle as a special case.
162  if ( $tag === 'Contact' ) {
163  $vals = $this->collapseContactInfo( $vals );
164  continue;
165  }
166 
167  foreach ( $vals as &$val ) {
168 
169  switch ( $tag ) {
170  case 'Compression':
171  switch ( $val ) {
172  case 1:
173  case 2:
174  case 3:
175  case 4:
176  case 5:
177  case 6:
178  case 7:
179  case 8:
180  case 32773:
181  case 32946:
182  case 34712:
183  $val = $this->exifMsg( $tag, $val );
184  break;
185  default:
186  /* If not recognized, display as is. */
187  break;
188  }
189  break;
190 
191  case 'PhotometricInterpretation':
192  switch ( $val ) {
193  case 2:
194  case 6:
195  $val = $this->exifMsg( $tag, $val );
196  break;
197  default:
198  /* If not recognized, display as is. */
199  break;
200  }
201  break;
202 
203  case 'Orientation':
204  switch ( $val ) {
205  case 1:
206  case 2:
207  case 3:
208  case 4:
209  case 5:
210  case 6:
211  case 7:
212  case 8:
213  $val = $this->exifMsg( $tag, $val );
214  break;
215  default:
216  /* If not recognized, display as is. */
217  break;
218  }
219  break;
220 
221  case 'PlanarConfiguration':
222  switch ( $val ) {
223  case 1:
224  case 2:
225  $val = $this->exifMsg( $tag, $val );
226  break;
227  default:
228  /* If not recognized, display as is. */
229  break;
230  }
231  break;
232 
233  // TODO: YCbCrSubSampling
234  case 'YCbCrPositioning':
235  switch ( $val ) {
236  case 1:
237  case 2:
238  $val = $this->exifMsg( $tag, $val );
239  break;
240  default:
241  /* If not recognized, display as is. */
242  break;
243  }
244  break;
245 
246  case 'XResolution':
247  case 'YResolution':
248  switch ( $resolutionunit ) {
249  case 2:
250  $val = $this->exifMsg( 'XYResolution', 'i', $this->formatNum( $val ) );
251  break;
252  case 3:
253  $val = $this->exifMsg( 'XYResolution', 'c', $this->formatNum( $val ) );
254  break;
255  default:
256  /* If not recognized, display as is. */
257  break;
258  }
259  break;
260 
261  // TODO: YCbCrCoefficients #p27 (see annex E)
262  case 'ExifVersion':
263  case 'FlashpixVersion':
264  $val = "$val" / 100;
265  break;
266 
267  case 'ColorSpace':
268  switch ( $val ) {
269  case 1:
270  case 65535:
271  $val = $this->exifMsg( $tag, $val );
272  break;
273  default:
274  /* If not recognized, display as is. */
275  break;
276  }
277  break;
278 
279  case 'ComponentsConfiguration':
280  switch ( $val ) {
281  case 0:
282  case 1:
283  case 2:
284  case 3:
285  case 4:
286  case 5:
287  case 6:
288  $val = $this->exifMsg( $tag, $val );
289  break;
290  default:
291  /* If not recognized, display as is. */
292  break;
293  }
294  break;
295 
296  case 'DateTime':
297  case 'DateTimeOriginal':
298  case 'DateTimeDigitized':
299  case 'DateTimeReleased':
300  case 'DateTimeExpires':
301  case 'GPSDateStamp':
302  case 'dc-date':
303  case 'DateTimeMetadata':
304  if ( $val == '0000:00:00 00:00:00' || $val == ' : : : : ' ) {
305  $val = $this->msg( 'exif-unknowndate' )->text();
306  } elseif ( preg_match(
307  '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/D',
308  $val
309  ) ) {
310  // Full date.
311  $time = wfTimestamp( TS_MW, $val );
312  if ( $time && intval( $time ) > 0 ) {
313  $val = $this->getLanguage()->timeanddate( $time );
314  }
315  } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d)$/D', $val ) ) {
316  // No second field. Still format the same
317  // since timeanddate doesn't include seconds anyways,
318  // but second still available in api
319  $time = wfTimestamp( TS_MW, $val . ':00' );
320  if ( $time && intval( $time ) > 0 ) {
321  $val = $this->getLanguage()->timeanddate( $time );
322  }
323  } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d)$/D', $val ) ) {
324  // If only the date but not the time is filled in.
325  $time = wfTimestamp( TS_MW, substr( $val, 0, 4 )
326  . substr( $val, 5, 2 )
327  . substr( $val, 8, 2 )
328  . '000000' );
329  if ( $time && intval( $time ) > 0 ) {
330  $val = $this->getLanguage()->date( $time );
331  }
332  }
333  // else it will just output $val without formatting it.
334  break;
335 
336  case 'ExposureProgram':
337  switch ( $val ) {
338  case 0:
339  case 1:
340  case 2:
341  case 3:
342  case 4:
343  case 5:
344  case 6:
345  case 7:
346  case 8:
347  $val = $this->exifMsg( $tag, $val );
348  break;
349  default:
350  /* If not recognized, display as is. */
351  break;
352  }
353  break;
354 
355  case 'SubjectDistance':
356  $val = $this->exifMsg( $tag, '', $this->formatNum( $val ) );
357  break;
358 
359  case 'MeteringMode':
360  switch ( $val ) {
361  case 0:
362  case 1:
363  case 2:
364  case 3:
365  case 4:
366  case 5:
367  case 6:
368  case 7:
369  case 255:
370  $val = $this->exifMsg( $tag, $val );
371  break;
372  default:
373  /* If not recognized, display as is. */
374  break;
375  }
376  break;
377 
378  case 'LightSource':
379  switch ( $val ) {
380  case 0:
381  case 1:
382  case 2:
383  case 3:
384  case 4:
385  case 9:
386  case 10:
387  case 11:
388  case 12:
389  case 13:
390  case 14:
391  case 15:
392  case 17:
393  case 18:
394  case 19:
395  case 20:
396  case 21:
397  case 22:
398  case 23:
399  case 24:
400  case 255:
401  $val = $this->exifMsg( $tag, $val );
402  break;
403  default:
404  /* If not recognized, display as is. */
405  break;
406  }
407  break;
408 
409  case 'Flash':
410  $flashDecode = array(
411  'fired' => $val & bindec( '00000001' ),
412  'return' => ( $val & bindec( '00000110' ) ) >> 1,
413  'mode' => ( $val & bindec( '00011000' ) ) >> 3,
414  'function' => ( $val & bindec( '00100000' ) ) >> 5,
415  'redeye' => ( $val & bindec( '01000000' ) ) >> 6,
416 // 'reserved' => ($val & bindec( '10000000' )) >> 7,
417  );
418  $flashMsgs = array();
419  # We do not need to handle unknown values since all are used.
420  foreach ( $flashDecode as $subTag => $subValue ) {
421  # We do not need any message for zeroed values.
422  if ( $subTag != 'fired' && $subValue == 0 ) {
423  continue;
424  }
425  $fullTag = $tag . '-' . $subTag;
426  $flashMsgs[] = $this->exifMsg( $fullTag, $subValue );
427  }
428  $val = $this->getLanguage()->commaList( $flashMsgs );
429  break;
430 
431  case 'FocalPlaneResolutionUnit':
432  switch ( $val ) {
433  case 2:
434  $val = $this->exifMsg( $tag, $val );
435  break;
436  default:
437  /* If not recognized, display as is. */
438  break;
439  }
440  break;
441 
442  case 'SensingMethod':
443  switch ( $val ) {
444  case 1:
445  case 2:
446  case 3:
447  case 4:
448  case 5:
449  case 7:
450  case 8:
451  $val = $this->exifMsg( $tag, $val );
452  break;
453  default:
454  /* If not recognized, display as is. */
455  break;
456  }
457  break;
458 
459  case 'FileSource':
460  switch ( $val ) {
461  case 3:
462  $val = $this->exifMsg( $tag, $val );
463  break;
464  default:
465  /* If not recognized, display as is. */
466  break;
467  }
468  break;
469 
470  case 'SceneType':
471  switch ( $val ) {
472  case 1:
473  $val = $this->exifMsg( $tag, $val );
474  break;
475  default:
476  /* If not recognized, display as is. */
477  break;
478  }
479  break;
480 
481  case 'CustomRendered':
482  switch ( $val ) {
483  case 0:
484  case 1:
485  $val = $this->exifMsg( $tag, $val );
486  break;
487  default:
488  /* If not recognized, display as is. */
489  break;
490  }
491  break;
492 
493  case 'ExposureMode':
494  switch ( $val ) {
495  case 0:
496  case 1:
497  case 2:
498  $val = $this->exifMsg( $tag, $val );
499  break;
500  default:
501  /* If not recognized, display as is. */
502  break;
503  }
504  break;
505 
506  case 'WhiteBalance':
507  switch ( $val ) {
508  case 0:
509  case 1:
510  $val = $this->exifMsg( $tag, $val );
511  break;
512  default:
513  /* If not recognized, display as is. */
514  break;
515  }
516  break;
517 
518  case 'SceneCaptureType':
519  switch ( $val ) {
520  case 0:
521  case 1:
522  case 2:
523  case 3:
524  $val = $this->exifMsg( $tag, $val );
525  break;
526  default:
527  /* If not recognized, display as is. */
528  break;
529  }
530  break;
531 
532  case 'GainControl':
533  switch ( $val ) {
534  case 0:
535  case 1:
536  case 2:
537  case 3:
538  case 4:
539  $val = $this->exifMsg( $tag, $val );
540  break;
541  default:
542  /* If not recognized, display as is. */
543  break;
544  }
545  break;
546 
547  case 'Contrast':
548  switch ( $val ) {
549  case 0:
550  case 1:
551  case 2:
552  $val = $this->exifMsg( $tag, $val );
553  break;
554  default:
555  /* If not recognized, display as is. */
556  break;
557  }
558  break;
559 
560  case 'Saturation':
561  switch ( $val ) {
562  case 0:
563  case 1:
564  case 2:
565  $val = $this->exifMsg( $tag, $val );
566  break;
567  default:
568  /* If not recognized, display as is. */
569  break;
570  }
571  break;
572 
573  case 'Sharpness':
574  switch ( $val ) {
575  case 0:
576  case 1:
577  case 2:
578  $val = $this->exifMsg( $tag, $val );
579  break;
580  default:
581  /* If not recognized, display as is. */
582  break;
583  }
584  break;
585 
586  case 'SubjectDistanceRange':
587  switch ( $val ) {
588  case 0:
589  case 1:
590  case 2:
591  case 3:
592  $val = $this->exifMsg( $tag, $val );
593  break;
594  default:
595  /* If not recognized, display as is. */
596  break;
597  }
598  break;
599 
600  //The GPS...Ref values are kept for compatibility, probably won't be reached.
601  case 'GPSLatitudeRef':
602  case 'GPSDestLatitudeRef':
603  switch ( $val ) {
604  case 'N':
605  case 'S':
606  $val = $this->exifMsg( 'GPSLatitude', $val );
607  break;
608  default:
609  /* If not recognized, display as is. */
610  break;
611  }
612  break;
613 
614  case 'GPSLongitudeRef':
615  case 'GPSDestLongitudeRef':
616  switch ( $val ) {
617  case 'E':
618  case 'W':
619  $val = $this->exifMsg( 'GPSLongitude', $val );
620  break;
621  default:
622  /* If not recognized, display as is. */
623  break;
624  }
625  break;
626 
627  case 'GPSAltitude':
628  if ( $val < 0 ) {
629  $val = $this->exifMsg( 'GPSAltitude', 'below-sealevel', $this->formatNum( -$val, 3 ) );
630  } else {
631  $val = $this->exifMsg( 'GPSAltitude', 'above-sealevel', $this->formatNum( $val, 3 ) );
632  }
633  break;
634 
635  case 'GPSStatus':
636  switch ( $val ) {
637  case 'A':
638  case 'V':
639  $val = $this->exifMsg( $tag, $val );
640  break;
641  default:
642  /* If not recognized, display as is. */
643  break;
644  }
645  break;
646 
647  case 'GPSMeasureMode':
648  switch ( $val ) {
649  case 2:
650  case 3:
651  $val = $this->exifMsg( $tag, $val );
652  break;
653  default:
654  /* If not recognized, display as is. */
655  break;
656  }
657  break;
658 
659  case 'GPSTrackRef':
660  case 'GPSImgDirectionRef':
661  case 'GPSDestBearingRef':
662  switch ( $val ) {
663  case 'T':
664  case 'M':
665  $val = $this->exifMsg( 'GPSDirection', $val );
666  break;
667  default:
668  /* If not recognized, display as is. */
669  break;
670  }
671  break;
672 
673  case 'GPSLatitude':
674  case 'GPSDestLatitude':
675  $val = $this->formatCoords( $val, 'latitude' );
676  break;
677  case 'GPSLongitude':
678  case 'GPSDestLongitude':
679  $val = $this->formatCoords( $val, 'longitude' );
680  break;
681 
682  case 'GPSSpeedRef':
683  switch ( $val ) {
684  case 'K':
685  case 'M':
686  case 'N':
687  $val = $this->exifMsg( 'GPSSpeed', $val );
688  break;
689  default:
690  /* If not recognized, display as is. */
691  break;
692  }
693  break;
694 
695  case 'GPSDestDistanceRef':
696  switch ( $val ) {
697  case 'K':
698  case 'M':
699  case 'N':
700  $val = $this->exifMsg( 'GPSDestDistance', $val );
701  break;
702  default:
703  /* If not recognized, display as is. */
704  break;
705  }
706  break;
707 
708  case 'GPSDOP':
709  // See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)
710  if ( $val <= 2 ) {
711  $val = $this->exifMsg( $tag, 'excellent', $this->formatNum( $val ) );
712  } elseif ( $val <= 5 ) {
713  $val = $this->exifMsg( $tag, 'good', $this->formatNum( $val ) );
714  } elseif ( $val <= 10 ) {
715  $val = $this->exifMsg( $tag, 'moderate', $this->formatNum( $val ) );
716  } elseif ( $val <= 20 ) {
717  $val = $this->exifMsg( $tag, 'fair', $this->formatNum( $val ) );
718  } else {
719  $val = $this->exifMsg( $tag, 'poor', $this->formatNum( $val ) );
720  }
721  break;
722 
723  // This is not in the Exif standard, just a special
724  // case for our purposes which enables wikis to wikify
725  // the make, model and software name to link to their articles.
726  case 'Make':
727  case 'Model':
728  $val = $this->exifMsg( $tag, '', $val );
729  break;
730 
731  case 'Software':
732  if ( is_array( $val ) ) {
733  //if its a software, version array.
734  $val = $this->msg( 'exif-software-version-value', $val[0], $val[1] )->text();
735  } else {
736  $val = $this->exifMsg( $tag, '', $val );
737  }
738  break;
739 
740  case 'ExposureTime':
741  // Show the pretty fraction as well as decimal version
742  $val = $this->msg( 'exif-exposuretime-format',
743  $this->formatFraction( $val ), $this->formatNum( $val ) )->text();
744  break;
745  case 'ISOSpeedRatings':
746  // If its = 65535 that means its at the
747  // limit of the size of Exif::short and
748  // is really higher.
749  if ( $val == '65535' ) {
750  $val = $this->exifMsg( $tag, 'overflow' );
751  } else {
752  $val = $this->formatNum( $val );
753  }
754  break;
755  case 'FNumber':
756  $val = $this->msg( 'exif-fnumber-format',
757  $this->formatNum( $val ) )->text();
758  break;
759 
760  case 'FocalLength':
761  case 'FocalLengthIn35mmFilm':
762  $val = $this->msg( 'exif-focallength-format',
763  $this->formatNum( $val ) )->text();
764  break;
765 
766  case 'MaxApertureValue':
767  if ( strpos( $val, '/' ) !== false ) {
768  // need to expand this earlier to calculate fNumber
769  list( $n, $d ) = explode( '/', $val );
770  if ( is_numeric( $n ) && is_numeric( $d ) ) {
771  $val = $n / $d;
772  }
773  }
774  if ( is_numeric( $val ) ) {
775  $fNumber = pow( 2, $val / 2 );
776  if ( $fNumber !== false ) {
777  $val = $this->msg( 'exif-maxaperturevalue-value',
778  $this->formatNum( $val ),
779  $this->formatNum( $fNumber, 2 )
780  )->text();
781  }
782  }
783  break;
784 
785  case 'iimCategory':
786  switch ( strtolower( $val ) ) {
787  // See pg 29 of IPTC photo
788  // metadata standard.
789  case 'ace':
790  case 'clj':
791  case 'dis':
792  case 'fin':
793  case 'edu':
794  case 'evn':
795  case 'hth':
796  case 'hum':
797  case 'lab':
798  case 'lif':
799  case 'pol':
800  case 'rel':
801  case 'sci':
802  case 'soi':
803  case 'spo':
804  case 'war':
805  case 'wea':
806  $val = $this->exifMsg(
807  'iimcategory',
808  $val
809  );
810  }
811  break;
812  case 'SubjectNewsCode':
813  // Essentially like iimCategory.
814  // 8 (numeric) digit hierarchical
815  // classification. We decode the
816  // first 2 digits, which provide
817  // a broad category.
818  $val = $this->convertNewsCode( $val );
819  break;
820  case 'Urgency':
821  // 1-8 with 1 being highest, 5 normal
822  // 0 is reserved, and 9 is 'user-defined'.
823  $urgency = '';
824  if ( $val == 0 || $val == 9 ) {
825  $urgency = 'other';
826  } elseif ( $val < 5 && $val > 1 ) {
827  $urgency = 'high';
828  } elseif ( $val == 5 ) {
829  $urgency = 'normal';
830  } elseif ( $val <= 8 && $val > 5 ) {
831  $urgency = 'low';
832  }
833 
834  if ( $urgency !== '' ) {
835  $val = $this->exifMsg( 'urgency',
836  $urgency, $val
837  );
838  }
839  break;
840 
841  // Things that have a unit of pixels.
842  case 'OriginalImageHeight':
843  case 'OriginalImageWidth':
844  case 'PixelXDimension':
845  case 'PixelYDimension':
846  case 'ImageWidth':
847  case 'ImageLength':
848  $val = $this->formatNum( $val ) . ' ' . $this->msg( 'unit-pixel' )->text();
849  break;
850 
851  // Do not transform fields with pure text.
852  // For some languages the formatNum()
853  // conversion results to wrong output like
854  // foo,bar@example,com or fooÙ«bar@exampleÙ«com.
855  // Also some 'numeric' things like Scene codes
856  // are included here as we really don't want
857  // commas inserted.
858  case 'ImageDescription':
859  case 'Artist':
860  case 'Copyright':
861  case 'RelatedSoundFile':
862  case 'ImageUniqueID':
863  case 'SpectralSensitivity':
864  case 'GPSSatellites':
865  case 'GPSVersionID':
866  case 'GPSMapDatum':
867  case 'Keywords':
868  case 'WorldRegionDest':
869  case 'CountryDest':
870  case 'CountryCodeDest':
871  case 'ProvinceOrStateDest':
872  case 'CityDest':
873  case 'SublocationDest':
874  case 'WorldRegionCreated':
875  case 'CountryCreated':
876  case 'CountryCodeCreated':
877  case 'ProvinceOrStateCreated':
878  case 'CityCreated':
879  case 'SublocationCreated':
880  case 'ObjectName':
881  case 'SpecialInstructions':
882  case 'Headline':
883  case 'Credit':
884  case 'Source':
885  case 'EditStatus':
886  case 'FixtureIdentifier':
887  case 'LocationDest':
888  case 'LocationDestCode':
889  case 'Writer':
890  case 'JPEGFileComment':
891  case 'iimSupplementalCategory':
892  case 'OriginalTransmissionRef':
893  case 'Identifier':
894  case 'dc-contributor':
895  case 'dc-coverage':
896  case 'dc-publisher':
897  case 'dc-relation':
898  case 'dc-rights':
899  case 'dc-source':
900  case 'dc-type':
901  case 'Lens':
902  case 'SerialNumber':
903  case 'CameraOwnerName':
904  case 'Label':
905  case 'Nickname':
906  case 'RightsCertificate':
907  case 'CopyrightOwner':
908  case 'UsageTerms':
909  case 'WebStatement':
910  case 'OriginalDocumentID':
911  case 'LicenseUrl':
912  case 'MorePermissionsUrl':
913  case 'AttributionUrl':
914  case 'PreferredAttributionName':
915  case 'PNGFileComment':
916  case 'Disclaimer':
917  case 'ContentWarning':
918  case 'GIFFileComment':
919  case 'SceneCode':
920  case 'IntellectualGenre':
921  case 'Event':
922  case 'OrginisationInImage':
923  case 'PersonInImage':
924 
925  $val = htmlspecialchars( $val );
926  break;
927 
928  case 'ObjectCycle':
929  switch ( $val ) {
930  case 'a':
931  case 'p':
932  case 'b':
933  $val = $this->exifMsg( $tag, $val );
934  break;
935  default:
936  $val = htmlspecialchars( $val );
937  break;
938  }
939  break;
940  case 'Copyrighted':
941  switch ( $val ) {
942  case 'True':
943  case 'False':
944  $val = $this->exifMsg( $tag, $val );
945  break;
946  }
947  break;
948  case 'Rating':
949  if ( $val == '-1' ) {
950  $val = $this->exifMsg( $tag, 'rejected' );
951  } else {
952  $val = $this->formatNum( $val );
953  }
954  break;
955 
956  case 'LanguageCode':
957  $lang = Language::fetchLanguageName( strtolower( $val ), $this->getLanguage()->getCode() );
958  if ( $lang ) {
959  $val = htmlspecialchars( $lang );
960  } else {
961  $val = htmlspecialchars( $val );
962  }
963  break;
964 
965  default:
966  $val = $this->formatNum( $val );
967  break;
968  }
969  }
970  // End formatting values, start flattening arrays.
971  $vals = $this->flattenArrayReal( $vals, $type );
972  }
973 
974  return $tags;
975  }
976 
991  public static function flattenArrayContentLang( $vals, $type = 'ul',
992  $noHtml = false, $context = false
993  ) {
995  $obj = new FormatMetadata;
996  if ( $context ) {
997  $obj->setContext( $context );
998  }
999  $context = new DerivativeContext( $obj->getContext() );
1000  $context->setLanguage( $wgContLang );
1001  $obj->setContext( $context );
1002 
1003  return $obj->flattenArrayReal( $vals, $type, $noHtml );
1004  }
1005 
1019  public static function flattenArray( $vals, $type = 'ul', $noHtml = false, $context = false ) {
1020  $obj = new FormatMetadata;
1021  if ( $context ) {
1022  $obj->setContext( $context );
1023  }
1024 
1025  return $obj->flattenArrayReal( $vals, $type, $noHtml );
1026  }
1027 
1044  public function flattenArrayReal( $vals, $type = 'ul', $noHtml = false ) {
1045  if ( !is_array( $vals ) ) {
1046  return $vals; // do nothing if not an array;
1047  }
1048 
1049  if ( isset( $vals['_type'] ) ) {
1050  $type = $vals['_type'];
1051  unset( $vals['_type'] );
1052  }
1053 
1054  if ( !is_array( $vals ) ) {
1055  return $vals; // do nothing if not an array;
1056  } elseif ( count( $vals ) === 1 && $type !== 'lang' ) {
1057  return $vals[0];
1058  } elseif ( count( $vals ) === 0 ) {
1059  wfDebug( __METHOD__ . " metadata array with 0 elements!\n" );
1060 
1061  return ""; // paranoia. This should never happen
1062  } else {
1063  /* @todo FIXME: This should hide some of the list entries if there are
1064  * say more than four. Especially if a field is translated into 20
1065  * languages, we don't want to show them all by default
1066  */
1067  switch ( $type ) {
1068  case 'lang':
1069  // Display default, followed by ContLang,
1070  // followed by the rest in no particular
1071  // order.
1072 
1073  // Todo: hide some items if really long list.
1074 
1075  $content = '';
1076 
1077  $priorityLanguages = $this->getPriorityLanguages();
1078  $defaultItem = false;
1079  $defaultLang = false;
1080 
1081  // If default is set, save it for later,
1082  // as we don't know if it's equal to
1083  // one of the lang codes. (In xmp
1084  // you specify the language for a
1085  // default property by having both
1086  // a default prop, and one in the language
1087  // that are identical)
1088  if ( isset( $vals['x-default'] ) ) {
1089  $defaultItem = $vals['x-default'];
1090  unset( $vals['x-default'] );
1091  }
1092  foreach ( $priorityLanguages as $pLang ) {
1093  if ( isset( $vals[$pLang] ) ) {
1094  $isDefault = false;
1095  if ( $vals[$pLang] === $defaultItem ) {
1096  $defaultItem = false;
1097  $isDefault = true;
1098  }
1099  $content .= $this->langItem(
1100  $vals[$pLang], $pLang,
1101  $isDefault, $noHtml );
1102 
1103  unset( $vals[$pLang] );
1104 
1105  if ( $this->singleLang ) {
1106  return Html::rawElement( 'span',
1107  array( 'lang' => $pLang ), $vals[$pLang] );
1108  }
1109  }
1110  }
1111 
1112  // Now do the rest.
1113  foreach ( $vals as $lang => $item ) {
1114  if ( $item === $defaultItem ) {
1115  $defaultLang = $lang;
1116  continue;
1117  }
1118  $content .= $this->langItem( $item,
1119  $lang, false, $noHtml );
1120  if ( $this->singleLang ) {
1121  return Html::rawElement( 'span',
1122  array( 'lang' => $lang ), $item );
1123  }
1124  }
1125  if ( $defaultItem !== false ) {
1126  $content = $this->langItem( $defaultItem,
1127  $defaultLang, true, $noHtml ) .
1128  $content;
1129  if ( $this->singleLang ) {
1130  return $defaultItem;
1131  }
1132  }
1133  if ( $noHtml ) {
1134  return $content;
1135  }
1136 
1137  return '<ul class="metadata-langlist">' .
1138  $content .
1139  '</ul>';
1140  case 'ol':
1141  if ( $noHtml ) {
1142  return "\n#" . implode( "\n#", $vals );
1143  }
1144 
1145  return "<ol><li>" . implode( "</li>\n<li>", $vals ) . '</li></ol>';
1146  case 'ul':
1147  default:
1148  if ( $noHtml ) {
1149  return "\n*" . implode( "\n*", $vals );
1150  }
1151 
1152  return "<ul><li>" . implode( "</li>\n<li>", $vals ) . '</li></ul>';
1153  }
1154  }
1155  }
1156 
1167  private function langItem( $value, $lang, $default = false, $noHtml = false ) {
1168  if ( $lang === false && $default === false ) {
1169  throw new MWException( '$lang and $default cannot both '
1170  . 'be false.' );
1171  }
1172 
1173  if ( $noHtml ) {
1174  $wrappedValue = $value;
1175  } else {
1176  $wrappedValue = '<span class="mw-metadata-lang-value">'
1177  . $value . '</span>';
1178  }
1179 
1180  if ( $lang === false ) {
1181  $msg = $this->msg( 'metadata-langitem-default', $wrappedValue );
1182  if ( $noHtml ) {
1183  return $msg->text() . "\n\n";
1184  } /* else */
1185 
1186  return '<li class="mw-metadata-lang-default">'
1187  . $msg->text()
1188  . "</li>\n";
1189  }
1190 
1191  $lowLang = strtolower( $lang );
1192  $langName = Language::fetchLanguageName( $lowLang );
1193  if ( $langName === '' ) {
1194  //try just the base language name. (aka en-US -> en ).
1195  list( $langPrefix ) = explode( '-', $lowLang, 2 );
1196  $langName = Language::fetchLanguageName( $langPrefix );
1197  if ( $langName === '' ) {
1198  // give up.
1199  $langName = $lang;
1200  }
1201  }
1202  // else we have a language specified
1203 
1204  $msg = $this->msg( 'metadata-langitem', $wrappedValue, $langName, $lang );
1205  if ( $noHtml ) {
1206  return '*' . $msg->text();
1207  } /* else: */
1208 
1209  $item = '<li class="mw-metadata-lang-code-'
1210  . $lang;
1211  if ( $default ) {
1212  $item .= ' mw-metadata-lang-default';
1213  }
1214  $item .= '" lang="' . $lang . '">';
1215  $item .= $msg->text();
1216  $item .= "</li>\n";
1217 
1218  return $item;
1219  }
1220 
1230  private function exifMsg( $tag, $val, $arg = null, $arg2 = null ) {
1232 
1233  if ( $val === '' ) {
1234  $val = 'value';
1235  }
1236 
1237  return $this->msg( $wgContLang->lc( "exif-$tag-$val" ), $arg, $arg2 )->text();
1238  }
1239 
1248  private function formatNum( $num, $round = false ) {
1249  $m = array();
1250  if ( is_array( $num ) ) {
1251  $out = array();
1252  foreach ( $num as $number ) {
1253  $out[] = $this->formatNum( $number );
1254  }
1255 
1256  return $this->getLanguage()->commaList( $out );
1257  }
1258  if ( preg_match( '/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
1259  if ( $m[2] != 0 ) {
1260  $newNum = $m[1] / $m[2];
1261  if ( $round !== false ) {
1262  $newNum = round( $newNum, $round );
1263  }
1264  } else {
1265  $newNum = $num;
1266  }
1267 
1268  return $this->getLanguage()->formatNum( $newNum );
1269  } else {
1270  if ( is_numeric( $num ) && $round !== false ) {
1271  $num = round( $num, $round );
1272  }
1273 
1274  return $this->getLanguage()->formatNum( $num );
1275  }
1276  }
1277 
1284  private function formatFraction( $num ) {
1285  $m = array();
1286  if ( preg_match( '/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
1287  $numerator = intval( $m[1] );
1288  $denominator = intval( $m[2] );
1289  $gcd = $this->gcd( abs( $numerator ), $denominator );
1290  if ( $gcd != 0 ) {
1291  // 0 shouldn't happen! ;)
1292  return $this->formatNum( $numerator / $gcd ) . '/' . $this->formatNum( $denominator / $gcd );
1293  }
1294  }
1295 
1296  return $this->formatNum( $num );
1297  }
1298 
1307  private function gcd( $a, $b ) {
1308  /*
1309  // http://en.wikipedia.org/wiki/Euclidean_algorithm
1310  // Recursive form would be:
1311  if( $b == 0 )
1312  return $a;
1313  else
1314  return gcd( $b, $a % $b );
1315  */
1316  while ( $b != 0 ) {
1317  $remainder = $a % $b;
1318 
1319  // tail recursion...
1320  $a = $b;
1321  $b = $remainder;
1322  }
1323 
1324  return $a;
1325  }
1326 
1339  private function convertNewsCode( $val ) {
1340  if ( !preg_match( '/^\d{8}$/D', $val ) ) {
1341  // Not a valid news code.
1342  return $val;
1343  }
1344  $cat = '';
1345  switch ( substr( $val, 0, 2 ) ) {
1346  case '01':
1347  $cat = 'ace';
1348  break;
1349  case '02':
1350  $cat = 'clj';
1351  break;
1352  case '03':
1353  $cat = 'dis';
1354  break;
1355  case '04':
1356  $cat = 'fin';
1357  break;
1358  case '05':
1359  $cat = 'edu';
1360  break;
1361  case '06':
1362  $cat = 'evn';
1363  break;
1364  case '07':
1365  $cat = 'hth';
1366  break;
1367  case '08':
1368  $cat = 'hum';
1369  break;
1370  case '09':
1371  $cat = 'lab';
1372  break;
1373  case '10':
1374  $cat = 'lif';
1375  break;
1376  case '11':
1377  $cat = 'pol';
1378  break;
1379  case '12':
1380  $cat = 'rel';
1381  break;
1382  case '13':
1383  $cat = 'sci';
1384  break;
1385  case '14':
1386  $cat = 'soi';
1387  break;
1388  case '15':
1389  $cat = 'spo';
1390  break;
1391  case '16':
1392  $cat = 'war';
1393  break;
1394  case '17':
1395  $cat = 'wea';
1396  break;
1397  }
1398  if ( $cat !== '' ) {
1399  $catMsg = $this->exifMsg( 'iimcategory', $cat );
1400  $val = $this->exifMsg( 'subjectnewscode', '', $val, $catMsg );
1401  }
1402 
1403  return $val;
1404  }
1405 
1414  private function formatCoords( $coord, $type ) {
1415  $ref = '';
1416  if ( $coord < 0 ) {
1417  $nCoord = -$coord;
1418  if ( $type === 'latitude' ) {
1419  $ref = 'S';
1420  } elseif ( $type === 'longitude' ) {
1421  $ref = 'W';
1422  }
1423  } else {
1424  $nCoord = $coord;
1425  if ( $type === 'latitude' ) {
1426  $ref = 'N';
1427  } elseif ( $type === 'longitude' ) {
1428  $ref = 'E';
1429  }
1430  }
1431 
1432  $deg = floor( $nCoord );
1433  $min = floor( ( $nCoord - $deg ) * 60.0 );
1434  $sec = round( ( ( $nCoord - $deg ) - $min / 60 ) * 3600, 2 );
1435 
1436  $deg = $this->formatNum( $deg );
1437  $min = $this->formatNum( $min );
1438  $sec = $this->formatNum( $sec );
1439 
1440  return $this->msg( 'exif-coordinate-format', $deg, $min, $sec, $ref, $coord )->text();
1441  }
1442 
1457  public function collapseContactInfo( $vals ) {
1458  if ( !( isset( $vals['CiAdrExtadr'] )
1459  || isset( $vals['CiAdrCity'] )
1460  || isset( $vals['CiAdrCtry'] )
1461  || isset( $vals['CiEmailWork'] )
1462  || isset( $vals['CiTelWork'] )
1463  || isset( $vals['CiAdrPcode'] )
1464  || isset( $vals['CiAdrRegion'] )
1465  || isset( $vals['CiUrlWork'] )
1466  ) ) {
1467  // We don't have any sub-properties
1468  // This could happen if its using old
1469  // iptc that just had this as a free-form
1470  // text value.
1471  // Note: We run this through htmlspecialchars
1472  // partially to be consistent, and partially
1473  // because people often insert >, etc into
1474  // the metadata which should not be interpreted
1475  // but we still want to auto-link urls.
1476  foreach ( $vals as &$val ) {
1477  $val = htmlspecialchars( $val );
1478  }
1479 
1480  return $this->flattenArrayReal( $vals );
1481  } else {
1482  // We have a real ContactInfo field.
1483  // Its unclear if all these fields have to be
1484  // set, so assume they do not.
1485  $url = $tel = $street = $city = $country = '';
1486  $email = $postal = $region = '';
1487 
1488  // Also note, some of the class names this uses
1489  // are similar to those used by hCard. This is
1490  // mostly because they're sensible names. This
1491  // does not (and does not attempt to) output
1492  // stuff in the hCard microformat. However it
1493  // might output in the adr microformat.
1494 
1495  if ( isset( $vals['CiAdrExtadr'] ) ) {
1496  // Todo: This can potentially be multi-line.
1497  // Need to check how that works in XMP.
1498  $street = '<span class="extended-address">'
1499  . htmlspecialchars(
1500  $vals['CiAdrExtadr'] )
1501  . '</span>';
1502  }
1503  if ( isset( $vals['CiAdrCity'] ) ) {
1504  $city = '<span class="locality">'
1505  . htmlspecialchars( $vals['CiAdrCity'] )
1506  . '</span>';
1507  }
1508  if ( isset( $vals['CiAdrCtry'] ) ) {
1509  $country = '<span class="country-name">'
1510  . htmlspecialchars( $vals['CiAdrCtry'] )
1511  . '</span>';
1512  }
1513  if ( isset( $vals['CiEmailWork'] ) ) {
1514  $emails = array();
1515  // Have to split multiple emails at commas/new lines.
1516  $splitEmails = explode( "\n", $vals['CiEmailWork'] );
1517  foreach ( $splitEmails as $e1 ) {
1518  // Also split on comma
1519  foreach ( explode( ',', $e1 ) as $e2 ) {
1520  $finalEmail = trim( $e2 );
1521  if ( $finalEmail == ',' || $finalEmail == '' ) {
1522  continue;
1523  }
1524  if ( strpos( $finalEmail, '<' ) !== false ) {
1525  // Don't do fancy formatting to
1526  // "My name" <foo@bar.com> style stuff
1527  $emails[] = $finalEmail;
1528  } else {
1529  $emails[] = '[mailto:'
1530  . $finalEmail
1531  . ' <span class="email">'
1532  . $finalEmail
1533  . '</span>]';
1534  }
1535  }
1536  }
1537  $email = implode( ', ', $emails );
1538  }
1539  if ( isset( $vals['CiTelWork'] ) ) {
1540  $tel = '<span class="tel">'
1541  . htmlspecialchars( $vals['CiTelWork'] )
1542  . '</span>';
1543  }
1544  if ( isset( $vals['CiAdrPcode'] ) ) {
1545  $postal = '<span class="postal-code">'
1546  . htmlspecialchars(
1547  $vals['CiAdrPcode'] )
1548  . '</span>';
1549  }
1550  if ( isset( $vals['CiAdrRegion'] ) ) {
1551  // Note this is province/state.
1552  $region = '<span class="region">'
1553  . htmlspecialchars(
1554  $vals['CiAdrRegion'] )
1555  . '</span>';
1556  }
1557  if ( isset( $vals['CiUrlWork'] ) ) {
1558  $url = '<span class="url">'
1559  . htmlspecialchars( $vals['CiUrlWork'] )
1560  . '</span>';
1561  }
1562 
1563  return $this->msg( 'exif-contact-value', $email, $url,
1564  $street, $city, $region, $postal, $country,
1565  $tel )->text();
1566  }
1567  }
1568 
1575  public static function getVisibleFields() {
1576  $fields = array();
1577  $lines = explode( "\n", wfMessage( 'metadata-fields' )->inContentLanguage()->text() );
1578  foreach ( $lines as $line ) {
1579  $matches = array();
1580  if ( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
1581  $fields[] = $matches[1];
1582  }
1583  }
1584  $fields = array_map( 'strtolower', $fields );
1585 
1586  return $fields;
1587  }
1588 
1596  public function fetchExtendedMetadata( File $file ) {
1597  global $wgMemc;
1598 
1599  wfProfileIn( __METHOD__ );
1600 
1601  // If revision deleted, exit immediately
1602  if ( $file->isDeleted( File::DELETED_FILE ) ) {
1603  wfProfileOut( __METHOD__ );
1604 
1605  return array();
1606  }
1607 
1608  $cacheKey = wfMemcKey(
1609  'getExtendedMetadata',
1610  $this->getLanguage()->getCode(),
1611  (int)$this->singleLang,
1612  $file->getSha1()
1613  );
1614 
1615  $cachedValue = $wgMemc->get( $cacheKey );
1616  if (
1617  $cachedValue
1618  && wfRunHooks( 'ValidateExtendedMetadataCache', array( $cachedValue['timestamp'], $file ) )
1619  ) {
1620  $extendedMetadata = $cachedValue['data'];
1621  } else {
1622  $maxCacheTime = ( $file instanceof ForeignAPIFile ) ? 60 * 60 * 12 : 60 * 60 * 24 * 30;
1623  $fileMetadata = $this->getExtendedMetadataFromFile( $file );
1624  $extendedMetadata = $this->getExtendedMetadataFromHook( $file, $fileMetadata, $maxCacheTime );
1625  if ( $this->singleLang ) {
1626  $this->resolveMultilangMetadata( $extendedMetadata );
1627  }
1628  // Make sure the metadata won't break the API when an XML format is used.
1629  // This is an API-specific function so it would be cleaner to call it from
1630  // outside fetchExtendedMetadata, but this way we don't need to redo the
1631  // computation on a cache hit.
1632  $this->sanitizeArrayForXml( $extendedMetadata );
1633  $valueToCache = array( 'data' => $extendedMetadata, 'timestamp' => wfTimestampNow() );
1634  $wgMemc->set( $cacheKey, $valueToCache, $maxCacheTime );
1635  }
1636 
1637  wfProfileOut( __METHOD__ );
1638 
1639  return $extendedMetadata;
1640  }
1641 
1651  protected function getExtendedMetadataFromFile( File $file ) {
1652  // If this is a remote file accessed via an API request, we already
1653  // have remote metadata so we just ignore any local one
1654  if ( $file instanceof ForeignAPIFile ) {
1655  // In case of error we pretend no metadata - this will get cached.
1656  // Might or might not be a good idea.
1657  return $file->getExtendedMetadata() ?: array();
1658  }
1659 
1660  wfProfileIn( __METHOD__ );
1661 
1662  $uploadDate = wfTimestamp( TS_ISO_8601, $file->getTimestamp() );
1663 
1664  $fileMetadata = array(
1665  // This is modification time, which is close to "upload" time.
1666  'DateTime' => array(
1667  'value' => $uploadDate,
1668  'source' => 'mediawiki-metadata',
1669  ),
1670  );
1671 
1672  $title = $file->getTitle();
1673  if ( $title ) {
1674  $text = $title->getText();
1675  $pos = strrpos( $text, '.' );
1676 
1677  if ( $pos ) {
1678  $name = substr( $text, 0, $pos );
1679  } else {
1680  $name = $text;
1681  }
1682 
1683  $fileMetadata['ObjectName'] = array(
1684  'value' => $name,
1685  'source' => 'mediawiki-metadata',
1686  );
1687  }
1688 
1689  $common = $file->getCommonMetaArray();
1690 
1691  if ( $common !== false ) {
1692  foreach ( $common as $key => $value ) {
1693  $fileMetadata[$key] = array(
1694  'value' => $value,
1695  'source' => 'file-metadata',
1696  );
1697  }
1698  }
1699 
1700  wfProfileOut( __METHOD__ );
1701 
1702  return $fileMetadata;
1703  }
1704 
1715  protected function getExtendedMetadataFromHook( File $file, array $extendedMetadata,
1716  &$maxCacheTime
1717  ) {
1718  wfProfileIn( __METHOD__ );
1719 
1720  wfRunHooks( 'GetExtendedMetadata', array(
1721  &$extendedMetadata,
1722  $file,
1723  $this->getContext(),
1724  $this->singleLang,
1725  &$maxCacheTime
1726  ) );
1727 
1728  $visible = array_flip( self::getVisibleFields() );
1729  foreach ( $extendedMetadata as $key => $value ) {
1730  if ( !isset( $visible[strtolower( $key )] ) ) {
1731  $extendedMetadata[$key]['hidden'] = '';
1732  }
1733  }
1734 
1735  wfProfileOut( __METHOD__ );
1736 
1737  return $extendedMetadata;
1738  }
1739 
1748  protected function resolveMultilangValue( $value ) {
1749  if (
1750  !is_array( $value )
1751  || !isset( $value['_type'] )
1752  || $value['_type'] != 'lang'
1753  ) {
1754  return $value; // do nothing if not a multilang array
1755  }
1756 
1757  // choose the language best matching user or site settings
1758  $priorityLanguages = $this->getPriorityLanguages();
1759  foreach ( $priorityLanguages as $lang ) {
1760  if ( isset( $value[$lang] ) ) {
1761  return $value[$lang];
1762  }
1763  }
1764 
1765  // otherwise go with the default language, if set
1766  if ( isset( $value['x-default'] ) ) {
1767  return $value['x-default'];
1768  }
1769 
1770  // otherwise just return any one language
1771  unset( $value['_type'] );
1772  if ( !empty( $value ) ) {
1773  return reset( $value );
1774  }
1775 
1776  // this should not happen; signal error
1777  return null;
1778  }
1779 
1786  protected function resolveMultilangMetadata( &$metadata ) {
1787  if ( !is_array( $metadata ) ) {
1788  return;
1789  }
1790  foreach ( $metadata as &$field ) {
1791  if ( isset( $field['value'] ) ) {
1792  $field['value'] = $this->resolveMultilangValue( $field['value'] );
1793  }
1794  }
1795  }
1796 
1802  protected function sanitizeArrayForXml( &$arr ) {
1803  if ( !is_array( $arr ) ) {
1804  return;
1805  }
1806 
1807  $counter = 1;
1808  foreach ( $arr as $key => &$value ) {
1809  $sanitizedKey = $this->sanitizeKeyForXml( $key );
1810  if ( $sanitizedKey !== $key ) {
1811  if ( isset( $arr[$sanitizedKey] ) ) {
1812  // Make the sanitized keys hopefully unique.
1813  // To make it definitely unique would be too much effort, given that
1814  // sanitizing is only needed for misformatted metadata anyway, but
1815  // this at least covers the case when $arr is numeric.
1816  $sanitizedKey .= $counter;
1817  ++$counter;
1818  }
1819  $arr[$sanitizedKey] = $arr[$key];
1820  unset( $arr[$key] );
1821  }
1822  if ( is_array( $value ) ) {
1823  $this->sanitizeArrayForXml( $value );
1824  }
1825  }
1826  }
1827 
1836  protected function sanitizeKeyForXml( $key ) {
1837  // drop all characters which are not valid in an XML tag name
1838  // a bunch of non-ASCII letters would be valid but probably won't
1839  // be used so we take the easy way
1840  $key = preg_replace( '/[^a-zA-z0-9_:.-]/', '', $key );
1841  // drop characters which are invalid at the first position
1842  $key = preg_replace( '/^[\d-.]+/', '', $key );
1843 
1844  if ( $key == '' ) {
1845  $key = '_';
1846  }
1847 
1848  // special case for an internal keyword
1849  if ( $key == '_element' ) {
1850  $key = 'element';
1851  }
1852 
1853  return $key;
1854  }
1855 
1862  protected function getPriorityLanguages() {
1863  $priorityLanguages =
1865  $priorityLanguages = array_merge(
1866  (array)$this->getLanguage()->getCode(),
1867  $priorityLanguages[0],
1868  $priorityLanguages[1]
1869  );
1870 
1871  return $priorityLanguages;
1872  }
1873 }
1874 
1881 class FormatExif {
1883  private $meta;
1884 
1888  function __construct( $meta ) {
1889  wfDeprecated( __METHOD__, '1.18' );
1890  $this->meta = $meta;
1891  }
1892 
1896  function getFormattedData() {
1897  return FormatMetadata::getFormattedData( $this->meta );
1898  }
1899 }
FormatExif\getFormattedData
getFormattedData()
Definition: FormatMetadata.php:1894
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1358
ContextSource\getContext
getContext()
Get the RequestContext object.
Definition: ContextSource.php:40
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
FormatMetadata\gcd
gcd( $a, $b)
Calculate the greatest common divisor of two integers.
Definition: FormatMetadata.php:1306
FormatMetadata\getFormattedData
static getFormattedData( $tags, $context=false)
Numbers given by Exif user agents are often magical, that is they should be replaced by a detailed ex...
Definition: FormatMetadata.php:79
ContextSource\msg
msg()
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:175
$wgMemc
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest to get request data $wgMemc
Definition: globals.txt:25
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2483
FormatMetadata\formatNum
formatNum( $num, $round=false)
Format a number, convert numbers from fractions into floating point numbers, joins arrays of numbers ...
Definition: FormatMetadata.php:1247
FormatMetadata\fetchExtendedMetadata
fetchExtendedMetadata(File $file)
Get an array of extended metadata.
Definition: FormatMetadata.php:1595
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
$n
$n
Definition: RandomTest.php:76
$s
$s
Definition: mergeMessageFileList.php:156
FormatExif\__construct
__construct( $meta)
Definition: FormatMetadata.php:1886
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
FormatMetadata\getExtendedMetadataFromFile
getExtendedMetadataFromFile(File $file)
Get file-based metadata in standardized format.
Definition: FormatMetadata.php:1650
FormatMetadata\formatFraction
formatFraction( $num)
Format a rational number, reducing fractions.
Definition: FormatMetadata.php:1283
FormatMetadata\setSingleLanguage
setSingleLanguage( $val)
Trigger only outputting single language for multilanguage fields.
Definition: FormatMetadata.php:62
FormatExif\$meta
array $meta
Definition: FormatMetadata.php:1881
ContextSource\getLanguage
getLanguage()
Get the Language object.
Definition: ContextSource.php:154
DerivativeContext
An IContextSource implementation which will inherit context from another source but allow individual ...
Definition: DerivativeContext.php:32
FormatMetadata\convertNewsCode
convertNewsCode( $val)
Fetch the human readable version of a news code.
Definition: FormatMetadata.php:1338
File
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition: File.php:50
MWException
MediaWiki exception.
Definition: MWException.php:26
wfMemcKey
wfMemcKey()
Get a cache key.
Definition: GlobalFunctions.php:3571
$out
$out
Definition: UtfNormalGenerate.php:167
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1127
FormatMetadata\formatCoords
formatCoords( $coord, $type)
Format a coordinate value, convert numbers from floating point into degree minute second representati...
Definition: FormatMetadata.php:1413
FormatMetadata\getPriorityLanguages
getPriorityLanguages()
Returns a list of languages (first is best) to use when formatting multilang fields,...
Definition: FormatMetadata.php:1861
FormatMetadata\langItem
langItem( $value, $lang, $default=false, $noHtml=false)
Helper function for creating lists of translations.
Definition: FormatMetadata.php:1166
ContextSource
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
Definition: ContextSource.php:30
TS_ISO_8601
const TS_ISO_8601
ISO 8601 format with no timezone: 1986-02-09T20:00:00Z.
Definition: GlobalFunctions.php:2448
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
wfMessage
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 and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
FormatMetadata\getExtendedMetadataFromHook
getExtendedMetadataFromHook(File $file, array $extendedMetadata, &$maxCacheTime)
Get additional metadata from hooks in standardized format.
Definition: FormatMetadata.php:1714
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4001
$lines
$lines
Definition: router.php:65
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:2514
ContextSource\setContext
setContext(IContextSource $context)
Set the IContextSource object.
Definition: ContextSource.php:57
FormatMetadata\collapseContactInfo
collapseContactInfo( $vals)
Format the contact info field into a single value.
Definition: FormatMetadata.php:1456
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
FormatExif
For compatability with old FormatExif class which some extensions use.
Definition: FormatMetadata.php:1880
$line
$line
Definition: cdb.php:57
TS_MW
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition: GlobalFunctions.php:2431
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:933
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
FormatMetadata\$singleLang
bool $singleLang
Only output a single language for multi-language fields.
Definition: FormatMetadata.php:54
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
$value
$value
Definition: styleTest.css.php:45
Language\fetchLanguageName
static fetchLanguageName( $code, $inLanguage=null, $include='all')
Definition: Language.php:936
FormatMetadata\flattenArrayContentLang
static flattenArrayContentLang( $vals, $type='ul', $noHtml=false, $context=false)
Flatten an array, using the content language for any messages.
Definition: FormatMetadata.php:990
TimestampException
Definition: TimestampException.php:6
$file
if(PHP_SAPI !='cli') $file
Definition: UtfNormalTest2.php:30
FormatMetadata
Format Image metadata values into a human readable form.
Definition: FormatMetadata.php:49
FormatMetadata\exifMsg
exifMsg( $tag, $val, $arg=null, $arg2=null)
Convenience function for getFormattedData()
Definition: FormatMetadata.php:1229
FormatMetadata\resolveMultilangMetadata
resolveMultilangMetadata(&$metadata)
Takes an array returned by the getExtendedMetadata* functions, and resolves multi-language values in ...
Definition: FormatMetadata.php:1785
Language\getFallbacksIncludingSiteLanguage
static getFallbacksIncludingSiteLanguage( $code)
Get the ordered list of fallback languages, ending with the fallback language chain for the site lang...
Definition: Language.php:4161
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
FormatMetadata\sanitizeKeyForXml
sanitizeKeyForXml( $key)
Turns a string into a valid XML identifier.
Definition: FormatMetadata.php:1835
FormatMetadata\flattenArrayReal
flattenArrayReal( $vals, $type='ul', $noHtml=false)
A function to collapse multivalued tags into a single value.
Definition: FormatMetadata.php:1043
File\DELETED_FILE
const DELETED_FILE
Definition: File.php:52
FormatMetadata\sanitizeArrayForXml
sanitizeArrayForXml(&$arr)
Makes sure the given array is a valid API response fragment (can be transformed into XML)
Definition: FormatMetadata.php:1801
Html\rawElement
static rawElement( $element, $attribs=array(), $contents='')
Returns an HTML element in a string.
Definition: Html.php:124
$e
if( $useReadline) $e
Definition: eval.php:66
ForeignAPIFile
Foreign file accessible through api.php requests.
Definition: ForeignAPIFile.php:30
FormatMetadata\getVisibleFields
static getVisibleFields()
Get a list of fields that are visible by default.
Definition: FormatMetadata.php:1574
FormatMetadata\flattenArray
static flattenArray( $vals, $type='ul', $noHtml=false, $context=false)
Flatten an array, using the user language for any messages.
Definition: FormatMetadata.php:1018
FormatMetadata\resolveMultilangValue
resolveMultilangValue( $value)
Turns an XMP-style multilang array into a single value.
Definition: FormatMetadata.php:1747
FormatMetadata\makeFormattedData
makeFormattedData( $tags)
Numbers given by Exif user agents are often magical, that is they should be replaced by a detailed ex...
Definition: FormatMetadata.php:99
$type
$type
Definition: testCompression.php:46