openrat-cms

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

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 }