51use Wikimedia\Assert\Assert;
53use Wikimedia\Parsoid\Core\TOCData;
55use Wikimedia\RemexHtml\Serializer\SerializerNode;
117 $target, $html =
null, $customAttribs = [], $query = [], $options = []
120 wfWarn( __METHOD__ .
': Requires $target to be a LinkTarget object.', 2 );
121 return "<!-- ERROR -->$html";
125 $options = (array)$options;
128 $linkRenderer = $services->getLinkRendererFactory()
129 ->createFromLegacyOptions( $options );
131 $linkRenderer = $services->getLinkRenderer();
134 if ( $html !==
null ) {
140 if ( in_array(
'known', $options,
true ) ) {
141 return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
144 if ( in_array(
'broken', $options,
true ) ) {
145 return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
148 if ( in_array(
'noclasses', $options,
true ) ) {
149 return $linkRenderer->makePreloadedLink( $target, $text,
'', $customAttribs, $query );
152 return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
169 $target, $html =
null, $customAttribs = [],
170 $query = [], $options = [
'known' ]
172 return self::link( $target, $html, $customAttribs, $query, $options );
192 public static function makeSelfLinkObj( $nt, $html =
'', $query =
'', $trail =
'', $prefix =
'', $hash =
'' ) {
195 'class' =>
'mw-selflink',
198 $attrs[
'href'] =
'#' . $hash;
199 $attrs[
'class'] =
'mw-selflink-fragment';
202 $attrs[
'class'] =
'mw-selflink selflink';
204 $ret = Html::rawElement(
'a', $attrs, $prefix . $html ) . $trail;
205 if ( !Hooks::runner()->onSelfLinkBegin( $nt, $html, $trail, $prefix, $ret ) ) {
210 $html = htmlspecialchars( $nt->getPrefixedText() );
213 return Html::rawElement(
'a', $attrs, $prefix . $html . $inside ) . $trail;
230 $name = $context->
msg(
'blanknamespace' )->text();
233 getFormattedNsText( $namespace );
235 return $context->
msg(
'invalidtitle-knownnamespace', $namespace, $name,
$title )->text();
238 return $context->
msg(
'invalidtitle-unknownnamespace', $namespace,
$title )->text();
249 private static function fnamePart( $url ) {
250 $basename = strrchr( $url,
'/' );
251 if ( $basename ===
false ) {
254 $basename = substr( $basename, 1 );
271 $alt = self::fnamePart( $url );
274 $success = Hooks::runner()->onLinkerMakeExternalImage( $url, $alt, $img );
276 wfDebug(
"Hook LinkerMakeExternalImage changed the output of external image "
277 .
"with url {$url} and alt text {$alt} to {$img}" );
280 return Html::element(
'img',
327 $file, $frameParams = [], $handlerParams = [], $time =
false,
328 $query =
'', $widthOption =
null
333 if ( !Hooks::runner()->onImageBeforeProduceHTML( $dummy,
$title,
335 $file, $frameParams, $handlerParams, $time,
$res,
337 $parser, $query, $widthOption )
342 if (
$file && !
$file->allowInlineDisplay() ) {
343 wfDebug( __METHOD__ .
': ' .
$title->getPrefixedDBkey() .
' does not allow inline display' );
348 $page = $handlerParams[
'page'] ??
false;
349 if ( !isset( $frameParams[
'align'] ) ) {
350 $frameParams[
'align'] =
'';
352 if ( !isset( $frameParams[
'title'] ) ) {
353 $frameParams[
'title'] =
'';
355 if ( !isset( $frameParams[
'class'] ) ) {
356 $frameParams[
'class'] =
'';
360 $config = $services->getMainConfig();
365 !isset( $handlerParams[
'width'] ) &&
366 !isset( $frameParams[
'manualthumb'] ) &&
367 !isset( $frameParams[
'framed'] )
369 $classes[] =
'mw-default-size';
372 $prefix = $postfix =
'';
374 if ( $enableLegacyMediaDOM ) {
375 if ( $frameParams[
'align'] ==
'center' ) {
376 $prefix =
'<div class="center">';
378 $frameParams[
'align'] =
'none';
382 if (
$file && !isset( $handlerParams[
'width'] ) ) {
383 if ( isset( $handlerParams[
'height'] ) &&
$file->isVectorized() ) {
387 $handlerParams[
'width'] = $svgMaxSize;
389 $handlerParams[
'width'] =
$file->getWidth( $page );
392 if ( isset( $frameParams[
'thumbnail'] )
393 || isset( $frameParams[
'manualthumb'] )
394 || isset( $frameParams[
'framed'] )
395 || isset( $frameParams[
'frameless'] )
396 || !$handlerParams[
'width']
400 if ( $widthOption ===
null || !isset( $thumbLimits[$widthOption] ) ) {
401 $userOptionsLookup = $services->getUserOptionsLookup();
402 $widthOption = $userOptionsLookup->getDefaultOption(
'thumbsize' );
406 if ( isset( $frameParams[
'upright'] ) && $frameParams[
'upright'] == 0 ) {
407 $frameParams[
'upright'] = $thumbUpright;
413 $prefWidth = isset( $frameParams[
'upright'] ) ?
414 round( $thumbLimits[$widthOption] * $frameParams[
'upright'], -1 ) :
415 $thumbLimits[$widthOption];
419 if ( !isset( $handlerParams[
'height'] ) && ( $handlerParams[
'width'] <= 0 ||
420 $prefWidth < $handlerParams[
'width'] ||
$file->isVectorized() ) ) {
421 $handlerParams[
'width'] = $prefWidth;
427 $hasVisibleCaption = isset( $frameParams[
'thumbnail'] ) ||
428 isset( $frameParams[
'manualthumb'] ) ||
429 isset( $frameParams[
'framed'] );
431 if ( $hasVisibleCaption ) {
432 if ( $enableLegacyMediaDOM ) {
437 # Create a thumbnail. Alignment depends on the writing direction of
438 # the page content language (right-aligned for LTR languages,
439 # left-aligned for RTL languages)
440 # If a thumbnail width has not been provided, it is set
441 # to the default user option as specified in Language*.php
442 if ( $frameParams[
'align'] ==
'' ) {
447 $title,
$file, $frameParams, $handlerParams, $time, $query,
452 $rdfaType =
'mw:File';
454 if ( isset( $frameParams[
'frameless'] ) ) {
455 $rdfaType .=
'/Frameless';
457 $srcWidth =
$file->getWidth( $page );
458 # For "frameless" option: do not present an image bigger than the
459 # source (for bitmap-style images). This is the same behavior as the
460 # "thumb" option does it already.
461 if ( $srcWidth && !
$file->mustRender() && $handlerParams[
'width'] > $srcWidth ) {
462 $handlerParams[
'width'] = $srcWidth;
467 if (
$file && isset( $handlerParams[
'width'] ) ) {
468 # Create a resized image, without the additional thumbnail features
469 $thumb =
$file->transform( $handlerParams );
474 $isBadFile =
$file && $thumb &&
477 if ( !$thumb || ( !$enableLegacyMediaDOM && $thumb->isError() ) || $isBadFile ) {
478 $rdfaType =
'mw:Error ' . $rdfaType;
480 if ( $enableLegacyMediaDOM ) {
481 $label = $frameParams[
'title'];
483 if ( $currentExists && !$thumb ) {
484 $label =
wfMessage(
'thumbnail_error',
'' )->text();
485 } elseif ( $thumb && $thumb->isError() ) {
488 'Unknown MediaTransformOutput: ' . get_class( $thumb )
490 $label = $thumb->toText();
492 $label = $frameParams[
'alt'] ??
'';
496 $title, $label,
'',
'',
'', (
bool)$time, $handlerParams, $currentExists
504 if ( isset( $frameParams[
'alt'] ) ) {
505 $params[
'alt'] = $frameParams[
'alt'];
507 $params[
'title'] = $frameParams[
'title'];
508 if ( $enableLegacyMediaDOM ) {
510 'valign' => $frameParams[
'valign'] ??
false,
511 'img-class' => $frameParams[
'class'],
513 if ( isset( $frameParams[
'border'] ) ) {
514 $params[
'img-class'] .= ( $params[
'img-class'] !==
'' ?
' ' :
'' ) .
'thumbborder';
518 $s = $thumb->toHtml( $params );
521 if ( $enableLegacyMediaDOM ) {
522 if ( $frameParams[
'align'] !=
'' ) {
523 $s = Html::rawElement(
525 [
'class' =>
'float' . $frameParams[
'align'] ],
529 return str_replace(
"\n",
' ', $prefix . $s . $postfix );
535 if ( $frameParams[
'align'] !=
'' ) {
538 $classes[] =
"mw-halign-{$frameParams['align']}";
539 $caption = Html::rawElement(
540 'figcaption', [], $frameParams[
'caption'] ??
''
542 } elseif ( isset( $frameParams[
'valign'] ) ) {
546 $classes[] =
"mw-valign-{$frameParams['valign']}";
549 if ( isset( $frameParams[
'border'] ) ) {
550 $classes[] =
'mw-image-border';
553 if ( isset( $frameParams[
'class'] ) ) {
554 $classes[] = $frameParams[
'class'];
559 'typeof' => $rdfaType,
562 $s = Html::rawElement( $wrapper, $attribs, $s . $caption );
564 return str_replace(
"\n",
' ', $s );
577 if ( isset( $frameParams[
'link-url'] ) && $frameParams[
'link-url'] !==
'' ) {
578 $mtoParams[
'custom-url-link'] = $frameParams[
'link-url'];
579 if ( isset( $frameParams[
'link-target'] ) ) {
580 $mtoParams[
'custom-target-link'] = $frameParams[
'link-target'];
583 $extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams[
'link-url'] );
584 foreach ( $extLinkAttrs as $name => $val ) {
586 $mtoParams[
'parser-extlink-' . $name] = $val;
589 } elseif ( isset( $frameParams[
'link-title'] ) && $frameParams[
'link-title'] !==
'' ) {
592 $linkRenderer->normalizeTarget( $frameParams[
'link-title'] )
594 if ( isset( $frameParams[
'link-title-query'] ) ) {
595 $mtoParams[
'custom-title-link-query'] = $frameParams[
'link-title-query'];
597 } elseif ( !empty( $frameParams[
'no-link'] ) ) {
600 $mtoParams[
'desc-link'] =
true;
601 $mtoParams[
'desc-query'] = $query;
620 $params = [], $framed =
false, $manualthumb =
''
628 if ( $manualthumb ) {
629 $frameParams[
'manualthumb'] = $manualthumb;
630 } elseif ( $framed ) {
631 $frameParams[
'framed'] =
true;
632 } elseif ( !isset( $params[
'width'] ) ) {
633 $classes[] =
'mw-default-size';
636 $title,
$file, $frameParams, $params,
false,
'', $classes
653 $time =
false, $query =
'', array $classes = [], ?
Parser $parser =
null
660 $page = $handlerParams[
'page'] ??
false;
661 if ( !isset( $frameParams[
'align'] ) ) {
662 $frameParams[
'align'] =
'';
663 if ( $enableLegacyMediaDOM ) {
664 $frameParams[
'align'] =
'right';
667 if ( !isset( $frameParams[
'caption'] ) ) {
668 $frameParams[
'caption'] =
'';
671 if ( empty( $handlerParams[
'width'] ) ) {
673 $handlerParams[
'width'] = isset( $frameParams[
'upright'] ) ? 130 : 180;
678 $manualthumb =
false;
680 $rdfaType =
'mw:File/Thumb';
684 if ( !isset( $frameParams[
'manualthumb'] ) && isset( $frameParams[
'framed'] ) ) {
685 $rdfaType =
'mw:File/Frame';
687 $outerWidth = $handlerParams[
'width'] + 2;
689 if ( isset( $frameParams[
'manualthumb'] ) ) {
690 # Use manually specified thumbnail
692 if ( $manual_title ) {
693 $manual_img = $services->getRepoGroup()
694 ->findFile( $manual_title );
696 $thumb = $manual_img->getUnscaledThumb( $handlerParams );
700 } elseif ( isset( $frameParams[
'framed'] ) ) {
702 $thumb =
$file->getUnscaledThumb( $handlerParams );
704 $rdfaType =
'mw:File/Frame';
706 # Do not present an image bigger than the source, for bitmap-style images
707 # This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
708 $srcWidth =
$file->getWidth( $page );
709 if ( $srcWidth && !
$file->mustRender() && $handlerParams[
'width'] > $srcWidth ) {
710 $handlerParams[
'width'] = $srcWidth;
712 $thumb =
$file->transform( $handlerParams );
716 $outerWidth = $thumb->getWidth() + 2;
718 $outerWidth = $handlerParams[
'width'] + 2;
723 $linkTitleQuery = [];
726 $linkTitleQuery[
'page'] = $page;
727 # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
728 # So we don't need to pass it here in $query. However, the URL for the
729 # zoom icon still needs it, so we make a unique query for it. See T16771
730 # FIXME: What about "lang" and other querystring parameters
735 && !isset( $frameParams[
'link-title'] )
736 && !isset( $frameParams[
'link-url'] )
737 && !isset( $frameParams[
'no-link'] ) ) {
738 $frameParams[
'link-title'] =
$title;
739 $frameParams[
'link-title-query'] = $linkTitleQuery;
742 if ( $frameParams[
'align'] !=
'' ) {
744 $classes[] =
"mw-halign-{$frameParams['align']}";
747 if ( isset( $frameParams[
'class'] ) ) {
748 $classes[] = $frameParams[
'class'];
753 if ( $enableLegacyMediaDOM ) {
754 $s .=
"<div class=\"thumb t{$frameParams['align']}\">"
755 .
"<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
758 $isBadFile = $exists && $thumb && $parser &&
759 $parser->getBadFileLookup()->isBadFile(
760 $manualthumb ? $manual_title :
$title->getDBkey(),
765 $rdfaType =
'mw:Error ' . $rdfaType;
767 if ( !$enableLegacyMediaDOM ) {
768 $label = $frameParams[
'alt'] ??
'';
771 $title, $label,
'',
'',
'', (
bool)$time, $handlerParams,
false
774 } elseif ( !$thumb || ( !$enableLegacyMediaDOM && $thumb->isError() ) || $isBadFile ) {
775 $rdfaType =
'mw:Error ' . $rdfaType;
776 if ( $enableLegacyMediaDOM ) {
778 $s .=
wfMessage(
'thumbnail_error',
'' )->escaped();
781 $title,
'',
'',
'',
'', (
bool)$time, $handlerParams,
true
785 if ( $thumb && $thumb->isError() ) {
788 'Unknown MediaTransformOutput: ' . get_class( $thumb )
790 $label = $thumb->toText();
791 } elseif ( !$thumb ) {
792 $label =
wfMessage(
'thumbnail_error',
'' )->text();
797 $title, $label,
'',
'',
'', (
bool)$time, $handlerParams,
true
802 if ( !$noscale && !$manualthumb ) {
809 if ( isset( $frameParams[
'alt'] ) ) {
810 $params[
'alt'] = $frameParams[
'alt'];
812 if ( $enableLegacyMediaDOM ) {
814 'img-class' => ( isset( $frameParams[
'class'] ) && $frameParams[
'class'] !==
''
815 ? $frameParams[
'class'] .
' '
816 :
'' ) .
'thumbimage'
820 $s .= $thumb->toHtml( $params );
821 if ( isset( $frameParams[
'framed'] ) ) {
824 $zoomIcon = Html::rawElement(
'div', [
'class' =>
'magnify' ],
825 Html::rawElement(
'a', [
827 'class' =>
'internal',
828 'title' =>
wfMessage(
'thumbnail-more' )->text(),
834 if ( $enableLegacyMediaDOM ) {
835 $s .=
' <div class="thumbcaption">' . $zoomIcon . $frameParams[
'caption'] .
'</div></div></div>';
836 return str_replace(
"\n",
' ', $s );
839 $s .= Html::rawElement(
840 'figcaption', [], $frameParams[
'caption'] ??
''
845 'typeof' => $rdfaType,
848 $s = Html::rawElement(
'figure', $attribs, $s );
850 return str_replace(
"\n",
' ', $s );
863 if ( $responsiveImages && $thumb && !$thumb->isError() ) {
865 $hp15[
'width'] = round( $hp[
'width'] * 1.5 );
867 $hp20[
'width'] = $hp[
'width'] * 2;
868 if ( isset( $hp[
'height'] ) ) {
869 $hp15[
'height'] = round( $hp[
'height'] * 1.5 );
870 $hp20[
'height'] = $hp[
'height'] * 2;
873 $thumb15 =
$file->transform( $hp15 );
874 $thumb20 =
$file->transform( $hp20 );
875 if ( $thumb15 && !$thumb15->isError() && $thumb15->getUrl() !== $thumb->getUrl() ) {
876 $thumb->responsiveUrls[
'1.5'] = $thumb15->getUrl();
878 if ( $thumb20 && !$thumb20->isError() && $thumb20->getUrl() !== $thumb->getUrl() ) {
879 $thumb->responsiveUrls[
'2'] = $thumb20->getUrl();
899 $title, $label =
'', $query =
'', $unused1 =
'', $unused2 =
'',
900 $time =
false, array $handlerParams = [],
bool $currentExists =
false
903 wfWarn( __METHOD__ .
': Requires $title to be a LinkTarget object.' );
904 return "<!-- ERROR -->" . htmlspecialchars( $label );
909 $mainConfig = $services->getMainConfig();
913 if ( $label ==
'' ) {
914 $label =
$title->getPrefixedText();
917 $html = Html::element(
'span', [
918 'class' =>
'mw-broken-media',
920 'data-width' => $handlerParams[
'width'] ??
null,
921 'data-height' => $handlerParams[
'height'] ??
null,
925 $html = htmlspecialchars( $label, ENT_COMPAT );
928 $repoGroup = $services->getRepoGroup();
929 $currentExists = $currentExists ||
930 ( $time && $repoGroup->findFile(
$title ) !== false );
932 if ( ( $uploadMissingFileUrl || $uploadNavigationUrl || $enableUploads )
937 $repoGroup->getLocalRepo()->checkRedirect(
$title )
943 [
'class' =>
'mw-redirect' ],
945 [
'known',
'noclasses' ]
948 return Html::rawElement(
'a', [
949 'href' => self::getUploadUrl(
$title, $query ),
951 'title' =>
$title->getPrefixedText()
959 [
'known',
'noclasses' ]
976 if ( $query !=
'' ) {
980 if ( $uploadMissingFileUrl ) {
984 if ( $uploadNavigationUrl ) {
990 return $upload->getLocalURL( $q );
1004 $title, [
'time' => $time ]
1023 $url =
$file->getUrl();
1024 $class =
'internal';
1030 $alt =
$title->getText();
1031 if ( $html ==
'' ) {
1042 if ( !Hooks::runner()->onLinkerMakeMediaLinkFile(
1045 wfDebug(
"Hook LinkerMakeMediaLinkFile changed the output of link "
1046 .
"with url {$url} and text {$html} to {$ret}" );
1050 return Html::rawElement(
'a', $attribs, $html );
1064 $queryPos = strpos( $name,
'?' );
1065 if ( $queryPos !==
false ) {
1066 $getParams =
wfCgiToArray( substr( $name, $queryPos + 1 ) );
1067 $name = substr( $name, 0, $queryPos );
1072 $slashPos = strpos( $name,
'/' );
1073 if ( $slashPos !==
false ) {
1074 $subpage = substr( $name, $slashPos + 1 );
1075 $name = substr( $name, 0, $slashPos );
1081 $key = strtolower( $name );
1111 $linktype =
'', $attribs = [],
$title =
null
1114 $class =
'external';
1116 $class .=
" $linktype";
1118 if ( isset( $attribs[
'class'] ) && $attribs[
'class'] ) {
1119 $class .=
" {$attribs['class']}";
1121 $attribs[
'class'] = $class;
1124 $text = htmlspecialchars( $text, ENT_COMPAT );
1131 if ( !isset( $attribs[
'rel'] ) || $attribs[
'rel'] ===
'' ) {
1132 $attribs[
'rel'] = $newRel;
1133 } elseif ( $newRel !==
null ) {
1135 $newRels = explode(
' ', $newRel );
1136 $oldRels = explode(
' ', $attribs[
'rel'] );
1137 $combined = array_unique( array_merge( $newRels, $oldRels ) );
1138 $attribs[
'rel'] = implode(
' ', $combined );
1141 $success = Hooks::runner()->onLinkerMakeExternalLink(
1142 $url, $text, $link, $attribs, $linktype );
1144 wfDebug(
"Hook LinkerMakeExternalLink changed the output of link "
1145 .
"with url {$url} and text {$text} to {$link}" );
1148 $attribs[
'href'] = $url;
1149 return Html::rawElement(
'a', $attribs, $text );
1167 $altUserName =
false,
1170 if ( $userName ===
'' || $userName ===
false || $userName ===
null ) {
1171 wfDebug( __METHOD__ .
' received an empty username. Are there database errors ' .
1172 'that need to be fixed?' );
1173 return wfMessage(
'empty-username' )->parse();
1176 $classes =
'mw-userlink';
1177 if ( $userId == 0 ) {
1178 $page = ExternalUserNames::getUserLinkTitle( $userName );
1180 if ( ExternalUserNames::isExternal( $userName ) ) {
1181 $classes .=
' mw-extuserlink';
1182 } elseif ( $altUserName ===
false ) {
1183 $altUserName = IPUtils::prettifyIP( $userName );
1185 $classes .=
' mw-anonuserlink';
1189 $services->getTempUserConfig()->isReservedName( $userName )
1191 $classes .=
' mw-tempuserlink';
1194 $page = TitleValue::tryNew(
NS_USER, strtr( $userName,
' ',
'_' ) );
1200 '<bdi>' . htmlspecialchars( $altUserName !==
false ? $altUserName : $userName ) .
'</bdi>';
1202 if ( isset( $attributes[
'class'] ) ) {
1203 $attributes[
'class'] .=
' ' . $classes;
1205 $attributes[
'class'] = $classes;
1209 ?
self::link( $page, $linkText, $attributes )
1210 : Html::rawElement(
'span', $attributes, $linkText );
1228 $userId, $userText, $redContribsWhenNoEdits =
false, $flags = 0, $edits =
null,
1229 $useParentheses =
true
1231 if ( $userText ===
'' ) {
1232 wfDebug( __METHOD__ .
' received an empty username. Are there database errors ' .
1233 'that need to be fixed?' );
1234 return ' ' .
wfMessage(
'empty-username' )->parse();
1238 $talkable = !( $disableAnonTalk && $userId == 0 );
1240 $addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
1242 if ( $userId == 0 && ExternalUserNames::isExternal( $userText ) ) {
1254 $attribs[
'class'] =
'mw-usertoollinks-contribs';
1255 if ( $redContribsWhenNoEdits ) {
1256 if ( intval( $edits ) === 0 && $edits !== 0 ) {
1258 $edits = $user->getEditCount();
1260 if ( $edits === 0 ) {
1263 $attribs[
'class'] .=
' mw-usertoollinks-contribs-no-edits';
1270 $userCanBlock = RequestContext::getMain()->getAuthority()->isAllowed(
'block' );
1271 if ( $blockable && $userCanBlock ) {
1275 $user = RequestContext::getMain()->getUser();
1276 if ( $addEmailLink && $user->canSendEmail() ) {
1280 Hooks::runner()->onUserToolLinksEdit( $userId, $userText, $items );
1286 if ( $useParentheses ) {
1287 return wfMessage(
'word-separator' )->escaped()
1288 .
'<span class="mw-usertoollinks">'
1289 .
wfMessage(
'parentheses' )->rawParams(
$wgLang->pipeList( $items ) )->escaped()
1294 foreach ( $items as $tool ) {
1295 $tools[] = Html::rawElement(
'span', [], $tool );
1297 return ' <span class="mw-usertoollinks mw-changeslist-links">' .
1298 implode(
' ', $tools ) .
'</span>';
1311 $userId, $userText, $edits =
null, $useParentheses =
true
1323 if ( $userText ===
'' ) {
1324 wfDebug( __METHOD__ .
' received an empty username. Are there database errors ' .
1325 'that need to be fixed?' );
1326 return wfMessage(
'empty-username' )->parse();
1329 $userTalkPage = TitleValue::tryNew(
NS_USER_TALK, strtr( $userText,
' ',
'_' ) );
1330 $moreLinkAttribs = [
'class' =>
'mw-usertoollinks-talk' ];
1331 $linkText =
wfMessage(
'talkpagelinktext' )->escaped();
1333 return $userTalkPage
1334 ?
self::link( $userTalkPage, $linkText, $moreLinkAttribs )
1335 : Html::rawElement(
'span', $moreLinkAttribs, $linkText );
1345 if ( $userText ===
'' ) {
1346 wfDebug( __METHOD__ .
' received an empty username. Are there database errors ' .
1347 'that need to be fixed?' );
1348 return wfMessage(
'empty-username' )->parse();
1352 $moreLinkAttribs = [
'class' =>
'mw-usertoollinks-block' ];
1366 if ( $userText ===
'' ) {
1367 wfLogWarning( __METHOD__ .
' received an empty username. Are there database errors ' .
1368 'that need to be fixed?' );
1369 return wfMessage(
'empty-username' )->parse();
1373 $moreLinkAttribs = [
'class' =>
'mw-usertoollinks-mail' ];
1393 $authority = RequestContext::getMain()->getAuthority();
1395 if ( $revRecord->
isDeleted( RevisionRecord::DELETED_USER ) && $isPublic ) {
1396 $link =
wfMessage(
'rev-deleted-user' )->escaped();
1397 } elseif ( $revRecord->
userCan( RevisionRecord::DELETED_USER, $authority ) ) {
1398 $revUser = $revRecord->
getUser( RevisionRecord::FOR_THIS_USER, $authority );
1400 $revUser ? $revUser->getId() : 0,
1401 $revUser ? $revUser->getName() :
''
1404 $link =
wfMessage(
'rev-deleted-user' )->escaped();
1406 if ( $revRecord->
isDeleted( RevisionRecord::DELETED_USER ) ) {
1408 return '<span class="' . $class .
'">' . $link .
'</span>';
1420 $class =
'history-deleted';
1421 if ( $revisionRecord->
isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
1422 $class .=
' mw-history-suppressed';
1442 $useParentheses =
true
1445 $authority = RequestContext::getMain()->getAuthority();
1447 if ( $revRecord->
userCan( RevisionRecord::DELETED_USER, $authority ) &&
1448 ( !$revRecord->
isDeleted( RevisionRecord::DELETED_USER ) || !$isPublic )
1450 $revUser = $revRecord->
getUser( RevisionRecord::FOR_THIS_USER, $authority );
1451 $userId = $revUser ? $revUser->getId() : 0;
1452 $userText = $revUser ? $revUser->getName() :
'';
1454 if ( $userId || $userText !==
'' ) {
1455 $link = self::userLink(
1459 [
'data-mw-revid' => $revRecord->
getId() ]
1460 ) . self::userToolLinks(
1471 if ( !isset( $link ) ) {
1472 $link =
wfMessage(
'rev-deleted-user' )->escaped();
1475 if ( $revRecord->
isDeleted( RevisionRecord::DELETED_USER ) ) {
1476 $class = self::getRevisionDeletedClass( $revRecord );
1477 return ' <span class="' . $class .
' mw-userlink">' . $link .
'</span>';
1493 return HtmlHelper::modifyElements(
1495 static function ( SerializerNode $node ):
bool {
1496 return $node->name ===
'a' && isset( $node->attrs[
'href'] );
1498 static function ( SerializerNode $node ): SerializerNode {
1499 $node->attrs[
'href'] =
1527 $comment,
$title =
null, $local =
false, $wikiId =
null
1530 return $formatter->format( $comment,
$title, $local, $wikiId );
1553 $comment,
$title =
null, $local =
false, $wikiId =
null
1556 return $formatter->formatLinksUnsafe( $comment,
$title, $local, $wikiId );
1568 # :Foobar -- override special treatment of prefix (images, language links)
1569 # /Foobar -- convert to CurrentPage/Foobar
1570 # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
1571 # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
1572 # ../Foobar -- convert to CurrentPage/Foobar,
1573 # (from CurrentPage/CurrentSubPage)
1574 # ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
1575 # (from CurrentPage/CurrentSubPage)
1577 $ret = $target; #
default return value is no change
1579 # Some namespaces don't allow subpages,
1580 # so only perform processing if subpages are allowed
1583 hasSubpages( $contextTitle->getNamespace() )
1585 $hash = strpos( $target,
'#' );
1586 if ( $hash !==
false ) {
1587 $suffix = substr( $target, $hash );
1588 $target = substr( $target, 0, $hash );
1593 $target = trim( $target );
1595 getPrefixedText( $contextTitle );
1596 # Look at the first character
1597 if ( $target !=
'' && $target[0] ===
'/' ) {
1598 # / at end means we don't want the slash to be shown
1600 $trailingSlashes = preg_match_all(
'%(/+)$%', $target, $m );
1601 if ( $trailingSlashes ) {
1602 $noslash = $target = substr( $target, 1, -strlen( $m[0][0] ) );
1604 $noslash = substr( $target, 1 );
1607 $ret = $contextPrefixedText .
'/' . trim( $noslash ) . $suffix;
1608 if ( $text ===
'' ) {
1609 $text = $target . $suffix;
1610 } #
this might be changed
for ugliness reasons
1612 # check for .. subpage backlinks
1614 $nodotdot = $target;
1615 while ( str_starts_with( $nodotdot,
'../' ) ) {
1617 $nodotdot = substr( $nodotdot, 3 );
1619 if ( $dotdotcount > 0 ) {
1620 $exploded = explode(
'/', $contextPrefixedText );
1621 if ( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
1622 $ret = implode(
'/', array_slice( $exploded, 0, -$dotdotcount ) );
1623 # / at the end means don't show full path
1624 if ( substr( $nodotdot, -1, 1 ) ===
'/' ) {
1625 $nodotdot = rtrim( $nodotdot,
'/' );
1626 if ( $text ===
'' ) {
1627 $text = $nodotdot . $suffix;
1630 $nodotdot = trim( $nodotdot );
1631 if ( $nodotdot !=
'' ) {
1632 $ret .=
'/' . $nodotdot;
1663 $comment,
$title =
null, $local =
false, $wikiId =
null, $useParentheses =
true
1666 ->formatBlock( $comment,
$title, $local, $wikiId, $useParentheses );
1688 $useParentheses =
true
1690 $authority = RequestContext::getMain()->getAuthority();
1692 return $formatter->formatRevision( $revRecord, $authority, $local, $isPublic, $useParentheses );
1702 $stxt =
wfMessage(
'historyempty' )->escaped();
1704 $stxt =
wfMessage(
'nbytes' )->numParams( $size )->escaped();
1706 return "<span class=\"history-size mw-diff-bytes\" data-mw-bytes=\"$size\">$stxt</span>";
1727 return "</li>\n" . str_repeat(
"</ul>\n</li>\n", $level > 0 ? $level : 0 );
1741 public static function tocLine( $linkAnchor, $tocline, $tocnumber, $level, $sectionIndex =
false ) {
1742 $classes =
"toclevel-$level";
1747 if ( $sectionIndex !==
false && $sectionIndex !==
'' && !str_starts_with( $sectionIndex,
"T-" ) ) {
1748 $classes .=
" tocsection-$sectionIndex";
1753 return Html::openElement(
'li', [
'class' => $classes ] )
1754 . Html::rawElement(
'a',
1755 [
'href' =>
"#$linkAnchor" ],
1756 Html::element(
'span', [
'class' =>
'tocnumber' ], $tocnumber )
1758 . Html::rawElement(
'span', [
'class' =>
'toctext' ], $tocline )
1782 $lang ??= RequestContext::getMain()->getLanguage();
1786 return '<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading">'
1787 . Html::element(
'input', [
1788 'type' =>
'checkbox',
1790 'id' =>
'toctogglecheckbox',
1791 'class' =>
'toctogglecheckbox',
1792 'style' =>
'display:none',
1794 . Html::openElement(
'div', [
1795 'class' =>
'toctitle',
1796 'lang' =>
$lang->getHtmlCode(),
1797 'dir' =>
$lang->getDir(),
1799 .
'<h2 id="mw-toc-heading">' .
$title .
'</h2>'
1800 .
'<span class="toctogglespan">'
1801 . Html::label(
'',
'toctogglecheckbox', [
1802 'class' =>
'toctogglelabel',
1807 .
"</ul>\n</div>\n";
1823 $maxTocLevel = $options[
'maxtoclevel'] ??
null;
1824 foreach ( ( $tocData ? $tocData->getSections() : [] ) as $section ) {
1825 $tocLevel = $section->tocLevel;
1826 if ( $maxTocLevel !==
null && $tocLevel < $maxTocLevel ) {
1827 if ( $tocLevel > $lastLevel ) {
1828 $toc .= self::tocIndent();
1829 } elseif ( $tocLevel < $lastLevel ) {
1830 if ( $lastLevel < $maxTocLevel ) {
1831 $toc .= self::tocUnindent(
1832 $lastLevel - $tocLevel );
1834 $toc .= self::tocLineEnd();
1837 $toc .= self::tocLineEnd();
1840 $toc .= self::tocLine( $section->linkAnchor,
1841 $section->line, $section->number,
1842 $tocLevel, $section->index );
1843 $lastLevel = $tocLevel;
1846 if ( $lastLevel < $maxTocLevel && $lastLevel > 0 ) {
1847 $toc .= self::tocUnindent( $lastLevel - 1 );
1849 return self::tocList( $toc,
$lang );
1869 $link, $fallbackAnchor =
false
1871 $anchorEscaped = htmlspecialchars( $anchor, ENT_COMPAT );
1873 if ( $fallbackAnchor !==
false && $fallbackAnchor !== $anchor ) {
1874 $fallbackAnchor = htmlspecialchars( $fallbackAnchor, ENT_COMPAT );
1875 $fallback =
"<span id=\"$fallbackAnchor\"></span>";
1877 return "<h$level$attribs"
1878 .
"$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
1890 $regex = MediaWikiServices::getInstance()->getContentLanguage()->linkTrail();
1892 if ( $trail !==
'' && preg_match( $regex, $trail, $m ) ) {
1893 [ , $inside, $trail ] = $m;
1895 return [ $inside, $trail ];
1933 $context ??= RequestContext::getMain();
1935 $editCount = self::getRollbackEditCount( $revRecord );
1936 if ( $editCount ===
false ) {
1940 $inner = self::buildRollbackLink( $revRecord, $context, $editCount );
1944 if ( !Hooks::runner()->onLinkerGenerateRollbackLink(
1945 $revRecord, $context, $options, $inner ) ) {
1949 if ( !in_array(
'noBrackets', $options,
true ) ) {
1950 $inner = $context->msg(
'brackets' )->rawParams( $inner )->escaped();
1953 if ( MediaWikiServices::getInstance()->getUserOptionsLookup()
1954 ->getBoolOption( $context->getUser(),
'showrollbackconfirmation' )
1956 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1957 $stats->increment(
'rollbackconfirmation.event.load' );
1958 $context->getOutput()->addModules(
'mediawiki.misc-authed-curate' );
1961 return '<span class="mw-rollback-link">' . $inner .
'</span>';
1983 if ( func_num_args() > 1 ) {
1984 wfDeprecated( __METHOD__ .
' with $verify parameter',
'1.40' );
1986 $showRollbackEditCount = MediaWikiServices::getInstance()->getMainConfig()
1987 ->get( MainConfigNames::ShowRollbackEditCount );
1989 if ( !is_int( $showRollbackEditCount ) || !$showRollbackEditCount > 0 ) {
1997 $revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo();
1998 $res =
$dbr->newSelectQueryBuilder()
1999 ->select( [
'rev_user_text' =>
$revQuery[
'fields'][
'rev_user_text'],
'rev_deleted' ] )
2001 ->where( [
'rev_page' => $revRecord->
getPageId() ] )
2003 ->useIndex( [
'revision' =>
'rev_page_timestamp' ] )
2004 ->orderBy( [
'rev_timestamp',
'rev_id' ], SelectQueryBuilder::SORT_DESC )
2005 ->limit( $showRollbackEditCount + 1 )
2006 ->caller( __METHOD__ )
2009 $revUser = $revRecord->
getUser( RevisionRecord::RAW );
2010 $revUserText = $revUser ? $revUser->getName() :
'';
2014 foreach (
$res as $row ) {
2015 if ( $row->rev_user_text != $revUserText ) {
2016 if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT
2017 || $row->rev_deleted & RevisionRecord::DELETED_USER
2030 if ( $editCount <= $showRollbackEditCount && !$moreRevs ) {
2058 $config = MediaWikiServices::getInstance()->getMainConfig();
2059 $showRollbackEditCount = $config->get( MainConfigNames::ShowRollbackEditCount );
2060 $miserMode = $config->get( MainConfigNames::MiserMode );
2062 $disableRollbackEditCountSpecialPage = [
'Recentchanges',
'Watchlist' ];
2064 $context ??= RequestContext::getMain();
2067 $revUser = $revRecord->
getUser();
2068 $revUserText = $revUser ? $revUser->getName() :
'';
2071 'action' =>
'rollback',
2072 'from' => $revUserText,
2073 'token' => $context->getUser()->getEditToken(
'rollback' ),
2077 'data-mw' =>
'interface',
2078 'title' => $context->msg(
'tooltip-rollback' )->text()
2081 $options = [
'known',
'noclasses' ];
2083 if ( $context->getRequest()->getBool(
'bot' ) ) {
2085 $query[
'hidediff'] =
'1';
2086 $query[
'bot'] =
'1';
2090 foreach ( $disableRollbackEditCountSpecialPage as $specialPage ) {
2091 if ( $context->getTitle()->isSpecial( $specialPage ) ) {
2092 $showRollbackEditCount =
false;
2099 $msg = [
'rollbacklink' ];
2100 if ( is_int( $showRollbackEditCount ) && $showRollbackEditCount > 0 ) {
2101 if ( !is_numeric( $editCount ) ) {
2102 $editCount = self::getRollbackEditCount( $revRecord );
2105 if ( $editCount > $showRollbackEditCount ) {
2106 $msg = [
'rollbacklinkcount-morethan',
Message::numParam( $showRollbackEditCount ) ];
2107 } elseif ( $editCount ) {
2112 $html = $context->msg( ...$msg )->parse();
2113 return self::link(
$title, $html, $attrs, $query, $options );
2126 if ( count( $hiddencats ) > 0 ) {
2127 # Construct the HTML
2128 $outText =
'<div class="mw-hiddenCategoriesExplanation">';
2129 $outText .=
wfMessage(
'hiddencategories' )->numParams( count( $hiddencats ) )->parseAsBlock();
2130 $outText .=
"</div><ul>\n";
2132 foreach ( $hiddencats as $titleObj ) {
2133 # If it's hidden, it must exist - no need to check with a LinkBatch
2135 . self::link( $titleObj,
null, [], [],
'known' )
2138 $outText .=
'</ul>';
2146 private static function getContextFromMain() {
2147 $context = RequestContext::getMain();
2169 public static function titleAttrib( $name, $options =
null, array $msgParams = [], $localizer =
null ) {
2170 if ( !$localizer ) {
2171 $localizer = self::getContextFromMain();
2173 $message = $localizer->msg(
"tooltip-$name", $msgParams );
2174 if ( $message->isDisabled() ) {
2177 $tooltip = $message->text();
2178 # Compatibility: formerly some tooltips had [alt-.] hardcoded
2179 $tooltip = preg_replace(
"/ ?\[alt-.\]$/",
'', $tooltip );
2182 $options = (array)$options;
2184 if ( in_array(
'nonexisting', $options ) ) {
2185 $tooltip = $localizer->msg(
'red-link-title', $tooltip ?:
'' )->text();
2187 if ( in_array(
'withaccess', $options ) ) {
2188 $accesskey = self::accesskey( $name, $localizer );
2189 if ( $accesskey !==
false ) {
2191 if ( $tooltip ===
false || $tooltip ===
'' ) {
2192 $tooltip = $localizer->msg(
'brackets', $accesskey )->text();
2194 $tooltip .= $localizer->msg(
'word-separator' )->text();
2195 $tooltip .= $localizer->msg(
'brackets', $accesskey )->text();
2217 public static function accesskey( $name, $localizer =
null ) {
2218 if ( !isset( self::$accesskeycache[$name] ) ) {
2219 if ( !$localizer ) {
2220 $localizer = self::getContextFromMain();
2222 $msg = $localizer->msg(
"accesskey-$name" );
2223 self::$accesskeycache[$name] = $msg->isDisabled() ? false : $msg->plain();
2225 return self::$accesskeycache[$name];
2247 $canHide = $performer->
isAllowed(
'deleterevision' );
2248 $canHideHistory = $performer->
isAllowed(
'deletedhistory' );
2249 if ( !$canHide && !( $revRecord->
getVisibility() && $canHideHistory ) ) {
2253 if ( !$revRecord->
userCan( RevisionRecord::DELETED_RESTRICTED, $performer ) ) {
2254 return self::revDeleteLinkDisabled( $canHide );
2256 $prefixedDbKey = MediaWikiServices::getInstance()->getTitleFormatter()->
2257 getPrefixedDBkey(
$title );
2258 if ( $revRecord->
getId() ) {
2262 'type' =>
'revision',
2263 'target' => $prefixedDbKey,
2264 'ids' => $revRecord->
getId()
2270 'type' =>
'archive',
2271 'target' => $prefixedDbKey,
2275 return self::revDeleteLink(
2277 $revRecord->
isDeleted( RevisionRecord::DELETED_RESTRICTED ),
2294 public static function revDeleteLink( $query = [], $restricted =
false, $delete =
true ) {
2296 $msgKey = $delete ?
'rev-delundel' :
'rev-showdeleted';
2297 $html =
wfMessage( $msgKey )->escaped();
2298 $tag = $restricted ?
'strong' :
'span';
2299 $link = self::link( $sp, $html, [], $query, [
'known',
'noclasses' ] );
2302 [
'class' =>
'mw-revdelundel-link' ],
2303 wfMessage(
'parentheses' )->rawParams( $link )->escaped()
2319 $msgKey = $delete ?
'rev-delundel' :
'rev-showdeleted';
2320 $html =
wfMessage( $msgKey )->escaped();
2321 $htmlParentheses =
wfMessage(
'parentheses' )->rawParams( $html )->escaped();
2322 return Xml::tags(
'span', [
'class' =>
'mw-revdelundel-link' ], $htmlParentheses );
2336 private static function updateWatchstarTooltipMessage(
2337 string &$tooltip, array &$msgParams, $config, $user, $relevantTitle
2339 if ( !$config || !$user || !$relevantTitle ) {
2340 $mainContext = self::getContextFromMain();
2342 $config = $mainContext->getConfig();
2345 $user = $mainContext->getUser();
2347 if ( !$relevantTitle ) {
2348 $relevantTitle = $mainContext->getSkin()->getRelevantTitle();
2352 $isWatchlistExpiryEnabled = $config->get( MainConfigNames::WatchlistExpiry );
2353 if ( !$isWatchlistExpiryEnabled || !$relevantTitle || !$relevantTitle->canExist() ) {
2357 $watchStore = MediaWikiServices::getInstance()->getWatchedItemStore();
2358 $watchedItem = $watchStore->getWatchedItem( $user, $relevantTitle );
2360 $diffInDays = $watchedItem->getExpiryInDays();
2362 if ( $diffInDays ) {
2363 $msgParams = [ $diffInDays ];
2365 $tooltip =
'ca-unwatch-expiring';
2367 $tooltip =
'ca-unwatch-expiring-hours';
2390 array $msgParams = [],
2395 $relevantTitle =
null
2397 $options = (array)$options;
2398 $options[] =
'withaccess';
2399 $tooltipTitle = $name;
2402 if ( !$localizer ) {
2403 $localizer = self::getContextFromMain();
2407 if ( $name ===
'ca-unwatch' ) {
2408 self::updateWatchstarTooltipMessage( $tooltipTitle, $msgParams, $config, $user, $relevantTitle );
2412 'title' => self::titleAttrib( $tooltipTitle, $options, $msgParams, $localizer ),
2413 'accesskey' => self::accesskey( $name, $localizer )
2415 if ( $attribs[
'title'] ===
false ) {
2416 unset( $attribs[
'title'] );
2418 if ( $attribs[
'accesskey'] ===
false ) {
2419 unset( $attribs[
'accesskey'] );
2431 public static function tooltip( $name, $options =
null ) {
2432 $tooltip = self::titleAttrib( $name, $options );
2433 if ( $tooltip ===
false ) {
2436 return Xml::expandAttributes( [
2443class_alias( Linker::class,
'Linker' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode $wgLang
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode $wgTitle
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
An IContextSource implementation which will inherit context from another source but allow individual ...
Class to parse and build external user names.
Implements some public methods and some protected utility functions which are required by multiple ch...
Marks HTML that shouldn't be escaped.
Base class for language-specific code.
A class containing constants representing the names of configuration variables.
const UploadNavigationUrl
Name constant for the UploadNavigationUrl setting, for use with Config::get()
const ThumbUpright
Name constant for the ThumbUpright setting, for use with Config::get()
const EnableUploads
Name constant for the EnableUploads setting, for use with Config::get()
const SVGMaxSize
Name constant for the SVGMaxSize setting, for use with Config::get()
const ResponsiveImages
Name constant for the ResponsiveImages setting, for use with Config::get()
const DisableAnonTalk
Name constant for the DisableAnonTalk setting, for use with Config::get()
const ParserEnableLegacyMediaDOM
Name constant for the ParserEnableLegacyMediaDOM setting, for use with Config::get()
const ThumbLimits
Name constant for the ThumbLimits setting, for use with Config::get()
const UploadMissingFileUrl
Name constant for the UploadMissingFileUrl setting, for use with Config::get()
The Message class deals with fetching and processing of interface message into a variety of formats.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
getTargetLanguage()
Get the target language for the content being parsed.
getBadFileLookup()
Get the BadFileLookup instance that this Parser is using.
static getExternalLinkRel( $url=false, LinkTarget $title=null)
Get the rel attribute for a particular external link.
Group all the pieces relevant to the context of a request into one instance.
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Represents a page (or page fragment) title within MediaWiki.
static newFromId( $id)
Static factory method for creation from a given user ID.
Representation of a pair of user and title for watchlist entries.
getExpiry(?int $style=TS_MW)
When the watched item will expire.
Module of static functions for generating XML.
Interface for configuration instances.
Interface for objects which can provide a MediaWiki context on request.
Interface for localizing messages in MediaWiki.
msg( $key,... $params)
This is the method for getting translated interface messages.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!isset( $args[0])) $lang