Example #1
0
 /**
  * @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;
 }
Example #2
0
 /**
  * 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);
     }
 }
Example #5
0
 /**
  * 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";
 }
Example #7
0
 /**
  * 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;
 }
Example #8
0
/**
 * 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);
}
Example #9
0
 /**
  * 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;
 }
Example #10
0
 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;
 }
Example #12
0
 /**
  * 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;
 }
Example #14
0
 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]);
 }