directory = $info['directory']; $this->url = $info['url']; $this->hashLevels = $info['hashLevels']; if(isset($info['cache'])){ $this->cache = getcwd().'/images/'.$info['cache']; } } } /** * A file loaded from InstantCommons */ class ICFile extends LocalFile{ static function newFromTitle($title,$repo){ return new self($title, $repo); } /** * Returns true if the file comes from the local file repository. * * @return bool */ function isLocal() { return true; } function load(){ if (!$this->dataLoaded ) { if ( !$this->loadFromCache() ) { if(!$this->loadFromDB()){ $this->loadFromIC(); } $this->saveToCache(); } $this->dataLoaded = true; } } /** * Load file metadata from the DB */ function loadFromDB() { wfProfileIn( __METHOD__ ); # Unconditionally set loaded=true, we don't want the accessors constantly rechecking $this->dataLoaded = true; $dbr = $this->repo->getSlaveDB(); $row = $dbr->selectRow( 'ic_image', $this->getCacheFields( 'img_' ), array( 'img_name' => $this->getName() ), __METHOD__ ); if ( $row ) { if (trim($row->img_media_type)==NULL) { $this->upgradeRow(); $this->upgraded = true; } $this->loadFromRow( $row ); //This means that these files are local so the repository locations are local $this->setUrlPathLocal(); $this->fileExists = true; //var_dump($this); exit; } else { $this->fileExists = false; } wfProfileOut( __METHOD__ ); return $this->fileExists; } /** * Fix assorted version-related problems with the image row by reloading it from the file */ function upgradeRow() { wfProfileIn( __METHOD__ ); $this->loadFromIC(); $dbw = $this->repo->getMasterDB(); list( $major, $minor ) = self::splitMime( $this->mime ); wfDebug(__METHOD__.': upgrading '.$this->getName()." to the current schema\n"); $dbw->update( 'ic_image', array( 'img_width' => $this->width, 'img_height' => $this->height, 'img_bits' => $this->bits, 'img_media_type' => $this->type, 'img_major_mime' => $major, 'img_minor_mime' => $minor, 'img_metadata' => $this->metadata, ), array( 'img_name' => $this->getName() ), __METHOD__ ); $this->saveToCache(); wfProfileOut( __METHOD__ ); } function exists(){ $this->load(); return $this->fileExists; } /** * Fetch the file from the repository. Check local ic_images table first. If not available, check remote server */ function loadFromIC(){ # Unconditionally set loaded=true, we don't want the accessors constantly rechecking $this->dataLoaded = true; $icUrl = $this->repo->directory.'&media='.$this->title->mDbkeyform; if($h = @fopen($icUrl, 'rb')){ $contents = fread($h, 3000); $image = $this->api_xml_to_array($contents); if($image['fileExists']){ foreach($image as $property=>$value){ if($property=="url"){$value=$this->repo->url.$value; } $this->$property = $value; } if($this->curl_file_get_contents($this->repo->url.$image['url'], $this->repo->cache.'/'.$image['name'])){ //Record the image $this->recordDownload("Downloaded with InstantCommons"); //Then cache it }else{//set fileExists back to false $this->fileExists = false; } } } } function setUrlPathLocal(){ global $wgScriptPath; $path = $wgScriptPath.'/'.substr($this->repo->cache, strlen($wgScriptPath)); $this->repo->url = $path;//.'/'.rawurlencode($this->title->mDbkeyform); $this->repo->directory = $this->repo->cache;//.'/'.rawurlencode($this->title->mDbkeyform); } function getThumbPath( $suffix=false ){ $path = $this->repo->cache; if ( $suffix !== false ) { $path .= '/thumb/' . rawurlencode( $suffix ); } return $path; } function getThumbUrl( $suffix=false ){ global $wgScriptPath; $path = $wgScriptPath.'/'.substr($this->repo->cache, strlen($wgScriptPath)); if ( $suffix !== false ) { $path .= '/thumb/' . rawurlencode( $suffix ); } return $path; } /** * Convert the InstantCommons Server API XML Response to an associative array */ function api_xml_to_array($xml){ preg_match("//",$xml,$match); preg_match_all("/(.*?=\".*?\")/",$match[1], $matches); foreach($matches[1] as $match){ list($key,$value) = split("=",$match); $image[trim($key,'<" ')]=trim($value,' "'); } return $image; } /** * Use cURL to read the content of a URL into a string * ref: http://groups-beta.google.com/group/comp.lang.php/browse_thread/thread/8efbbaced3c45e3c/d63c7891cf8e380b?lnk=raot * @param string $url - the URL to fetch * @param resource $fp - filename to write file contents to * @param boolean $bg - call cURL in the background (don't hang page until complete) * @param int $timeout - cURL connect timeout */ function curl_file_get_contents($url, $fp, $bg=TRUE, $timeout = 1) { # Call curl in the background to download the file $cmd = 'curl '.wfEscapeShellArg($url).' -o '.$fp.' &'; wfDebug('Curl download initiated='.$cmd ); $success = false; $file_contents = array(); $file_contents['err'] = wfShellExec($cmd, $file_contents['return']); if($file_contents['err']==0){//Success $success = true; } return $success; } function getMasterDB() { if ( !isset( $this->dbConn ) ) { $class = 'Database' . ucfirst( $this->dbType ); $this->dbConn = new $class( $this->dbServer, $this->dbUser, $this->dbPassword, $this->dbName, false, $this->dbFlags, $this->tablePrefix ); } return $this->dbConn; } /** * Record a file upload in the upload log and the image table */ private function recordDownload($comment='', $timestamp = false ){ global $wgUser; $dbw = $this->repo->getMasterDB(); if ( $timestamp === false ) { $timestamp = $dbw->timestamp(); } list( $major, $minor ) = self::splitMime( $this->mime ); # 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( 'ic_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->type, 'img_major_mime' => $major, 'img_minor_mime' => $minor, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $wgUser->getID(), 'img_user_text' => $wgUser->getName(), 'img_metadata' => $this->metadata, ), __METHOD__, 'IGNORE' ); if( $dbw->affectedRows() == 0 ) { # Collision, this is an update of a file # Update the current image row $dbw->update( 'ic_image', array( /* SET */ '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' => $wgUser->getID(), 'img_user_text' => $wgUser->getName(), 'img_metadata' => $this->metadata, ), array( /* WHERE */ '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(); $article = new Article( $descTitle ); # Add the log entry $log = new LogPage( 'icdownload' ); $log->addEntry( 'InstantCommons download', $descTitle, $comment ); if( $descTitle->exists() ) { # Create a null revision $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(), $log->getRcComment(), false ); $nullRevision->insertOn( $dbw ); $article->updateRevisionOn( $dbw, $nullRevision ); # Invalidate the cache for the description page $descTitle->invalidateCache(); $descTitle->purgeSquid(); } # Commit the transaction now, in case something goes wrong later # The most important thing is that files don't get lost, especially archives $dbw->immediateCommit(); # Invalidate cache for all pages using this file $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' ); $update->doUpdate(); return true; } }