File.class.php (13867B)
1 <?php 2 namespace cms\model; 3 // OpenRat Content Management System 4 // Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de 5 // 6 // This program is free software; you can redistribute it and/or 7 // modify it under the terms of the GNU General Public License 8 // as published by the Free Software Foundation; either version 2 9 // of the License, or (at your option) any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program; if not, write to the Free Software 18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 20 21 // Standard Mime-Type 22 use cms\publish\filter\AbstractFilter; 23 use cms\publish\PublishEdit; 24 use cms\publish\PublishPreview; 25 use cms\publish\PublishPublic; 26 use cms\publish\PublishShow; 27 use JSqueeze; 28 use Less_Parser; 29 use Logger; 30 use util\FileCache; 31 32 define('OR_FILE_DEFAULT_MIMETYPE','application/octet-stream'); 33 34 35 /** 36 * Datei. 37 * 38 * @author Jan Dankert 39 * @package openrat.objects 40 */ 41 class File extends BaseObject 42 { 43 var $fileid; 44 45 var $size = 0; 46 var $value = ''; 47 var $extension = ''; 48 var $log_filenames = array(); 49 var $fullFilename = ''; 50 51 /** 52 * @var \Publish 53 */ 54 var $publish = null; 55 56 var $mime_type = ''; 57 58 var $tmpfile; 59 60 var $content_negotiation = false; 61 62 63 64 /** 65 * Um Probleme mit BLOB-Feldern und Datenbank-Besonderheiten zu vermeiden, 66 * kann der Binaerinhalt BASE64-kodiert gespeichert werden. 67 * @type Boolean 68 */ 69 var $storeValueAsBase64 = false; 70 71 public $filterid; 72 73 public $public = false ; 74 75 /** 76 * Konstruktor 77 * 78 * @param Objekt-Id 79 */ 80 function __construct( $objectid='' ) 81 { 82 $this->storeValueAsBase64 = db()->conf['base64']; 83 84 parent::__construct( $objectid ); 85 $this->isFile = true; 86 $this->typeid = BaseObject::TYPEID_FILE; 87 } 88 89 90 /** 91 * @return FileCache 92 */ 93 public function getCache() { 94 $cacheKey = array('db'=>db()->id,'file'=>$this->objectid,'publish'=>\ClassUtils::getSimpleClassName($this->publisher)); 95 96 return new FileCache( $cacheKey,function() { 97 return $this->loadValueFromDatabase(); 98 }, $this->lastchangeDate ); 99 } 100 101 /** 102 * Ermitteln des Dateinamens dieser Datei 103 * 104 * @return String Kompletter Dateiname, z.B. '/pfad/datei.jpeg' 105 */ 106 function full_filename() 107 { 108 if ( !empty($this->fullFilename) ) 109 return $this->fullFilename; 110 111 $filename = parent::full_filename(); 112 113 if ( $this->content_negotiation && config('publish','negotiation','file_negotiate_type' ) ) 114 { 115 // Link auf Datei: Extension bleibt aufgrund Content-Negotiation leer 116 } 117 else 118 { 119 // Nein, wurde bereits in filename() ergänzt. 120 //if ( !empty($this->extension) ) 121 // $filename .= '.'.$this->extension; 122 } 123 124 $this->fullFilename = $filename; 125 return $filename; 126 } 127 128 129 130 /** 131 * Ermitteln des Dateinamens dieser Datei (ohne Pfadangabe) 132 * 133 * @return String Kompletter Dateiname, z.B. '/pfad/datei.jpeg' 134 */ 135 public function filename() 136 { 137 if ( $this->extension ) 138 return parent::filename().'.'.$this->extension; 139 else 140 return parent::filename(); 141 142 } 143 144 145 146 /** 147 * Ermitteln aller Eigenschaften. 148 * 149 * @return array 150 */ 151 function getProperties() 152 { 153 return array_merge( parent::getProperties(), 154 array('full_filename'=>$this->fullFilename, 155 'extension' =>$this->extension, 156 'size' =>$this->size, 157 'filterid' =>$this->filterid, 158 'mimetype' =>$this->mimetype() ) ); 159 } 160 161 162 163 /** 164 * @deprecated 165 */ 166 function getFileObjectIdsByExtension( $extension ) 167 { 168 global $SESS; 169 $db = db_connection(); 170 171 $sqlquery = 'SELECT * FROM {{object}} '; 172 173 if ( $extension != '' ) 174 { 175 $sqlquery .= " WHERE extension='"; 176 177 $ext = explode(',',$extension); 178 $sqlquery .= implode( "' OR extension='",$ext ); 179 $sqlquery .= "' AND typeid=".BaseObject::TYPEID_FILE." AND projectid={projectid}"; 180 } 181 else 182 { 183 $sqlquery .= " WHERE typeid=".BaseObject::TYPEID_FILE." AND projectid={projectid}"; 184 } 185 186 $sql = $db->sql( $sqlquery ); 187 $sql->setInt( 'projectid',$SESS['projectid'] ); 188 189 return $sql->getCol(); 190 } 191 192 193 194 /** 195 * Es werden Objekte zu einer Dateierweiterung ermittelt 196 * 197 * @param String Dateierweiterung ohne fuehrenden Punkt (z.B. 'jpeg') 198 * @return array Liste der gefundenen Objekt-IDs 199 */ 200 public static function getObjectIdsByExtension( $extension ) 201 { 202 $db = db_connection(); 203 204 $sql = $db->sql( 'SELECT {{file}}.objectid FROM {{file}} '. 205 ' LEFT JOIN {{object}} '. 206 ' ON {{object}}.id={{file}}.objectid'. 207 ' WHERE {{file}}.extension={extension}' ); 208 $sql->setString( 'extension',$extension ); 209 210 return $sql->getCol(); 211 } 212 213 214 215 /** 216 * Ermittelt den Mime-Type zu dieser Datei 217 * 218 * @return String Mime-Type 219 */ 220 public function mimeType() 221 { 222 if ( !empty( $this->mime_type ) ) 223 return $this->mime_type; 224 225 global $conf; 226 $mime_types = $conf['mime-types']; 227 228 229 230 $ext = strtolower( $this->getRealExtension() ); 231 232 if ( !empty($mime_types[$ext]) ) 233 $this->mime_type = $mime_types[$ext]; 234 else 235 // Wenn kein Mime-Type gefunden, dann Standartwert setzen 236 $this->mime_type = OR_FILE_DEFAULT_MIMETYPE; 237 238 return( $this->mime_type ); 239 } 240 241 242 /** 243 * Lesen der Datei aus der Datenbank. 244 * 245 * Es werden nur die Meta-Daten (Erweiterung, Gr��e) gelesen. Zum Lesen des 246 * Datei-Inhaltes muss #loadValue() aufgerufen werden. 247 */ 248 public function load() 249 { 250 $db = db_connection(); 251 252 $sql = $db->sql( 'SELECT id,extension,size,filterid'. 253 ' FROM {{file}}'. 254 ' WHERE objectid={objectid}' ); 255 $sql->setInt( 'objectid',$this->objectid ); 256 $row = $sql->getRow(); 257 258 if ( count($row)!=0 ) 259 { 260 $this->fileid = $row['id' ]; 261 $this->extension = $row['extension']; 262 $this->size = $row['size' ]; 263 $this->filterid = $row['filterid' ]; 264 } 265 266 $this->objectLoad(); 267 268 return $this; 269 } 270 271 272 273 /** 274 * Unwiderrufliches L�schen der Datei aus der Datenbank. 275 */ 276 function delete() 277 { 278 $db = db_connection(); 279 280 // Datei l?schen 281 $sql = $db->sql( 'DELETE FROM {{file}} '. 282 ' WHERE objectid={objectid}' ); 283 $sql->setInt( 'objectid',$this->objectid ); 284 $sql->query(); 285 286 $this->objectDelete(); 287 } 288 289 290 291 /** 292 * Stellt anhand der Dateiendung fest, ob es sich bei dieser Datei um ein Bild handelt 293 */ 294 function isImage() 295 { 296 return substr($this->mimeType(),0,6)=='image/'; 297 } 298 299 300 301 /** 302 * Ermittelt die Datei-Endung. 303 * 304 * @return String Datei-Endung 305 */ 306 function extension() 307 { 308 if ($this->extension != '') 309 return $this->extension; 310 311 $this->load(); 312 return $this->extension; 313 } 314 315 316 /** 317 * Einen Dateinamen in Dateiname und Extension aufteilen. 318 * @param filename Dateiname 319 */ 320 function parse_filename($filename) 321 { 322 $filename = basename($filename); 323 324 $p = strrpos($filename, '.'); 325 if ($p !== false) 326 { 327 $this->extension = substr($filename, $p +1); 328 $this->filename = substr($filename, 0, $p); 329 } 330 else 331 { 332 $this->extension = ''; 333 $this->filename = $filename; 334 } 335 } 336 337 338 /** 339 * Speichert die Datei-Informationen in der Datenbank. 340 */ 341 public function save() 342 { 343 $db = db_connection(); 344 345 $sql = $db->sql( <<<EOF 346 UPDATE {{file}} SET 347 size = {size}, 348 filterid = {filterid}, 349 extension = {extension} 350 WHERE objectid={objectid} 351 EOF 352 ); 353 $sql->setString('size' ,$this->size ); 354 $sql->setString('extension',$this->extension ); 355 $sql->setString('objectid' ,$this->objectid ); 356 $sql->setInt ('filterid' ,$this->filterid ); 357 $sql->query(); 358 359 $this->objectSave(); 360 } 361 362 363 /** 364 * Kopieren des Inhaltes von einer anderen Datei 365 * @param ID der Datei, von der der Inhalt kopiert werden soll 366 */ 367 function copyValueFromFile( $otherfileid ) 368 { 369 $of = new File( $otherfileid ); 370 $this->value = $of->loadValue(); 371 $this->saveValue(); 372 } 373 374 375 public function loadValue() 376 { 377 if ( is_file( $this->getCache()->getFilename() ) && filemtime($this->getCache()->getFilename() < $this->lastchangeDate)) 378 $this->getCache()->invalidate(); 379 380 return $this->getCache()->get(); 381 } 382 383 384 /** 385 * Lesen des Inhaltes der Datei aus der Datenbank. 386 * 387 * @return String Inhalt der Datei 388 */ 389 private function loadValueFromDatabase() 390 { 391 // Read from cache, if cache exist and is not too old. 392 393 $settings = $this->getSettings(); 394 $proxyFileId = @$settings['proxy-file-id']; 395 396 if ( $proxyFileId ) 397 $objectid = $proxyFileId; // This is a proxy for another file. 398 else 399 $objectid = $this->objectid; 400 401 $sql = db()->sql( 'SELECT size,value'. 402 ' FROM {{file}}'. 403 ' WHERE objectid={objectid}' ); 404 $sql->setInt( 'objectid', $objectid); 405 $row = $sql->getRow(); 406 407 if ( count($row) != 0 ) 408 { 409 $this->value = $row['value']; 410 $this->size = $row['size' ]; 411 } 412 413 if ( $this->storeValueAsBase64 ) 414 $this->value = base64_decode( $this->value ); 415 416 $this->filterValue(); 417 418 return $this->value; 419 } 420 421 422 /** 423 * Speichert den Inhalt in der Datenbank. 424 */ 425 function saveValue( $value = '' ) 426 { 427 $this->getCache()->invalidate(); 428 429 $db = db_connection(); 430 431 $sql = $db->sql( 'UPDATE {{file}}'. 432 ' SET value={value}, '. 433 ' size={size} '. 434 ' WHERE objectid={objectid}' ); 435 $sql->setString( 'objectid' ,$this->objectid ); 436 $sql->setInt ( 'size' ,strlen($this->value) ); 437 438 if ( $this->storeValueAsBase64 ) 439 $sql->setString( 'value',base64_encode($this->value) ); 440 else 441 $sql->setString( 'value',$this->value ); 442 443 $sql->query(); 444 } 445 446 447 /** 448 * Lesen der Datei aus der Datenbank und schreiben in temporaere Datei 449 */ 450 function write() 451 { 452 $this->getCache()->load(); 453 } 454 455 456 /** 457 * F�gt die Datei der Datenbank hinzu. 458 */ 459 function add() 460 { 461 parent::add(); 462 463 $sql = db()->sql('SELECT MAX(id) FROM {{file}}'); 464 $this->fileid = intval($sql->getOne())+1; 465 466 $sql = db()->sql('INSERT INTO {{file}}'. 467 ' (id,objectid,extension,size,value)'. 468 " VALUES( {fileid},{objectid},{extension},0,'' )" ); 469 $sql->setInt ('fileid' ,$this->fileid ); 470 $sql->setInt ('objectid' ,$this->objectid ); 471 $sql->setString('extension',$this->extension ); 472 473 $sql->query(); 474 475 $this->saveValue(); 476 } 477 478 479 public function publish() 480 { 481 $this->write(); 482 $this->publisher->copy( $this->getCache()->getFilename(),$this->full_filename(),$this->lastchangeDate ); 483 484 $this->publisher->publishedObjects[] = $this->getProperties(); 485 } 486 487 488 /** 489 * Setzt den Zeitstempel der Datei auf die aktuelle Zeit. 490 * 491 * @see objectClasses/Object#setTimestamp() 492 */ 493 494 function setTimestamp() 495 { 496 $this->getCache()->invalidate(); 497 498 parent::setTimestamp(); 499 } 500 501 502 503 504 /** 505 * Change type. 506 * 507 * This is only allowed for files, because it is only allowed to switch between the following types: file,image,text. 508 */ 509 public function updateType() 510 { 511 512 $stmt = db()->sql(<<<SQL 513 UPDATE {{object}} SET 514 typeid = {typeid} 515 WHERE id={objectid} 516 SQL 517 ); 518 519 $stmt->setInt('typeid' , $this->typeid ); 520 $stmt->setInt('objectid', $this->objectid); 521 $stmt->query(); 522 } 523 524 525 526 527 /** 528 * Ermittelt die wirksame Datei-Endung. Diese kann sich 529 * in der Extra-Dateiendung, aber auch direkt im Dateiname 530 * befinden. 531 * 532 * @return Dateiendung 533 */ 534 function getRealExtension() 535 { 536 if ( $this->extension ) 537 { 538 return $this->extension; 539 } 540 else 541 { 542 $pos = strrpos($this->filename,'.'); 543 if ( $pos === false ) 544 return ''; 545 else 546 return substr($this->filename,$pos+1); 547 } 548 } 549 550 private function filterValue() 551 { 552 $publishType = strtolower(substr(\ClassUtils::getSimpleClassName($this->publisher),7)); 553 554 foreach(\ArrayUtils::getSubArray($this->getTotalSettings(), array( 'filter')) as $filterEntry ) 555 { 556 $filterName = @$filterEntry['name']; 557 $extension = @$filterEntry['extension']; 558 559 if ( $extension && strtolower($extension) != strtolower($this->getRealExtension()) ) 560 continue; // File extension does not match 561 562 $onPublish = (array) @$filterEntry['on']; 563 if ( ! $onPublish || in_array('all',$onPublish ) ) 564 $onPublish = ['edit','public','preview','show']; 565 566 if ( $onPublish && ! in_array($publishType,$onPublish)) 567 continue; // Publish type does not match 568 569 $parameter = (array) @$filterEntry['parameter']; 570 571 $filterClassNameWithNS = 'cms\\publish\\filter\\' . $filterName.'Filter'; 572 573 if ( !class_exists( $filterClassNameWithNS ) ) { 574 Logger::warn("Filter '$filterName' does not exist."); 575 continue; 576 } 577 578 /** @var AbstractFilter $filter */ 579 $filter = new $filterClassNameWithNS(); 580 581 // Copy filter configuration to filter instance. 582 foreach( $parameter as $name=>$value) { 583 if ( property_exists($filter,$name)) 584 $filter->$name = $value; 585 } 586 587 588 // Execute the filter. 589 Logger::debug("Filtering '$this->filename' with filter '$filterName'."); 590 591 try { 592 593 $this->value = $filter->filter( $this->value ); 594 } catch( \Exception $e ) { 595 // Filter has some undefined error. 596 Logger::warn( $e->getTraceAsString() ); 597 if ( $this->publisher instanceof PublishPublic ) 598 return ''; // Do not show errors on public publishing. 599 else 600 return $e->getMessage()."\n".$e->getTraceAsString(); 601 } 602 } 603 604 // Store in cache. 605 $this->getCache()->invalidate(); 606 } 607 608 609 610 public function getSize() 611 { 612 return $this->size; 613 } 614 } 615 616 ?>