commit 42ec67aa39e543f0b168393ef217937647a469d3
parent 49ef887d824c27977862c700c5d7a74460b1a8b2
Author: Jan Dankert <develop@jandankert.de>
Date: Fri, 18 Sep 2020 22:48:46 +0200
Refactoring: Every project has 1 publishing target.
Diffstat:
21 files changed, 1033 insertions(+), 510 deletions(-)
diff --git a/modules/cms/action/PageelementAction.class.php b/modules/cms/action/PageelementAction.class.php
@@ -13,11 +13,8 @@ use cms\model\Folder;
use cms\model\BaseObject;
use cms\publish\PublishEdit;
use cms\publish\PublishPreview;
-use cms\publish\PublishShow;
use util\Html;
-use util\Http;
use LogicException;
-use util\Session;
use util\Transformer;
use util\Text;
use util\exception\ValidationException;
diff --git a/modules/cms/base/DB.class.php b/modules/cms/base/DB.class.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace cms\base;
+
+class DB {
+
+ /**
+ * Turns a SQL query into a Statement.
+ *
+ * @param $sql SQL-query
+ * @return \database\Statement SQL-Statement
+ */
+ public static function sql( $sql ) {
+ return db()->sql( $sql );
+ }
+}+
\ No newline at end of file
diff --git a/modules/cms/model/File.class.php b/modules/cms/model/File.class.php
@@ -20,12 +20,7 @@ namespace cms\model;
// Standard Mime-Type
use cms\publish\filter\AbstractFilter;
-use cms\publish\PublishEdit;
-use cms\publish\PublishPreview;
use cms\publish\PublishPublic;
-use cms\publish\PublishShow;
-use util\JSqueeze;
-use \Less_Parser;
use logger\Logger;
use util\cache\FileCache;
diff --git a/modules/cms/model/Project.class.php b/modules/cms/model/Project.class.php
@@ -2,6 +2,7 @@
namespace cms\model;
+use cms\base\DB;
use database\Database;
use util\Session;
@@ -21,7 +22,6 @@ class Project extends ModelBase
const FLAG_PUBLISH_PAGE_EXTENSION = 8;
const FLAG_LINK_ABSOLUTE = 16;
-
// Eigenschaften
public $projectid;
public $name;
@@ -289,9 +289,9 @@ class Project extends ModelBase
// Speichern
public function save()
{
- $db = db_connection();
+ $this->cleanTarget();
- $sql = $db->sql( <<<SQL
+ $stmt = DB::sql( <<<SQL
UPDATE {{project}}
SET name = {name},
target_dir = {target_dir},
@@ -304,12 +304,12 @@ class Project extends ModelBase
SQL
);
- $sql->setString('ftp_url' ,$this->ftp_url );
- $sql->setString('url' ,$this->url );
- $sql->setString('name' ,$this->name );
- $sql->setString('target_dir' ,$this->target_dir );
- $sql->setInt ('ftp_passive' ,$this->ftp_passive );
- $sql->setString('cmd_after_publish' ,$this->cmd_after_publish );
+ $stmt->setString('ftp_url' ,$this->ftp_url );
+ $stmt->setString('url' ,$this->url );
+ $stmt->setString('name' ,$this->name );
+ $stmt->setString('target_dir' ,$this->target_dir );
+ $stmt->setInt ('ftp_passive' ,$this->ftp_passive );
+ $stmt->setString('cmd_after_publish' ,$this->cmd_after_publish );
$flags = 0;
if( $this->cut_index) $flags |= self::FLAG_CUT_INDEX;
@@ -318,10 +318,10 @@ SQL
if( $this->publishPageExtension) $flags |= self::FLAG_PUBLISH_PAGE_EXTENSION;
if( $this->linkAbsolute ) $flags |= self::FLAG_LINK_ABSOLUTE;
- $sql->setInt ('flags' ,$flags );
- $sql->setInt ('projectid' ,$this->projectid );
+ $stmt->setInt ('flags' ,$flags );
+ $stmt->setInt ('projectid' ,$this->projectid );
- $sql->query();
+ $stmt->query();
try
{
@@ -1061,6 +1061,29 @@ SQL
{
return $this->name;
}
+
+
+ /**
+ * Cleans up the target url.
+ */
+ private function cleanTarget()
+ {
+ $target = parse_url( $this->target_dir );
+
+ $scheme = isset($target['scheme']) ? $target['scheme'] . '://' : '';
+ if ( empty($scheme) )
+ $scheme = 'file:/';
+
+ $host = isset($target['host']) ? $target['host'] : '';
+ $port = isset($target['port']) ? ':' . $target['port'] : '';
+ $user = isset($target['user']) ? $target['user'] : '';
+ $pass = isset($target['pass']) ? ':' . $target['pass'] : '';
+ $pass = ($user || $pass) ? "$pass@" : '';
+ $path = isset($target['path']) ? $target['path'] : '';
+ $query = isset($target['query']) ? '?' . $target['query'] : '';
+ $fragment = isset($target['fragment']) ? '#' . $target['fragment'] : '';
+
+ $this->target_dir = "$scheme$user$pass$host$port$path$query$fragment";
+ }
}
-?>-
\ No newline at end of file
diff --git a/modules/cms/publish/FilePublisher.class.php b/modules/cms/publish/FilePublisher.class.php
@@ -1,22 +0,0 @@
-<?php
-/**
- * Created by PhpStorm.
- * User: dankert
- * Date: 10.08.18
- * Time: 23:33
- */
-// UNUSED?
-class FilePublisher
-{
- public function publish()
- {
- if ( ! is_object($this->publish) )
- $this->publish = new \Publish();
-
- $this->write();
- $this->publish->copy( $this->tmpfile(),$this->full_filename(),$this->lastchangeDate );
-
- $this->publish->publishedObjects[] = $this->getProperties();
- }
-
-}-
\ No newline at end of file
diff --git a/modules/cms/publish/FolderPublisher.class.php b/modules/cms/publish/FolderPublisher.class.php
@@ -1,68 +0,0 @@
-<?php
-
-use util\Text;
-
-/**
- * Created by PhpStorm.
- * User: dankert
- * Date: 10.08.18
- * Time: 23:35
- */
-// UNUSED???
-class FolderPublisher
-{
- function publish( $withPages,$withFiles,$subdirs = false )
- {
- set_time_limit(300);
- if ( ! is_object($this->publish) )
- $this->publish = new \Publish();
-
- foreach( $this->getObjectIds() as $oid )
- {
- $o = new BaseObject( $oid );
- $o->objectLoadRaw();
-
- if ( $o->isPage && $withPages )
- {
- $p = new Page( $oid );
- $p->load();
- $p->publish = &$this->publish;
- $p->publish();
- }
-
- if ( $o->isFile && $withFiles )
- {
- $f = new File( $oid );
- $f->load();
- $f->publish = &$this->publish;
- $f->publish();
- }
-
- if ( $o->isImage && $withFiles )
- {
- $f = new Image( $oid );
- $f->load();
- $f->publish = &$this->publish;
- $f->publish();
- }
-
- if ( $o->isText && $withFiles )
- {
- $f = new Text( $oid );
- $f->load();
- $f->publish = &$this->publish;
- $f->publish();
- }
-
- if ( $o->isFolder && $subdirs )
- {
- $f = new Folder( $oid );
- $f->load();
- $f->publish = &$this->publish;
- $f->publish( $withPages,$withFiles,true );
- }
- }
- }
-
-
-}-
\ No newline at end of file
diff --git a/modules/cms/publish/Ftp.class.php b/modules/cms/publish/Ftp.class.php
@@ -1,202 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-namespace cms\publish;
-
-use logger\Logger;
-use util\exception\PublisherException;
-use util\exception\UIException;
-
-
-/**
- * Darstellen einer FTP-Verbindung, das beinhaltet
- * das Login, das Kopieren von Dateien sowie praktische
- * FTP-Funktionen
- *
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Ftp
-{
- var $verb;
- var $url;
- var $log = array();
-
- var $passive = false;
-
- var $ok = true;
-
- private $path;
-
-
- // Konstruktor
- public function __construct($url)
- {
- $this->connect($url);
- }
-
-
- // Aufbauen der Verbindung
- private function connect($url)
- {
- $this->url = $url;
-
- global $conf;
-
- $conf_ftp = $conf['publish']['ftp'];
- $ftp = parse_url($this->url);
-
- // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten
- $ftp = array_merge($conf_ftp, $ftp);
-
- // Nur FTP und FTPS (seit PHP 4.3) erlaubt
- if (!in_array(@$ftp['scheme'], array('ftp', 'ftps'))) {
- throw new PublisherException('Unknown scheme in FTP Url: ' . @$ftp['scheme'] .
- '. Only FTP (and FTPS, if compiled in) are supported');
- }
-
- if (function_exists('ftp_ssl_connect') && $ftp['scheme'] == 'ftps')
- $this->verb = @ftp_ssl_connect($ftp['host'], $ftp['port']);
- else
- $this->verb = @ftp_connect($ftp['host'], $ftp['port']);
-
- if (!$this->verb) {
- Logger::error('Cannot connect to ' . $ftp['host'] . ':' . $ftp['port']);
- throw new PublisherException('Cannot connect to ' . $ftp['scheme'] . '-server: ' . $ftp['host'] . ':' . $ftp['port']);
- }
-
- $this->log[] = 'Connected to FTP server ' . $ftp['host'] . ':' . $ftp['port'];
-
- if (empty($ftp['user'])) {
- $ftp['user'] = 'anonymous';
- $ftp['pass'] = 'openrat@openrat.de';
- }
-
- if (!ftp_login($this->verb, $ftp['user'], $ftp['pass']))
- throw new PublisherException('Unable to login as user ' . $ftp['user']);
-
- $this->log[] = 'Logged in as user ' . $ftp['user'];
-
- $pasv = (!empty($ftp['fragment']) && $ftp['fragment'] == 'passive');
-
- $this->log[] = 'entering passive mode ' . ($pasv ? 'on' : 'off');
- if (!ftp_pasv($this->verb, true))
- throw new PublisherException('Cannot switch to FTP PASV mode');
-
- if (!empty($ftp['query'])) {
- parse_str($ftp['query'], $ftp_var);
-
- if (isset($ftp_var['site'])) {
- $site_commands = explode(',', $ftp_var['site']);
- foreach ($site_commands as $cmd) {
- if (!@ftp_site($this->verb, $cmd))
- throw new PublisherException('unable to do SITE command: ' . $cmd);
- }
- }
- }
-
- $this->path = rtrim($ftp['path'], '/');
-
- $this->log[] = 'Changing directory to ' . $this->path;
-
- if (!@ftp_chdir($this->verb, $this->path))
- throw new PublisherException('unable CHDIR to directory: ' . $this->path);
- }
-
-
- /**
- * Kopieren einer Datei vom lokalen System auf den FTP-Server.
- *
- * @param String Quelle
- * @param String Ziel
- * @param int FTP-Mode (BINARY oder ASCII)
- */
- public function put($source, $dest)
- {
- $dest = $this->path . '/' . $dest;
-
- $this->log .= "Copying file: $source -> $dest ...\n";
-
- $mode = FTP_BINARY;
- $p = strrpos(basename($dest), '.'); // Letzten Punkt suchen
-
- if ($p !== false) // Wennn letzten Punkt gefunden, dann dort aufteilen
- {
- $extension = substr(basename($dest), $p + 1);
- $type = config('mime-types', $extension);
- if (substr($type, 0, 5) == 'text/')
- $mode = FTP_ASCII;
- }
-
- Logger::debug("FTP PUT target:$dest mode:" . (($mode == FTP_ASCII) ? 'ascii' : 'binary'));
-
- if (!@ftp_put($this->verb, $dest, $source, $mode)) {
- if (!$this->mkdirs(dirname($dest)))
- return; // Fehler.
-
- ftp_chdir($this->verb, $this->path);
-
- if (!@ftp_put($this->verb, $dest, $source, $mode))
- throw new PublisherException("FTP PUT failed.\n" .
- "source : $source\n" .
- "destination: $dest");
-
- }
- }
-
-
- /**
- * Private Methode zum rekursiven Anlegen von Verzeichnissen.
- *
- * @param String Pfad
- * @return boolean true, wenn ok
- */
- private function mkdirs($strPath)
- {
- if (@ftp_chdir($this->verb, $strPath))
- return true; // Verzeichnis existiert schon :)
-
- $pStrPath = dirname($strPath);
-
- if (!$this->mkdirs($pStrPath))
- return false;
-
- if (!@ftp_mkdir($this->verb, $strPath))
- throw new PublisherException("failed to create remote directory: $strPath");
-
- return true;
- }
-
-
- /**
- * Schliessen der FTP-Verbindung.<br>
- * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben.
- */
- public function close()
- {
- if (!@ftp_quit($this->verb)) {
- // Closing not possible.
- // Only logging. Maybe we could throw an Exception here?
- Logger::warn('Failed to close FTP connection. Continueing...');
- return;
- }
- }
-}
-
-
-?>-
\ No newline at end of file
diff --git a/modules/cms/publish/PublishPublic.class.php b/modules/cms/publish/PublishPublic.class.php
@@ -9,9 +9,17 @@ use cms\model\Link;
use cms\model\Page;
use cms\model\Project;
use cms\model\Url;
+use cms\publish\target\Dav;
+use cms\publish\target\Fax;
+use cms\publish\target\Ftp;
+use cms\publish\target\Ftps;
+use cms\publish\target\Local;
+use cms\publish\target\NoTarget;
+use cms\publish\target\Scp;
+use cms\publish\target\SFtp;
+use cms\publish\target\Target;
use util\exception\PublisherException;
use util\FileUtils;
-use cms\publish\Ftp;
use logger\Logger;
use util\exception\UIException;
use util\Session;
@@ -33,11 +41,11 @@ class PublishPublic extends Publish
/**
- * Enthaelt bei Bedarf das FTP-Objekt. N�mlich dann, wenn
- * zu einem FTP-Server veroeffentlicht werden soll.
- * @var Object
+ * The target to which the file will be copied to.
+ *
+ * @var Target
*/
- private $ftp;
+ private $target;
private $localDestinationDirectory = '';
@@ -89,40 +97,22 @@ class PublishPublic extends Publish
$this->linkSchema = ($project->linkAbsolute ? self::SCHEMA_ABSOLUTE : self::SCHEMA_RELATIVE);
- // Feststellen, ob FTP benutzt wird.
- // Dazu muss FTP aktiviert sein (enable=true) und eine URL vorhanden sein.
- $ftpUrl = '';
- if ( $confPublish['ftp']['enable'] )
- {
- if ( $confPublish['ftp']['per_project'] && !empty($project->ftp_url) )
- $ftpUrl = $project->ftp_url;
- elseif ( !empty($confPublish['ftp']['host']) )
- $ftpUrl = $project->ftp_url;
- }
+ $targetScheme = parse_url( $project->target_dir,PHP_URL_SCHEME );
- if ( $ftpUrl && $ftpUrl[0]!='#' )
- {
- $this->ftp = new \cms\publish\Ftp($project->ftp_url); // Aufbauen einer FTP-Verbindung
-
- $this->ftp->passive = ( $project->ftp_passive == '1' );
- }
-
- $targetDir = rtrim( $project->target_dir,'/' );
-
- if ( FileUtils::isAbsolutePath($targetDir) && $confPublish['filesystem']['per_project'] )
- {
- $this->localDestinationDirectory = FileUtils::toAbsolutePath([$targetDir]); // Projekteinstellung verwenden.
- }
- else
- {
- // Konfiguriertes Verzeichnis verwenden.
- $this->localDestinationDirectory = FileUtils::toAbsolutePath([$confPublish['filesystem']['directory'],$targetDir]);
- }
+ $availableTargets = [ Local::class,Ftp::class,Ftps::class,Fax::class,SFtp::class,Scp::class,Dav::class ];
+ /** @var Target $target */
+ foreach($availableTargets as $target )
+ {
+ if ( $target::isAvailable() && $target::accepts( $targetScheme ))
+ {
+ $this->target = new $target( $project->target_dir );
+ break;
+ }
+ }
- // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist.
- if ( $this->localDestinationDirectory && $this->localDestinationDirectory[0] == '#')
- $this->localDestinationDirectory = '';
+ if ( empty( $this->target ) )
+ throw new PublisherException('Cannot publish to the scheme '.$targetScheme );
$this->contentNegotiation = ( $project->content_negotiation == '1' );
$this->cutIndex = ( $project->cut_index == '1' );
@@ -142,10 +132,8 @@ class PublishPublic extends Publish
if ( config('security','nopublish') )
{
+ $this->target = new NoTarget();
Logger::warn('publishing is disabled.');
- $this->commandAfterPublish = '';
- $this->localDestinationDirectory = '';
- $this->ftp = null;
}
}
@@ -302,92 +290,11 @@ class PublishPublic extends Publish
*/
public function copy( $tmp_filename,$dest_filename,$lastChangeDate=null )
{
- global $conf;
- $source = $tmp_filename;
-
-
-
- if ( $this->localDestinationDirectory )
- {
- // Is the output directory writable?
- if ( !is_writeable( $this->localDestinationDirectory ) )
- throw new PublisherException('directory not writable: ' . $this->localDestinationDirectory);
-
- $dest = $this->localDestinationDirectory.'/'.$dest_filename;
-
- // Is the destination writable?
- if ( is_file($dest) && !is_writeable( $dest ) )
- throw new PublisherException('file not writable: ' . $this->dest);
-
- // Copy file to destination
- if (!@copy( $source,$dest ));
- {
- // Create directories, if necessary.
- $this->mkdirs( dirname($dest) );
-
- if (!@copy( $source,$dest ))
- throw new PublisherException( 'failed copying local file:' . "\n" .
- 'source : ' . $source . "\n" .
- 'destination: ' . $dest);
-
- // Das Änderungsdatum der Datei auch in der Zieldatei setzen.
- if ( $conf['publish']['set_modification_date'] )
- if ( ! is_null($lastChangeDate) )
- @touch( $dest,$lastChangeDate );
-
- Logger::debug("published: $dest");
- }
-
- if (!empty($conf['security']['chmod']))
- {
- // CHMOD auf der Datei ausfuehren.
- if ( ! @chmod($dest,octdec($conf['security']['chmod'])) )
- throw new PublisherException('Unable to CHMOD file ' . $dest);
- }
- }
-
- if ( $this->ftp ) // Falls FTP aktiviert
- {
- $dest = $dest_filename;
- $this->ftp->put( $source,$dest );
- }
+ $this->target->put($tmp_filename,$dest_filename,$lastChangeDate);
}
- /**
- * Rekursives Anlagen von Verzeichnisse
- * Nett gemacht.
- * Quelle: http://de3.php.net/manual/de/function.mkdir.php
- * Thx to acroyear at io dot com
- *
- * @param String Verzeichnis
- * @return boolean
- */
- private function mkdirs($path )
- {
- global $conf;
-
- if ( is_dir($path) )
- return; // Path exists
-
- $parentPath = dirname($path);
-
- $this->mkdirs($parentPath);
-
- //
- if ( ! @mkdir($path) )
- throw new PublisherException( 'Cannot create directory: ' . $path);
-
- // CHMOD auf dem Verzeichnis ausgef�hren.
- if (!empty($conf['security']['chmod_dir']))
- {
- if ( ! @chmod($path,octdec($conf['security']['chmod_dir'])) )
- throw new PublisherException('Unable to CHMOD directory: ' . $path);
- }
- }
-
-
/**
* Beenden des Ver�ffentlichungs-Vorganges.<br>
@@ -396,11 +303,7 @@ class PublishPublic extends Publish
*/
public function close()
{
- if ( $this->ftp )
- {
- Logger::debug('Closing FTP connection' );
- $this->ftp->close();
- }
+ $this->target->close();
// Ausfuehren des Systemkommandos.
if ( !empty($this->commandAfterPublish) )
@@ -412,6 +315,7 @@ class PublishPublic extends Publish
putenv("CMS_USER_NAME=".$user->name );
putenv("CMS_USER_ID=" .$user->userid);
putenv("CMS_USER_MAIL=".$user->mail );
+
exec( $this->commandAfterPublish,$ausgabe,$rc );
if ( $rc != 0 ) // Wenn Returncode ungleich 0, dann Fehler melden.
diff --git a/modules/cms/publish/PublishShow.class.php b/modules/cms/publish/PublishShow.class.php
@@ -1,51 +0,0 @@
-<?php
-
-namespace cms\publish;
-
-use cms\model\BaseObject;
-use cms\model\Link;
-use cms\model\Url;
-
-/**
- * Created by PhpStorm.
- * User: dankert
- * Date: 10.08.18
- * Time: 23:47
- */
-
-class PublishShow extends Publish
-{
- /**
- * @param $from \cms\model\BaseObject
- * @param $to \cms\model\BaseObject
- */
- public function linkToObject( BaseObject $from, BaseObject $to )
- {
- return "...";
- }
-
- public function isPublic()
- {
- return false;
- }
-
- public function copy($tmp_filename,$dest_filename,$lastChangeDate=null)
- {
- // nothing to do.
- }
-
- public function clean()
- {
- // nothing to do.
- }
-
- public function close()
- {
- // nothing to do.
- }
-
- public function isSimplePreview()
- {
- return false;
- }
-}-
\ No newline at end of file
diff --git a/modules/cms/publish/require.php b/modules/cms/publish/require.php
@@ -1,15 +0,0 @@
-<?php
-
-require_once( __DIR__.'/'.'Publish.class.php' );
-require_once(__DIR__ . '/'.'PublishPreview.class.php');
-require_once(__DIR__ . '/'.'PublishEdit.class.php');
-require_once(__DIR__ . '/'.'PublishShow.class.php');
-require_once(__DIR__ . '/'.'PublishPublic.class.php');
-require_once( __DIR__.'/'.'Ftp.class.php' );
-
-require_once( __DIR__.'/'.'filter/AbstractFilter.class.php' );
-require_once( __DIR__.'/'.'filter/JavascriptMinifierFilter.class.php' );
-require_once( __DIR__.'/'.'filter/LessFilter.class.php' );
-require_once( __DIR__.'/'.'filter/Base64DecodeFilter.class.php' );
-require_once( __DIR__.'/'.'filter/Base64EncodeFilter.class.php' );
-require_once( __DIR__.'/'.'filter/Csv2HtmlFilter.class.php' );
diff --git a/modules/cms/publish/target/Dav.class.php b/modules/cms/publish/target/Dav.class.php
@@ -0,0 +1,88 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+use util\Url;
+
+
+/**
+ * Publishing a file over WebDAV.
+ *
+ * @author Jan Dankert
+ */
+class Dav extends Target
+{
+ /**
+ * @var false|resource
+ */
+ private $fp;
+
+ public function checkConnection()
+ {
+ $content = "HEAD / HTTP/1.1\r\n";
+ $content .= "Host: ".$this->url->host."\r\n";
+ $content .= "Connection: Close\r\n";
+ $content .= "\r\n";
+
+ fwrite($this->fp, $content );
+ }
+
+
+ public function put($source, $dest, $time)
+ {
+ $content = "PUT $dest HTTP/1.1\r\n";
+ $content .= "Host: ".$this->url->host."\r\n";
+ $content .= "Connection: Close\r\n";
+ $content .= "\r\n";
+
+ fwrite($this->fp, $content);
+ fwrite($this->fp, file_get_contents($source));
+ }
+
+
+ public function mkcol( $dir ) {
+ $content = "MKCOL $dir HTTP/1.1\r\n";
+ $content .= "Host: ".$this->url->host."\r\n";
+ $content .= "Connection: Close\r\n";
+ $content .= "\r\n";
+ fwrite($this->fp, $content);
+ }
+
+ public function close()
+ {
+ fclose($this->fp);
+ }
+
+ protected static function acceptsSchemes()
+ {
+ return ['dav'];
+ }
+
+ public function open()
+ {
+ $this->fp = fsockopen($this->url->host, empty($this->url->port)?80:$this->url->port, $errno, $errstr, 5);
+
+ if(!$this->fp)
+ throw new PublisherException("cannot connect to DAV server: $errno -> $errstr");
+
+ $this->checkConnection();
+ }
+}+
\ No newline at end of file
diff --git a/modules/cms/publish/target/Fax.class.php b/modules/cms/publish/target/Fax.class.php
@@ -0,0 +1,57 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+
+
+/**
+ * Publishing a file over fax. Who says that this is not possible?
+ *
+ * @author Jan Dankert
+ */
+class Fax extends Target
+{
+ public function open()
+ {
+ Logger::debug("Dialing ...");
+ }
+
+ public function put($source, $dest, $time)
+ {
+ Logger::debug("düüüüüüüüüüüükrrrkkrkrkrkkrkrkrkr");
+ // very, very funny, right?
+ }
+
+ public function close()
+ {
+ Logger::debug("Hanging up ...");
+ }
+
+ protected static function acceptsSchemes()
+ {
+ return ['fax'];
+ }
+
+ public function __construct($targetUrl)
+ {
+ }
+}
+// ok, this class was a joke.+
\ No newline at end of file
diff --git a/modules/cms/publish/target/Ftp.class.php b/modules/cms/publish/target/Ftp.class.php
@@ -0,0 +1,178 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+
+
+/**
+ * FTP-Target.
+ *
+ * @author Jan Dankert
+ */
+class Ftp extends Target
+{
+ private $connection;
+
+ public static function acceptsSchemes() {
+ return ['ftp'];
+ }
+
+ // Aufbauen der Verbindung
+ public function open()
+ {
+ //global $conf;
+ //$conf_ftp = $conf['publish']['ftp'];
+ $ftp = $this->url;
+
+ // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten
+ //$ftp = array_merge($conf_ftp, $ftp);
+
+ $this->connection = $this->createConnection();
+
+ if (!$this->connection) {
+ Logger::error('Cannot connect to ' . $this->url->host . ':' . $this->url->port);
+ throw new PublisherException('Cannot connect to ' . $this->url->scheme . '-server: ' . $this->url->host . ':' . $this->url->port);
+ }
+
+ if (empty($this->url->user)) {
+ $ftp['user'] = 'anonymous';
+ $ftp['pass'] = 'openrat@openrat.de';
+ }
+
+ if (!ftp_login($this->connection, $this->url->user, $this->url->pass))
+ throw new PublisherException('Unable to login as user ' . $this->url->user);
+
+ $pasv = $this->url->fragment == 'passive';
+
+ if ( $pasv )
+ if (!ftp_pasv($this->connection, true))
+ throw new PublisherException('Cannot switch to FTP PASV mode');
+
+ if ( $this->url->query ) {
+ parse_str($this->url->query, $ftp_var);
+
+ if (isset($ftp_var['site'])) {
+ $site_commands = explode(',', $ftp_var['site']);
+ foreach ($site_commands as $cmd) {
+ if (!@ftp_site($this->connection, $cmd))
+ throw new PublisherException('unable to do SITE command: ' . $cmd);
+ }
+ }
+ }
+
+ $path = rtrim($this->url->path, '/');
+
+ if (!@ftp_chdir($this->connection, $path))
+ throw new PublisherException('unable CHDIR to directory: ' . $path);
+ }
+
+
+ /**
+ * Kopieren einer Datei vom lokalen System auf den FTP-Server.
+ *
+ * @param String Quelle
+ * @param String Ziel
+ * @param int FTP-Mode (BINARY oder ASCII)
+ */
+ public function put($source, $dest, $lastChangeDate)
+ {
+ $dest = $this->url->path . '/' . $dest;
+
+ //$this->log .= "Copying file: $source -> $dest ...\n";
+
+ $mode = FTP_BINARY;
+ $p = strrpos(basename($dest), '.'); // Letzten Punkt suchen
+
+ if ($p !== false) // Wennn letzten Punkt gefunden, dann dort aufteilen
+ {
+ $extension = substr(basename($dest), $p + 1);
+ $type = config('mime-types', $extension);
+ if (substr($type, 0, 5) == 'text/')
+ $mode = FTP_ASCII;
+ }
+
+ Logger::debug("FTP PUT target:$dest mode:" . (($mode == FTP_ASCII) ? 'ascii' : 'binary'));
+
+ if (!@ftp_put($this->connection, $dest, $source, $mode)) {
+ if (!$this->mkdirs(dirname($dest)))
+ return; // Fehler.
+
+ ftp_chdir($this->connection, $this->url->path);
+
+ if (!@ftp_put($this->connection, $dest, $source, $mode))
+ throw new PublisherException("FTP PUT failed.\n" .
+ "source : $source\n" .
+ "destination: $dest");
+
+ }
+ }
+
+
+ /**
+ * Private Methode zum rekursiven Anlegen von Verzeichnissen.
+ *
+ * @param String Pfad
+ * @return boolean true, wenn ok
+ */
+ private function mkdirs($strPath)
+ {
+ if (@ftp_chdir($this->connection, $strPath))
+ return true; // Verzeichnis existiert schon :)
+
+ $pStrPath = dirname($strPath);
+
+ if (!$this->mkdirs($pStrPath))
+ return false;
+
+ if (!@ftp_mkdir($this->connection, $strPath))
+ throw new PublisherException("failed to create remote directory: $strPath");
+
+ return true;
+ }
+
+
+ /**
+ * Schliessen der FTP-Verbindung.<br>
+ * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben.
+ */
+ public function close()
+ {
+ if (!@ftp_quit($this->connection)) {
+ // Closing not possible.
+ // Only logging. Maybe we could throw an Exception here?
+ Logger::warn('Failed to close FTP connection. Continueing...');
+ return;
+ }
+ }
+
+ protected function createConnection()
+ {
+ return ftp_connect($this->url->host, $this->url->port);
+ }
+
+ public static function isAvailable()
+ {
+ return function_exists('ftp_connect');
+ }
+}
+
+
+?>+
\ No newline at end of file
diff --git a/modules/cms/publish/target/Ftps.class.php b/modules/cms/publish/target/Ftps.class.php
@@ -0,0 +1,53 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+
+
+/**
+ * Publishing via FTPS.
+ *
+ * @author Jan Dankert
+ */
+class Ftps extends Ftp
+{
+ public static function acceptsSchemes() {
+ return ['ftps'];
+ }
+
+ /**
+ * Creates a new connection.
+ *
+ * @param $ftp
+ * @return false|resource
+ */
+ protected function createConnection()
+ {
+ return ftp_ssl_connect($this->url->host, $this->url->port);
+ }
+
+
+ public static function isAvailable()
+ {
+ return function_exists('ftp_ssl_connect');
+
+ }
+}+
\ No newline at end of file
diff --git a/modules/cms/publish/target/Local.class.php b/modules/cms/publish/target/Local.class.php
@@ -0,0 +1,152 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+use util\FileUtils;
+use util\Url;
+
+
+/**
+ * Publishing to the local filesystem.
+ *
+ * @author Jan Dankert
+ */
+class Local extends Target
+{
+ /**
+ * @var string
+ */
+ private $localDestinationDirectory;
+
+ public static function acceptsSchemes() {
+ return ['file',''];
+ }
+
+
+ /**
+ * @param $url Url
+ */
+ public function open()
+ {
+ $confPublish = config('publish');
+
+ $targetDir = rtrim( $this->url->path,'/' );
+
+ if ( FileUtils::isAbsolutePath($targetDir) && $confPublish['filesystem']['per_project'] )
+ {
+ $this->localDestinationDirectory = FileUtils::toAbsolutePath([$targetDir]); // Projekteinstellung verwenden.
+ }
+ else
+ {
+ // Konfiguriertes Verzeichnis verwenden.
+ $this->localDestinationDirectory = FileUtils::toAbsolutePath([$confPublish['filesystem']['directory'],$targetDir]);
+ }
+
+
+ // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist.
+ if ( $this->localDestinationDirectory && $this->localDestinationDirectory[0] == '#')
+ $this->localDestinationDirectory = '';
+
+ }
+
+ /**
+ * Copying a file to local filesystem.
+ *
+ * @param String Quelle
+ * @param String Ziel
+ */
+ public function put($source, $dest, $lastChangeDate)
+ {
+ global $conf;
+
+ // Is the output directory writable?
+ if ( !is_writeable( $this->localDestinationDirectory ) )
+ throw new PublisherException('directory not writable: ' . $this->localDestinationDirectory);
+
+ $dest = $this->localDestinationDirectory.'/'.$dest;
+
+ // Is the destination writable?
+ if ( is_file($dest) && !is_writeable( $dest ) )
+ throw new PublisherException('file not writable: ' . $dest);
+
+ // Copy file to destination
+ if (!@copy( $source,$dest ));
+ {
+ // Create directories, if necessary.
+ $this->mkdirs( dirname($dest) );
+
+ if (!@copy( $source,$dest ))
+ throw new PublisherException( 'failed copying local file:' . "\n" .
+ 'source : ' . $source . "\n" .
+ 'destination: ' . $dest);
+
+ // Das Änderungsdatum der Datei auch in der Zieldatei setzen.
+ if ( $conf['publish']['set_modification_date'] )
+ if ( ! is_null($lastChangeDate) )
+ @touch( $dest,$lastChangeDate );
+
+ Logger::debug("published: $dest");
+ }
+
+ if (!empty($conf['security']['chmod']))
+ {
+ // CHMOD auf der Datei ausfuehren.
+ if ( ! @chmod($dest,octdec($conf['security']['chmod'])) )
+ throw new PublisherException('Unable to CHMOD file ' . $dest);
+ }
+
+ }
+
+
+
+ /**
+ * Rekursives Anlagen von Verzeichnisse
+ * Nett gemacht.
+ * Quelle: http://de3.php.net/manual/de/function.mkdir.php
+ * Thx to acroyear at io dot com
+ *
+ * @param String Verzeichnis
+ * @return boolean
+ */
+ private function mkdirs($path )
+ {
+ global $conf;
+
+ if ( is_dir($path) )
+ return; // Path exists
+
+ $parentPath = dirname($path);
+
+ $this->mkdirs($parentPath);
+
+ //
+ if ( ! @mkdir($path) )
+ throw new PublisherException( 'Cannot create directory: ' . $path);
+
+ // CHMOD auf dem Verzeichnis ausgef�hren.
+ if (!empty($conf['security']['chmod_dir']))
+ {
+ if ( ! @chmod($path,octdec($conf['security']['chmod_dir'])) )
+ throw new PublisherException('Unable to CHMOD directory: ' . $path);
+ }
+ }
+
+}+
\ No newline at end of file
diff --git a/modules/cms/publish/target/NoTarget.class.php b/modules/cms/publish/target/NoTarget.class.php
@@ -0,0 +1,46 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+
+
+/**
+ * Empty target.
+ *
+ * @author Jan Dankert
+ */
+class NoTarget extends Target
+{
+ public static function acceptsSchemes()
+ {
+ return ['null', 'example'];
+ }
+
+ public function put($source, $dest, $lastChangeDate)
+ {
+ // Do nothing.
+ }
+
+ public function __construct($targetUrl)
+ {
+
+ }
+}+
\ No newline at end of file
diff --git a/modules/cms/publish/target/README.md b/modules/cms/publish/target/README.md
@@ -0,0 +1,24 @@
+# Targets
+
+While publishing files and pages are pushed to a target.
+
+Possible publishing targets are
+- Local filesystem
+- FTP,FTPS
+- WebDAV
+- SCP
+- SFTP
+
+The corresponding scheme names are
+- `file`
+- `ftp`
+- `ftps`
+- `dav`
+- `scp`
+- `sftp`
+
+The target is selected by the scheme in the target url in the project properties.
+
+## Example
+
+`scp://user@host/var/www` is publishing all files via SCP to the SSH-Server on host 'host'. +
\ No newline at end of file
diff --git a/modules/cms/publish/target/SFtp.class.php b/modules/cms/publish/target/SFtp.class.php
@@ -0,0 +1,112 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+
+
+/**
+ * Darstellen einer FTP-Verbindung, das beinhaltet
+ * das Login, das Kopieren von Dateien sowie praktische
+ * FTP-Funktionen
+ *
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class SFtp extends Scp
+{
+ /**
+ * @var resource
+ */
+ protected $sftpConnection;
+
+
+ public static function acceptsSchemes() {
+ return ['sftp'];
+ }
+
+ // Aufbauen der Verbindung
+ public function open()
+ {
+ global $conf;
+
+ //$conf_ftp = $conf['publish']['sftp'];
+
+ // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten
+ //$sftp = array_merge($conf_ftp, $sftp);
+
+
+ $this->createConnection();
+
+ $this->sftpConnection = @ssh2_sftp($this->sshConnection);
+
+ if (! $this->sftpConnection)
+ throw new PublisherException("Could not initialize SFTP subsystem.");
+
+ }
+
+
+ /**
+ * Kopieren einer Datei vom lokalen System auf den SFTP-Server.
+ *
+ * @param String Quelle
+ * @param String Ziel
+ * @param int time)
+ */
+ public function put($source, $dest, $lastChangeDate)
+ {
+ $dest = $this->url->path . '/' . $dest;
+
+ $sftp = $this->sftpConnection;
+
+ $stream = @fopen("ssh2.sftp://$sftp$dest", 'w');
+
+ if (! $stream)
+ throw new PublisherException("Could not create SFTP-Stream on file: $dest");
+
+ $data_to_send = @file_get_contents($source);
+
+ if ($data_to_send === false)
+ throw new PublisherException("Could not open local file: $source");
+
+ if (@fwrite($stream, $data_to_send) === false)
+ throw new PublisherException("Could not send data from file: $source.");
+
+ @fclose($stream);
+ }
+
+
+ /**
+ * Schliessen der FTP-Verbindung.<br>
+ * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben.
+ */
+ public function close()
+ {
+ parent::close();
+ }
+
+
+ public static function isAvailable()
+ {
+ return parent::isAvailable() && function_exists('ssh2_sftp');
+ }
+}
+
diff --git a/modules/cms/publish/target/Scp.class.php b/modules/cms/publish/target/Scp.class.php
@@ -0,0 +1,104 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+
+
+/**
+ * Darstellen einer FTP-Verbindung, das beinhaltet
+ * das Login, das Kopieren von Dateien sowie praktische
+ * FTP-Funktionen
+ *
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Scp extends Target
+{
+ /**
+ * @var resource
+ */
+ protected $sshConnection;
+
+ public static function acceptsSchemes() {
+ return ['scp'];
+ }
+
+ // Aufbauen der Verbindung
+ public function open()
+ {
+ $this->sshConnection = $this->createConnection();
+ }
+
+
+
+ protected function createConnection() {
+
+ $sshConnection = @ssh2_connect($this->url->host,$this->url->port );
+
+ if (! $sshConnection)
+ throw new PublisherException("Could not connect to ".$this->url );
+
+
+ if (! @ssh2_auth_password($sshConnection, $this->url->user,$this->url->pass) )
+ throw new PublisherException("Could not authenticate");
+
+ $this->sshConnection = $sshConnection;
+
+ return $sshConnection;
+ }
+
+
+
+ /**
+ * Kopieren einer Datei vom lokalen System auf den FTP-Server.
+ *
+ * @param String Quelle
+ * @param String Ziel
+ * @param int FTP-Mode (BINARY oder ASCII)
+ */
+ public function put($source, $dest, $lastChangeDate)
+ {
+ $dest = $this->url->path . '/' . $dest;
+
+
+ ssh2_scp_send($this->sshConnection, $source, $dest, 0644);
+ }
+
+
+ /**
+ * Schliessen der FTP-Verbindung.<br>
+ * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben.
+ */
+ public function close()
+ {
+ ssh2_disconnect($this->sshConnection);
+ }
+
+
+ public static function isAvailable()
+ {
+ return function_exists('ssh2_connect');
+ }
+}
+
+
+?>+
\ No newline at end of file
diff --git a/modules/cms/publish/target/Target.class.php b/modules/cms/publish/target/Target.class.php
@@ -0,0 +1,79 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+namespace cms\publish\target;
+
+use logger\Logger;
+use util\exception\PublisherException;
+use util\exception\UIException;
+use util\Url;
+
+
+/**
+ * Superclass for publication targets.
+ *
+ * @author Jan Dankert
+ */
+abstract class Target
+{
+ /**
+ * @var Url
+ */
+ protected $url;
+
+ public function __construct( $targetUrl ) {
+ $this->url = new Url( $targetUrl );
+
+ $this->open();
+ }
+
+
+ public static function accepts( $scheme ) {
+ return in_array( $scheme, static::acceptsSchemes() );
+ }
+
+
+ /**
+ * For which types this target is reponsible?
+ *
+ * @return array
+ */
+ protected abstract static function acceptsSchemes();
+
+
+ public function open() {
+
+ }
+
+
+ public abstract function put($source, $dest, $timestamp);
+
+
+ /**
+ * Closes the connection.
+ */
+ public function close() {
+
+ }
+
+
+ public static function isAvailable() {
+
+ return true;
+ }
+
+}
diff --git a/modules/util/Url.class.php b/modules/util/Url.class.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace util;
+
+class Url {
+
+ public $scheme;
+ public $host;
+ public $port;
+ public $user;
+ public $pass;
+ public $path;
+ public $query;
+ public $fragment;
+
+ function __construct( $url = null ) {
+
+ if ( !empty($url) )
+ $this->parseUrl($url);
+ }
+
+
+ public function parseUrl( $url ) {
+ foreach ( parse_url($url) as $key=>$value )
+ $this->$key = $value;
+ }
+
+
+ public function __toString()
+ {
+ $scheme = !empty($this->scheme) ? $this->scheme . '://' : '';
+
+ if ( empty($scheme) )
+ $scheme = 'file:/';
+
+ $host = !empty($this->host ) ? $this->host : '';
+ $port = !empty($this->port ) ? ':' . $this->port : '';
+ $user = !empty($this->user ) ? $this->user : '';
+ $pass = !empty($this->pass ) ? ':' . $this->pass : '';
+ $pass = ($user || $pass) ? "$pass@" : '';
+ $path = !empty($this->path ) ? $this->path : '';
+ $query = !empty($this->query ) ? '?' . $this->query : '';
+ $fragment = !empty($this->fragment) ? '#' . $this->fragment : '';
+
+ return "$scheme$user$pass$host$port$path$query$fragment";
+ }
+
+}+
\ No newline at end of file