]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/filerepo/ICRepo.php
MediaWiki 1.11.0
[autoinstalls/mediawiki.git] / includes / filerepo / ICRepo.php
1 <?php
2
3 /**
4  * A repository for files accessible via InstantCommons. 
5  */
6
7 class ICRepo extends LocalRepo {
8         var $directory, $url, $hashLevels, $cache;      
9         var $fileFactory = array( 'ICFile', 'newFromTitle' );
10         var $oldFileFactory = false;
11
12         function __construct( $info ) {
13                 parent::__construct( $info );           
14                 // Required settings
15                 $this->directory = $info['directory'];
16                 $this->url = $info['url'];
17                 $this->hashLevels = $info['hashLevels'];
18                 if(isset($info['cache'])){
19                         $this->cache = getcwd().'/images/'.$info['cache'];
20                 }               
21         }               
22 }
23
24 /**
25  * A file loaded from InstantCommons
26  */
27 class ICFile extends LocalFile{
28         static function newFromTitle($title,$repo){
29                 return new self($title, $repo);         
30         }
31         
32         /**
33          * Returns true if the file comes from the local file repository.
34          *
35          * @return bool
36          */
37         function isLocal() { 
38                 return true; 
39         }
40                 
41         function load(){
42                 if (!$this->dataLoaded ) {
43                         if ( !$this->loadFromCache() ) {
44                                 if(!$this->loadFromDB()){
45                                         $this->loadFromIC();
46                                 }                               
47                                 $this->saveToCache(); 
48                         }
49                         $this->dataLoaded = true;
50                 }               
51         }
52         
53         /**
54          * Load file metadata from the DB
55          */
56         function loadFromDB() {
57                 wfProfileIn( __METHOD__ );
58
59                 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
60                 $this->dataLoaded = true;
61
62                 $dbr = $this->repo->getSlaveDB();
63
64                 $row = $dbr->selectRow( 'ic_image', $this->getCacheFields( 'img_' ),
65                         array( 'img_name' => $this->getName() ), __METHOD__ ); 
66                 if ( $row ) {
67                         if (trim($row->img_media_type)==NULL) {
68                                 $this->upgradeRow();
69                                 $this->upgraded = true;
70                         } 
71                         $this->loadFromRow( $row );
72                         //This means that these files are local so the repository locations are local
73                         $this->setUrlPathLocal();                       
74                         $this->fileExists = true;
75                         //var_dump($this); exit;
76                 } else {
77                         $this->fileExists = false;
78                 }
79
80                 wfProfileOut( __METHOD__ );
81                 
82                 return $this->fileExists;
83         }
84         
85                 /**
86          * Fix assorted version-related problems with the image row by reloading it from the file
87          */
88         function upgradeRow() {
89                 wfProfileIn( __METHOD__ );
90
91                 $this->loadFromIC();
92
93                 $dbw = $this->repo->getMasterDB();
94                 list( $major, $minor ) = self::splitMime( $this->mime );
95
96                 wfDebug(__METHOD__.': upgrading '.$this->getName()." to the current schema\n");
97
98                 $dbw->update( 'ic_image',
99                         array(
100                                 'img_width' => $this->width,
101                                 'img_height' => $this->height,
102                                 'img_bits' => $this->bits,
103                                 'img_media_type' => $this->type,
104                                 'img_major_mime' => $major,
105                                 'img_minor_mime' => $minor,
106                                 'img_metadata' => $this->metadata,
107                         ), array( 'img_name' => $this->getName() ),
108                         __METHOD__
109                 );
110                 $this->saveToCache();
111                 wfProfileOut( __METHOD__ );
112         }
113         
114         function exists(){
115                 $this->load();
116                 return $this->fileExists;
117                 
118         }
119         
120         /**
121          * Fetch the file from the repository. Check local ic_images table first. If not available, check remote server
122          */      
123          function loadFromIC(){
124                 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
125                 $this->dataLoaded = true;
126                 $icUrl = $this->repo->directory.'&media='.$this->title->mDbkeyform; 
127                 if($h = @fopen($icUrl, 'rb')){                  
128                         $contents = fread($h, 3000);
129                         $image = $this->api_xml_to_array($contents);
130                         if($image['fileExists']){
131                                 foreach($image as $property=>$value){
132                                         if($property=="url"){$value=$this->repo->url.$value; }                          
133                                         $this->$property = $value;
134                                 }  
135                                  if($this->curl_file_get_contents($this->repo->url.$image['url'], $this->repo->cache.'/'.$image['name'])){
136                                         //Record the image      
137                                         $this->recordDownload("Downloaded with InstantCommons");
138                         
139                                         //Then cache it                                                 
140                                  }else{//set fileExists back to false                           
141                                         $this->fileExists = false;
142                                  }                       
143                         }
144                 }
145          }
146          
147         
148          function setUrlPathLocal(){
149                 global $wgScriptPath;
150                 $path = $wgScriptPath.'/'.substr($this->repo->cache, strlen($wgScriptPath));
151                 $this->repo->url = $path;//.'/'.rawurlencode($this->title->mDbkeyform);         
152                 $this->repo->directory = $this->repo->cache;//.'/'.rawurlencode($this->title->mDbkeyform);
153                 
154          }
155         
156          function getThumbPath( $suffix=false ){
157                 $path = $this->repo->cache;
158                 if ( $suffix !== false ) {
159                         $path .= '/thumb/' . rawurlencode( $suffix );
160                 }
161                 return $path;
162          }
163          function getThumbUrl( $suffix=false ){
164                 global $wgScriptPath;
165                 $path = $wgScriptPath.'/'.substr($this->repo->cache, strlen($wgScriptPath));
166                 if ( $suffix !== false ) {
167                         $path .= '/thumb/' . rawurlencode( $suffix );
168                 }
169                 return $path;
170          }
171         
172          /**
173           * Convert the InstantCommons Server API XML Response to an associative array
174           */
175           function api_xml_to_array($xml){
176                  preg_match("/<instantcommons><image(.*?)<\/instantcommons>/",$xml,$match);
177                  preg_match_all("/(.*?=\".*?\")/",$match[1], $matches);
178                  foreach($matches[1] as $match){
179                         list($key,$value) = split("=",$match);
180                         $image[trim($key,'<" ')]=trim($value,' "');
181                  }      
182                  return $image;
183           }
184           
185         /**
186      * Use cURL to read the content of a URL into a string
187      * ref: http://groups-beta.google.com/group/comp.lang.php/browse_thread/thread/8efbbaced3c45e3c/d63c7891cf8e380b?lnk=raot
188      * @param string $url - the URL to fetch
189      * @param resource $fp - filename to write file contents to
190      * @param boolean $bg - call cURL in the background (don't hang page until complete)
191      * @param int $timeout - cURL connect timeout
192      */
193     function curl_file_get_contents($url, $fp, $bg=TRUE, $timeout = 1) {
194         {
195                 # Call curl in the background to download the file
196                 $cmd = 'curl '.wfEscapeShellArg($url).' -o '.$fp.' &';
197                 wfDebug('Curl download initiated='.$cmd );
198                 $success = false;  
199                 $file_contents = array();                        
200             $file_contents['err'] = wfShellExec($cmd, $file_contents['return']);
201             if($file_contents['err']==0){//Success
202                 $success = true;
203             }                
204         }
205        return $success;                
206     }
207         
208         function getMasterDB() {
209                 if ( !isset( $this->dbConn ) ) {
210                         $class = 'Database' . ucfirst( $this->dbType );
211                         $this->dbConn = new $class( $this->dbServer, $this->dbUser, 
212                                 $this->dbPassword, $this->dbName, false, $this->dbFlags, 
213                                 $this->tablePrefix );
214                 }
215                 return $this->dbConn;
216         }
217
218         /**
219          * Record a file upload in the upload log and the image table
220          */
221         private function recordDownload($comment='', $timestamp = false ){
222                 global $wgUser; 
223
224                 $dbw = $this->repo->getMasterDB();
225                 
226                 if ( $timestamp === false ) {
227                         $timestamp = $dbw->timestamp();
228                 }
229                 list( $major, $minor ) = self::splitMime( $this->mime );
230
231                 # Test to see if the row exists using INSERT IGNORE
232                 # This avoids race conditions by locking the row until the commit, and also
233                 # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
234                 $dbw->insert( 'ic_image',
235                         array(
236                                 'img_name' => $this->getName(),
237                                 'img_size'=> $this->size,
238                                 'img_width' => intval( $this->width ),
239                                 'img_height' => intval( $this->height ),
240                                 'img_bits' => $this->bits,
241                                 'img_media_type' => $this->type,
242                                 'img_major_mime' => $major,
243                                 'img_minor_mime' => $minor,
244                                 'img_timestamp' => $timestamp,
245                                 'img_description' => $comment,
246                                 'img_user' => $wgUser->getID(),
247                                 'img_user_text' => $wgUser->getName(),
248                                 'img_metadata' => $this->metadata,
249                         ),
250                         __METHOD__,
251                         'IGNORE'
252                 );
253
254                 if( $dbw->affectedRows() == 0 ) {
255                         # Collision, this is an update of a file                        
256                         # Update the current image row
257                         $dbw->update( 'ic_image',
258                                 array( /* SET */
259                                         'img_size' => $this->size,
260                                         'img_width' => intval( $this->width ),
261                                         'img_height' => intval( $this->height ),
262                                         'img_bits' => $this->bits,
263                                         'img_media_type' => $this->media_type,
264                                         'img_major_mime' => $this->major_mime,
265                                         'img_minor_mime' => $this->minor_mime,
266                                         'img_timestamp' => $timestamp,
267                                         'img_description' => $comment,
268                                         'img_user' => $wgUser->getID(),
269                                         'img_user_text' => $wgUser->getName(),
270                                         'img_metadata' => $this->metadata,
271                                 ), array( /* WHERE */
272                                         'img_name' => $this->getName()
273                                 ), __METHOD__
274                         );
275                 } else {
276                         # This is a new file
277                         # Update the image count
278                         $site_stats = $dbw->tableName( 'site_stats' );
279                         $dbw->query( "UPDATE $site_stats SET ss_images=ss_images+1", __METHOD__ );
280                 }
281
282                 $descTitle = $this->getTitle();
283                 $article = new Article( $descTitle );
284
285                 # Add the log entry
286                 $log = new LogPage( 'icdownload' );
287                 $log->addEntry( 'InstantCommons download', $descTitle, $comment );
288
289                 if( $descTitle->exists() ) {
290                         # Create a null revision
291                         $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(), $log->getRcComment(), false );
292                         $nullRevision->insertOn( $dbw );
293                         $article->updateRevisionOn( $dbw, $nullRevision );
294
295                         # Invalidate the cache for the description page
296                         $descTitle->invalidateCache();
297                         $descTitle->purgeSquid();
298                 }
299
300                 
301                 # Commit the transaction now, in case something goes wrong later
302                 # The most important thing is that files don't get lost, especially archives
303                 $dbw->immediateCommit();
304
305                 # Invalidate cache for all pages using this file
306                 $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
307                 $update->doUpdate();
308
309                 return true;
310         }
311         
312 }
313