public function execute() { $stdin = $this->getStdin(); $urls = array(); while (!feof($stdin)) { $page = trim(fgets($stdin)); if (substr($page, 0, 7) == 'http://') { $urls[] = $page; } elseif ($page !== '') { $title = Title::newFromText($page); if ($title) { $url = $title->getFullUrl(); $this->output("{$url}\n"); $urls[] = $url; if (isset($options['purge'])) { $title->invalidateCache(); } } else { $this->output("(Invalid title '{$page}')\n"); } } } $this->output("Purging " . count($urls) . " urls...\n"); $u = new SquidUpdate($urls); $u->doUpdate(); $this->output("Done!\n"); }
public function execute() { global $wgUser, $wgArticleFeedbackRatingTypes, $wgArticleFeedbackSMaxage, $wgArticleFeedbackNamespaces; $params = $this->extractRequestParams(); // Anon token check if ($wgUser->isAnon()) { if (!isset($params['anontoken'])) { $this->dieUsageMsg(array('missingparam', 'anontoken')); } elseif (strlen($params['anontoken']) != 32) { $this->dieUsage('The anontoken is not 32 characters', 'invalidtoken'); } $token = $params['anontoken']; } else { $token = ''; } // Load check, is this page ArticleFeedback-enabled ? // Keep in sync with ext.articleFeedback.startup.js $title = Title::newFromID($params['pageid']); if (is_null($title) || !in_array($title->getNamespace(), $wgArticleFeedbackNamespaces) || $title->isRedirect()) { // ...then error out $this->dieUsage('ArticleFeedback is not enabled on this page', 'invalidpage'); } $dbw = wfGetDB(DB_MASTER); // Query the latest ratings by this user for this page, // possibly for an older revision // Select from the master to prevent replag-induced bugs $res = $dbw->select('article_feedback', array('aa_rating_id', 'aa_rating_value', 'aa_revision'), array('aa_user_text' => $wgUser->getName(), 'aa_page_id' => $params['pageid'], 'aa_rating_id' => array_keys($wgArticleFeedbackRatingTypes), 'aa_user_anon_token' => $token), __METHOD__, array('ORDER BY' => 'aa_revision DESC', 'LIMIT' => count($wgArticleFeedbackRatingTypes))); $lastRatings = array(); foreach ($res as $row) { $lastRatings[$row->aa_rating_id]['value'] = $row->aa_rating_value; $lastRatings[$row->aa_rating_id]['revision'] = $row->aa_revision; } $pageId = $params['pageid']; $revisionId = $params['revid']; foreach ($wgArticleFeedbackRatingTypes as $ratingID => $unused) { $lastPageRating = false; $lastRevRating = false; if (isset($lastRatings[$ratingID])) { $lastPageRating = intval($lastRatings[$ratingID]['value']); if (intval($lastRatings[$ratingID]['revision']) == $revisionId) { $lastRevRating = $lastPageRating; } } $thisRating = false; if (isset($params["r{$ratingID}"])) { $thisRating = intval($params["r{$ratingID}"]); } $this->insertRevisionRating($pageId, $revisionId, $ratingID, $thisRating - $lastRevRating, $thisRating, $lastRevRating); $this->insertPageRating($pageId, $ratingID, $thisRating - $lastPageRating, $thisRating, $lastPageRating); $this->insertUserRatings($pageId, $revisionId, $wgUser, $token, $ratingID, $thisRating, $params['bucket']); } $this->insertProperties($revisionId, $wgUser, $token, $params); $squidUpdate = new SquidUpdate(array(wfAppendQuery(wfScript('api'), array('action' => 'query', 'format' => 'json', 'list' => 'articlefeedback', 'afpageid' => $pageId, 'afanontoken' => '', 'afuserrating' => 0, 'maxage' => 0, 'smaxage' => $wgArticleFeedbackSMaxage)))); $squidUpdate->doUpdate(); wfRunHooks('ArticleFeedbackChangeRating', array($params)); $r = array('result' => 'Success'); $this->getResult()->addValue(null, $this->getModuleName(), $r); }
public function purgeVarnish() { global $wgHubRssFeeds, $wgServer; echo "| Purging varnishen..." . PHP_EOL; $urls = []; foreach ($wgHubRssFeeds as $feedEndpoint) { $urls[] = SpecialPage::getTitleFor(HubRssFeedSpecialController::SPECIAL_NAME)->getFullUrl() . '/' . $feedEndpoint; $urls[] = implode('/', [$wgServer, 'rss', $feedEndpoint]); } $u = new SquidUpdate($urls); $u->doUpdate(); }
function renderTimeline($timelinesrc) { global $wgUploadDirectory, $wgUploadPath, $IP, $wgTimelineSettings, $wgArticlePath, $wgTmpDirectory; $hash = md5($timelinesrc); $dest = $wgUploadDirectory . "/timeline/"; if (!is_dir($dest)) { mkdir($dest, 0777); } if (!is_dir($wgTmpDirectory)) { mkdir($wgTmpDirectory, 0777); } $fname = $dest . $hash; $previouslyFailed = file_exists($fname . ".err"); $previouslyRendered = file_exists($fname . ".png"); $expired = $previouslyRendered && filemtime($fname . ".png") < wfTimestamp(TS_UNIX, $wgTimelineSettings->epochTimestamp); if ($expired || !$previouslyRendered && !$previouslyFailed) { $handle = fopen($fname, "w"); fwrite($handle, $timelinesrc); fclose($handle); $cmdline = wfEscapeShellArg($wgTimelineSettings->perlCommand, $wgTimelineSettings->timelineFile) . " -i " . wfEscapeShellArg($fname) . " -m -P " . wfEscapeShellArg($wgTimelineSettings->ploticusCommand) . " -T " . wfEscapeShellArg($wgTmpDirectory) . " -A " . wfEscapeShellArg($wgArticlePath); $ret = `{$cmdline}`; unlink($fname); if ($ret == "") { // Message not localized, only relevant during install return "<div id=\"toc\"><tt>Timeline error: Executable not found. Command line was: {$cmdline}</tt></div>"; } } @($err = file_get_contents($fname . ".err")); if ($err != "") { $txt = "<div id=\"toc\"><tt>{$err}</tt></div>"; } else { @($map = file_get_contents($fname . ".map")); if (substr(php_uname(), 0, 7) == "Windows") { $ext = "gif"; } else { $ext = "png"; } $url = "{$wgUploadPath}/timeline/{$hash}.{$ext}"; $txt = "<map name=\"{$hash}\">{$map}</map>" . "<img usemap=\"#{$hash}\" src=\"{$url}\">"; if ($expired) { // Replacing an older file, we may need to purge the old one. global $wgUseSquid; if ($wgUseSquid) { $u = new SquidUpdate(array($url)); $u->doUpdate(); } } } return $txt; }
/** * Purge the cache for backlinking pages (that is, pages containing * a reference to the Title associated with this task) * * @param string|array $tables */ public function purge($tables) { global $wgUseFileCache, $wgUseSquid; $affectedTitles = $this->getAffectedTitles((array) $tables); $affectedCount = count($affectedTitles); $this->info("Purge Request", ['title' => $this->title->getPrefixedText(), 'count' => $affectedCount, 'tables' => $tables]); // abort if no pages link to the associated Title if ($affectedCount == 0) { return 0; } $dbw = wfGetDB(DB_MASTER); (new \WikiaSQL())->UPDATE('page')->SET('page_touched', $dbw->timestamp())->WHERE('page_id')->IN(array_map(function ($t) { return $t->getArticleID(); }, $affectedTitles))->run($dbw); // Update squid/varnish if ($wgUseSquid) { \SquidUpdate::newFromTitles($affectedTitles)->doUpdate(); } // Update file cache if ($wgUseFileCache) { foreach ($affectedTitles as $title) { \HTMLFileCache::clearFileCache($title); } } return $affectedCount; }
/** * Invalidate a set of pages, right now */ public function invalidate($startId = false, $endId = false) { global $wgUseFileCache, $wgUseSquid; $titleArray = $this->mCache->getLinks($this->mTable, $startId, $endId); if ($titleArray->count() == 0) { return; } $dbw = wfGetDB(DB_MASTER); $timestamp = $dbw->timestamp(); # Get all IDs in this query into an array $ids = array(); foreach ($titleArray as $title) { $ids[] = $title->getArticleID(); } # Update page_touched $dbw->update('page', array('page_touched' => $timestamp), array('page_id IN (' . $dbw->makeList($ids) . ')'), __METHOD__); # Update squid if ($wgUseSquid) { $u = SquidUpdate::newFromTitles($titleArray); $u->doUpdate(); } # Update file cache if ($wgUseFileCache) { foreach ($titleArray as $title) { HTMLFileCache::clearFileCache($title); } } }
/** * 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"; }
/** * Add array of URLs to the purger queue * * @param Array $urlArr list of URLs to purge * @throws WikiaException */ static function purge($urlArr) { global $wgEnableScribeReport; wfProfileIn(__METHOD__); if (empty($wgEnableScribeReport)) { wfProfileOut(__METHOD__); return; } foreach ($urlArr as $url) { if (!is_string($url)) { throw new WikiaException('Bad purge URL'); } $url = SquidUpdate::expand($url); $method = self::getPurgeCaller(); wfDebug("Purging URL {$url} from {$method} via Scribe\n"); wfDebug("Purging backtrace: " . wfGetAllCallers(false) . "\n"); // add to the queue, will be sent by onRestInPeace method self::$urls[$url] = ['url' => $url, 'time' => time(), 'method' => $method]; self::$urlsCount++; } wfProfileOut(__METHOD__); }
/** * Invalidate an array (or iterator) of Title objects, right now * @param $titleArray array */ protected function invalidateTitles($titleArray) { global $wgUseFileCache, $wgUseSquid; $dbw = wfGetDB(DB_MASTER); $timestamp = $dbw->timestamp(); # Get all IDs in this query into an array $ids = array(); foreach ($titleArray as $title) { $ids[] = $title->getArticleID(); } if (!$ids) { return; } # Update page_touched $batches = array_chunk($ids, $this->mRowsPerQuery); foreach ($batches as $batch) { $dbw->update('page', array('page_touched' => $timestamp), array('page_id' => $batch), __METHOD__); } # Update squid if ($wgUseSquid) { $u = SquidUpdate::newFromTitles($titleArray); $u->doUpdate(); } # Update file cache if ($wgUseFileCache) { foreach ($titleArray as $title) { HTMLFileCache::clearFileCache($title); } } }
/** * Helper to purge an array of $urls * @param array $urls List of URLS to purge from squids */ private function sendPurgeRequest($urls) { if ($this->hasOption('delay')) { $delay = floatval($this->getOption('delay')); foreach ($urls as $url) { if ($this->hasOption('verbose')) { $this->output($url . "\n"); } $u = new SquidUpdate(array($url)); $u->doUpdate(); usleep($delay * 1000000.0); } } else { if ($this->hasOption('verbose')) { $this->output(implode("\n", $urls) . "\n"); } $u = new SquidUpdate($urls); $u->doUpdate(); } }
function recordUpload($oldver, $desc, $copyStatus = '', $source = '', $watch = false) { global $wgUser, $wgLang, $wgTitle, $wgDeferredUpdateList; global $wgUseCopyrightUpload, $wgUseSquid, $wgPostCommitUpdateList; $img = Image::newFromName($this->mUploadSaveName); $fname = 'Image::recordUpload'; $dbw =& wfGetDB(DB_MASTER); Image::checkDBSchema($dbw); // Delete thumbnails and refresh the metadata cache $img->purgeCache(); // Fail now if the image isn't there if (!$img->fileExists || $img->fromSharedDirectory) { wfDebug("Image::recordUpload: File " . $img->imagePath . " went missing!\n"); return false; } if ($wgUseCopyrightUpload) { $textdesc = '== ' . wfMsg('filedesc') . " ==\n" . $desc . "\n" . '== ' . wfMsg('filestatus') . " ==\n" . $copyStatus . "\n" . '== ' . wfMsg('filesource') . " ==\n" . $source; } else { $textdesc = $desc; } $now = $dbw->timestamp(); #split mime type if (strpos($img->mime, '/') !== false) { list($major, $minor) = explode('/', $img->mime, 2); } else { $major = $img->mime; $minor = "unknown"; } # 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' => $img->name, 'img_size' => $img->size, 'img_width' => IntVal($img->width), 'img_height' => IntVal($img->height), 'img_bits' => $img->bits, 'img_media_type' => $img->type, 'img_major_mime' => $major, 'img_minor_mime' => $minor, 'img_timestamp' => $now, 'img_description' => $desc, 'img_user' => $wgUser->getID(), 'img_user_text' => $wgUser->getName(), 'img_metadata' => $img->metadata), $fname, 'IGNORE'); $descTitle = $img->getTitle(); $purgeURLs = array(); $article = new Article($descTitle); $minor = false; $watch = $watch || $wgUser->isWatched($descTitle); $suppressRC = true; // There's already a log entry, so don't double the RC load if ($descTitle->exists()) { // TODO: insert a null revision into the page history for this update. if ($watch) { $wgUser->addWatch($descTitle); } # Invalidate the cache for the description page $descTitle->invalidateCache(); $purgeURLs[] = $descTitle->getInternalURL(); } else { $this->insertNewArticle($article, $textdesc, $desc, $minor, $watch, $suppressRC); } # Invalidate cache for all pages using this image $linksTo = $img->getLinksTo(); if ($wgUseSquid) { $u = SquidUpdate::newFromTitles($linksTo, $purgeURLs); array_push($wgPostCommitUpdateList, $u); } Title::touchArray($linksTo); $log = new LogPage('upload'); $log->addEntry('upload', $descTitle, $desc); return true; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * Perform the actions of a page purging */ public function doPurge() { global $wgUseSquid; // Invalidate the cache $this->mTitle->invalidateCache(); if ($wgUseSquid) { // Commit the transaction before the purge is sent $dbw = wfGetDB(DB_MASTER); $dbw->commit(); // Send purge $update = SquidUpdate::newSimplePurge($this->mTitle); $update->doUpdate(); } if ($this->mTitle->getNamespace() == NS_MEDIAWIKI) { global $wgMessageCache; if ($this->getID() == 0) { $text = false; } else { $text = $this->getRawText(); } $wgMessageCache->replace($this->mTitle->getDBkey(), $text); } }
/** * Queue a purge operation * * @param string $url */ public function queuePurge($url) { global $wgSquidPurgeUseHostHeader; $url = SquidUpdate::expand(str_replace("\n", '', $url)); $request = array(); if ($wgSquidPurgeUseHostHeader) { $url = wfParseUrl($url); $host = $url['host']; if (isset($url['port']) && strlen($url['port']) > 0) { $host .= ":" . $url['port']; } $path = $url['path']; if (isset($url['query']) && is_string($url['query'])) { $path = wfAppendQuery($path, $url['query']); } $request[] = "PURGE {$path} HTTP/1.1"; $request[] = "Host: {$host}"; } else { $request[] = "PURGE {$url} HTTP/1.0"; } $request[] = "Connection: Keep-Alive"; $request[] = "Proxy-Connection: Keep-Alive"; $request[] = "User-Agent: " . Http::userAgent() . ' ' . __CLASS__; // Two ''s to create \r\n\r\n $request[] = ''; $request[] = ''; $this->requests[] = implode("\r\n", $request); if ($this->currentRequestIndex === null) { $this->nextRequest(); } }
<?php /** * Send purge requests for listed pages to squid */ require_once "commandLine.inc"; $stdin = fopen("php://stdin", "rt"); $urls = array(); while (!feof($stdin)) { $page = trim(fgets($stdin)); if (substr($page, 0, 7) == 'http://') { $urls[] = $page; } elseif ($page !== '') { $title = Title::newFromText($page); if ($title) { $url = $title->getFullUrl(); echo "{$url}\n"; $urls[] = $url; if (isset($options['purge'])) { $title->invalidateCache(); } } else { echo "(Invalid title '{$page}')\n"; } } } echo "Purging " . count($urls) . " urls...\n"; $u = new SquidUpdate($urls); $u->doUpdate(); echo "Done!\n";
/** * 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; }
/** * Invalidate a set of IDs, right now */ function invalidateIDs(ResultWrapper $res) { global $wgUseFileCache, $wgUseSquid; if ($res->numRows() == 0) { return; } $dbw =& wfGetDB(DB_MASTER); $timestamp = $dbw->timestamp(); $done = false; while (!$done) { # Get all IDs in this query into an array $ids = array(); for ($i = 0; $i < $this->mRowsPerQuery; $i++) { $row = $res->fetchRow(); if ($row) { $ids[] = $row[0]; } else { $done = true; break; } } if (!count($ids)) { break; } # Update page_touched $dbw->update('page', array('page_touched' => $timestamp), array('page_id IN (' . $dbw->makeList($ids) . ')'), __METHOD__); # Update squid if ($wgUseSquid || $wgUseFileCache) { $titles = Title::newFromIDs($ids); if ($wgUseSquid) { $u = SquidUpdate::newFromTitles($titles); $u->doUpdate(); } # Update file cache if ($wgUseFileCache) { foreach ($titles as $title) { $cm = new CacheManager($title); @unlink($cm->fileCacheName()); } } } } }
/** * Perform the actions of a page purging * @return bool */ public function doPurge() { if (!Hooks::run('ArticlePurge', array(&$this))) { return false; } $title = $this->mTitle; wfGetDB(DB_MASTER)->onTransactionIdle(function () use($title) { global $wgUseSquid; // Invalidate the cache in auto-commit mode $title->invalidateCache(); if ($wgUseSquid) { // Send purge now that page_touched update was committed above $update = new SquidUpdate($title->getSquidURLs()); $update->doUpdate(); } }); if ($this->mTitle->getNamespace() == NS_MEDIAWIKI) { // @todo move this logic to MessageCache if ($this->exists()) { // NOTE: use transclusion text for messages. // This is consistent with MessageCache::getMsgFromNamespace() $content = $this->getContent(); $text = $content === null ? null : $content->getWikitextForTransclusion(); if ($text === null) { $text = false; } } else { $text = false; } MessageCache::singleton()->replace($this->mTitle->getDBkey(), $text); } return true; }
/** * Purge all applicable Squid URLs */ public function purgeSquid() { global $wgUseSquid; if ($wgUseSquid) { $urls = $this->getSquidURLs(); $u = new SquidUpdate($urls); $u->doUpdate(); } }
/** * Invalidate an array (or iterator) of Title objects, right now * @param $titleArray array */ protected function invalidateTitles($titleArray) { global $wgUseFileCache, $wgUseSquid; $dbw = wfGetDB(DB_MASTER); $timestamp = $dbw->timestamp(); # Get all IDs in this query into an array $ids = array(); foreach ($titleArray as $title) { $ids[] = $title->getArticleID(); } if (!$ids) { return; } # Don't invalidated pages that were already invalidated $touchedCond = isset($this->params['rootJobTimestamp']) ? array("page_touched < " . $dbw->addQuotes($dbw->timestamp($this->params['rootJobTimestamp']))) : array(); # Update page_touched $batches = array_chunk($ids, $this->rowsPerQuery); foreach ($batches as $batch) { $dbw->update('page', array('page_touched' => $timestamp), array('page_id' => $batch) + $touchedCond, __METHOD__); } # Update squid if ($wgUseSquid) { $u = SquidUpdate::newFromTitles($titleArray); $u->doUpdate(); } # Update file cache if ($wgUseFileCache) { foreach ($titleArray as $title) { HTMLFileCache::clearFileCache($title); } } }
/** * Perform the actions of a page purging * @return bool */ public function doPurge() { global $wgUseSquid; if ( !wfRunHooks( 'ArticlePurge', array( &$this ) ) ) { return false; } // Invalidate the cache $this->mTitle->invalidateCache(); if ( $wgUseSquid ) { // Commit the transaction before the purge is sent $dbw = wfGetDB( DB_MASTER ); $dbw->commit( __METHOD__ ); // Send purge $update = SquidUpdate::newSimplePurge( $this->mTitle ); $update->doUpdate(); } if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { // @todo move this logic to MessageCache if ( $this->exists() ) { // NOTE: use transclusion text for messages. // This is consistent with MessageCache::getMsgFromNamespace() $content = $this->getContent(); $text = $content === null ? null : $content->getWikitextForTransclusion(); if ( $text === null ) { $text = false; } } else { $text = false; } MessageCache::singleton()->replace( $this->mTitle->getDBkey(), $text ); } return true; }
/** * Move page to a title which is at present a redirect to the * source page * * @param Title &$nt the page to move to, which should currently * be a redirect */ private function moveOverExistingRedirect(&$nt, $reason = '') { global $wgUseSquid; $fname = 'Title::moveOverExistingRedirect'; $comment = wfMsgForContent('1movedto2_redir', $this->getPrefixedText(), $nt->getPrefixedText()); if ($reason) { $comment .= ": {$reason}"; } $now = wfTimestampNow(); $newid = $nt->getArticleID(); $oldid = $this->getArticleID(); $dbw = wfGetDB(DB_MASTER); $linkCache =& LinkCache::singleton(); # Delete the old redirect. We don't save it to history since # by definition if we've got here it's rather uninteresting. # We have to remove it so that the next step doesn't trigger # a conflict on the unique namespace+title index... $dbw->delete('page', array('page_id' => $newid), $fname); # Save a null revision in the page's history notifying of the move $nullRevision = Revision::newNullRevision($dbw, $oldid, $comment, true); $nullRevId = $nullRevision->insertOn($dbw); # Change the name of the target page: $dbw->update('page', array('page_touched' => $dbw->timestamp($now), 'page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey(), 'page_latest' => $nullRevId), array('page_id' => $oldid), $fname); $linkCache->clearLink($nt->getPrefixedDBkey()); # Recreate the redirect, this time in the other direction. $mwRedir = MagicWord::get('redirect'); $redirectText = $mwRedir->getSynonym(0) . ' [[' . $nt->getPrefixedText() . "]]\n"; $redirectArticle = new Article($this); $newid = $redirectArticle->insertOn($dbw); $redirectRevision = new Revision(array('page' => $newid, 'comment' => $comment, 'text' => $redirectText)); $redirectRevision->insertOn($dbw); $redirectArticle->updateRevisionOn($dbw, $redirectRevision, 0); $linkCache->clearLink($this->getPrefixedDBkey()); # Log the move $log = new LogPage('move'); $log->addEntry('move_redir', $this, $reason, array(1 => $nt->getPrefixedText())); # Now, we record the link from the redirect to the new title. # It should have no other outgoing links... $dbw->delete('pagelinks', array('pl_from' => $newid), $fname); $dbw->insert('pagelinks', array('pl_from' => $newid, 'pl_namespace' => $nt->getNamespace(), 'pl_title' => $nt->getDbKey()), $fname); # Purge squid if ($wgUseSquid) { $urls = array_merge($nt->getSquidURLs(), $this->getSquidURLs()); $u = new SquidUpdate($urls); $u->doUpdate(); } }
/** * 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; }
function render() { global $wgTmpDirectory, $wgInputEncoding; global $wgTexvc, $wgMathCheckFiles, $wgTexvcBackgroundColor; if ($this->mode == MW_MATH_SOURCE) { # No need to render or parse anything more! # New lines are replaced with spaces, which avoids confusing our parser (bugs 23190, 22818) return '<span class="tex">$ ' . str_replace("\n", " ", htmlspecialchars($this->tex)) . ' $</span>'; } if ($this->tex == '') { return; # bug 8372 } if (!$this->_recall()) { if ($wgMathCheckFiles) { # Ensure that the temp and output directories are available before continuing... if (!file_exists($wgTmpDirectory)) { if (!wfMkdirParents($wgTmpDirectory)) { return $this->_error('math_bad_tmpdir'); } } elseif (!is_dir($wgTmpDirectory) || !is_writable($wgTmpDirectory)) { return $this->_error('math_bad_tmpdir'); } } if (!is_executable($wgTexvc)) { return $this->_error('math_notexvc'); } $cmd = $wgTexvc . ' ' . escapeshellarg($wgTmpDirectory) . ' ' . escapeshellarg($wgTmpDirectory) . ' ' . escapeshellarg($this->tex) . ' ' . escapeshellarg($wgInputEncoding) . ' ' . escapeshellarg($wgTexvcBackgroundColor); if (wfIsWindows()) { # Invoke it within cygwin sh, because texvc expects sh features in its default shell $cmd = 'sh -c ' . wfEscapeShellArg($cmd); } wfDebug("TeX: {$cmd}\n"); $contents = wfShellExec($cmd); wfDebug("TeX output:\n {$contents}\n---\n"); if (strlen($contents) == 0) { return $this->_error('math_unknown_error'); } $retval = substr($contents, 0, 1); $errmsg = ''; if ($retval == 'C' || $retval == 'M' || $retval == 'L') { if ($retval == 'C') { $this->conservativeness = 2; } else { if ($retval == 'M') { $this->conservativeness = 1; } else { $this->conservativeness = 0; } } $outdata = substr($contents, 33); $i = strpos($outdata, ""); $this->html = substr($outdata, 0, $i); $this->mathml = substr($outdata, $i + 1); } else { if ($retval == 'c' || $retval == 'm' || $retval == 'l') { $this->html = substr($contents, 33); if ($retval == 'c') { $this->conservativeness = 2; } else { if ($retval == 'm') { $this->conservativeness = 1; } else { $this->conservativeness = 0; } } $this->mathml = null; } else { if ($retval == 'X') { $this->html = null; $this->mathml = substr($contents, 33); $this->conservativeness = 0; } else { if ($retval == '+') { $this->html = null; $this->mathml = null; $this->conservativeness = 0; } else { $errbit = htmlspecialchars(substr($contents, 1)); switch ($retval) { case 'E': $errmsg = $this->_error('math_lexing_error', $errbit); break; case 'S': $errmsg = $this->_error('math_syntax_error', $errbit); break; case 'F': $errmsg = $this->_error('math_unknown_function', $errbit); break; default: $errmsg = $this->_error('math_unknown_error', $errbit); } } } } } if (!$errmsg) { $this->hash = substr($contents, 1, 32); } wfRunHooks('MathAfterTexvc', array(&$this, &$errmsg)); if ($errmsg) { return $errmsg; } if (!preg_match("/^[a-f0-9]{32}\$/", $this->hash)) { return $this->_error('math_unknown_error'); } if (!file_exists("{$wgTmpDirectory}/{$this->hash}.png")) { return $this->_error('math_image_error'); } if (filesize("{$wgTmpDirectory}/{$this->hash}.png") == 0) { return $this->_error('math_image_error'); } $hashpath = $this->_getHashPath(); if (!file_exists($hashpath)) { wfSuppressWarnings(); $ret = wfMkdirParents($hashpath, 0755); wfRestoreWarnings(); if (!$ret) { return $this->_error('math_bad_output'); } } elseif (!is_dir($hashpath) || !is_writable($hashpath)) { return $this->_error('math_bad_output'); } if (!rename("{$wgTmpDirectory}/{$this->hash}.png", "{$hashpath}/{$this->hash}.png")) { return $this->_error('math_output_error'); } # Now save it back to the DB: if (!wfReadOnly()) { $outmd5_sql = pack('H32', $this->hash); $md5_sql = pack('H32', $this->md5); # Binary packed, not hex $dbw = wfGetDB(DB_MASTER); $dbw->replace('math', array('math_inputhash'), array('math_inputhash' => $dbw->encodeBlob($md5_sql), 'math_outputhash' => $dbw->encodeBlob($outmd5_sql), 'math_html_conservativeness' => $this->conservativeness, 'math_html' => $this->html, 'math_mathml' => $this->mathml), __METHOD__); } // If we're replacing an older version of the image, make sure it's current. global $wgUseSquid; if ($wgUseSquid) { $urls = array($this->_mathImageUrl()); $u = new SquidUpdate($urls); $u->doUpdate(); } } return $this->_doRender(); }