PublishPublic.class.php (14581B)
1 <?php 2 3 namespace cms\publish; 4 5 use cms\model\BaseObject; 6 use cms\model\File; 7 use cms\model\Folder; 8 use cms\model\Link; 9 use cms\model\Page; 10 use cms\model\Project; 11 use cms\model\Url; 12 use FileUtils; 13 use Ftp; 14 use Logger; 15 use OpenRatException; 16 use Session; 17 18 19 20 /** 21 * User: dankert 22 * Date: 10.08.18 23 * Time: 23:47 24 */ 25 26 class PublishPublic extends Publish 27 { 28 const SCHEMA_ABSOLUTE = 1; 29 const SCHEMA_RELATIVE = 2; 30 31 32 /** 33 * Enthaelt bei Bedarf das FTP-Objekt. N�mlich dann, wenn 34 * zu einem FTP-Server veroeffentlicht werden soll. 35 * @var Object 36 */ 37 private $ftp; 38 39 private $localDestinationDirectory = ''; 40 41 /** 42 * Enthaelt die gleichnamige Einstellung aus dem Projekt. 43 * @var boolean 44 */ 45 private $contentNegotiation = false; 46 47 /** 48 * Enthaelt die gleichnamige Einstellung aus dem Projekt. 49 * @var boolean 50 */ 51 private $cutIndex = false; 52 53 /** 54 * Enthaelt die gleichnamige Einstellung aus dem Projekt. 55 * @var String 56 */ 57 private $commandAfterPublish = ''; 58 59 /** 60 * Enthaelt am Ende der Ver�ffentlichung ein Array mit den ver�ffentlichten Objekten. 61 * @var Array 62 */ 63 public $publishedObjects = array(); 64 65 /** 66 * Enthaelt im Fehlerfall (wenn 'ok' auf 'false' steht) eine 67 * Fehlermeldung. 68 * 69 * @var String 70 */ 71 public $log = array(); 72 73 /** 74 * Konstruktor.<br> 75 * <br> 76 * Oeffnet ggf. Verbindungen. 77 * 78 * @return Publish 79 */ 80 public function __construct( $projectid ) 81 { 82 $confPublish = config('publish'); 83 84 $project = Project::create( $projectid ); 85 $project->load(); 86 87 $this->linkSchema = ($project->linkAbsolute ? self::SCHEMA_ABSOLUTE : self::SCHEMA_RELATIVE); 88 89 // Feststellen, ob FTP benutzt wird. 90 // Dazu muss FTP aktiviert sein (enable=true) und eine URL vorhanden sein. 91 $ftpUrl = ''; 92 if ( $confPublish['ftp']['enable'] ) 93 { 94 if ( $confPublish['ftp']['per_project'] && !empty($project->ftp_url) ) 95 $ftpUrl = $project->ftp_url; 96 elseif ( !empty($confPublish['ftp']['host']) ) 97 $ftpUrl = $project->ftp_url; 98 } 99 100 if ( $ftpUrl && $ftpUrl[0]!='#' ) 101 { 102 $this->ftp = new \Ftp($project->ftp_url); // Aufbauen einer FTP-Verbindung 103 104 $this->ftp->passive = ( $project->ftp_passive == '1' ); 105 } 106 107 $targetDir = rtrim( $project->target_dir,'/' ); 108 109 if ( FileUtils::isAbsolutePath($targetDir) && $confPublish['filesystem']['per_project'] ) 110 { 111 $this->localDestinationDirectory = FileUtils::toAbsolutePath([$targetDir]); // Projekteinstellung verwenden. 112 } 113 else 114 { 115 // Konfiguriertes Verzeichnis verwenden. 116 $this->localDestinationDirectory = FileUtils::toAbsolutePath([$confPublish['filesystem']['directory'],$targetDir]); 117 } 118 119 120 // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist. 121 if ( $this->localDestinationDirectory && $this->localDestinationDirectory[0] == '#') 122 $this->localDestinationDirectory = ''; 123 124 $this->contentNegotiation = ( $project->content_negotiation == '1' ); 125 $this->cutIndex = ( $project->cut_index == '1' ); 126 127 if ( $confPublish['command']['enable'] ) 128 { 129 if ( $confPublish['command']['per_project'] && !empty($project->cmd_after_publish) ) 130 $this->commandAfterPublish = $project->cmd_after_publish; 131 else 132 $this->commandAfterPublish = @$confPublish['command']['command']; 133 } 134 135 // Im Systemkommando Variablen ersetzen 136 $this->commandAfterPublish = str_replace('{name}' ,$project->name ,$this->commandAfterPublish); 137 $this->commandAfterPublish = str_replace('{dir}' ,$this->localDestinationDirectory ,$this->commandAfterPublish); 138 $this->commandAfterPublish = str_replace('{dirbase}',basename($this->localDestinationDirectory),$this->commandAfterPublish); 139 140 if ( config('security','nopublish') ) 141 { 142 Logger::warn('publishing is disabled.'); 143 $this->commandAfterPublish = ''; 144 $this->localDestinationDirectory = ''; 145 $this->ftp = null; 146 } 147 } 148 149 150 151 /** 152 * @var int 153 */ 154 private $linkSchema; 155 156 /** 157 * @param $from \cms\model\Page 158 * @param $to \cms\model\BaseObject 159 */ 160 public function linkToObject( $from, $to ) { 161 162 $schema = $this->linkSchema; 163 164 $counter = 0; 165 while( $to->typeid == BaseObject::TYPEID_LINK ) 166 { 167 if ( $counter++ > 10 ) 168 throw new \LogicException("Too much redirects while following a link. Stopped at #".$to->objectid ); 169 170 $link = new Link( $to->objectid ); 171 $link->load(); 172 173 $to = new BaseObject( $link->linkedObjectId ); 174 $to->objectLoad(); 175 } 176 177 switch( $to->typeid ) 178 { 179 case BaseObject::TYPEID_FILE: 180 case BaseObject::TYPEID_IMAGE: 181 case BaseObject::TYPEID_TEXT: 182 183 $f = new File( $to->objectid ); 184 185 $p = Project::create( $to->projectid )->load(); 186 $f->content_negotiation = $p->content_negotiation; 187 188 $f->load(); 189 $filename = $f->filename(); 190 break; 191 192 case BaseObject::TYPEID_PAGE: 193 194 $p = new Page( $to->objectid ); 195 $p->languageid = $from->languageid; 196 $p->modelid = $from->modelid; 197 $p->cut_index = $from->cut_index; 198 $p->content_negotiation = $from->content_negotiation; 199 $p->withLanguage = $from->withLanguage; 200 $p->withModel = $from->withModel; 201 $p->load(); 202 $filename = $p->getFilename(); 203 break; 204 205 case BaseObject::TYPEID_URL: 206 $url = new Url( $to->objectid ); 207 $url->load(); 208 return $url->url; 209 default: 210 throw new \LogicException("Could not build a link to the unknown Type ".$to->typeid.':'.$to->getType() ); 211 } 212 213 214 if ( $from->projectid != $to->projectid ) 215 { 216 // Target object is in another project. 217 // we have to use absolute URLs. 218 $schema = self::SCHEMA_ABSOLUTE; 219 220 // Target is in another Project. So we have to create an absolute URL. 221 $targetProject = Project::create( $to->projectid )->load(); 222 $host = $targetProject->url; 223 224 if ( ! strpos($host,'//' ) === FALSE ) { 225 // No protocol in hostname. So we have to prepend the URL with '//'. 226 $host = '//'.$host; 227 } 228 } 229 else { 230 $host = ''; 231 } 232 233 234 235 236 if ( $schema == self::SCHEMA_RELATIVE ) 237 { 238 $folder = new Folder( $from->getParentFolderId() ); 239 $folder->load(); 240 $fromPathFolders = $folder->parentObjectFileNames(false,true); 241 242 243 $folder = new Folder($to->getParentFolderId() ); 244 245 $toPathFolders = $folder->parentObjectFileNames(false, true); 246 247 // Shorten the relative URL 248 // if the actual page is /path/folder1/page1 249 // and the target page is /path/folder2/page2 250 // we shorten the link from ../../path/folder2/page2 251 // to ../folder2/page2 252 foreach( $fromPathFolders as $folderId => $folderFileName ) { 253 if ( count($toPathFolders) >= 1 && array_keys($toPathFolders)[0] == $folderId ) { 254 unset( $fromPathFolders[$folderId] ); 255 unset( $toPathFolders [$folderId] ); 256 }else { 257 break; 258 } 259 260 } 261 262 if ( $fromPathFolders ) 263 $path = str_repeat( '../',count($fromPathFolders) ); 264 else 265 $path = './'; // Just to clarify- this could be blank too. 266 267 if ( $toPathFolders ) 268 $path .= implode('/',$toPathFolders).'/'; 269 } 270 else { 271 // Absolute Pfadangaben 272 $folder = new Folder( $to->getParentFolderId() ); 273 $toPathFolders = $folder->parentObjectFileNames(false, true); 274 275 $path = '/'; 276 277 if ( $toPathFolders ) 278 $path .= implode('/',$toPathFolders).'/'; 279 } 280 281 282 $uri = $host . $path . $filename; 283 284 if( !$uri ) 285 $uri = '.'; 286 287 return $uri; 288 } 289 290 291 292 293 294 /** 295 * Kopieren einer Datei aus dem tempor�ren Verzeichnis in das Zielverzeichnis.<br> 296 * Falls notwenig, wird ein Hochladen per FTP ausgef�hrt. 297 * 298 * @param String $tmp_filename 299 * @param String $dest_filename 300 */ 301 public function copy( $tmp_filename,$dest_filename,$lastChangeDate=null ) 302 { 303 global $conf; 304 $source = $tmp_filename; 305 306 307 308 if ( $this->localDestinationDirectory ) 309 { 310 // Is the output directory writable? 311 if ( !is_writeable( $this->localDestinationDirectory ) ) 312 throw new OpenRatException('ERROR_PUBLISH','directory not writable: '.$this->localDestinationDirectory ); 313 314 $dest = $this->localDestinationDirectory.'/'.$dest_filename; 315 316 // Is the destination writable? 317 if ( is_file($dest) && !is_writeable( $dest ) ) 318 throw new OpenRatException('ERROR_PUBLISH','file not writable: '.$this->dest ); 319 320 // Copy file to destination 321 if (!@copy( $source,$dest )); 322 { 323 // Create directories, if necessary. 324 $this->mkdirs( dirname($dest) ); 325 326 if (!@copy( $source,$dest )) 327 throw new OpenRatException('ERROR_PUBLISH','failed copying local file:'."\n". 328 'source : '.$source."\n". 329 'destination: '.$dest); 330 331 // Das Änderungsdatum der Datei auch in der Zieldatei setzen. 332 if ( $conf['publish']['set_modification_date'] ) 333 if ( ! is_null($lastChangeDate) ) 334 @touch( $dest,$lastChangeDate ); 335 336 Logger::debug("published: $dest"); 337 } 338 339 if (!empty($conf['security']['chmod'])) 340 { 341 // CHMOD auf der Datei ausfuehren. 342 if ( ! @chmod($dest,octdec($conf['security']['chmod'])) ) 343 throw new OpenRatException('ERROR_PUBLISH','Unable to CHMOD file '.$dest); 344 } 345 } 346 347 if ( $this->ftp ) // Falls FTP aktiviert 348 { 349 $dest = $dest_filename; 350 $this->ftp->put( $source,$dest ); 351 } 352 } 353 354 355 356 /** 357 * Rekursives Anlagen von Verzeichnisse 358 * Nett gemacht. 359 * Quelle: http://de3.php.net/manual/de/function.mkdir.php 360 * Thx to acroyear at io dot com 361 * 362 * @param String Verzeichnis 363 * @return boolean 364 */ 365 private function mkdirs($path ) 366 { 367 global $conf; 368 369 if ( is_dir($path) ) 370 return; // Path exists 371 372 $parentPath = dirname($path); 373 374 $this->mkdirs($parentPath); 375 376 // 377 if ( ! @mkdir($path) ) 378 throw new OpenRatException('ERROR_PUBLISH','Cannot create directory: '.$path); 379 380 // CHMOD auf dem Verzeichnis ausgef�hren. 381 if (!empty($conf['security']['chmod_dir'])) 382 { 383 if ( ! @chmod($path,octdec($conf['security']['chmod_dir'])) ) 384 throw new OpenRatException('ERROR_PUBLISH','Unable to CHMOD directory: '.$path); 385 } 386 } 387 388 389 390 /** 391 * Beenden des Ver�ffentlichungs-Vorganges.<br> 392 * Eine vorhandene FTP-Verbindung wird geschlossen.<br> 393 * Falls entsprechend konfiguriert, wird ein Systemkommando ausgef�hrt. 394 */ 395 public function close() 396 { 397 if ( $this->ftp ) 398 { 399 Logger::debug('Closing FTP connection' ); 400 $this->ftp->close(); 401 } 402 403 // Ausfuehren des Systemkommandos. 404 if ( !empty($this->commandAfterPublish) ) 405 { 406 $ausgabe = array(); 407 $rc = false; 408 Logger::debug('Executing system command: '.$this->commandAfterPublish ); 409 $user = Session::getUser(); 410 putenv("CMS_USER_NAME=".$user->name ); 411 putenv("CMS_USER_ID=" .$user->userid); 412 putenv("CMS_USER_MAIL=".$user->mail ); 413 exec( $this->commandAfterPublish,$ausgabe,$rc ); 414 415 if ( $rc != 0 ) // Wenn Returncode ungleich 0, dann Fehler melden. 416 throw new OpenRatException('ERROR_PUBLISH','System command failed - returncode is '.$rc."\n". 417 $ausgabe); 418 else 419 Logger::debug('System command successful' ); 420 421 } 422 } 423 424 425 426 /** 427 * Aufraeumen des Zielverzeichnisses.<br><br> 428 * Es wird der komplette Zielordner samt Unterverzeichnissen durchsucht. Jede 429 * Datei, die laenger existiert als der aktuelle Request alt ist, wird geloescht.<br> 430 * Natuerlich darf diese Funktion nur nach einem Gesamt-Veroeffentlichen ausgefuehrt werden. 431 */ 432 public function clean() 433 { 434 if ( !empty($this->localDestinationDirectory) ) 435 $this->cleanFolder($this->localDestinationDirectory); 436 } 437 438 439 440 /** 441 * Aufr�umen eines Verzeichnisses.<br><br> 442 * Dateien, die l�nger existieren als der aktuelle Request alt ist, werden gel�scht.<br> 443 * 444 * @param String Verzeichnis 445 */ 446 private function cleanFolder( $folderName ) 447 { 448 $dh = opendir( $folderName ); 449 450 while( $file = readdir($dh) ) 451 { 452 if ( $file != '.' && $file != '..') 453 { 454 $fullpath = $folderName.'/'.$file; 455 456 // Wenn eine Datei beschreibbar und entsprechend alt 457 // ist, dann entfernen 458 if ( is_file($fullpath) && 459 is_writable($fullpath) && 460 filemtime($fullpath) < START_TIME ) 461 unlink($fullpath); 462 463 // Bei Ordnern rekursiv absteigen 464 if ( is_dir( $fullpath) ) 465 { 466 $this->cleanFolder($fullpath); 467 @rmdir($fullpath); 468 } 469 } 470 } 471 } 472 473 474 public function isSimplePreview() 475 { 476 return false; 477 } 478 479 public function isPublic() 480 { 481 return true; 482 } 483 }