/** * @return array|null */ public function getDuplicates() { $this->loadFile(); if (!is_null($this->mDupes)) { return $this->mDupes; } $hash = $this->mFile->getSha1(); if (!$hash) { $this->mDupes = []; return $this->mDupes; } $dupes = RepoGroup::singleton()->findBySha1($hash); // Remove duplicates with self and non matching file sizes $self = $this->mFile->getRepoName() . ':' . $this->mFile->getName(); $size = $this->mFile->getSize(); /** * @var $file File */ foreach ($dupes as $index => $file) { $key = $file->getRepoName() . ':' . $file->getName(); if ($key == $self) { unset($dupes[$index]); } if ($file->getSize() != $size) { unset($dupes[$index]); } } $this->mDupes = $dupes; return $this->mDupes; }
/** * Checks if the ProcessedFile needs reprocessing * * @return bool */ public function needsReprocessing() { $fileMustBeRecreated = false; // if original is missing we can not reprocess the file if ($this->originalFile->isMissing()) { return false; } // processedFile does not exist if (!$this->usesOriginalFile() && !$this->exists()) { $fileMustBeRecreated = true; } // hash does not match if (array_key_exists('checksum', $this->properties) && $this->calculateChecksum() !== $this->properties['checksum']) { $fileMustBeRecreated = true; } // original file changed if ($this->originalFile->getSha1() !== $this->originalFileSha1) { $fileMustBeRecreated = true; } if (!array_key_exists('uid', $this->properties)) { $fileMustBeRecreated = true; } // remove outdated file if ($fileMustBeRecreated && $this->exists()) { $this->delete(); } return $fileMustBeRecreated; }
public function execute($par) { $this->setHeaders(); $this->outputHeader(); $this->filename = $par !== null ? $par : $this->getRequest()->getText('filename'); $this->file = null; $this->hash = ''; $title = Title::newFromText($this->filename, NS_FILE); if ($title && $title->getText() != '') { $this->file = wfFindFile($title); } $out = $this->getOutput(); # Create the input form $formFields = array('filename' => array('type' => 'text', 'name' => 'filename', 'label-message' => 'fileduplicatesearch-filename', 'id' => 'filename', 'size' => 50, 'value' => $this->filename, 'cssclass' => 'mw-ui-input-inline')); $hiddenFields = array('title' => $this->getPageTitle()->getPrefixedDBKey()); $htmlForm = HTMLForm::factory('inline', $formFields, $this->getContext()); $htmlForm->addHiddenFields($hiddenFields); $htmlForm->setAction(wfScript()); $htmlForm->setMethod('get'); $htmlForm->setSubmitProgressive(); $htmlForm->setSubmitTextMsg($this->msg('fileduplicatesearch-submit')); $htmlForm->setWrapperLegendMsg('fileduplicatesearch-legend'); // The form should be visible always, even if it was submitted (e.g. to perform another action). // To bypass the callback validation of HTMLForm, use prepareForm() and displayForm(). $htmlForm->prepareForm()->displayForm(false); if ($this->file) { $this->hash = $this->file->getSha1(); } elseif ($this->filename !== '') { $out->wrapWikiMsg("<p class='mw-fileduplicatesearch-noresults'>\n\$1\n</p>", array('fileduplicatesearch-noresults', wfEscapeWikiText($this->filename))); } if ($this->hash != '') { # Show a thumbnail of the file $img = $this->file; if ($img) { $thumb = $img->transform(array('width' => 120, 'height' => 120)); if ($thumb) { $out->addModuleStyles('mediawiki.special'); $out->addHTML('<div id="mw-fileduplicatesearch-icon">' . $thumb->toHtml(array('desc-link' => false)) . '<br />' . $this->msg('fileduplicatesearch-info')->numParams($img->getWidth(), $img->getHeight())->params($this->getLanguage()->formatSize($img->getSize()), $img->getMimeType())->parseAsBlock() . '</div>'); } } $dupes = $this->getDupes(); $numRows = count($dupes); # Show a short summary if ($numRows == 1) { $out->wrapWikiMsg("<p class='mw-fileduplicatesearch-result-1'>\n\$1\n</p>", array('fileduplicatesearch-result-1', wfEscapeWikiText($this->filename))); } elseif ($numRows) { $out->wrapWikiMsg("<p class='mw-fileduplicatesearch-result-n'>\n\$1\n</p>", array('fileduplicatesearch-result-n', wfEscapeWikiText($this->filename), $this->getLanguage()->formatNum($numRows - 1))); } $this->doBatchLookups($dupes); $this->showList($dupes); } }
function execute($par) { global $wgScript; $this->setHeaders(); $this->outputHeader(); $this->filename = isset($par) ? $par : $this->getRequest()->getText('filename'); $this->file = null; $this->hash = ''; $title = Title::newFromText($this->filename, NS_FILE); if ($title && $title->getText() != '') { $this->file = wfFindFile($title); } $out = $this->getOutput(); # Create the input form $out->addHTML(Html::openElement('form', array('id' => 'fileduplicatesearch', 'method' => 'get', 'action' => $wgScript)) . "\n" . Html::hidden('title', $this->getPageTitle()->getPrefixedDBkey()) . "\n" . Html::openElement('fieldset') . "\n" . Html::element('legend', null, $this->msg('fileduplicatesearch-legend')->text()) . "\n" . Xml::inputLabel($this->msg('fileduplicatesearch-filename')->text(), 'filename', 'filename', 50, $this->filename) . "\n" . Xml::submitButton($this->msg('fileduplicatesearch-submit')->text()) . "\n" . Html::closeElement('fieldset') . "\n" . Html::closeElement('form')); if ($this->file) { $this->hash = $this->file->getSha1(); } elseif ($this->filename !== '') { $out->wrapWikiMsg("<p class='mw-fileduplicatesearch-noresults'>\n\$1\n</p>", array('fileduplicatesearch-noresults', wfEscapeWikiText($this->filename))); } if ($this->hash != '') { # Show a thumbnail of the file $img = $this->file; if ($img) { $thumb = $img->transform(array('width' => 120, 'height' => 120)); if ($thumb) { $out->addHTML('<div id="mw-fileduplicatesearch-icon">' . $thumb->toHtml(array('desc-link' => false)) . '<br />' . $this->msg('fileduplicatesearch-info')->numParams($img->getWidth(), $img->getHeight())->params($this->getLanguage()->formatSize($img->getSize()), $img->getMimeType())->parseAsBlock() . '</div>'); } } $dupes = $this->getDupes(); $numRows = count($dupes); # Show a short summary if ($numRows == 1) { $out->wrapWikiMsg("<p class='mw-fileduplicatesearch-result-1'>\n\$1\n</p>", array('fileduplicatesearch-result-1', wfEscapeWikiText($this->filename))); } elseif ($numRows) { $out->wrapWikiMsg("<p class='mw-fileduplicatesearch-result-n'>\n\$1\n</p>", array('fileduplicatesearch-result-n', wfEscapeWikiText($this->filename), $this->getLanguage()->formatNum($numRows - 1))); } $this->doBatchLookups($dupes); $this->showList($dupes); } }
/** * Set the displayed file version * * @param File|bool $file * @return mixed Previous value */ public function setFileVersion($file) { $val = null; if ($file instanceof File && $file->exists()) { $val = array('time' => $file->getTimestamp(), 'sha1' => $file->getSha1()); } return wfSetVar($this->mFileVersion, $val, true); }
/** * @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"; }
/** * 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; }
/** * Actually try to generate a new thumbnail * * @param File $file * @param array $params * @param string $thumbName * @param string $thumbPath * @return array (MediaTransformOutput|bool, string|bool error message HTML) */ function wfGenerateThumbnail(File $file, array $params, $thumbName, $thumbPath) { global $wgMemc, $wgAttemptFailureEpoch; $key = wfMemcKey('attempt-failures', $wgAttemptFailureEpoch, $file->getRepo()->getName(), $file->getSha1(), md5($thumbName)); // Check if this file keeps failing to render if ($wgMemc->get($key) >= 4) { return array(false, wfMessage('thumbnail_image-failure-limit', 4)); } $done = false; // Record failures on PHP fatals in addition to caching exceptions register_shutdown_function(function () use(&$done, $key) { if (!$done) { // transform() gave a fatal global $wgMemc; // Randomize TTL to reduce stampedes $wgMemc->incrWithInit($key, 3600 + mt_rand(0, 300)); } }); $thumb = false; $errorHtml = false; // guard thumbnail rendering with PoolCounter to avoid stampedes // expensive files use a separate PoolCounter config so it is possible // to set up a global limit on them if ($file->isExpensiveToThumbnail()) { $poolCounterType = 'FileRenderExpensive'; } else { $poolCounterType = 'FileRender'; } // Thumbnail isn't already there, so create the new thumbnail... try { $work = new PoolCounterWorkViaCallback($poolCounterType, sha1($file->getName()), array('doWork' => function () use($file, $params) { return $file->transform($params, File::RENDER_NOW); }, 'getCachedWork' => function () use($file, $params, $thumbPath) { // If the worker that finished made this thumbnail then use it. // Otherwise, it probably made a different thumbnail for this file. return $file->getRepo()->fileExists($thumbPath) ? $file->transform($params, File::RENDER_NOW) : false; // retry once more in exclusive mode }, 'fallback' => function () { return wfMessage('generic-pool-error')->parse(); }, 'error' => function (Status $status) { return $status->getHTML(); })); $result = $work->execute(); if ($result instanceof MediaTransformOutput) { $thumb = $result; } elseif (is_string($result)) { // error $errorHtml = $result; } } catch (Exception $e) { // Tried to select a page on a non-paged file? } /** @noinspection PhpUnusedLocalVariableInspection */ $done = true; // no PHP fatal occured if (!$thumb || $thumb->isError()) { // Randomize TTL to reduce stampedes $wgMemc->incrWithInit($key, 3600 + mt_rand(0, 300)); } return array($thumb, $errorHtml); }
/** * 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; }
protected function getDimensionInfo(File $file) { $that = $this; return ObjectCache::getMainWANInstance()->getWithSetCallback(wfMemcKey('file-djvu', 'dimensions', $file->getSha1()), WANObjectCache::TTL_INDEFINITE, function () use($that, $file) { $tree = $that->getMetaTree($file); if (!$tree) { return false; } $dimsByPage = array(); $count = count($tree->xpath('//OBJECT')); for ($i = 0; $i < $count; ++$i) { $o = $tree->BODY[0]->OBJECT[$i]; if ($o) { $dimsByPage[$i] = array('width' => (int) $o['width'], 'height' => (int) $o['height']); } else { $dimsByPage[$i] = false; } } return array('pageCount' => $count, 'dimensionsByPage' => $dimsByPage); }); }
/** * Update thumbnail data (update database and clear cache) * @param File $file * @return Status $status */ public function updateThumbnailData($file) { wfProfileIn(__METHOD__); // check for read only mode if (wfReadOnly()) { wfProfileOut(__METHOD__); return Status::newFatal(wfMessage('videos-error-readonly')->plain()); } $props = $file->repo->getFileProps($file->getVirtualUrl()); if (empty($props['size']) || empty($props['width']) || empty($props['height']) || empty($props['bits']) || empty($props['sha1']) || $props['sha1'] == $file->getSha1()) { wfProfileOut(__METHOD__); return Status::newGood(0); } $dbw = wfGetDB(DB_MASTER); $dbw->begin(); $dbw->update('image', array('img_size' => $props['size'], 'img_width' => intval($props['width']), 'img_height' => intval($props['height']), 'img_bits' => $props['bits'], 'img_sha1' => $props['sha1']), array('img_name' => $file->getName()), __METHOD__); $affected = $dbw->affectedRows(); $dbw->commit(); $status = Status::newGood($affected); if ($affected > 0) { $file->purgeEverything(); } wfProfileOut(__METHOD__); return $status; }
/** * Returns the Sha1 of this file * * @return string */ public function getSha1() { return $this->originalFile->getSha1(); }
public static function addToFileHistLine($hist, File $file, &$s, &$rowClass) { if (!$file->isVisible()) { return true; // Don't bother showing notice for deleted revs } # Quality level for old versions selected all at once. # Commons queries cannot be done all at once... if (!$file->isOld() || !$file->isLocal()) { $dbr = wfGetDB(DB_SLAVE); $quality = $dbr->selectField('flaggedrevs', 'fr_quality', array('fr_img_sha1' => $file->getSha1(), 'fr_img_timestamp' => $dbr->timestamp($file->getTimestamp())), __METHOD__); } else { $quality = is_null($file->quality) ? false : $file->quality; } # If reviewed, class the line if ($quality !== false) { $rowClass = FlaggedRevsXML::getQualityColor($quality); } return true; }
protected function getDimensionInfo(File $file) { $cache = ObjectCache::getMainWANInstance(); return $cache->getWithSetCallback($cache->makeKey('file-djvu', 'dimensions', $file->getSha1()), $cache::TTL_INDEFINITE, function () use($file) { $tree = $this->getMetaTree($file); if (!$tree) { return false; } $dimsByPage = []; $count = count($tree->xpath('//OBJECT')); for ($i = 0; $i < $count; $i++) { $o = $tree->BODY[0]->OBJECT[$i]; if ($o) { $dimsByPage[$i] = ['width' => (int) $o['width'], 'height' => (int) $o['height']]; } else { $dimsByPage[$i] = false; } } return ['pageCount' => $count, 'dimensionsByPage' => $dimsByPage]; }, ['pcTTL' => $cache::TTL_INDEFINITE]); }