28 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
55 use ProtectedHookAccessorTrait;
71 $this->singleLang = $val;
90 $obj->setContext( $context );
93 return $obj->makeFormattedData( $tags );
108 $resolutionunit = !isset( $tags[
'ResolutionUnit'] ) || $tags[
'ResolutionUnit'] == 2 ? 2 : 3;
109 unset( $tags[
'ResolutionUnit'] );
112 unset( $tags[
'HasExtendedXMP'] );
113 unset( $tags[
'AuthorsPosition'] );
114 unset( $tags[
'LocationCreated'] );
115 unset( $tags[
'LocationShown'] );
116 unset( $tags[
'GPSAltitudeRef'] );
118 foreach ( $tags as $tag => &$vals ) {
121 if ( !is_array( $vals ) ) {
126 if ( isset( $vals[
'_type'] ) ) {
127 $type = $vals[
'_type'];
128 unset( $vals[
'_type'] );
135 if ( isset( $tags[$tag][
'_formatted'] ) ) {
137 $tags[$tag][
'_formatted'], $type
143 if ( $tag ===
'GPSTimeStamp' && count( $vals ) === 3 ) {
146 $h = explode(
'/', $vals[0], 2 );
147 $m = explode(
'/', $vals[1], 2 );
148 $s = explode(
'/', $vals[2], 2 );
163 $vals = str_pad( (
string)( (
int)$h[0] / (
int)$h[1] ), 2,
'0', STR_PAD_LEFT )
164 .
':' . str_pad( (
string)( (
int)$m[0] / (
int)$m[1] ), 2,
'0', STR_PAD_LEFT )
165 .
':' . str_pad( (
string)( (
int)$s[0] / (
int)$s[1] ), 2,
'0', STR_PAD_LEFT );
167 $time =
wfTimestamp( TS_MW,
'1971:01:01 ' . $vals );
169 if ( $time && (
int)$time > 0 ) {
178 if ( $tag ===
'Contact' || $tag ===
'CreatorContactInfo' ) {
183 foreach ( $vals as &$val ) {
198 $val = $this->exifMsg( $tag, $val );
207 case 'PhotometricInterpretation':
221 $val = $this->exifMsg( $tag, $val );
240 $val = $this->exifMsg( $tag, $val );
249 case 'PlanarConfiguration':
253 $val = $this->exifMsg( $tag, $val );
263 case 'YCbCrPositioning':
267 $val = $this->exifMsg( $tag, $val );
278 switch ( $resolutionunit ) {
280 $val = $this->exifMsg(
'XYResolution',
'i', $this->formatNum( $val ) );
283 $val = $this->exifMsg(
'XYResolution',
'c', $this->formatNum( $val ) );
299 case 'FlashPixVersion':
302 case 'FlashpixVersion':
303 $val = $this->
literal( (
int)$val / 100 );
310 $val = $this->exifMsg( $tag, $val );
319 case 'ComponentsConfiguration':
328 $val = $this->exifMsg( $tag, $val );
338 case 'DateTimeOriginal':
339 case 'DateTimeDigitized':
340 case 'DateTimeReleased':
341 case 'DateTimeExpires':
344 case 'DateTimeMetadata':
345 case 'FirstPhotoDate':
346 case 'LastPhotoDate':
347 if ( $val ===
'0000:00:00 00:00:00' || $val ===
' : : : : ' ) {
348 $val = $this->
msg(
'exif-unknowndate' )->text();
352 '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/D',
357 if ( $time && (
int)$time > 0 ) {
361 } elseif ( preg_match(
'/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d)$/D', $val ) ) {
366 if ( $time && (
int)$time > 0 ) {
370 } elseif ( preg_match(
'/^(?:\d{4}):(?:\d\d):(?:\d\d)$/D', $val ) ) {
373 . substr( $val, 5, 2 )
374 . substr( $val, 8, 2 )
376 if ( $time && (
int)$time > 0 ) {
385 case 'ExposureProgram':
396 $val = $this->exifMsg( $tag, $val );
405 case 'SubjectDistance':
406 $val = $this->exifMsg( $tag,
'', $this->formatNum( $val ) );
420 $val = $this->exifMsg( $tag, $val );
452 $val = $this->exifMsg( $tag, $val );
463 'fired' => $val & 0b00000001,
464 'return' => ( $val & 0b00000110 ) >> 1,
465 'mode' => ( $val & 0b00011000 ) >> 3,
466 'function' => ( $val & 0b00100000 ) >> 5,
467 'redeye' => ( $val & 0b01000000 ) >> 6,
471 # We do not need to handle unknown values since all are used.
472 foreach ( $flashDecode as $subTag => $subValue ) {
473 # We do not need any message for zeroed values.
474 if ( $subTag !==
'fired' && $subValue === 0 ) {
477 $fullTag = $tag .
'-' . $subTag;
478 $flashMsgs[] = $this->exifMsg( $fullTag, $subValue );
480 $val = $this->
getLanguage()->commaList( $flashMsgs );
483 case 'FocalPlaneResolutionUnit':
486 $val = $this->exifMsg( $tag, $val );
495 case 'SensingMethod':
504 $val = $this->exifMsg( $tag, $val );
516 $val = $this->exifMsg( $tag, $val );
528 $val = $this->exifMsg( $tag, $val );
537 case 'CustomRendered':
549 $val = $this->exifMsg( $tag, $val );
563 $val = $this->exifMsg( $tag, $val );
575 $val = $this->exifMsg( $tag, $val );
584 case 'SceneCaptureType':
590 $val = $this->exifMsg( $tag, $val );
606 $val = $this->exifMsg( $tag, $val );
620 $val = $this->exifMsg( $tag, $val );
634 $val = $this->exifMsg( $tag, $val );
648 $val = $this->exifMsg( $tag, $val );
657 case 'SubjectDistanceRange':
663 $val = $this->exifMsg( $tag, $val );
673 case 'GPSLatitudeRef':
674 case 'GPSDestLatitudeRef':
678 $val = $this->exifMsg(
'GPSLatitude', $val );
687 case 'GPSLongitudeRef':
688 case 'GPSDestLongitudeRef':
692 $val = $this->exifMsg(
'GPSLongitude', $val );
703 $val = $this->exifMsg(
'GPSAltitude',
'below-sealevel', $this->formatNum( -$val, 3 ) );
705 $val = $this->exifMsg(
'GPSAltitude',
'above-sealevel', $this->formatNum( $val, 3 ) );
713 $val = $this->exifMsg( $tag, $val );
722 case 'GPSMeasureMode':
726 $val = $this->exifMsg( $tag, $val );
736 case 'GPSImgDirectionRef':
737 case 'GPSDestBearingRef':
741 $val = $this->exifMsg(
'GPSDirection', $val );
751 case 'GPSDestLatitude':
752 $val = $this->formatCoords( $val,
'latitude' );
755 case 'GPSDestLongitude':
756 $val = $this->formatCoords( $val,
'longitude' );
764 $val = $this->exifMsg(
'GPSSpeed', $val );
773 case 'GPSDestDistanceRef':
778 $val = $this->exifMsg(
'GPSDestDistance', $val );
790 $val = $this->exifMsg( $tag,
'excellent', $this->formatNum( $val ) );
791 } elseif ( $val <= 5 ) {
792 $val = $this->exifMsg( $tag,
'good', $this->formatNum( $val ) );
793 } elseif ( $val <= 10 ) {
794 $val = $this->exifMsg( $tag,
'moderate', $this->formatNum( $val ) );
795 } elseif ( $val <= 20 ) {
796 $val = $this->exifMsg( $tag,
'fair', $this->formatNum( $val ) );
798 $val = $this->exifMsg( $tag,
'poor', $this->formatNum( $val ) );
807 $val = $this->exifMsg( $tag,
'', $this->
literal( $val ) );
811 if ( is_array( $val ) ) {
812 if ( count( $val ) > 1 ) {
815 'exif-software-version-value',
821 $val = $this->exifMsg( $tag,
'', $this->
literal( $val[0] ) );
824 $val = $this->exifMsg( $tag,
'', $this->
literal( $val ) );
830 $val = $this->
msg(
'exif-exposuretime-format',
831 $this->formatFraction( $val ), $this->formatNum( $val ) )->text();
833 case 'ISOSpeedRatings':
837 if ( $val ===
'65535' ) {
838 $val = $this->exifMsg( $tag,
'overflow' );
840 $val = $this->formatNum( $val );
844 $val = $this->
msg(
'exif-fnumber-format',
845 $this->formatNum( $val ) )->text();
849 case 'FocalLengthIn35mmFilm':
850 $val = $this->
msg(
'exif-focallength-format',
851 $this->formatNum( $val ) )->text();
854 case 'MaxApertureValue':
855 if ( strpos( $val,
'/' ) !==
false ) {
857 [ $n, $d ] = explode(
'/', $val, 2 );
858 if ( is_numeric( $n ) && is_numeric( $d ) ) {
859 $val = (int)$n / (
int)$d;
862 if ( is_numeric( $val ) ) {
863 $fNumber = 2 ** ( $val / 2 );
864 if ( is_finite( $fNumber ) ) {
865 $val = $this->
msg(
'exif-maxaperturevalue-value',
866 $this->formatNum( $val ),
867 $this->formatNum( $fNumber, 2 )
876 switch ( strtolower( $val ) ) {
896 $val = $this->exifMsg(
905 case 'SubjectNewsCode':
911 $val = $this->convertNewsCode( $val );
917 if ( $val === 0 || $val === 9 ) {
919 } elseif ( $val < 5 && $val > 1 ) {
921 } elseif ( $val === 5 ) {
923 } elseif ( $val <= 8 && $val > 5 ) {
927 if ( $urgency !==
'' ) {
928 $val = $this->exifMsg(
'urgency',
929 $urgency, $this->
literal( $val )
937 case 'OriginalImageHeight':
938 case 'OriginalImageWidth':
939 case 'PixelXDimension':
940 case 'PixelYDimension':
943 $val = $this->formatNum( $val ) .
' ' . $this->
msg(
'unit-pixel' )->text();
953 case 'ImageDescription':
957 case 'RelatedSoundFile':
958 case 'ImageUniqueID':
959 case 'SpectralSensitivity':
960 case 'GPSSatellites':
964 case 'WorldRegionDest':
966 case 'CountryCodeDest':
967 case 'ProvinceOrStateDest':
969 case 'SublocationDest':
970 case 'WorldRegionCreated':
971 case 'CountryCreated':
972 case 'CountryCodeCreated':
973 case 'ProvinceOrStateCreated':
975 case 'SublocationCreated':
977 case 'SpecialInstructions':
982 case 'FixtureIdentifier':
984 case 'LocationDestCode':
986 case 'JPEGFileComment':
987 case 'iimSupplementalCategory':
988 case 'OriginalTransmissionRef':
990 case 'dc-contributor':
999 case 'CameraOwnerName':
1002 case 'RightsCertificate':
1003 case 'CopyrightOwner':
1005 case 'WebStatement':
1006 case 'OriginalDocumentID':
1008 case 'MorePermissionsUrl':
1009 case 'AttributionUrl':
1010 case 'PreferredAttributionName':
1011 case 'PNGFileComment':
1013 case 'ContentWarning':
1014 case 'GIFFileComment':
1016 case 'IntellectualGenre':
1018 case 'OrganisationInImage':
1019 case 'PersonInImage':
1020 case 'CaptureSoftware':
1021 case 'GPSAreaInformation':
1022 case 'GPSProcessingMethod':
1023 case 'StitchingSoftware':
1025 case 'SubSecTimeOriginal':
1026 case 'SubSecTimeDigitized':
1027 $val = $this->
literal( $val );
1030 case 'ProjectionType':
1032 case 'equirectangular':
1033 $val = $this->exifMsg( $tag, $val );
1036 $val = $this->
literal( $val );
1045 $val = $this->exifMsg( $tag, $val );
1048 $val = $this->
literal( $val );
1053 case 'UsePanoramaViewer':
1054 case 'ExposureLockUsed':
1058 $val = $this->exifMsg( $tag, $val );
1061 $val = $this->
literal( $val );
1066 if ( $val ===
'-1' ) {
1067 $val = $this->exifMsg( $tag,
'rejected' );
1069 $val = $this->formatNum( $val );
1073 case 'LanguageCode':
1074 $lang = MediaWikiServices::getInstance()
1075 ->getLanguageNameUtils()
1076 ->getLanguageName( strtolower( $val ), $this->
getLanguage()->getCode() );
1077 $val = $this->
literal( $lang ?: $val );
1081 $val = $this->formatNum( $val,
false, $tag );
1110 if ( !is_array( $vals ) ) {
1114 if ( isset( $vals[
'_type'] ) ) {
1115 $type = $vals[
'_type'];
1116 unset( $vals[
'_type'] );
1119 if ( count( $vals ) === 1 && $type !==
'lang' && isset( $vals[0] ) ) {
1122 if ( count( $vals ) === 0 ) {
1123 wfDebug( __METHOD__ .
" metadata array with 0 elements!" );
1128 $containsNestedArrays = in_array(
true, array_map(
'is_array', $vals ),
true );
1129 if ( $containsNestedArrays ) {
1130 wfLogWarning( __METHOD__ .
': Invalid $vals, contains nested arrays: ' . json_encode( $vals ) );
1147 $defaultItem =
false;
1148 $defaultLang =
false;
1154 if ( isset( $vals[
'x-default'] ) ) {
1155 $defaultItem = $vals[
'x-default'];
1156 unset( $vals[
'x-default'] );
1158 foreach ( $priorityLanguages as $pLang ) {
1159 if ( isset( $vals[$pLang] ) ) {
1161 if ( $vals[$pLang] === $defaultItem ) {
1162 $defaultItem =
false;
1165 $content .= $this->langItem( $vals[$pLang], $pLang, $isDefault, $noHtml );
1167 unset( $vals[$pLang] );
1169 if ( $this->singleLang ) {
1170 return Html::rawElement(
'span', [
'lang' => $pLang ], $vals[$pLang] );
1176 foreach ( $vals as $lang => $item ) {
1177 if ( $item === $defaultItem ) {
1178 $defaultLang = $lang;
1181 $content .= $this->langItem( $item, $lang,
false, $noHtml );
1182 if ( $this->singleLang ) {
1183 return Html::rawElement(
'span', [
'lang' => $lang ], $item );
1186 if ( $defaultItem !==
false ) {
1187 $content = $this->langItem( $defaultItem, $defaultLang,
true, $noHtml ) .
$content;
1188 if ( $this->singleLang ) {
1189 return $defaultItem;
1196 return '<ul class="metadata-langlist">' .
$content .
'</ul>';
1199 return "\n#" . implode(
"\n#", $vals );
1202 return "<ol><li>" . implode(
"</li>\n<li>", $vals ) .
'</li></ol>';
1206 return "\n*" . implode(
"\n*", $vals );
1209 return "<ul><li>" . implode(
"</li>\n<li>", $vals ) .
'</li></ul>';
1222 private function langItem( $value, $lang, $default =
false, $noHtml =
false ) {
1223 if ( $lang ===
false && $default ===
false ) {
1224 throw new BadMethodCallException(
'$lang and $default cannot both be false.' );
1228 $wrappedValue = $this->
literal( $value );
1230 $wrappedValue =
'<span class="mw-metadata-lang-value">' . $this->
literal( $value ) .
'</span>';
1233 if ( $lang ===
false ) {
1234 $msg = $this->
msg(
'metadata-langitem-default', $wrappedValue );
1236 return $msg->text() .
"\n\n";
1239 return '<li class="mw-metadata-lang-default">' . $msg->text() .
"</li>\n";
1242 $lowLang = strtolower( $lang );
1243 $languageNameUtils = MediaWikiServices::getInstance()->getLanguageNameUtils();
1244 $langName = $languageNameUtils->getLanguageName( $lowLang );
1245 if ( $langName ===
'' ) {
1247 $langPrefix = explode(
'-', $lowLang, 2 )[0];
1248 $langName = $languageNameUtils->getLanguageName( $langPrefix );
1249 if ( $langName ===
'' ) {
1256 $msg = $this->
msg(
'metadata-langitem', $wrappedValue, $langName, $lang );
1258 return '*' . $msg->text();
1261 $item =
'<li class="mw-metadata-lang-code-' . $lang;
1263 $item .=
' mw-metadata-lang-default';
1265 $item .=
'" lang="' . $lang .
'">';
1266 $item .= $msg->text();
1280 if ( $val === null ) {
1287 return htmlspecialchars( $val );
1299 private function exifMsg( $tag, $val, $arg =
null, $arg2 =
null ) {
1300 if ( $val ===
'' ) {
1305 MediaWikiServices::getInstance()->getContentLanguage()->lc(
"exif-$tag-$val" ),
1320 private function formatNum( $num, $round =
false, $tagName =
null ) {
1322 if ( is_array( $num ) ) {
1324 foreach ( $num as $number ) {
1325 $out[] = $this->formatNum( $number, $round, $tagName );
1328 return $this->getLanguage()->commaList( $out );
1330 if ( is_numeric( $num ) ) {
1331 if ( $round !==
false ) {
1332 $num = round( $num, $round );
1334 return $this->getLanguage()->formatNum( $num );
1337 if ( preg_match(
'/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
1338 if ( $m[2] !== 0 ) {
1339 $newNum = (int)$m[1] / (
int)$m[2];
1340 if ( $round !==
false ) {
1341 $newNum = round( $newNum, $round );
1347 return $this->getLanguage()->formatNum( $newNum );
1349 # T267370: there are a lot of strange EXIF tags floating around.
1350 LoggerFactory::getInstance(
'formatnum' )->warning(
1351 'FormatMetadata::formatNum with non-numeric value',
1357 return $this->literal( $num );
1366 private function formatFraction( $num ) {
1368 if ( preg_match(
'/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
1369 $numerator = (int)$m[1];
1370 $denominator = (int)$m[2];
1371 $gcd = $this->gcd( abs( $numerator ), $denominator );
1374 return $this->formatNum( $numerator / $gcd ) .
'/' . $this->formatNum( $denominator / $gcd );
1378 return $this->formatNum( $num );
1388 private function gcd( $a, $b ) {
1398 $remainder = $a % $b;
1420 private function convertNewsCode( $val ) {
1421 if ( !preg_match(
'/^\d{8}$/D', $val ) ) {
1426 switch ( substr( $val, 0, 2 ) ) {
1479 if ( $cat !==
'' ) {
1480 $catMsg = $this->exifMsg(
'iimcategory', $cat );
1481 $val = $this->exifMsg(
'subjectnewscode',
'', $this->literal( $val ), $catMsg );
1495 private function formatCoords( $coord,
string $type ) {
1496 if ( !is_numeric( $coord ) ) {
1497 wfDebugLog(
'exif', __METHOD__ .
": \"$coord\" is not a number" );
1498 return $this->literal( (
string)$coord );
1504 if ( $type ===
'latitude' ) {
1506 } elseif ( $type ===
'longitude' ) {
1510 $nCoord = (float)$coord;
1511 if ( $type ===
'latitude' ) {
1513 } elseif ( $type ===
'longitude' ) {
1518 $deg = floor( $nCoord );
1519 $min = floor( ( $nCoord - $deg ) * 60 );
1520 $sec = round( ( ( $nCoord - $deg ) * 60 - $min ) * 60, 2 );
1522 $deg = $this->formatNum( $deg );
1523 $min = $this->formatNum( $min );
1524 $sec = $this->formatNum( $sec );
1527 return $this->msg(
'exif-coordinate-format', $deg, $min, $sec, $ref, $this->literal( $coord ) )->text();
1545 if ( !( isset( $vals[
'CiAdrExtadr'] )
1546 || isset( $vals[
'CiAdrCity'] )
1547 || isset( $vals[
'CiAdrCtry'] )
1548 || isset( $vals[
'CiEmailWork'] )
1549 || isset( $vals[
'CiTelWork'] )
1550 || isset( $vals[
'CiAdrPcode'] )
1551 || isset( $vals[
'CiAdrRegion'] )
1552 || isset( $vals[
'CiUrlWork'] )
1561 foreach ( $vals as &$val ) {
1562 $val = $this->literal( $val );
1565 return $this->flattenArrayReal( $vals );
1571 $url = $tel = $street = $city = $country =
'';
1572 $email = $postal = $region =
'';
1581 if ( isset( $vals[
'CiAdrExtadr'] ) ) {
1584 $street =
'<span class="extended-address">'
1586 $vals[
'CiAdrExtadr'] )
1589 if ( isset( $vals[
'CiAdrCity'] ) ) {
1590 $city =
'<span class="locality">'
1591 . $this->literal( $vals[
'CiAdrCity'] )
1594 if ( isset( $vals[
'CiAdrCtry'] ) ) {
1595 $country =
'<span class="country-name">'
1596 . $this->literal( $vals[
'CiAdrCtry'] )
1599 if ( isset( $vals[
'CiEmailWork'] ) ) {
1602 $splitEmails = explode(
"\n", $vals[
'CiEmailWork'] );
1603 foreach ( $splitEmails as $e1 ) {
1605 foreach ( explode(
',', $e1 ) as $e2 ) {
1606 $finalEmail = trim( $e2 );
1607 if ( $finalEmail ===
',' || $finalEmail ===
'' ) {
1610 if ( strpos( $finalEmail,
'<' ) !==
false ) {
1613 $emails[] = $this->literal( $finalEmail );
1615 $emails[] =
'[mailto:'
1617 .
' <span class="email">'
1618 . $this->literal( $finalEmail )
1623 $email = implode(
', ', $emails );
1625 if ( isset( $vals[
'CiTelWork'] ) ) {
1626 $tel =
'<span class="tel">'
1627 . $this->literal( $vals[
'CiTelWork'] )
1630 if ( isset( $vals[
'CiAdrPcode'] ) ) {
1631 $postal =
'<span class="postal-code">'
1632 . $this->literal( $vals[
'CiAdrPcode'] )
1635 if ( isset( $vals[
'CiAdrRegion'] ) ) {
1637 $region =
'<span class="region">'
1638 . $this->literal( $vals[
'CiAdrRegion'] )
1641 if ( isset( $vals[
'CiUrlWork'] ) ) {
1642 $url =
'<span class="url">'
1643 . $this->literal( $vals[
'CiUrlWork'] )
1647 return $this->msg(
'exif-contact-value', $email, $url,
1648 $street, $city, $region, $postal, $country, $tel )->text();
1659 $lines = explode(
"\n",
wfMessage(
'metadata-fields' )->inContentLanguage()->text() );
1660 foreach (
$lines as $line ) {
1662 if ( preg_match(
'/^\\*\s*(.*?)\s*$/', $line,
$matches ) ) {
1666 $fields = array_map(
'strtolower', $fields );
1679 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1686 $cacheKey = $cache->makeKey(
1687 'getExtendedMetadata',
1688 $this->getLanguage()->getCode(),
1689 (
int)$this->singleLang,
1693 $cachedValue = $cache->get( $cacheKey );
1696 && $this->getHookRunner()->onValidateExtendedMetadataCache( $cachedValue[
'timestamp'],
$file )
1698 $extendedMetadata = $cachedValue[
'data'];
1701 $fileMetadata = $this->getExtendedMetadataFromFile(
$file );
1702 $extendedMetadata = $this->getExtendedMetadataFromHook(
$file, $fileMetadata, $maxCacheTime );
1703 if ( $this->singleLang ) {
1704 $this->resolveMultilangMetadata( $extendedMetadata );
1706 $this->discardMultipleValues( $extendedMetadata );
1711 $this->sanitizeArrayForAPI( $extendedMetadata );
1712 $valueToCache = [
'data' => $extendedMetadata,
'timestamp' =>
wfTimestampNow() ];
1713 $cache->set( $cacheKey, $valueToCache, $maxCacheTime );
1716 return $extendedMetadata;
1734 return $file->getExtendedMetadata() ?: [];
1742 'value' => $uploadDate,
1743 'source' =>
'mediawiki-metadata',
1747 $title =
$file->getTitle();
1749 $text = $title->getText();
1750 $pos = strrpos( $text,
'.' );
1753 $name = substr( $text, 0, $pos );
1758 $fileMetadata[
'ObjectName'] = [
1760 'source' =>
'mediawiki-metadata',
1764 return $fileMetadata;
1780 $this->getHookRunner()->onGetExtendedMetadata(
1788 $visible = array_fill_keys( self::getVisibleFields(),
true );
1789 foreach ( $extendedMetadata as $key => $value ) {
1790 if ( !isset( $visible[strtolower( $key )] ) ) {
1791 $extendedMetadata[$key][
'hidden'] =
'';
1795 return $extendedMetadata;
1809 || !isset( $value[
'_type'] )
1810 || $value[
'_type'] !==
'lang'
1816 $priorityLanguages = $this->getPriorityLanguages();
1817 foreach ( $priorityLanguages as $lang ) {
1818 if ( isset( $value[$lang] ) ) {
1819 return $value[$lang];
1824 if ( isset( $value[
'x-default'] ) ) {
1825 return $value[
'x-default'];
1829 unset( $value[
'_type'] );
1831 return reset( $value );
1848 if ( !is_array( $value ) ) {
1851 if ( isset( $value[
'_type'] ) && $value[
'_type'] ===
'lang' ) {
1854 foreach ( $value as $k => $v ) {
1855 $newValue[$k] = $this->resolveMultivalueValue( $v );
1860 $v = reset( $value );
1861 if ( key( $value ) ===
'_type' ) {
1862 $v = next( $value );
1874 if ( !is_array( $metadata ) ) {
1877 foreach ( $metadata as &$field ) {
1878 if ( isset( $field[
'value'] ) ) {
1879 $field[
'value'] = $this->resolveMultilangValue( $field[
'value'] );
1891 if ( !is_array( $metadata ) ) {
1894 foreach ( $metadata as $key => &$field ) {
1895 if ( $key ===
'Software' || $key ===
'Contact' ) {
1900 if ( isset( $field[
'value'] ) ) {
1901 $field[
'value'] = $this->resolveMultivalueValue( $field[
'value'] );
1911 if ( !is_array( $arr ) ) {
1916 foreach ( $arr as $key => &$value ) {
1917 $sanitizedKey = $this->sanitizeKeyForAPI( $key );
1918 if ( $sanitizedKey !== $key ) {
1919 if ( isset( $arr[$sanitizedKey] ) ) {
1924 $sanitizedKey .= $counter;
1927 $arr[$sanitizedKey] = $arr[$key];
1928 unset( $arr[$key] );
1930 if ( is_array( $value ) ) {
1931 $this->sanitizeArrayForAPI( $value );
1937 $keys = array_filter( array_keys( $arr ), [ ApiResult::class,
'isMetadataKey' ] );
1953 $key = preg_replace(
'/[^a-zA-z0-9_:.\-]/',
'', $key );
1955 $key = preg_replace(
'/^[\d\-.]+/',
'', $key );
1957 if ( $key ===
'' ) {
1960 } elseif ( $key ===
'_element' ) {
1974 $priorityLanguages = MediaWikiServices::getInstance()
1975 ->getLanguageFallback()
1976 ->getAllIncludingSiteLanguage( $this->getLanguage()->getCode() );
1977 $priorityLanguages = array_merge(
1978 (array)$this->getLanguage()->getCode(),
1979 $priorityLanguages[0],
1980 $priorityLanguages[1]
1983 return $priorityLanguages;
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined('MW_SETUP_CALLBACK'))
static setPreserveKeysList(array &$arr, $names)
Preserve specified keys.
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Implements some public methods and some protected utility functions which are required by multiple ch...
Foreign file accessible through api.php requests.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!file_exists( $CREDITS)) $lines