openrat-cms

OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs

PublishPublic.class.php (14650B)


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