Initial commit that reproduces 1.10 implementation functionality
authorPaa Kwesi Imbeah <suuch@users.mediawiki.org>
Tue, 10 Jul 2007 00:38:29 +0000 (00:38 +0000)
committerPaa Kwesi Imbeah <suuch@users.mediawiki.org>
Tue, 10 Jul 2007 00:38:29 +0000 (00:38 +0000)
includes/filerepo/ICRepo.php [new file with mode: 0644]

diff --git a/includes/filerepo/ICRepo.php b/includes/filerepo/ICRepo.php
new file mode 100644 (file)
index 0000000..57ec332
--- /dev/null
@@ -0,0 +1,312 @@
+<?php
+
+/**
+ * A repository for files accessible via InstantCommons. 
+ */
+
+class ICRepo extends LocalRepo {
+       var $directory, $url, $hashLevels, $cache;      
+       var $fileFactory = array( 'ICFile', 'newFromTitle' );
+       var $oldFileFactory = false;
+
+       function __construct( $info ) {
+               parent::__construct( $info );           
+               // Required settings
+               $this->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("/<instantcommons><image(.*?)<\/instantcommons>/",$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
+     * @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){//ref: http://groups-beta.google.com/group/comp.lang.php/browse_thread/thread/8efbbaced3c45e3c/d63c7891cf8e380b?lnk=raot
+        {//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 );
+
+                       # 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;
+       }
+       
+}
+