/**
  * Record a file upload in the upload log and the image table
  */
 function recordUpload2($oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null)
 {
     if (is_null($user)) {
         global $wgUser;
         $user = $wgUser;
     }
     $dbw = $this->repo->getMasterDB();
     $dbw->begin();
     if (!$props) {
         $props = $this->repo->getFileProps($this->getVirtualUrl());
     }
     $props['description'] = $comment;
     $props['user'] = $user->getId();
     $props['user_text'] = $user->getName();
     $props['timestamp'] = wfTimestamp(TS_MW);
     $this->setProps($props);
     // Delete thumbnails and refresh the metadata cache
     $this->purgeThumbnails();
     $this->saveToCache();
     SquidUpdate::purge(array($this->getURL()));
     /* Wikia change begin - @author: Marooned, see RT#44185 */
     global $wgLogo;
     if ($this->url == $wgLogo) {
         SquidUpdate::purge(array($this->url));
     }
     /* Wikia change end */
     // Fail now if the file isn't there
     if (!$this->fileExists) {
         wfDebug(__METHOD__ . ": File " . $this->getPath() . " went missing!\n");
         return false;
     }
     $reupload = false;
     if ($timestamp === false) {
         $timestamp = $dbw->timestamp();
     }
     # Test to see if the row exists using INSERT IGNORE
     # This avoids race conditions by locking the row until the commit, and also
     # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
     $dbw->insert('image', array('img_name' => $this->getName(), 'img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $this->metadata, 'img_sha1' => $this->sha1), __METHOD__, 'IGNORE');
     if ($dbw->affectedRows() == 0) {
         $reupload = true;
         # Collision, this is an update of a file
         # Insert previous contents into oldimage
         $dbw->insertSelect('oldimage', 'image', array('oi_name' => 'img_name', 'oi_archive_name' => $dbw->addQuotes($oldver), 'oi_size' => 'img_size', 'oi_width' => 'img_width', 'oi_height' => 'img_height', 'oi_bits' => 'img_bits', 'oi_timestamp' => 'img_timestamp', 'oi_description' => 'img_description', 'oi_user' => 'img_user', 'oi_user_text' => 'img_user_text', 'oi_metadata' => 'img_metadata', 'oi_media_type' => 'img_media_type', 'oi_major_mime' => 'img_major_mime', 'oi_minor_mime' => 'img_minor_mime', 'oi_sha1' => 'img_sha1'), array('img_name' => $this->getName()), __METHOD__);
         # Update the current image row
         $dbw->update('image', array('img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $this->metadata, 'img_sha1' => $this->sha1), array('img_name' => $this->getName()), __METHOD__);
     } else {
         # This is a new file
         # Update the image count
         $site_stats = $dbw->tableName('site_stats');
         $dbw->query("UPDATE {$site_stats} SET ss_images=ss_images+1", __METHOD__);
     }
     # Commit the transaction now, in case something goes wrong later
     # The most important thing is that files don't get lost, especially archives
     $dbw->commit();
     # Invalidate cache for all pages using this file
     $update = new HTMLCacheUpdate($this->getTitle(), 'imagelinks');
     $update->doUpdate();
     return true;
 }
 private function purge($url, $var, $typeVal, $val, $likeVal)
 {
     $wikis = $this->getListOfWikisWithVar($var, $typeVal, $val, $likeVal);
     $purgeUrls = array();
     foreach ($wikis as $cityId => $wikiData) {
         $purgeUrls[] = $wikiData['u'] . $url;
     }
     SquidUpdate::purge($purgeUrls);
     return $purgeUrls;
 }
/** @todo document */
function benchSquid($urls, $trials = 1)
{
    $start = wfTime();
    for ($i = 0; $i < $trials; $i++) {
        SquidUpdate::purge($urls);
    }
    $delta = wfTime() - $start;
    $pertrial = $delta / $trials;
    $pertitle = $pertrial / count($urls);
    return sprintf("%4d titles in %6.2fms (%6.2fms each)", count($urls), $pertrial * 1000.0, $pertitle * 1000.0);
}
 /**
  * Remove the thumb from DFS and purge it from CDN
  *
  * @param string $thumb
  */
 private function purgeThumb($thumb)
 {
     $url = sprintf("http://images.wikia.com/%s%s/%s", $this->storage->getContainerName(), $this->storage->getPathPrefix(), $thumb);
     #$this->output(sprintf("%s: %s <%s>...", __METHOD__, $thumb, $url));
     $this->output(sprintf("%s: '%s'... ", __METHOD__, $thumb));
     if ($this->isDryRun) {
         $this->output("dry run!\n");
     } else {
         $this->storage->remove($thumb);
         SquidUpdate::purge([$url]);
         $this->output("done\n");
     }
 }
 public function execute()
 {
     $this->dryRun = $this->hasOption('dry-run');
     $this->verbose = $this->hasOption('verbose');
     $wikiId = $this->getOption('wikiId', '');
     if (empty($wikiId)) {
         die("Error: Empty wiki id.\n");
     }
     $dbname = WikiFactory::IDtoDB($wikiId);
     if (empty($dbname)) {
         die("Error: Cannot find dbname.\n");
     }
     $pageLimit = 20000;
     $totalLimit = $this->getOption('limit', $pageLimit);
     if (empty($totalLimit) || $totalLimit < -1) {
         die("Error: invalid limit.\n");
     }
     if ($totalLimit == -1) {
         $totalLimit = $this->getTotalPages($dbname);
     }
     $maxSet = ceil($totalLimit / $pageLimit);
     $limit = $totalLimit > $pageLimit ? $pageLimit : $totalLimit;
     $totalPages = 0;
     for ($set = 1; $set <= $maxSet; $set++) {
         $cnt = 0;
         if ($set == $maxSet) {
             $limit = $totalLimit - $pageLimit * ($set - 1);
         }
         $offset = ($set - 1) * $pageLimit;
         $pages = $this->getAllPages($dbname, $limit, $offset);
         $total = count($pages);
         foreach ($pages as $page) {
             $cnt++;
             echo "Wiki {$wikiId} - Page {$page['id']} [{$cnt} of {$total}, set {$set} of {$maxSet}]: ";
             $title = GlobalTitle::newFromId($page['id'], $wikiId);
             if ($title instanceof GlobalTitle) {
                 $url = $title->getFullURL();
                 echo "{$url}\n";
                 if (!$this->dryRun) {
                     SquidUpdate::purge([$url]);
                 }
                 $this->success++;
             } else {
                 echo "ERROR: Cannot find global title for {$page['title']}\n";
             }
         }
         $totalPages = $totalPages + $total;
     }
     echo "\nWiki {$wikiId}: Total pages: {$totalPages}, Success: {$this->success}, Failed: " . ($totalPages - $this->success) . "\n\n";
 }
/**
 * This is obsolete, use SquidUpdate::purge()
 * @deprecated
 */
function wfPurgeSquidServers($urlArr)
{
    SquidUpdate::purge($urlArr);
}
 /**
  * @author Maciej Błaszkowski <marooned at wikia-inc.com>
  */
 static function purgeCommunityWidgetInVarnish($title)
 {
     global $wgScript, $wgContentNamespaces, $wgContLang, $wgMemc;
     if (in_array($title->getNamespace(), $wgContentNamespaces)) {
         $lang = $wgContLang->getCode();
         $key = wfMemcKey('community_widget_v1', $lang);
         $wgMemc->delete($key);
         SquidUpdate::purge(array($wgScript . "?action=ajax&rs=CommunityWidgetAjax&uselang={$lang}"));
     }
     return true;
 }
Exemple #8
0
 /**
  * Delete an old version of the file.
  *
  * Moves the file into an archive directory (or deletes it)
  * and removes the database row.
  *
  * Cache purging is done; logging is caller's responsibility.
  *
  * @param string $archiveName
  * @param string $reason
  * @param bool $suppress
  * @throws MWException Exception on database or file store failure
  * @return FileRepoStatus
  */
 function deleteOld($archiveName, $reason, $suppress = false)
 {
     global $wgUseSquid;
     if ($this->getRepo()->getReadOnlyReason() !== false) {
         return $this->readOnlyFatalStatus();
     }
     $batch = new LocalFileDeleteBatch($this, $reason, $suppress);
     $this->lock();
     // begin
     $batch->addOld($archiveName);
     $status = $batch->execute();
     $this->unlock();
     // done
     $this->purgeOldThumbnails($archiveName);
     if ($status->isOK()) {
         $this->purgeDescription();
         $this->purgeHistory();
     }
     if ($wgUseSquid) {
         // Purge the squid
         SquidUpdate::purge(array($this->getArchiveUrl($archiveName)));
     }
     return $status;
 }
Exemple #9
0
 /**
  * Run the transaction
  */
 function execute()
 {
     global $wgUseSquid;
     wfProfileIn(__METHOD__);
     $this->file->lock();
     // Leave private files alone
     $privateFiles = array();
     list($oldRels, $deleteCurrent) = $this->getOldRels();
     $dbw = $this->file->repo->getMasterDB();
     if (!empty($oldRels)) {
         $res = $dbw->select('oldimage', array('oi_archive_name'), array('oi_name' => $this->file->getName(), 'oi_archive_name IN (' . $dbw->makeList(array_keys($oldRels)) . ')', $dbw->bitAnd('oi_deleted', File::DELETED_FILE) => File::DELETED_FILE), __METHOD__);
         foreach ($res as $row) {
             $privateFiles[$row->oi_archive_name] = 1;
         }
     }
     // Prepare deletion batch
     $hashes = $this->getHashes();
     $this->deletionBatch = array();
     $ext = $this->file->getExtension();
     $dotExt = $ext === '' ? '' : ".{$ext}";
     foreach ($this->srcRels as $name => $srcRel) {
         // Skip files that have no hash (missing source).
         // Keep private files where they are.
         if (isset($hashes[$name]) && !array_key_exists($name, $privateFiles)) {
             $hash = $hashes[$name];
             $key = $hash . $dotExt;
             $dstRel = $this->file->repo->getDeletedHashPath($key) . $key;
             $this->deletionBatch[$name] = array($srcRel, $dstRel);
         }
     }
     // Lock the filearchive rows so that the files don't get deleted by a cleanup operation
     // We acquire this lock by running the inserts now, before the file operations.
     //
     // This potentially has poor lock contention characteristics -- an alternative
     // scheme would be to insert stub filearchive entries with no fa_name and commit
     // them in a separate transaction, then run the file ops, then update the fa_name fields.
     $this->doDBInserts();
     // Removes non-existent file from the batch, so we don't get errors.
     $this->deletionBatch = $this->removeNonexistentFiles($this->deletionBatch);
     // Execute the file deletion batch
     $status = $this->file->repo->deleteBatch($this->deletionBatch);
     if (!$status->isGood()) {
         $this->status->merge($status);
     }
     if (!$this->status->ok) {
         // Critical file deletion error
         // Roll back inserts, release lock and abort
         // TODO: delete the defunct filearchive rows if we are using a non-transactional DB
         $this->file->unlockAndRollback();
         wfProfileOut(__METHOD__);
         return $this->status;
     }
     // Purge squid
     if ($wgUseSquid) {
         $urls = array();
         foreach ($this->srcRels as $srcRel) {
             $urlRel = str_replace('%2F', '/', rawurlencode($srcRel));
             $urls[] = $this->file->repo->getZoneUrl('public') . '/' . $urlRel;
         }
         SquidUpdate::purge($urls);
     }
     // Delete image/oldimage rows
     $this->doDBDeletes();
     // Commit and return
     $this->file->unlock();
     wfProfileOut(__METHOD__);
     return $this->status;
 }
 /**
  * Purges squids and invalidates caches of pages that link to $title
  * interwiki.
  */
 public static function PurgeReferringPages($title)
 {
     global $wgDBname, $wgInterwikiIntegrationPrefix;
     $mDb = wfGetDB(DB_MASTER);
     $titleName = str_replace(' ', '_', $title->getFullText());
     $prefix = array();
     $prefix = array_keys($wgInterwikiIntegrationPrefix, $wgDBname);
     $purgeArray = array();
     foreach ($prefix as $thisPrefix) {
         $thisPrefix = ucfirst($thisPrefix);
         $result = $mDb->selectRow(array('integration_iwlinks'), array('integration_iwl_from_url', 'integration_iwl_from_db', 'integration_iwl_from'), array('integration_iwl_prefix' => $thisPrefix, 'integration_iwl_title' => $titleName));
         if ($result) {
             $referringPage = $result->integration_iwl_from;
             $purgeArray[] = $result->integration_iwl_from_url;
             $referringDb = $result->integration_iwl_from_db;
             $dbwReferring = wfGetDB(DB_MASTER, array(), $referringDb);
             $dbwReferring->update('page', array('page_touched' => $dbwReferring->timestamp()), array('page_id' => $referringPage));
         }
     }
     if ($result) {
         SquidUpdate::purge($purgeArray);
     }
     return true;
 }
Exemple #11
0
 function purgeUrl()
 {
     // Purge the avatar URL and the proportions commonly used in Oasis.
     global $wgUseSquid;
     if ($wgUseSquid) {
         // FIXME: is there a way to know what sizes will be used w/o hardcoding them here?
         $urls = array($this->getPurgeUrl(), $this->getThumbnailPurgeUrl(20), $this->getThumbnailPurgeUrl(50), $this->getThumbnailPurgeUrl(100), $this->getThumbnailPurgeUrl(200));
         SquidUpdate::purge($urls);
     }
 }
 /**
  * Purges Varnish and Memcached data mapping to the specified set of paramenters
  *
  * @param array $options @see getMultiTypePackage
  */
 public function purgeMultiTypePackageCache(array $options)
 {
     SquidUpdate::purge(array(F::build('AssetsManager', array(), 'getInstance')->getMultiTypePackageURL($options)));
 }
 /**
  * @param $title Title
  * @param $user User
  */
 public function purge($title, $user)
 {
     global $wgScript, $wgServer;
     AttributionCache::getInstance()->updateArticleContribs($title, $user);
     // invalidate varnish cache
     if ($user->getId() != 0) {
         SquidUpdate::purge(array("{$wgServer}{$wgScript}?action=ajax&rs=wfAnswersGetEditPointsAjax&userId={$user->getId()}"));
     }
 }
Exemple #14
0
 /**
  * @brief remove thumbnails for avatar by cleaning up whole folder
  *
  * @author Krzysztof Krzyżaniak (eloy) <*****@*****.**>
  * @access private
  *
  * @return boolean status of operation
  */
 private function purgeThumbnails()
 {
     global $wgAvatarsUseSwiftStorage, $wgBlogAvatarPath, $wgBlogAvatarSwiftContainer, $wgBlogAvatarSwiftPathPrefix;
     // get path to thumbnail folder
     wfProfileIn(__METHOD__);
     // dirty hack, should work in this case
     if (!empty($wgAvatarsUseSwiftStorage)) {
         $swift = $this->getSwiftStorage();
         $backend = FileBackendGroup::singleton()->get('swift-backend');
         $dir = sprintf('mwstore://swift-backend/%s%s%s', $wgBlogAvatarSwiftContainer, $wgBlogAvatarSwiftPathPrefix, $this->getLocalPath());
         $dir = $this->getThumbPath($dir);
         $avatarRemotePath = sprintf("thumb%s", $this->getLocalPath());
         $urls = [];
         $files = [];
         $iterator = $backend->getFileList(array('dir' => $dir));
         foreach ($iterator as $file) {
             $files[] = sprintf("%s/%s", $avatarRemotePath, $file);
         }
         // deleting files on file system and creating an array of URLs to purge
         if (!empty($files)) {
             foreach ($files as $file) {
                 $status = $swift->remove($file);
                 if (!$status->isOk()) {
                     wfDebugLog("avatar", __METHOD__ . ": {$file} exists but cannot be removed.\n", true);
                 } else {
                     $urls[] = wfReplaceImageServer($wgBlogAvatarPath) . "/{$file}";
                     wfDebugLog("avatar", __METHOD__ . ": {$file} removed.\n", true);
                 }
             }
         }
         wfDebugLog("avatar", __METHOD__ . ": all thumbs removed.\n", true);
     } else {
         $dir = $this->getFullPath();
         $dir = $this->getThumbPath($dir);
         if (is_dir($dir)) {
             $urls = [];
             $files = [];
             // copied from LocalFile->getThumbnails
             $handle = opendir($dir);
             if ($handle) {
                 while (false !== ($file = readdir($handle))) {
                     if ($file[0] != '.') {
                         $files[] = $file;
                     }
                 }
                 closedir($handle);
             }
             // partialy copied from LocalFile->purgeThumbnails()
             foreach ($files as $file) {
                 // deleting files on file system
                 @unlink("{$dir}/{$file}");
                 $urls[] = $this->getPurgeUrl('/thumb/') . "/{$file}";
                 wfDebugLog("avatar", __METHOD__ . ": removing {$dir}/{$file}\n", true);
             }
         } else {
             wfDebugLog("avatar", __METHOD__ . ": {$dir} exists but is not directory so not removed.\n", true);
         }
         wfDebugLog("avatar", __METHOD__ . ": all thumbs removed.\n", true);
     }
     // purging avatars urls
     SquidUpdate::purge($urls);
     wfProfileOut(__METHOD__);
 }
 /**
  * @brief Whenever data is saved in GG Content Managment Tool
  * purge Varnish cache for it
  *
  * @return bool
  */
 static function onGameGuidesContentSave()
 {
     $app = F::app();
     SquidUpdate::purge(array($app->wf->AppendQuery($app->wf->ExpandUrl($app->wg->Server . $app->wg->ScriptPath . '/wikia.php'), array('controller' => __CLASS__, 'method' => 'getTags'))));
     return true;
 }
Exemple #16
0
 /**
  * Purges the list of URLs passed to the constructor
  */
 function doUpdate()
 {
     SquidUpdate::purge($this->urlArr);
 }
 /**
  * Record a file upload in the upload log and the image table
  * @param $oldver
  * @param $comment string
  * @param $pageText string
  * @param $props bool|array
  * @param $timestamp bool|string
  * @param $user null|User
  * @return bool
  */
 function recordUpload2($oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null)
 {
     wfProfileIn(__METHOD__);
     if (is_null($user)) {
         global $wgUser;
         $user = $wgUser;
     }
     $dbw = $this->repo->getMasterDB();
     $dbw->begin(__METHOD__);
     if (!$props) {
         wfProfileIn(__METHOD__ . '-getProps');
         $props = $this->repo->getFileProps($this->getVirtualUrl());
         wfProfileOut(__METHOD__ . '-getProps');
     }
     if ($timestamp === false) {
         $timestamp = $dbw->timestamp();
     }
     $props['description'] = $comment;
     $props['user'] = $user->getId();
     $props['user_text'] = $user->getName();
     $props['timestamp'] = wfTimestamp(TS_MW, $timestamp);
     // DB -> TS_MW
     $this->setProps($props);
     # Fail now if the file isn't there
     if (!$this->fileExists) {
         wfDebug(__METHOD__ . ": File " . $this->getRel() . " went missing!\n");
         wfProfileOut(__METHOD__);
         return false;
     }
     $reupload = false;
     # Test to see if the row exists using INSERT IGNORE
     # This avoids race conditions by locking the row until the commit, and also
     # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
     $dbw->insert('image', array('img_name' => $this->getName(), 'img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $this->metadata, 'img_sha1' => $this->sha1), __METHOD__, 'IGNORE');
     if ($dbw->affectedRows() == 0) {
         # (bug 34993) Note: $oldver can be empty here, if the previous
         # version of the file was broken. Allow registration of the new
         # version to continue anyway, because that's better than having
         # an image that's not fixable by user operations.
         $reupload = true;
         # Collision, this is an update of a file
         # Insert previous contents into oldimage
         $dbw->insertSelect('oldimage', 'image', array('oi_name' => 'img_name', 'oi_archive_name' => $dbw->addQuotes($oldver), 'oi_size' => 'img_size', 'oi_width' => 'img_width', 'oi_height' => 'img_height', 'oi_bits' => 'img_bits', 'oi_timestamp' => 'img_timestamp', 'oi_description' => 'img_description', 'oi_user' => 'img_user', 'oi_user_text' => 'img_user_text', 'oi_metadata' => 'img_metadata', 'oi_media_type' => 'img_media_type', 'oi_major_mime' => 'img_major_mime', 'oi_minor_mime' => 'img_minor_mime', 'oi_sha1' => 'img_sha1'), array('img_name' => $this->getName()), __METHOD__);
         # Update the current image row
         $dbw->update('image', array('img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $this->metadata, 'img_sha1' => $this->sha1), array('img_name' => $this->getName()), __METHOD__);
     } else {
         # This is a new file, so update the image count
         DeferredUpdates::addUpdate(SiteStatsUpdate::factory(array('images' => 1)));
     }
     $descTitle = $this->getTitle();
     $wikiPage = new WikiFilePage($descTitle);
     $wikiPage->setFile($this);
     # Add the log entry
     $log = new LogPage('upload');
     $action = $reupload ? 'overwrite' : 'upload';
     $logId = $log->addEntry($action, $descTitle, $comment, array(), $user);
     wfProfileIn(__METHOD__ . '-edit');
     $exists = $descTitle->exists();
     if ($exists) {
         # Create a null revision
         $latest = $descTitle->getLatestRevID();
         $nullRevision = Revision::newNullRevision($dbw, $descTitle->getArticleID(), $log->getRcComment(), false);
         if (!is_null($nullRevision)) {
             $nullRevision->insertOn($dbw);
             wfRunHooks('NewRevisionFromEditComplete', array($wikiPage, $nullRevision, $latest, $user));
             $wikiPage->updateRevisionOn($dbw, $nullRevision);
         }
     }
     # Commit the transaction now, in case something goes wrong later
     # The most important thing is that files don't get lost, especially archives
     # NOTE: once we have support for nested transactions, the commit may be moved
     #       to after $wikiPage->doEdit has been called.
     $dbw->commit(__METHOD__);
     if ($exists) {
         # Invalidate the cache for the description page
         $descTitle->invalidateCache();
         $descTitle->purgeSquid();
     } else {
         # New file; create the description page.
         # There's already a log entry, so don't make a second RC entry
         # Squid and file cache for the description page are purged by doEditContent.
         $content = ContentHandler::makeContent($pageText, $descTitle);
         $status = $wikiPage->doEditContent($content, $comment, EDIT_NEW | EDIT_SUPPRESS_RC, false, $user);
         if (isset($status->value['revision'])) {
             // XXX; doEdit() uses a transaction
             $dbw->begin(__METHOD__);
             $dbw->update('logging', array('log_page' => $status->value['revision']->getPage()), array('log_id' => $logId), __METHOD__);
             $dbw->commit(__METHOD__);
             // commit before anything bad can happen
         }
     }
     wfProfileOut(__METHOD__ . '-edit');
     # Save to cache and purge the squid
     # We shall not saveToCache before the commit since otherwise
     # in case of a rollback there is an usable file from memcached
     # which in fact doesn't really exist (bug 24978)
     $this->saveToCache();
     if ($reupload) {
         # Delete old thumbnails
         wfProfileIn(__METHOD__ . '-purge');
         $this->purgeThumbnails();
         wfProfileOut(__METHOD__ . '-purge');
         # Remove the old file from the squid cache
         SquidUpdate::purge(array($this->getURL()));
     }
     # Hooks, hooks, the magic of hooks...
     wfProfileIn(__METHOD__ . '-hooks');
     wfRunHooks('FileUpload', array($this, $reupload, $descTitle->exists()));
     wfProfileOut(__METHOD__ . '-hooks');
     # Invalidate cache for all pages using this file
     $update = new HTMLCacheUpdate($this->getTitle(), 'imagelinks');
     $update->doUpdate();
     # Invalidate cache for all pages that redirects on this page
     $redirs = $this->getTitle()->getRedirectsHere();
     foreach ($redirs as $redir) {
         $update = new HTMLCacheUpdate($redir, 'imagelinks');
         $update->doUpdate();
     }
     wfProfileOut(__METHOD__);
     return true;
 }
Exemple #18
0
 /**
  * doSubmit
  *
  * parse tag attributes
  *
  * @author Krzysztof Krzyżaniak <*****@*****.**>
  * @access public
  *
  * @param object	$request	WebRequest object
  *
  * @return array	rendered HTML answer and status of operation
  */
 public function doSubmit(&$request)
 {
     global $wgTitle, $wgMemc;
     wfProfileIn(__METHOD__);
     $status = false;
     $vote = $request->getVal("wpPollRadio" . $this->mId, null);
     if (!is_null($vote)) {
         if ($this->doVote($vote)) {
             $status = wfMsg("ajaxpoll-thankyou");
             // invalidate cache
             $wgTitle->invalidateCache();
             // Send purge for the article (don't purge its history page - PLATFORM-92)
             SquidUpdate::purge([$wgTitle->getFullURL()]);
         } else {
             $status = wfMsg("ajaxpoll-error");
         }
     }
     list($votes, $total) = $this->getVotes(true);
     //true because we need DB_MASTER and we don't want to use memcache here
     $response = array("id" => $this->mId, "votes" => $votes, "total" => $total, "status" => $status);
     // Purge the vote stats.
     $memcKey = $this->getVotesMemKey();
     $wgMemc->delete($memcKey);
     wfProfileOut(__METHOD__);
     return $response;
 }
Exemple #19
0
 /**
  * Record a file upload in the upload log and the image table
  */
 function recordUpload2($oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null)
 {
     if (is_null($user)) {
         global $wgUser;
         $user = $wgUser;
     }
     $dbw = $this->repo->getMasterDB();
     $dbw->begin();
     if (!$props) {
         $props = $this->repo->getFileProps($this->getVirtualUrl());
     }
     $props['description'] = $comment;
     $props['user'] = $user->getId();
     $props['user_text'] = $user->getName();
     $props['timestamp'] = wfTimestamp(TS_MW);
     $this->setProps($props);
     // Delete thumbnails and refresh the metadata cache
     $this->purgeThumbnails();
     $this->saveToCache();
     SquidUpdate::purge(array($this->getURL()));
     // Fail now if the file isn't there
     if (!$this->fileExists) {
         wfDebug(__METHOD__ . ": File " . $this->getPath() . " went missing!\n");
         return false;
     }
     $reupload = false;
     if ($timestamp === false) {
         $timestamp = $dbw->timestamp();
     }
     # Test to see if the row exists using INSERT IGNORE
     # This avoids race conditions by locking the row until the commit, and also
     # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
     $dbw->insert('image', array('img_name' => $this->getName(), 'img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $this->metadata, 'img_sha1' => $this->sha1), __METHOD__, 'IGNORE');
     if ($dbw->affectedRows() == 0) {
         $reupload = true;
         # Collision, this is an update of a file
         # Insert previous contents into oldimage
         $dbw->insertSelect('oldimage', 'image', array('oi_name' => 'img_name', 'oi_archive_name' => $dbw->addQuotes($oldver), 'oi_size' => 'img_size', 'oi_width' => 'img_width', 'oi_height' => 'img_height', 'oi_bits' => 'img_bits', 'oi_timestamp' => 'img_timestamp', 'oi_description' => 'img_description', 'oi_user' => 'img_user', 'oi_user_text' => 'img_user_text', 'oi_metadata' => 'img_metadata', 'oi_media_type' => 'img_media_type', 'oi_major_mime' => 'img_major_mime', 'oi_minor_mime' => 'img_minor_mime', 'oi_sha1' => 'img_sha1'), array('img_name' => $this->getName()), __METHOD__);
         # Update the current image row
         $dbw->update('image', array('img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $this->metadata, 'img_sha1' => $this->sha1), array('img_name' => $this->getName()), __METHOD__);
     } else {
         # This is a new file
         # Update the image count
         $site_stats = $dbw->tableName('site_stats');
         $dbw->query("UPDATE {$site_stats} SET ss_images=ss_images+1", __METHOD__);
     }
     $descTitle = $this->getTitle();
     $wikiPage = new WikiFilePage($descTitle);
     $wikiPage->setFile($this);
     # Add the log entry...
     $action = $reupload ? 'overwrite' : 'upload';
     $logEntry = new ManualLogEntry('upload', $action);
     $logEntry->setTimestamp($this->timestamp);
     $logEntry->setPerformer($user);
     $logEntry->setComment($comment);
     $logEntry->setTarget($descTitle);
     // Allow people using the api to associate log entries with the upload.
     // Log has a timestamp, but sometimes different from upload timestamp.
     $logEntry->setParameters(array('img_sha1' => $this->sha1, 'img_timestamp' => $timestamp));
     // Note we keep $logId around since during new image
     // creation, page doesn't exist yet, so log_page = 0
     // but we want it to point to the page we're making,
     // so we later modify the log entry.
     // For a similar reason, we avoid making an RC entry
     // now and wait until the page exists.
     $logId = $logEntry->insert();
     $exists = $descTitle->exists();
     if ($exists) {
         // Page exists, do RC entry now (otherwise we wait for later).
         $logEntry->publish($logId);
         // TODO: same if statement twice? pointless?
         //}
         //if ( $exists ) {
         // Create a null revision
         $latest = $descTitle->getLatestRevID();
         // Use own context to get the action text in content language
         $formatter = LogFormatter::newFromEntry($logEntry);
         $formatter->setContext(RequestContext::newExtraneousContext($descTitle));
         $editSummary = $formatter->getPlainActionText();
         $nullRevision = Revision::newNullRevision($dbw, $descTitle->getArticleID(), $editSummary, false, $user);
         if (!is_null($nullRevision)) {
             $nullRevision->insertOn($dbw);
             Hooks::run('NewRevisionFromEditComplete', array($wikiPage, $nullRevision, $nullRevision->getParentId(), $user));
             $wikiPage->updateRevisionOn($dbw, $nullRevision);
         }
     }
     # Commit the transaction now, in case something goes wrong later
     # The most important thing is that files don't get lost, especially archives
     # NOTE: once we have support for nested transactions, the commit may be moved
     #       to after $wikiPage->doEdit has been called.
     $dbw->commit(__METHOD__);
     # Update memcache after the commit
     $this->invalidateCache();
     if ($exists) {
         # Invalidate the cache for the description page
         $descTitle->invalidateCache();
         $descTitle->purgeSquid();
     } else {
         # New file; create the description page.
         # There's already a log entry, so don't make a second RC entry
         # Squid and file cache for the description page are purged by doEditContent.
         $content = ContentHandler::makeContent($pageText, $descTitle);
         $status = $wikiPage->doEditContent($content, $comment, EDIT_NEW | EDIT_SUPPRESS_RC, false, $user);
         $dbw->begin(__METHOD__);
         // XXX; doEdit() uses a transaction
         // Now that the page exists, make an RC entry.
         $logEntry->publish($logId);
         if (isset($status->value['revision'])) {
             $dbw->update('logging', array('log_page' => $status->value['revision']->getPage()), array('log_id' => $logId), __METHOD__);
         }
         $dbw->commit(__METHOD__);
         // commit before anything bad can happen
     }
     if ($reupload) {
         # Delete old thumbnails
         $this->purgeThumbnails();
         # Remove the old file from the squid cache
         SquidUpdate::purge(array($this->getURL()));
     }
     # Hooks, hooks, the magic of hooks...
     Hooks::run('FileUpload', array($this, $reupload, $descTitle->exists()));
     # Invalidate cache for all pages using this file
     $update = new HTMLCacheUpdate($this->getTitle(), 'imagelinks');
     $update->doUpdate();
     if (!$reupload) {
         LinksUpdate::queueRecursiveJobsForTable($this->getTitle(), 'imagelinks');
     }
     return true;
 }
 /**
  * The only thing changed here is to strip NS from the file name
  * Delete cached transformed files
  */
 function purgeThumbnails($options = array())
 {
     global $wgUseSquid;
     // Delete thumbnails
     $files = $this->getThumbnails();
     $dir = $this->getThumbPath();
     $urls = array();
     foreach ($files as $file) {
         # Check that the base file name is part of the thumb name
         # This is a basic sanity check to avoid erasing unrelated directories
         /* This is the part that changed from LocalFile */
         if (strpos($file, $this->getFileNameStripped($this->getName())) !== false) {
             /* End of changes */
             $url = $this->getThumbUrl($file);
             $urls[] = $url;
             wfSuppressWarnings();
             unlink("{$dir}/{$file}");
             wfRestoreWarnings();
         }
     }
     // Purge the squid
     if ($wgUseSquid) {
         SquidUpdate::purge($urls);
     }
 }
Exemple #21
0
 /**
  * Run the transaction
  */
 function execute()
 {
     global $wgUser, $wgUseSquid;
     wfProfileIn(__METHOD__);
     $this->file->lock();
     // Prepare deletion batch
     $hashes = $this->getHashes();
     $this->deletionBatch = array();
     $ext = $this->file->getExtension();
     $dotExt = $ext === '' ? '' : ".{$ext}";
     foreach ($this->srcRels as $name => $srcRel) {
         // Skip files that have no hash (missing source)
         if (isset($hashes[$name])) {
             $hash = $hashes[$name];
             $key = $hash . $dotExt;
             $dstRel = $this->file->repo->getDeletedHashPath($key) . $key;
             $this->deletionBatch[$name] = array($srcRel, $dstRel);
         }
     }
     // Lock the filearchive rows so that the files don't get deleted by a cleanup operation
     // We acquire this lock by running the inserts now, before the file operations.
     //
     // This potentially has poor lock contention characteristics -- an alternative
     // scheme would be to insert stub filearchive entries with no fa_name and commit
     // them in a separate transaction, then run the file ops, then update the fa_name fields.
     $this->doDBInserts();
     // Execute the file deletion batch
     $status = $this->file->repo->deleteBatch($this->deletionBatch);
     if (!$status->isGood()) {
         $this->status->merge($status);
     }
     if (!$this->status->ok) {
         // Critical file deletion error
         // Roll back inserts, release lock and abort
         // TODO: delete the defunct filearchive rows if we are using a non-transactional DB
         $this->file->unlockAndRollback();
         return $this->status;
     }
     // Purge squid
     if ($wgUseSquid) {
         $urls = array();
         foreach ($this->srcRels as $srcRel) {
             $urlRel = str_replace('%2F', '/', rawurlencode($srcRel));
             $urls[] = $this->file->repo->getZoneUrl('public') . '/' . $urlRel;
         }
         SquidUpdate::purge($urls);
     }
     // Delete image/oldimage rows
     $this->doDBDeletes();
     // Commit and return
     $this->file->unlock();
     wfProfileOut(__METHOD__);
     return $this->status;
 }
Exemple #22
0
 /**
  * Generates a thumbnail according to the given parameters and saves it to storage
  * @param TempFSFile $tmpFile Temporary file where the rendered thumbnail will be saved
  * @param array $transformParams
  * @param int $flags
  * @return bool|MediaTransformOutput
  */
 public function generateAndSaveThumb($tmpFile, $transformParams, $flags)
 {
     global $wgUseSquid, $wgIgnoreImageErrors;
     $handler = $this->getHandler();
     $normalisedParams = $transformParams;
     $handler->normaliseParams($this, $normalisedParams);
     $thumbName = $this->thumbName($normalisedParams);
     $thumbUrl = $this->getThumbUrl($thumbName);
     $thumbPath = $this->getThumbPath($thumbName);
     // final thumb path
     $tmpThumbPath = $tmpFile->getPath();
     if ($handler->supportsBucketing()) {
         $this->generateBucketsIfNeeded($normalisedParams, $flags);
     }
     // Actually render the thumbnail...
     $thumb = $handler->doTransform($this, $tmpThumbPath, $thumbUrl, $transformParams);
     $tmpFile->bind($thumb);
     // keep alive with $thumb
     if (!$thumb) {
         // bad params?
         $thumb = false;
     } elseif ($thumb->isError()) {
         // transform error
         $this->lastError = $thumb->toText();
         // Ignore errors if requested
         if ($wgIgnoreImageErrors && !($flags & self::RENDER_NOW)) {
             $thumb = $handler->getTransform($this, $tmpThumbPath, $thumbUrl, $transformParams);
         }
     } elseif ($this->repo && $thumb->hasFile() && !$thumb->fileIsSource()) {
         // Copy the thumbnail from the file system into storage...
         $disposition = $this->getThumbDisposition($thumbName);
         $status = $this->repo->quickImport($tmpThumbPath, $thumbPath, $disposition);
         if ($status->isOK()) {
             $thumb->setStoragePath($thumbPath);
         } else {
             $thumb = $this->transformErrorOutput($thumbPath, $thumbUrl, $transformParams, $flags);
         }
         // Give extensions a chance to do something with this thumbnail...
         Hooks::run('FileTransformed', array($this, $thumb, $tmpThumbPath, $thumbPath));
     }
     // Purge. Useful in the event of Core -> Squid connection failure or squid
     // purge collisions from elsewhere during failure. Don't keep triggering for
     // "thumbs" which have the main image URL though (bug 13776)
     if ($wgUseSquid) {
         if (!$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL()) {
             SquidUpdate::purge(array($thumbUrl));
         }
     }
     return $thumb;
 }
Exemple #23
0
 /**
  * Transform a media file
  *
  * @param array $params An associative array of handler-specific parameters. Typical
  *                      keys are width, height and page.
  * @param integer $flags A bitfield, may contain self::RENDER_NOW to force rendering
  * @return MediaTransformOutput
  */
 function transform($params, $flags = 0)
 {
     global $wgUseSquid, $wgIgnoreImageErrors;
     wfProfileIn(__METHOD__);
     do {
         if (!$this->canRender()) {
             // not a bitmap or renderable image, don't try.
             $thumb = $this->iconThumb();
             break;
         }
         $script = $this->getTransformScript();
         if ($script && !($flags & self::RENDER_NOW)) {
             // Use a script to transform on client request, if possible
             $thumb = $this->handler->getScriptedTransform($this, $script, $params);
             if ($thumb) {
                 break;
             }
         }
         $normalisedParams = $params;
         $this->handler->normaliseParams($this, $normalisedParams);
         $thumbName = $this->thumbName($normalisedParams);
         $thumbPath = $this->getThumbPath($thumbName);
         $thumbUrl = $this->getThumbUrl($thumbName);
         if ($this->repo->canTransformVia404() && !($flags & self::RENDER_NOW)) {
             $thumb = $this->handler->getTransform($this, $thumbPath, $thumbUrl, $params);
             break;
         }
         wfDebug(__METHOD__ . ": Doing stat for {$thumbPath}\n");
         $this->migrateThumbFile($thumbName);
         if (file_exists($thumbPath)) {
             $thumb = $this->handler->getTransform($this, $thumbPath, $thumbUrl, $params);
             break;
         }
         $thumb = $this->handler->doTransform($this, $thumbPath, $thumbUrl, $params);
         // Ignore errors if requested
         if (!$thumb) {
             $thumb = null;
         } elseif ($thumb->isError()) {
             $this->lastError = $thumb->toText();
             if ($wgIgnoreImageErrors && !($flags & self::RENDER_NOW)) {
                 $thumb = $this->handler->getTransform($this, $thumbPath, $thumbUrl, $params);
             }
         }
         // Purge. Useful in the event of Core -> Squid connection failure or squid
         // purge collisions from elsewhere during failure. Don't keep triggering for
         // "thumbs" which have the main image URL though (bug 13776)
         if ($wgUseSquid && (!$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL())) {
             SquidUpdate::purge(array($thumbUrl));
         }
     } while (false);
     wfProfileOut(__METHOD__);
     return $thumb;
 }
 public function doPostCommitUpdates()
 {
     $file = wfLocalFile($this->title);
     $file->purgeCache();
     $file->purgeDescription();
     $purgeUrls = array();
     foreach ($this->ids as $timestamp) {
         $archiveName = $timestamp . '!' . $this->title->getDBkey();
         $file->purgeOldThumbnails($archiveName);
         $purgeUrls[] = $file->getArchiveUrl($archiveName);
     }
     if ($this->getConfig()->get('UseSquid')) {
         // purge full images from cache
         SquidUpdate::purge($purgeUrls);
     }
     return Status::newGood();
 }
Exemple #25
0
 /**
  * Transform a media file
  *
  * @param array $params an associative array of handler-specific parameters.
  *                Typical keys are width, height and page.
  * @param $flags Integer: a bitfield, may contain self::RENDER_NOW to force rendering
  * @return MediaTransformOutput|bool False on failure
  */
 function transform($params, $flags = 0)
 {
     global $wgUseSquid, $wgIgnoreImageErrors, $wgThumbnailEpoch;
     wfProfileIn(__METHOD__);
     do {
         if (!$this->canRender()) {
             $thumb = $this->iconThumb();
             break;
             // not a bitmap or renderable image, don't try
         }
         // Get the descriptionUrl to embed it as comment into the thumbnail. Bug 19791.
         $descriptionUrl = $this->getDescriptionUrl();
         if ($descriptionUrl) {
             $params['descriptionUrl'] = wfExpandUrl($descriptionUrl, PROTO_CANONICAL);
         }
         $handler = $this->getHandler();
         $script = $this->getTransformScript();
         if ($script && !($flags & self::RENDER_NOW)) {
             // Use a script to transform on client request, if possible
             $thumb = $handler->getScriptedTransform($this, $script, $params);
             if ($thumb) {
                 break;
             }
         }
         $normalisedParams = $params;
         $handler->normaliseParams($this, $normalisedParams);
         $thumbName = $this->thumbName($normalisedParams);
         $thumbUrl = $this->getThumbUrl($thumbName);
         $thumbPath = $this->getThumbPath($thumbName);
         // final thumb path
         if ($this->repo) {
             // Defer rendering if a 404 handler is set up...
             if ($this->repo->canTransformVia404() && !($flags & self::RENDER_NOW)) {
                 wfDebug(__METHOD__ . " transformation deferred.");
                 // XXX: Pass in the storage path even though we are not rendering anything
                 // and the path is supposed to be an FS path. This is due to getScalerType()
                 // getting called on the path and clobbering $thumb->getUrl() if it's false.
                 $thumb = $handler->getTransform($this, $thumbPath, $thumbUrl, $params);
                 break;
             }
             // Clean up broken thumbnails as needed
             $this->migrateThumbFile($thumbName);
             // Check if an up-to-date thumbnail already exists...
             wfDebug(__METHOD__ . ": Doing stat for {$thumbPath}\n");
             if (!($flags & self::RENDER_FORCE) && $this->repo->fileExists($thumbPath)) {
                 $timestamp = $this->repo->getFileTimestamp($thumbPath);
                 if ($timestamp !== false && $timestamp >= $wgThumbnailEpoch) {
                     // XXX: Pass in the storage path even though we are not rendering anything
                     // and the path is supposed to be an FS path. This is due to getScalerType()
                     // getting called on the path and clobbering $thumb->getUrl() if it's false.
                     $thumb = $handler->getTransform($this, $thumbPath, $thumbUrl, $params);
                     $thumb->setStoragePath($thumbPath);
                     break;
                 }
             } elseif ($flags & self::RENDER_FORCE) {
                 wfDebug(__METHOD__ . " forcing rendering per flag File::RENDER_FORCE\n");
             }
         }
         // If the backend is ready-only, don't keep generating thumbnails
         // only to return transformation errors, just return the error now.
         if ($this->repo->getReadOnlyReason() !== false) {
             $thumb = $this->transformErrorOutput($thumbPath, $thumbUrl, $params, $flags);
             break;
         }
         // Create a temp FS file with the same extension and the thumbnail
         $thumbExt = FileBackend::extensionFromPath($thumbPath);
         $tmpFile = TempFSFile::factory('transform_', $thumbExt);
         if (!$tmpFile) {
             $thumb = $this->transformErrorOutput($thumbPath, $thumbUrl, $params, $flags);
             break;
         }
         $tmpThumbPath = $tmpFile->getPath();
         // path of 0-byte temp file
         // Actually render the thumbnail...
         wfProfileIn(__METHOD__ . '-doTransform');
         $thumb = $handler->doTransform($this, $tmpThumbPath, $thumbUrl, $params);
         wfProfileOut(__METHOD__ . '-doTransform');
         $tmpFile->bind($thumb);
         // keep alive with $thumb
         if (!$thumb) {
             // bad params?
             $thumb = null;
         } elseif ($thumb->isError()) {
             // transform error
             $this->lastError = $thumb->toText();
             // Ignore errors if requested
             if ($wgIgnoreImageErrors && !($flags & self::RENDER_NOW)) {
                 $thumb = $handler->getTransform($this, $tmpThumbPath, $thumbUrl, $params);
             }
         } elseif ($this->repo && $thumb->hasFile() && !$thumb->fileIsSource()) {
             // Copy the thumbnail from the file system into storage...
             $disposition = $this->getThumbDisposition($thumbName);
             $status = $this->repo->quickImport($tmpThumbPath, $thumbPath, $disposition);
             if ($status->isOK()) {
                 $thumb->setStoragePath($thumbPath);
             } else {
                 $thumb = $this->transformErrorOutput($thumbPath, $thumbUrl, $params, $flags);
             }
             // Give extensions a chance to do something with this thumbnail...
             wfRunHooks('FileTransformed', array($this, $thumb, $tmpThumbPath, $thumbPath));
         }
         // Purge. Useful in the event of Core -> Squid connection failure or squid
         // purge collisions from elsewhere during failure. Don't keep triggering for
         // "thumbs" which have the main image URL though (bug 13776)
         if ($wgUseSquid) {
             if (!$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL()) {
                 SquidUpdate::purge(array($thumbUrl));
             }
         }
     } while (false);
     wfProfileOut(__METHOD__);
     return is_object($thumb) ? $thumb : false;
 }
Exemple #26
0
 /**
  * Transform a media file
  *
  * @param $params Array: an associative array of handler-specific parameters.
  *                Typical keys are width, height and page.
  * @param $flags Integer: a bitfield, may contain self::RENDER_NOW to force rendering
  * @return MediaTransformOutput | false
  */
 function transform($params, $flags = 0)
 {
     global $wgUseSquid, $wgIgnoreImageErrors, $wgThumbnailEpoch, $wgServer;
     wfProfileIn(__METHOD__);
     do {
         if (!$this->canRender()) {
             // not a bitmap or renderable image, don't try.
             $thumb = $this->iconThumb();
             break;
         }
         // Get the descriptionUrl to embed it as comment into the thumbnail. Bug 19791.
         $descriptionUrl = $this->getDescriptionUrl();
         if ($descriptionUrl) {
             $params['descriptionUrl'] = wfExpandUrl($descriptionUrl, PROTO_CANONICAL);
         }
         $script = $this->getTransformScript();
         if ($script && !($flags & self::RENDER_NOW)) {
             // Use a script to transform on client request, if possible
             $thumb = $this->handler->getScriptedTransform($this, $script, $params);
             if ($thumb) {
                 break;
             }
         }
         $normalisedParams = $params;
         $this->handler->normaliseParams($this, $normalisedParams);
         $thumbName = $this->thumbName($normalisedParams);
         $thumbPath = $this->getThumbPath($thumbName);
         $thumbUrl = $this->getThumbUrl($thumbName);
         if ($this->repo && $this->repo->canTransformVia404() && !($flags & self::RENDER_NOW)) {
             $thumb = $this->handler->getTransform($this, $thumbPath, $thumbUrl, $params);
             break;
         }
         wfDebug(__METHOD__ . ": Doing stat for {$thumbPath}\n");
         $this->migrateThumbFile($thumbName);
         if (file_exists($thumbPath)) {
             $thumbTime = filemtime($thumbPath);
             if ($thumbTime !== FALSE && gmdate('YmdHis', $thumbTime) >= $wgThumbnailEpoch) {
                 $thumb = $this->handler->getTransform($this, $thumbPath, $thumbUrl, $params);
                 break;
             }
         }
         $thumb = $this->handler->doTransform($this, $thumbPath, $thumbUrl, $params);
         // Ignore errors if requested
         if (!$thumb) {
             $thumb = null;
         } elseif ($thumb->isError()) {
             $this->lastError = $thumb->toText();
             if ($wgIgnoreImageErrors && !($flags & self::RENDER_NOW)) {
                 $thumb = $this->handler->getTransform($this, $thumbPath, $thumbUrl, $params);
             }
         }
         // Purge. Useful in the event of Core -> Squid connection failure or squid
         // purge collisions from elsewhere during failure. Don't keep triggering for
         // "thumbs" which have the main image URL though (bug 13776)
         if ($wgUseSquid && (!$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL())) {
             SquidUpdate::purge(array($thumbUrl));
         }
     } while (false);
     wfProfileOut(__METHOD__);
     return is_object($thumb) ? $thumb : false;
 }