2012-03-06 09:58:01 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Kolab format model class wrapping libkolabxml bindings
|
|
|
|
*
|
2012-03-14 19:48:54 +01:00
|
|
|
* Abstract base class for different Kolab groupware objects read from/written
|
|
|
|
* to the new Kolab 3 format using the PHP bindings of libkolabxml.
|
|
|
|
*
|
2012-03-06 09:58:01 +01:00
|
|
|
* @version @package_version@
|
|
|
|
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as
|
|
|
|
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
abstract class kolab_format
|
|
|
|
{
|
|
|
|
public static $timezone;
|
|
|
|
|
2012-05-22 10:14:56 +02:00
|
|
|
public /*abstract*/ $CTYPE;
|
|
|
|
|
2012-11-01 20:06:09 +01:00
|
|
|
protected /*abstract*/ $objclass;
|
2012-05-22 10:14:56 +02:00
|
|
|
protected /*abstract*/ $read_func;
|
|
|
|
protected /*abstract*/ $write_func;
|
|
|
|
|
2012-03-14 18:51:38 +01:00
|
|
|
protected $obj;
|
|
|
|
protected $data;
|
2012-05-02 17:41:02 +02:00
|
|
|
protected $xmldata;
|
2012-11-01 20:06:09 +01:00
|
|
|
protected $xmlobject;
|
2012-05-02 17:41:02 +02:00
|
|
|
protected $loaded = false;
|
2012-11-01 20:06:09 +01:00
|
|
|
protected $version = 3.0;
|
2012-03-14 18:51:38 +01:00
|
|
|
|
2012-06-22 15:37:09 +02:00
|
|
|
const KTYPE_PREFIX = 'application/x-vnd.kolab.';
|
2012-11-01 20:06:09 +01:00
|
|
|
const PRODUCT_ID = 'Roundcube-libkolab-0.9';
|
2012-06-08 15:15:43 +02:00
|
|
|
|
2012-03-06 09:58:01 +01:00
|
|
|
/**
|
2012-11-01 20:06:09 +01:00
|
|
|
* Factory method to instantiate a kolab_format object of the given type and version
|
2012-05-02 17:41:02 +02:00
|
|
|
*
|
|
|
|
* @param string Object type to instantiate
|
|
|
|
* @param string Cached xml data to initialize with
|
2012-11-01 20:06:09 +01:00
|
|
|
* @param float Format version
|
2012-05-02 17:41:02 +02:00
|
|
|
* @return object kolab_format
|
2012-03-06 09:58:01 +01:00
|
|
|
*/
|
2012-11-01 20:06:09 +01:00
|
|
|
public static function factory($type, $xmldata = null, $version = 3.0)
|
2012-03-06 09:58:01 +01:00
|
|
|
{
|
|
|
|
if (!isset(self::$timezone))
|
|
|
|
self::$timezone = new DateTimeZone('UTC');
|
|
|
|
|
2012-11-01 20:06:09 +01:00
|
|
|
if (!self::supports($version))
|
|
|
|
return PEAR::raiseError("No support for Kolab format version " . $version);
|
|
|
|
|
2012-05-23 14:34:08 +02:00
|
|
|
$type = preg_replace('/configuration\.[a-z.]+$/', 'configuration', $type);
|
2012-03-06 09:58:01 +01:00
|
|
|
$suffix = preg_replace('/[^a-z]+/', '', $type);
|
|
|
|
$classname = 'kolab_format_' . $suffix;
|
|
|
|
if (class_exists($classname))
|
2012-11-01 20:06:09 +01:00
|
|
|
return new $classname($xmldata, $version);
|
2012-03-06 09:58:01 +01:00
|
|
|
|
2012-05-23 14:34:08 +02:00
|
|
|
return PEAR::raiseError("Failed to load Kolab Format wrapper for type " . $type);
|
2012-03-06 09:58:01 +01:00
|
|
|
}
|
|
|
|
|
2012-11-01 20:06:09 +01:00
|
|
|
/**
|
|
|
|
* Determine support for the given format version
|
|
|
|
*
|
|
|
|
* @param float Format version to check
|
|
|
|
* @return boolean True if supported, False otherwise
|
|
|
|
*/
|
|
|
|
public static function supports($version)
|
|
|
|
{
|
|
|
|
if ($version == 2.0)
|
|
|
|
return class_exists('kolabobject');
|
|
|
|
// default is version 3
|
|
|
|
return class_exists('kolabformat');
|
|
|
|
}
|
|
|
|
|
2012-03-06 09:58:01 +01:00
|
|
|
/**
|
2012-03-08 10:21:21 +01:00
|
|
|
* Convert the given date/time value into a cDateTime object
|
2012-03-06 09:58:01 +01:00
|
|
|
*
|
|
|
|
* @param mixed Date/Time value either as unix timestamp, date string or PHP DateTime object
|
2012-05-23 15:39:53 +02:00
|
|
|
* @param DateTimeZone The timezone the date/time is in. Use global default if Null, local time if False
|
2012-03-06 09:58:01 +01:00
|
|
|
* @param boolean True of the given date has no time component
|
2012-03-14 18:51:38 +01:00
|
|
|
* @return object The libkolabxml date/time object
|
2012-03-06 09:58:01 +01:00
|
|
|
*/
|
2012-03-08 10:21:21 +01:00
|
|
|
public static function get_datetime($datetime, $tz = null, $dateonly = false)
|
2012-03-06 09:58:01 +01:00
|
|
|
{
|
2012-07-05 11:43:22 +02:00
|
|
|
// use timezone information from datetime of global setting
|
|
|
|
if (!$tz && $tz !== false) {
|
|
|
|
if ($datetime instanceof DateTime)
|
|
|
|
$tz = $datetime->getTimezone();
|
|
|
|
if (!$tz)
|
|
|
|
$tz = self::$timezone;
|
|
|
|
}
|
2012-03-14 18:51:38 +01:00
|
|
|
$result = new cDateTime();
|
2012-03-06 09:58:01 +01:00
|
|
|
|
2012-03-30 19:07:56 +02:00
|
|
|
// got a unix timestamp (in UTC)
|
|
|
|
if (is_numeric($datetime)) {
|
|
|
|
$datetime = new DateTime('@'.$datetime, new DateTimeZone('UTC'));
|
|
|
|
if ($tz) $datetime->setTimezone($tz);
|
|
|
|
}
|
2012-03-08 10:21:21 +01:00
|
|
|
else if (is_string($datetime) && strlen($datetime))
|
2012-05-23 15:39:53 +02:00
|
|
|
$datetime = new DateTime($datetime, $tz ?: null);
|
2012-03-06 09:58:01 +01:00
|
|
|
|
2012-07-05 11:43:22 +02:00
|
|
|
if ($datetime instanceof DateTime) {
|
2012-03-06 22:23:34 +01:00
|
|
|
$result->setDate($datetime->format('Y'), $datetime->format('n'), $datetime->format('j'));
|
2012-03-06 09:58:01 +01:00
|
|
|
|
|
|
|
if (!$dateonly)
|
|
|
|
$result->setTime($datetime->format('G'), $datetime->format('i'), $datetime->format('s'));
|
2012-03-30 19:07:56 +02:00
|
|
|
|
2012-04-05 08:56:37 +02:00
|
|
|
if ($tz && $tz->getName() == 'UTC')
|
|
|
|
$result->setUTC(true);
|
2012-05-23 15:39:53 +02:00
|
|
|
else if ($tz !== false)
|
2012-07-06 10:58:50 +02:00
|
|
|
$result->setTimezone($tz->getName());
|
2012-03-06 09:58:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2012-03-08 10:21:21 +01:00
|
|
|
/**
|
|
|
|
* Convert the given cDateTime into a PHP DateTime object
|
|
|
|
*
|
|
|
|
* @param object cDateTime The libkolabxml datetime object
|
|
|
|
* @return object DateTime PHP datetime instance
|
|
|
|
*/
|
|
|
|
public static function php_datetime($cdt)
|
|
|
|
{
|
|
|
|
if (!is_object($cdt) || !$cdt->isValid())
|
|
|
|
return null;
|
|
|
|
|
|
|
|
$d = new DateTime;
|
|
|
|
$d->setTimezone(self::$timezone);
|
|
|
|
|
|
|
|
try {
|
|
|
|
if ($tzs = $cdt->timezone()) {
|
|
|
|
$tz = new DateTimeZone($tzs);
|
|
|
|
$d->setTimezone($tz);
|
|
|
|
}
|
2012-04-05 08:56:37 +02:00
|
|
|
else if ($cdt->isUTC()) {
|
|
|
|
$d->setTimezone(new DateTimeZone('UTC'));
|
|
|
|
}
|
2012-03-08 10:21:21 +01:00
|
|
|
}
|
|
|
|
catch (Exception $e) { }
|
|
|
|
|
|
|
|
$d->setDate($cdt->year(), $cdt->month(), $cdt->day());
|
|
|
|
|
|
|
|
if ($cdt->isDateOnly()) {
|
|
|
|
$d->_dateonly = true;
|
|
|
|
$d->setTime(12, 0, 0); // set time to noon to avoid timezone troubles
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$d->setTime($cdt->hour(), $cdt->minute(), $cdt->second());
|
|
|
|
}
|
|
|
|
|
|
|
|
return $d;
|
|
|
|
}
|
|
|
|
|
2012-03-06 09:58:01 +01:00
|
|
|
/**
|
|
|
|
* Convert a libkolabxml vector to a PHP array
|
|
|
|
*
|
|
|
|
* @param object vector Object
|
|
|
|
* @return array Indexed array contaning vector elements
|
|
|
|
*/
|
2012-03-06 22:23:34 +01:00
|
|
|
public static function vector2array($vec, $max = PHP_INT_MAX)
|
2012-03-06 09:58:01 +01:00
|
|
|
{
|
|
|
|
$arr = array();
|
2012-03-06 22:23:34 +01:00
|
|
|
for ($i=0; $i < $vec->size() && $i < $max; $i++)
|
2012-03-06 09:58:01 +01:00
|
|
|
$arr[] = $vec->get($i);
|
|
|
|
return $arr;
|
|
|
|
}
|
|
|
|
|
2012-03-06 22:23:34 +01:00
|
|
|
/**
|
|
|
|
* Build a libkolabxml vector (string) from a PHP array
|
|
|
|
*
|
|
|
|
* @param array Array with vector elements
|
|
|
|
* @return object vectors
|
|
|
|
*/
|
|
|
|
public static function array2vector($arr)
|
|
|
|
{
|
|
|
|
$vec = new vectors;
|
2012-03-14 18:51:38 +01:00
|
|
|
foreach ((array)$arr as $val) {
|
|
|
|
if (strlen($val))
|
|
|
|
$vec->push($val);
|
|
|
|
}
|
2012-03-06 22:23:34 +01:00
|
|
|
return $vec;
|
|
|
|
}
|
|
|
|
|
2012-06-22 15:37:09 +02:00
|
|
|
/**
|
|
|
|
* Parse the X-Kolab-Type header from MIME messages and return the object type in short form
|
|
|
|
*
|
|
|
|
* @param string X-Kolab-Type header value
|
|
|
|
* @return string Kolab object type (contact,event,task,note,etc.)
|
|
|
|
*/
|
|
|
|
public static function mime2object_type($x_kolab_type)
|
|
|
|
{
|
|
|
|
return preg_replace('/dictionary.[a-z.]+$/', 'dictionary', substr($x_kolab_type, strlen(self::KTYPE_PREFIX)));
|
|
|
|
}
|
|
|
|
|
2012-11-01 20:06:09 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor of all kolab_format_* objects
|
|
|
|
*/
|
|
|
|
public function __construct($xmldata = null, $version = null)
|
|
|
|
{
|
|
|
|
$this->obj = new $this->objclass;
|
|
|
|
$this->xmldata = $xmldata;
|
|
|
|
|
|
|
|
if ($version)
|
|
|
|
$this->version = $version;
|
|
|
|
|
|
|
|
// use libkolab module if available
|
|
|
|
if (class_exists('kolabobject'))
|
|
|
|
$this->xmlobject = new XMLObject();
|
|
|
|
}
|
|
|
|
|
2012-05-16 14:15:05 +02:00
|
|
|
/**
|
|
|
|
* Check for format errors after calling kolabformat::write*()
|
|
|
|
*
|
|
|
|
* @return boolean True if there were errors, False if OK
|
|
|
|
*/
|
|
|
|
protected function format_errors()
|
|
|
|
{
|
|
|
|
$ret = $log = false;
|
|
|
|
switch (kolabformat::error()) {
|
2012-06-06 10:22:51 +02:00
|
|
|
case kolabformat::NoError:
|
2012-05-16 14:15:05 +02:00
|
|
|
$ret = false;
|
|
|
|
break;
|
2012-06-06 10:22:51 +02:00
|
|
|
case kolabformat::Warning:
|
2012-05-16 14:15:05 +02:00
|
|
|
$ret = false;
|
|
|
|
$log = "Warning";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
$ret = true;
|
|
|
|
$log = "Error";
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($log) {
|
2012-06-05 09:15:23 +02:00
|
|
|
rcube::raise_error(array(
|
2012-05-16 14:15:05 +02:00
|
|
|
'code' => 660,
|
|
|
|
'type' => 'php',
|
|
|
|
'file' => __FILE__,
|
|
|
|
'line' => __LINE__,
|
|
|
|
'message' => "kolabformat write $log: " . kolabformat::errorMessage(),
|
|
|
|
), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2012-03-14 18:51:38 +01:00
|
|
|
/**
|
|
|
|
* Save the last generated UID to the object properties.
|
|
|
|
* Should be called after kolabformat::writeXXXX();
|
|
|
|
*/
|
|
|
|
protected function update_uid()
|
|
|
|
{
|
|
|
|
// get generated UID
|
|
|
|
if (!$this->data['uid']) {
|
|
|
|
$this->data['uid'] = kolabformat::getSerializedUID();
|
|
|
|
$this->obj->setUid($this->data['uid']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-02 17:41:02 +02:00
|
|
|
/**
|
|
|
|
* Initialize libkolabxml object with cached xml data
|
|
|
|
*/
|
|
|
|
protected function init()
|
|
|
|
{
|
|
|
|
if (!$this->loaded) {
|
|
|
|
if ($this->xmldata) {
|
|
|
|
$this->load($this->xmldata);
|
|
|
|
$this->xmldata = null;
|
|
|
|
}
|
|
|
|
$this->loaded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-01 20:06:09 +01:00
|
|
|
/**
|
|
|
|
* Get constant value for libkolab's version parameter
|
|
|
|
*
|
|
|
|
* @param float Version value to convert
|
|
|
|
* @return int Constant value of either kolabobject::KolabV2 or kolabobject::KolabV3 or false if kolabobject module isn't available
|
|
|
|
*/
|
|
|
|
protected function libversion($v = null)
|
|
|
|
{
|
|
|
|
if (class_exists('kolabobject')) {
|
|
|
|
$version = $v ?: $this->version;
|
|
|
|
if ($version <= 2.0)
|
|
|
|
return kolabobject::KolabV2;
|
|
|
|
else
|
|
|
|
return kolabobject::KolabV3;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine the correct libkolab(xml) wrapper function for the given call
|
|
|
|
* depending on the available PHP modules
|
|
|
|
*/
|
|
|
|
protected function libfunc($func)
|
|
|
|
{
|
|
|
|
if (is_array($func) || strpos($func, '::'))
|
|
|
|
return $func;
|
|
|
|
else if (class_exists('kolabobject'))
|
|
|
|
return array($this->xmlobject, $func);
|
|
|
|
else
|
|
|
|
return 'kolabformat::' . $func;
|
|
|
|
}
|
|
|
|
|
2012-03-14 18:51:38 +01:00
|
|
|
/**
|
|
|
|
* Direct getter for object properties
|
|
|
|
*/
|
2012-05-02 17:41:02 +02:00
|
|
|
public function __get($var)
|
2012-03-14 18:51:38 +01:00
|
|
|
{
|
|
|
|
return $this->data[$var];
|
|
|
|
}
|
|
|
|
|
2012-03-06 09:58:01 +01:00
|
|
|
/**
|
|
|
|
* Load Kolab object data from the given XML block
|
|
|
|
*
|
|
|
|
* @param string XML data
|
2012-11-01 20:06:09 +01:00
|
|
|
* @return boolean True on success, False on failure
|
2012-03-06 09:58:01 +01:00
|
|
|
*/
|
2012-05-22 10:14:56 +02:00
|
|
|
public function load($xml)
|
|
|
|
{
|
2012-11-01 20:06:09 +01:00
|
|
|
$r = call_user_func($this->libfunc($this->read_func), $xml, $this->libversion());
|
|
|
|
if (is_resource($r))
|
|
|
|
$this->obj = new $this->objclass($r);
|
|
|
|
else if (is_a($r, $this->objclass))
|
|
|
|
$this->obj = $r;
|
|
|
|
|
2012-05-22 10:14:56 +02:00
|
|
|
$this->loaded = !$this->format_errors();
|
|
|
|
}
|
2012-03-06 09:58:01 +01:00
|
|
|
|
|
|
|
/**
|
2012-05-22 10:14:56 +02:00
|
|
|
* Write object data to XML format
|
2012-03-06 09:58:01 +01:00
|
|
|
*
|
2012-11-01 20:06:09 +01:00
|
|
|
* @param float Format version to write
|
2012-05-22 10:14:56 +02:00
|
|
|
* @return string XML data
|
2012-03-06 09:58:01 +01:00
|
|
|
*/
|
2012-11-01 20:06:09 +01:00
|
|
|
public function write($version = null)
|
2012-05-22 10:14:56 +02:00
|
|
|
{
|
|
|
|
$this->init();
|
2012-11-02 13:45:05 +01:00
|
|
|
$write_func = $this->libfunc($this->write_func);
|
|
|
|
if (is_array($write_func))
|
|
|
|
$this->xmldata = call_user_func($write_func, $this->obj, $this->libversion($version), self::PRODUCT_ID);
|
|
|
|
else
|
|
|
|
$this->xmldata = call_user_func($write_func, $this->obj, self::PRODUCT_ID);
|
2012-05-22 10:14:56 +02:00
|
|
|
|
|
|
|
if (!$this->format_errors())
|
|
|
|
$this->update_uid();
|
|
|
|
else
|
|
|
|
$this->xmldata = null;
|
|
|
|
|
|
|
|
return $this->xmldata;
|
|
|
|
}
|
2012-03-06 09:58:01 +01:00
|
|
|
|
|
|
|
/**
|
2012-05-22 10:14:56 +02:00
|
|
|
* Set properties to the kolabformat object
|
2012-03-06 09:58:01 +01:00
|
|
|
*
|
2012-05-22 10:14:56 +02:00
|
|
|
* @param array Object data as hash array
|
2012-03-06 09:58:01 +01:00
|
|
|
*/
|
2012-05-22 10:14:56 +02:00
|
|
|
abstract public function set(&$object);
|
2012-03-06 09:58:01 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
2012-05-22 10:14:56 +02:00
|
|
|
abstract public function is_valid();
|
2012-03-06 09:58:01 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert the Kolab object into a hash array data structure
|
|
|
|
*
|
|
|
|
* @return array Kolab object data as hash array
|
|
|
|
*/
|
|
|
|
abstract public function to_array();
|
|
|
|
|
2012-03-14 11:22:42 +01:00
|
|
|
/**
|
|
|
|
* Load object data from Kolab2 format
|
|
|
|
*
|
|
|
|
* @param array Hash array with object properties (produced by Horde Kolab_Format classes)
|
|
|
|
*/
|
|
|
|
abstract public function fromkolab2($object);
|
2012-05-15 19:05:46 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback for kolab_storage_cache to get object specific tags to cache
|
|
|
|
*
|
|
|
|
* @return array List of tags to save in cache
|
|
|
|
*/
|
|
|
|
public function get_tags()
|
|
|
|
{
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback for kolab_storage_cache to get words to index for fulltext search
|
|
|
|
*
|
|
|
|
* @return array List of words to save in cache
|
|
|
|
*/
|
|
|
|
public function get_words()
|
|
|
|
{
|
|
|
|
return array();
|
|
|
|
}
|
2012-03-06 09:58:01 +01:00
|
|
|
}
|