/** * Wrap and format the file's comment block, if the current * user is allowed to view it. * * @return string HTML */ protected function getComment() { if ($this->file->userCan(File::DELETED_COMMENT, $this->list->getUser())) { $block = Linker::commentBlock($this->file->getDescription()); } else { $block = ' ' . $this->list->msg('rev-deleted-comment')->escaped(); } if ($this->file->isDeleted(File::DELETED_COMMENT)) { return "<span class=\"history-deleted\">{$block}</span>"; } return $block; }
/** * @param File $file * @param bool $dumpContents * @return string */ function writeUpload($file, $dumpContents = false) { if ($file->isOld()) { $archiveName = " " . Xml::element('archivename', null, $file->getArchiveName()) . "\n"; } else { $archiveName = ''; } if ($dumpContents) { $be = $file->getRepo()->getBackend(); # Dump file as base64 # Uses only XML-safe characters, so does not need escaping # @todo Too bad this loads the contents into memory (script might swap) $contents = ' <contents encoding="base64">' . chunk_split(base64_encode($be->getFileContents(array('src' => $file->getPath())))) . " </contents>\n"; } else { $contents = ''; } if ($file->isDeleted(File::DELETED_COMMENT)) { $comment = Xml::element('comment', array('deleted' => 'deleted')); } else { $comment = Xml::elementClean('comment', null, $file->getDescription()); } return " <upload>\n" . $this->writeTimestamp($file->getTimestamp()) . $this->writeContributor($file->getUser('id'), $file->getUser('text')) . " " . $comment . "\n" . " " . Xml::element('filename', null, $file->getName()) . "\n" . $archiveName . " " . Xml::element('src', null, $file->getCanonicalURL()) . "\n" . " " . Xml::element('size', null, $file->getSize()) . "\n" . " " . Xml::element('sha1base36', null, $file->getSha1()) . "\n" . " " . Xml::element('rel', null, $file->getRel()) . "\n" . $contents . " </upload>\n"; }
/** * Fetch file upload comment if it's available to this user * * @param File|ArchivedFile $file * @return string HTML fragment */ function getFileComment($file) { if (!$file->userCan(File::DELETED_COMMENT, $this->getUser())) { return '<span class="history-deleted"><span class="comment">' . $this->msg('rev-deleted-comment')->escaped() . '</span></span>'; } $link = Linker::commentBlock($file->getRawDescription()); if ($file->isDeleted(File::DELETED_COMMENT)) { $link = '<span class="history-deleted">' . $link . '</span>'; } return $link; }
/** * Get result information for an image revision * * @param File $file * @param array $prop Array of properties to get (in the keys) * @param ApiResult $result * @param array $thumbParams Containing 'width' and 'height' items, or null * @param array|bool|string $opts Options for data fetching. * This is an array consisting of the keys: * 'version': The metadata version for the metadata option * 'language': The language for extmetadata property * 'multilang': Return all translations in extmetadata property * 'revdelUser': User to use when checking whether to show revision-deleted fields. * @return array Result array */ static function getInfo($file, $prop, $result, $thumbParams = null, $opts = false) { global $wgContLang; $anyHidden = false; if (!$opts || is_string($opts)) { $opts = array('version' => $opts ?: 'latest', 'language' => $wgContLang, 'multilang' => false, 'extmetadatafilter' => array(), 'revdelUser' => null); } $version = $opts['version']; $vals = array(ApiResult::META_TYPE => 'assoc'); // Timestamp is shown even if the file is revdelete'd in interface // so do same here. if (isset($prop['timestamp'])) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $file->getTimestamp()); } // Handle external callers who don't pass revdelUser if (isset($opts['revdelUser']) && $opts['revdelUser']) { $revdelUser = $opts['revdelUser']; $canShowField = function ($field) use($file, $revdelUser) { return $file->userCan($field, $revdelUser); }; } else { $canShowField = function ($field) use($file) { return !$file->isDeleted($field); }; } $user = isset($prop['user']); $userid = isset($prop['userid']); if ($user || $userid) { if ($file->isDeleted(File::DELETED_USER)) { $vals['userhidden'] = true; $anyHidden = true; } if ($canShowField(File::DELETED_USER)) { if ($user) { $vals['user'] = $file->getUser(); } if ($userid) { $vals['userid'] = $file->getUser('id'); } if (!$file->getUser('id')) { $vals['anon'] = true; } } } // This is shown even if the file is revdelete'd in interface // so do same here. if (isset($prop['size']) || isset($prop['dimensions'])) { $vals['size'] = intval($file->getSize()); $vals['width'] = intval($file->getWidth()); $vals['height'] = intval($file->getHeight()); $pageCount = $file->pageCount(); if ($pageCount !== false) { $vals['pagecount'] = $pageCount; } // length as in how many seconds long a video is. $length = $file->getLength(); if ($length) { // Call it duration, because "length" can be ambiguous. $vals['duration'] = (double) $length; } } $pcomment = isset($prop['parsedcomment']); $comment = isset($prop['comment']); if ($pcomment || $comment) { if ($file->isDeleted(File::DELETED_COMMENT)) { $vals['commenthidden'] = true; $anyHidden = true; } if ($canShowField(File::DELETED_COMMENT)) { if ($pcomment) { $vals['parsedcomment'] = Linker::formatComment($file->getDescription(File::RAW), $file->getTitle()); } if ($comment) { $vals['comment'] = $file->getDescription(File::RAW); } } } $canonicaltitle = isset($prop['canonicaltitle']); $url = isset($prop['url']); $sha1 = isset($prop['sha1']); $meta = isset($prop['metadata']); $extmetadata = isset($prop['extmetadata']); $commonmeta = isset($prop['commonmetadata']); $mime = isset($prop['mime']); $mediatype = isset($prop['mediatype']); $archive = isset($prop['archivename']); $bitdepth = isset($prop['bitdepth']); $uploadwarning = isset($prop['uploadwarning']); if ($uploadwarning) { $vals['html'] = SpecialUpload::getExistsWarning(UploadBase::getExistsWarning($file)); } if ($file->isDeleted(File::DELETED_FILE)) { $vals['filehidden'] = true; $anyHidden = true; } if ($anyHidden && $file->isDeleted(File::DELETED_RESTRICTED)) { $vals['suppressed'] = true; } if (!$canShowField(File::DELETED_FILE)) { //Early return, tidier than indenting all following things one level return $vals; } if ($canonicaltitle) { $vals['canonicaltitle'] = $file->getTitle()->getPrefixedText(); } if ($url) { if (!is_null($thumbParams)) { $mto = $file->transform($thumbParams); self::$transformCount++; if ($mto && !$mto->isError()) { $vals['thumburl'] = wfExpandUrl($mto->getUrl(), PROTO_CURRENT); // bug 23834 - If the URL's are the same, we haven't resized it, so shouldn't give the wanted // thumbnail sizes for the thumbnail actual size if ($mto->getUrl() !== $file->getUrl()) { $vals['thumbwidth'] = intval($mto->getWidth()); $vals['thumbheight'] = intval($mto->getHeight()); } else { $vals['thumbwidth'] = intval($file->getWidth()); $vals['thumbheight'] = intval($file->getHeight()); } if (isset($prop['thumbmime']) && $file->getHandler()) { list(, $mime) = $file->getHandler()->getThumbType($mto->getExtension(), $file->getMimeType(), $thumbParams); $vals['thumbmime'] = $mime; } } elseif ($mto && $mto->isError()) { $vals['thumberror'] = $mto->toText(); } } $vals['url'] = wfExpandUrl($file->getFullURL(), PROTO_CURRENT); $vals['descriptionurl'] = wfExpandUrl($file->getDescriptionUrl(), PROTO_CURRENT); } if ($sha1) { $vals['sha1'] = wfBaseConvert($file->getSha1(), 36, 16, 40); } if ($meta) { wfSuppressWarnings(); $metadata = unserialize($file->getMetadata()); wfRestoreWarnings(); if ($metadata && $version !== 'latest') { $metadata = $file->convertMetadataVersion($metadata, $version); } $vals['metadata'] = $metadata ? self::processMetaData($metadata, $result) : null; } if ($commonmeta) { $metaArray = $file->getCommonMetaArray(); $vals['commonmetadata'] = $metaArray ? self::processMetaData($metaArray, $result) : array(); } if ($extmetadata) { // Note, this should return an array where all the keys // start with a letter, and all the values are strings. // Thus there should be no issue with format=xml. $format = new FormatMetadata(); $format->setSingleLanguage(!$opts['multilang']); $format->getContext()->setLanguage($opts['language']); $extmetaArray = $format->fetchExtendedMetadata($file); if ($opts['extmetadatafilter']) { $extmetaArray = array_intersect_key($extmetaArray, array_flip($opts['extmetadatafilter'])); } $vals['extmetadata'] = $extmetaArray; } if ($mime) { $vals['mime'] = $file->getMimeType(); } if ($mediatype) { $vals['mediatype'] = $file->getMediaType(); } if ($archive && $file->isOld()) { $vals['archivename'] = $file->getArchiveName(); } if ($bitdepth) { $vals['bitdepth'] = $file->getBitDepth(); } return $vals; }
/** * @param File $file * @returns string */ private function fileLine($file) { global $wgLang, $wgTitle; $target = $this->page->getPrefixedText(); $date = $wgLang->timeanddate($file->getTimestamp(), true); $del = ''; # Hidden files... if ($file->isDeleted(File::DELETED_FILE)) { $del = ' <tt>' . wfMsgHtml('deletedrev') . '</tt>'; if (!$file->userCan(File::DELETED_FILE)) { $pageLink = $date; } else { $pageLink = $this->skin->makeKnownLinkObj($wgTitle, $date, "target={$target}&file={$file->sha1}." . $file->getExtension()); } $pageLink = '<span class="history-deleted">' . $pageLink . '</span>'; # Regular files... } else { $url = $file->getUrlRel(); $pageLink = "<a href=\"{$url}\">{$date}</a>"; } $data = wfMsg('widthheight', $wgLang->formatNum($file->getWidth()), $wgLang->formatNum($file->getHeight())) . ' (' . wfMsgExt('nbytes', 'parsemag', $wgLang->formatNum($file->getSize())) . ')'; $data = htmlspecialchars($data); return "<li>{$pageLink} " . $this->fileUserTools($file) . " {$data} " . $this->fileComment($file) . "{$del}</li>"; }
/** * Get an array of extended metadata. (See the imageinfo API for format.) * * @param File $file File to use * @return array [<property name> => ['value' => <value>]], or [] on error * @since 1.23 */ public function fetchExtendedMetadata(File $file) { $cache = ObjectCache::getMainWANInstance(); // If revision deleted, exit immediately if ($file->isDeleted(File::DELETED_FILE)) { return []; } $cacheKey = wfMemcKey('getExtendedMetadata', $this->getLanguage()->getCode(), (int) $this->singleLang, $file->getSha1()); $cachedValue = $cache->get($cacheKey); if ($cachedValue && Hooks::run('ValidateExtendedMetadataCache', [$cachedValue['timestamp'], $file])) { $extendedMetadata = $cachedValue['data']; } else { $maxCacheTime = $file instanceof ForeignAPIFile ? 60 * 60 * 12 : 60 * 60 * 24 * 30; $fileMetadata = $this->getExtendedMetadataFromFile($file); $extendedMetadata = $this->getExtendedMetadataFromHook($file, $fileMetadata, $maxCacheTime); if ($this->singleLang) { $this->resolveMultilangMetadata($extendedMetadata); } $this->discardMultipleValues($extendedMetadata); // Make sure the metadata won't break the API when an XML format is used. // This is an API-specific function so it would be cleaner to call it from // outside fetchExtendedMetadata, but this way we don't need to redo the // computation on a cache hit. $this->sanitizeArrayForAPI($extendedMetadata); $valueToCache = ['data' => $extendedMetadata, 'timestamp' => wfTimestampNow()]; $cache->set($cacheKey, $valueToCache, $maxCacheTime); } return $extendedMetadata; }
/** * @param File $file * @return string */ protected function getThumbForLine($file) { $lang = $this->getLanguage(); $user = $this->getUser(); if ($file->allowInlineDisplay() && $file->userCan(File::DELETED_FILE, $user) && !$file->isDeleted(File::DELETED_FILE)) { $params = ['width' => '120', 'height' => '120']; $timestamp = wfTimestamp(TS_MW, $file->getTimestamp()); $thumbnail = $file->transform($params); $options = ['alt' => $this->msg('filehist-thumbtext', $lang->userTimeAndDate($timestamp, $user), $lang->userDate($timestamp, $user), $lang->userTime($timestamp, $user))->text(), 'file-link' => true]; if (!$thumbnail) { return $this->msg('filehist-nothumb')->escaped(); } return $thumbnail->toHtml($options); } else { return $this->msg('filehist-nothumb')->escaped(); } }
/** * Wrap and format the file's comment block, if the current * user is allowed to view it. * * @return string HTML */ protected function getComment() { if ($this->file->userCan(File::DELETED_COMMENT)) { $block = Linker::commentBlock($this->file->description); } else { $block = ' ' . wfMsgHtml('rev-deleted-comment'); } if ($this->file->isDeleted(File::DELETED_COMMENT)) { return "<span class=\"history-deleted\">{$block}</span>"; } return $block; }
/** * Get an array of extended metadata. (See the imageinfo API for format.) * * @param File $file File to use * @return array [<property name> => ['value' => <value>]], or [] on error * @since 1.23 */ public function fetchExtendedMetadata(File $file) { global $wgMemc; wfProfileIn(__METHOD__); // If revision deleted, exit immediately if ($file->isDeleted(File::DELETED_FILE)) { wfProfileOut(__METHOD__); return array(); } $cacheKey = wfMemcKey('getExtendedMetadata', $this->getLanguage()->getCode(), (int) $this->singleLang, $file->getSha1()); $cachedValue = $wgMemc->get($cacheKey); if ($cachedValue && wfRunHooks('ValidateExtendedMetadataCache', array($cachedValue['timestamp'], $file))) { $extendedMetadata = $cachedValue['data']; } else { $maxCacheTime = $file instanceof ForeignAPIFile ? 60 * 60 * 12 : 60 * 60 * 24 * 30; $fileMetadata = $this->getExtendedMetadataFromFile($file); $extendedMetadata = $this->getExtendedMetadataFromHook($file, $fileMetadata, $maxCacheTime); if ($this->singleLang) { $this->resolveMultilangMetadata($extendedMetadata); } // Make sure the metadata won't break the API when an XML format is used. // This is an API-specific function so it would be cleaner to call it from // outside fetchExtendedMetadata, but this way we don't need to redo the // computation on a cache hit. $this->sanitizeArrayForXml($extendedMetadata); $valueToCache = array('data' => $extendedMetadata, 'timestamp' => wfTimestampNow()); $wgMemc->set($cacheKey, $valueToCache, $maxCacheTime); } wfProfileOut(__METHOD__); return $extendedMetadata; }