Merge branch 'dev/libcalendaring'
This commit is contained in:
commit
cf5c88437e
16 changed files with 220 additions and 182 deletions
|
@ -376,7 +376,15 @@ class kolab_calendar
|
|||
*/
|
||||
public function _get_recurring_events($event, $start, $end, $event_id = null)
|
||||
{
|
||||
$recurrence = new kolab_date_recurrence($event);
|
||||
$object = $event['_formatobj'];
|
||||
if (!$object) {
|
||||
$rec = $this->storage->get_object($event['id']);
|
||||
$object = $rec['_formatobj'];
|
||||
}
|
||||
if (!is_object($object))
|
||||
return array();
|
||||
|
||||
$recurrence = new kolab_date_recurrence($object);
|
||||
|
||||
$i = 0;
|
||||
$events = array();
|
||||
|
@ -401,6 +409,10 @@ class kolab_calendar
|
|||
}
|
||||
else if ($next_event['start'] > $end) // stop loop if out of range
|
||||
break;
|
||||
|
||||
// avoid endless recursion loops
|
||||
if ($i > 1000)
|
||||
break;
|
||||
}
|
||||
|
||||
return $events;
|
||||
|
|
|
@ -77,7 +77,7 @@ class kolab_config extends rcube_plugin
|
|||
|
||||
// if no folder is set as default, choose the first one
|
||||
if (!$this->default)
|
||||
$this->default = $this->folders[0];
|
||||
$this->default = reset($this->folders);
|
||||
|
||||
// check if configuration folder exist
|
||||
if ($this->default && $this->default->name) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
/* Configuration for libkolab */
|
||||
|
||||
$rcmail_config['kolab_cache'] = true;
|
||||
$rcmail_config['kolab_format_version'] = 3.0;
|
||||
|
||||
$rcmail_config['kolab_freebusy_server'] = 'https://' . $_SESSION['imap_host'] . '/freebusy';
|
||||
$rcmail_config['kolab_ssl_verify_peer'] = true;
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
/**
|
||||
* Recurrence computation class for xcal-based Kolab format objects
|
||||
*
|
||||
* Uitility class to compute instances of recurring events.
|
||||
* Utility class to compute instances of recurring events.
|
||||
* It requires the libcalendaring PHP module to be installed and loaded.
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
|
@ -25,14 +26,12 @@
|
|||
*/
|
||||
class kolab_date_recurrence
|
||||
{
|
||||
private $engine;
|
||||
private $object;
|
||||
private $next;
|
||||
private $duration;
|
||||
private $tz_offset = 0;
|
||||
private $dst_start = 0;
|
||||
private $allday = false;
|
||||
private $hour = 0;
|
||||
private /* EventCal */ $engine;
|
||||
private /* kolab_format_xcal */ $object;
|
||||
private /* DateTime */ $start;
|
||||
private /* DateTime */ $next;
|
||||
private /* cDateTime */ $cnext;
|
||||
private /* DateInterval */ $duration;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
|
@ -41,27 +40,17 @@ class kolab_date_recurrence
|
|||
*/
|
||||
function __construct($object)
|
||||
{
|
||||
$data = $object->to_array();
|
||||
|
||||
$this->object = $object;
|
||||
$this->next = new Horde_Date($object['start'], kolab_format::$timezone->getName());
|
||||
$this->engine = $object->to_libcal();
|
||||
$this->start = $this->next = $data['start'];
|
||||
$this->cnext = kolab_format::get_datetime($this->next);
|
||||
|
||||
if (is_object($object['start']) && is_object($object['end']))
|
||||
$this->duration = $object['start']->diff($object['end']);
|
||||
if (is_object($data['start']) && is_object($data['end']))
|
||||
$this->duration = $data['start']->diff($data['end']);
|
||||
else
|
||||
$this->duration = new DateInterval('PT' . ($object['end'] - $object['start']) . 'S');
|
||||
|
||||
// use (copied) Horde classes to compute recurring instances
|
||||
// TODO: replace with something that has less than 6'000 lines of code
|
||||
$this->engine = new Horde_Date_Recurrence($this->next);
|
||||
$this->engine->fromRRule20($this->to_rrule($object['recurrence'])); // TODO: get that string directly from libkolabxml
|
||||
|
||||
foreach ((array)$object['recurrence']['EXDATE'] as $exdate)
|
||||
$this->engine->addException($exdate->format('Y'), $exdate->format('n'), $exdate->format('j'));
|
||||
|
||||
$now = new DateTime('now', kolab_format::$timezone);
|
||||
$this->tz_offset = $object['allday'] ? $now->getOffset() - date('Z') : 0;
|
||||
$this->dst_start = $this->next->format('I');
|
||||
$this->allday = $object['allday'];
|
||||
$this->hour = $this->next->hour;
|
||||
$this->duration = new DateInterval('PT' . ($data['end'] - $data['start']) . 'S');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,20 +62,14 @@ class kolab_date_recurrence
|
|||
public function next_start($timestamp = false)
|
||||
{
|
||||
$time = false;
|
||||
if ($this->next && ($next = $this->engine->nextActiveRecurrence(array('year' => $this->next->year, 'month' => $this->next->month, 'mday' => $this->next->mday + 1, 'hour' => $this->next->hour, 'min' => $this->next->min, 'sec' => $this->next->sec)))) {
|
||||
if ($this->allday) {
|
||||
$next->hour = $this->hour; # fix time for all-day events
|
||||
$next->min = 0;
|
||||
|
||||
if ($this->engine && $this->next) {
|
||||
if (($cnext = new cDateTime($this->engine->getNextOccurence($this->cnext))) && $cnext->isValid()) {
|
||||
$next = kolab_format::php_datetime($cnext);
|
||||
$time = $timestamp ? $next->format('U') : $next;
|
||||
$this->cnext = $cnext;
|
||||
$this->next = $next;
|
||||
}
|
||||
if ($timestamp) {
|
||||
# consider difference in daylight saving between base event and recurring instance
|
||||
$dst_diff = ($this->dst_start - $next->format('I')) * 3600;
|
||||
$time = $next->timestamp() - $this->tz_offset - $dst_diff;
|
||||
}
|
||||
else {
|
||||
$time = $next->toDateTime();
|
||||
}
|
||||
$this->next = $next;
|
||||
}
|
||||
|
||||
return $time;
|
||||
|
@ -103,7 +86,7 @@ class kolab_date_recurrence
|
|||
$next_end = clone $next_start;
|
||||
$next_end->add($this->duration);
|
||||
|
||||
$next = $this->object;
|
||||
$next = $this->object->to_array();
|
||||
$next['recurrence_id'] = $next_start->format('Y-m-d');
|
||||
$next['start'] = $next_start;
|
||||
$next['end'] = $next_end;
|
||||
|
@ -123,49 +106,11 @@ class kolab_date_recurrence
|
|||
*/
|
||||
public function end($limit = 'now +1 year')
|
||||
{
|
||||
if ($this->object['recurrence']['UNTIL'])
|
||||
return $this->object['recurrence']['UNTIL']->format('U');
|
||||
|
||||
$limit_time = strtotime($limit);
|
||||
while ($next_start = $this->next_start(true)) {
|
||||
if ($next_start > $limit_time)
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->next) {
|
||||
$next_end = $this->next->toDateTime();
|
||||
$next_end->add($this->duration);
|
||||
return $next_end->format('U');
|
||||
$limit_dt = new DateTime($limit);
|
||||
if ($this->engine && ($cend = $this->engine->getLastOccurrence()) && ($end_dt = kolab_format::php_datetime(new cDateTime($cend))) && $end_dt < $limit_dt) {
|
||||
return $end_dt->format('U');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the internal structured data into a vcalendar RRULE 2.0 string
|
||||
*/
|
||||
private function to_rrule($recurrence)
|
||||
{
|
||||
if (is_string($recurrence))
|
||||
return $recurrence;
|
||||
|
||||
$rrule = '';
|
||||
foreach ((array)$recurrence as $k => $val) {
|
||||
$k = strtoupper($k);
|
||||
switch ($k) {
|
||||
case 'UNTIL':
|
||||
$val = $val->format('Ymd\THis');
|
||||
break;
|
||||
case 'EXDATE':
|
||||
foreach ((array)$val as $i => $ex)
|
||||
$val[$i] = $ex->format('Ymd\THis');
|
||||
$val = join(',', (array)$val);
|
||||
break;
|
||||
}
|
||||
$rrule .= $k . '=' . $val . ';';
|
||||
}
|
||||
|
||||
return $rrule;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,39 +30,61 @@ abstract class kolab_format
|
|||
public static $timezone;
|
||||
|
||||
public /*abstract*/ $CTYPE;
|
||||
public /*abstract*/ $CTYPEv2;
|
||||
|
||||
protected /*abstract*/ $objclass;
|
||||
protected /*abstract*/ $read_func;
|
||||
protected /*abstract*/ $write_func;
|
||||
|
||||
protected $obj;
|
||||
protected $data;
|
||||
protected $xmldata;
|
||||
protected $xmlobject;
|
||||
protected $loaded = false;
|
||||
protected $version = 3.0;
|
||||
|
||||
const VERSION = '3.0';
|
||||
const KTYPE_PREFIX = 'application/x-vnd.kolab.';
|
||||
const PRODUCT_ID = 'Roundcube-libkolab-0.9';
|
||||
|
||||
/**
|
||||
* Factory method to instantiate a kolab_format object of the given type
|
||||
* Factory method to instantiate a kolab_format object of the given type and version
|
||||
*
|
||||
* @param string Object type to instantiate
|
||||
* @param float Format version
|
||||
* @param string Cached xml data to initialize with
|
||||
* @return object kolab_format
|
||||
*/
|
||||
public static function factory($type, $xmldata = null)
|
||||
public static function factory($type, $version = 3.0, $xmldata = null)
|
||||
{
|
||||
if (!isset(self::$timezone))
|
||||
self::$timezone = new DateTimeZone('UTC');
|
||||
|
||||
if (!self::supports($version))
|
||||
return PEAR::raiseError("No support for Kolab format version " . $version);
|
||||
|
||||
$type = preg_replace('/configuration\.[a-z.]+$/', 'configuration', $type);
|
||||
$suffix = preg_replace('/[^a-z]+/', '', $type);
|
||||
$classname = 'kolab_format_' . $suffix;
|
||||
if (class_exists($classname))
|
||||
return new $classname($xmldata);
|
||||
return new $classname($xmldata, $version);
|
||||
|
||||
return PEAR::raiseError("Failed to load Kolab Format wrapper for type " . $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given date/time value into a cDateTime object
|
||||
*
|
||||
|
@ -184,6 +206,23 @@ abstract class kolab_format
|
|||
return preg_replace('/dictionary.[a-z.]+$/', 'dictionary', substr($x_kolab_type, strlen(self::KTYPE_PREFIX)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for format errors after calling kolabformat::write*()
|
||||
*
|
||||
|
@ -211,7 +250,7 @@ abstract class kolab_format
|
|||
'type' => 'php',
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => "kolabformat write $log: " . kolabformat::errorMessage(),
|
||||
'message' => "kolabformat $log: " . kolabformat::errorMessage(),
|
||||
), true);
|
||||
}
|
||||
|
||||
|
@ -226,7 +265,7 @@ abstract class kolab_format
|
|||
{
|
||||
// get generated UID
|
||||
if (!$this->data['uid']) {
|
||||
$this->data['uid'] = kolabformat::getSerializedUID();
|
||||
$this->data['uid'] = $this->xmlobject ? $this->xmlobject->getSerializedUID() : kolabformat::getSerializedUID();
|
||||
$this->obj->setUid($this->data['uid']);
|
||||
}
|
||||
}
|
||||
|
@ -245,6 +284,39 @@ abstract class kolab_format
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct getter for object properties
|
||||
*/
|
||||
|
@ -257,22 +329,39 @@ abstract class kolab_format
|
|||
* Load Kolab object data from the given XML block
|
||||
*
|
||||
* @param string XML data
|
||||
* @return boolean True on success, False on failure
|
||||
*/
|
||||
public function load($xml)
|
||||
{
|
||||
$this->obj = call_user_func($this->read_func, $xml, false);
|
||||
$read_func = $this->libfunc($this->read_func);
|
||||
|
||||
if (is_array($read_func))
|
||||
$r = call_user_func($read_func, $xml, $this->libversion());
|
||||
else
|
||||
$r = call_user_func($read_func, $xml, false);
|
||||
|
||||
if (is_resource($r))
|
||||
$this->obj = new $this->objclass($r);
|
||||
else if (is_a($r, $this->objclass))
|
||||
$this->obj = $r;
|
||||
|
||||
$this->loaded = !$this->format_errors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write object data to XML format
|
||||
*
|
||||
* @param float Format version to write
|
||||
* @return string XML data
|
||||
*/
|
||||
public function write()
|
||||
public function write($version = null)
|
||||
{
|
||||
$this->init();
|
||||
$this->xmldata = call_user_func($this->write_func, $this->obj);
|
||||
$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);
|
||||
|
||||
if (!$this->format_errors())
|
||||
$this->update_uid();
|
||||
|
|
|
@ -25,9 +25,11 @@
|
|||
class kolab_format_configuration extends kolab_format
|
||||
{
|
||||
public $CTYPE = 'application/x-vnd.kolab.configuration';
|
||||
public $CTYPEv2 = 'application/x-vnd.kolab.configuration';
|
||||
|
||||
protected $read_func = 'kolabformat::readConfiguration';
|
||||
protected $write_func = 'kolabformat::writeConfiguration';
|
||||
protected $objclass = 'Configuration';
|
||||
protected $read_func = 'readConfiguration';
|
||||
protected $write_func = 'writeConfiguration';
|
||||
|
||||
private $type_map = array(
|
||||
'dictionary' => Configuration::TypeDictionary,
|
||||
|
@ -35,12 +37,6 @@ class kolab_format_configuration extends kolab_format
|
|||
);
|
||||
|
||||
|
||||
function __construct($xmldata = null)
|
||||
{
|
||||
$this->obj = new Configuration;
|
||||
$this->xmldata = $xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties to the kolabformat object
|
||||
*
|
||||
|
@ -74,7 +70,7 @@ class kolab_format_configuration extends kolab_format
|
|||
$this->obj->setCreated(self::get_datetime($object['created']));
|
||||
|
||||
// adjust content-type string
|
||||
$this->CTYPE = 'application/x-vnd.kolab.configuration.' . $object['type'];
|
||||
$this->CTYPE = $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type'];
|
||||
|
||||
// cache this data
|
||||
$this->data = $object;
|
||||
|
@ -126,7 +122,7 @@ class kolab_format_configuration extends kolab_format
|
|||
|
||||
// adjust content-type string
|
||||
if ($object['type'])
|
||||
$this->CTYPE = 'application/x-vnd.kolab.configuration.' . $object['type'];
|
||||
$this->CTYPE = $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type'];
|
||||
|
||||
$this->data = $object;
|
||||
return $this->data;
|
||||
|
|
|
@ -25,9 +25,11 @@
|
|||
class kolab_format_contact extends kolab_format
|
||||
{
|
||||
public $CTYPE = 'application/vcard+xml';
|
||||
public $CTYPEv2 = 'application/x-vnd.kolab.contact';
|
||||
|
||||
protected $read_func = 'kolabformat::readContact';
|
||||
protected $write_func = 'kolabformat::writeContact';
|
||||
protected $objclass = 'Contact';
|
||||
protected $read_func = 'readContact';
|
||||
protected $write_func = 'writeContact';
|
||||
|
||||
public static $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'email');
|
||||
|
||||
|
@ -106,10 +108,9 @@ class kolab_format_contact extends kolab_format
|
|||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
function __construct($xmldata = null)
|
||||
function __construct($xmldata = null, $version = 3.0)
|
||||
{
|
||||
$this->obj = new Contact;
|
||||
$this->xmldata = $xmldata;
|
||||
parent::__construct($xmldata, $version);
|
||||
|
||||
// complete phone types
|
||||
$this->phonetypes['homefax'] |= Telephone::Home;
|
||||
|
@ -378,6 +379,8 @@ class kolab_format_contact extends kolab_format
|
|||
|
||||
if ($this->obj->photoMimetype())
|
||||
$object['photo'] = $this->obj->photo();
|
||||
else if ($this->xmlobject && ($photo_name = $this->xmlobject->pictureAttachmentName()))
|
||||
$object['photo'] = $photo_name;
|
||||
|
||||
// relateds -> spouse, children
|
||||
$this->read_relateds($this->obj->relateds(), $object);
|
||||
|
|
|
@ -25,17 +25,13 @@
|
|||
class kolab_format_distributionlist extends kolab_format
|
||||
{
|
||||
public $CTYPE = 'application/vcard+xml';
|
||||
public $CTYPEv2 = 'application/x-vnd.kolab.distribution-list';
|
||||
|
||||
protected $read_func = 'kolabformat::readDistlist';
|
||||
protected $write_func = 'kolabformat::writeDistlist';
|
||||
protected $objclass = 'DistList';
|
||||
protected $read_func = 'readDistlist';
|
||||
protected $write_func = 'writeDistlist';
|
||||
|
||||
|
||||
function __construct($xmldata = null)
|
||||
{
|
||||
$this->obj = new DistList;
|
||||
$this->xmldata = $xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties to the kolabformat object
|
||||
*
|
||||
|
|
|
@ -24,8 +24,11 @@
|
|||
|
||||
class kolab_format_event extends kolab_format_xcal
|
||||
{
|
||||
protected $read_func = 'kolabformat::readEvent';
|
||||
protected $write_func = 'kolabformat::writeEvent';
|
||||
public $CTYPEv2 = 'application/x-vnd.kolab.event';
|
||||
|
||||
protected $objclass = 'Event';
|
||||
protected $read_func = 'readEvent';
|
||||
protected $write_func = 'writeEvent';
|
||||
|
||||
private $kolab2_rolemap = array(
|
||||
'required' => 'REQ-PARTICIPANT',
|
||||
|
@ -43,12 +46,13 @@ class kolab_format_event extends kolab_format_xcal
|
|||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
* Clones into an instance of libcalendaring's extended EventCal class
|
||||
*
|
||||
* @return mixed EventCal object or false on failure
|
||||
*/
|
||||
function __construct($xmldata = null)
|
||||
public function to_libcal()
|
||||
{
|
||||
$this->obj = new Event;
|
||||
$this->xmldata = $xmldata;
|
||||
return class_exists('kolabcalendaring') ? new EventCal($this->obj) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,7 +147,7 @@ class kolab_format_event extends kolab_format_xcal
|
|||
$attach = $vattach->get($i);
|
||||
|
||||
// skip cid: attachments which are mime message parts handled by kolab_storage_folder
|
||||
if (substr($attach->uri(), 0, 4) != 'cid:') {
|
||||
if (substr($attach->uri(), 0, 4) != 'cid:' && $attach->label()) {
|
||||
$name = $attach->label();
|
||||
$data = $attach->data();
|
||||
$object['_attachments'][$name] = array(
|
||||
|
|
|
@ -25,17 +25,13 @@
|
|||
class kolab_format_journal extends kolab_format
|
||||
{
|
||||
public $CTYPE = 'application/calendar+xml';
|
||||
public $CTYPEv2 = 'application/x-vnd.kolab.journal';
|
||||
|
||||
protected $read_func = 'kolabformat::readJournal';
|
||||
protected $write_func = 'kolabformat::writeJournal';
|
||||
protected $objclass = 'Journal';
|
||||
protected $read_func = 'readJournal';
|
||||
protected $write_func = 'writeJournal';
|
||||
|
||||
|
||||
function __construct($xmldata = null)
|
||||
{
|
||||
$this->obj = new Journal;
|
||||
$this->xmldata = $xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties to the kolabformat object
|
||||
*
|
||||
|
|
|
@ -25,17 +25,13 @@
|
|||
class kolab_format_note extends kolab_format
|
||||
{
|
||||
public $CTYPE = 'application/x-vnd.kolab.note';
|
||||
public $CTYPEv2 = 'application/x-vnd.kolab.note';
|
||||
|
||||
protected $read_func = 'kolabformat::readNote';
|
||||
protected $write_func = 'kolabformat::writeNote';
|
||||
protected $objclass = 'Note';
|
||||
protected $read_func = 'readNote';
|
||||
protected $write_func = 'writeNote';
|
||||
|
||||
|
||||
function __construct($xmldata = null)
|
||||
{
|
||||
$this->obj = new Note;
|
||||
$this->xmldata = $xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties to the kolabformat object
|
||||
*
|
||||
|
|
|
@ -24,15 +24,12 @@
|
|||
|
||||
class kolab_format_task extends kolab_format_xcal
|
||||
{
|
||||
protected $read_func = 'kolabformat::readTodo';
|
||||
protected $write_func = 'kolabformat::writeTodo';
|
||||
public $CTYPEv2 = 'application/x-vnd.kolab.task';
|
||||
|
||||
protected $objclass = 'Todo';
|
||||
protected $read_func = 'readTodo';
|
||||
protected $write_func = 'writeTodo';
|
||||
|
||||
function __construct($xmldata = null)
|
||||
{
|
||||
$this->obj = new Todo;
|
||||
$this->xmldata = $xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties to the kolabformat object
|
||||
|
|
|
@ -31,6 +31,7 @@ class kolab_storage
|
|||
const SERVERSIDE_SUBSCRIPTION = 0;
|
||||
const CLIENTSIDE_SUBSCRIPTION = 1;
|
||||
|
||||
public static $version = 3.0;
|
||||
public static $last_error;
|
||||
|
||||
private static $ready = false;
|
||||
|
@ -49,6 +50,7 @@ class kolab_storage
|
|||
|
||||
$rcmail = rcube::get_instance();
|
||||
self::$config = $rcmail->config;
|
||||
self::$version = $rcmail->config->get('kolab_format_version', self::$version);
|
||||
self::$imap = $rcmail->get_storage();
|
||||
self::$ready = class_exists('kolabformat') &&
|
||||
(self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2'));
|
||||
|
|
|
@ -517,8 +517,8 @@ class kolab_storage_cache
|
|||
$sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']);
|
||||
|
||||
// extend date range for recurring events
|
||||
if ($object['recurrence']) {
|
||||
$recurrence = new kolab_date_recurrence($object);
|
||||
if ($object['recurrence'] && $object['_formatobj']) {
|
||||
$recurrence = new kolab_date_recurrence($object['_formatobj']);
|
||||
$sql_data['dtend'] = date('Y-m-d 23:59:59', $recurrence->end() ?: strtotime('now +1 year'));
|
||||
}
|
||||
}
|
||||
|
@ -534,7 +534,7 @@ class kolab_storage_cache
|
|||
}
|
||||
|
||||
if ($object['_formatobj']) {
|
||||
$sql_data['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write());
|
||||
$sql_data['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write(3.0));
|
||||
$sql_data['tags'] = ' ' . join(' ', $object['_formatobj']->get_tags()) . ' '; // pad with spaces for strict/prefix search
|
||||
$sql_data['words'] = ' ' . join(' ', $object['_formatobj']->get_words()) . ' ';
|
||||
}
|
||||
|
@ -582,7 +582,7 @@ class kolab_storage_cache
|
|||
$object['_type'] = $sql_arr['type'];
|
||||
$object['_msguid'] = $sql_arr['msguid'];
|
||||
$object['_mailbox'] = $this->folder->name;
|
||||
$object['_formatobj'] = kolab_format::factory($sql_arr['type'], $sql_arr['xml']);
|
||||
$object['_formatobj'] = kolab_format::factory($sql_arr['type'], 3.0, $sql_arr['xml']);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
@ -717,7 +717,8 @@ class kolab_storage_cache
|
|||
{
|
||||
if (!isset($this->uid2msg[$uid])) {
|
||||
// use IMAP SEARCH to get the right message
|
||||
$index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') . 'HEADER SUBJECT ' . $uid);
|
||||
$index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') .
|
||||
'HEADER SUBJECT ' . rcube_imap_generic::escape($uid));
|
||||
$results = $index->get();
|
||||
$this->uid2msg[$uid] = $results[0];
|
||||
}
|
||||
|
|
|
@ -477,39 +477,27 @@ class kolab_storage_folder
|
|||
return false;
|
||||
}
|
||||
|
||||
$format = kolab_format::factory($object_type);
|
||||
|
||||
if (is_a($format, 'PEAR_Error'))
|
||||
return false;
|
||||
|
||||
// check kolab format version
|
||||
$mime_version = $headers->others['x-kolab-mime-version'];
|
||||
if (empty($mime_version)) {
|
||||
$format_version = $headers->others['x-kolab-mime-version'];
|
||||
if (empty($format_version)) {
|
||||
list($xmltype, $subtype) = explode('.', $object_type);
|
||||
$xmlhead = substr($xml, 0, 512);
|
||||
|
||||
// detect old Kolab 2.0 format
|
||||
if (strpos($xmlhead, '<' . $xmltype) !== false && strpos($xmlhead, 'xmlns=') === false)
|
||||
$mime_version = 2.0;
|
||||
$format_version = 2.0;
|
||||
else
|
||||
$mime_version = 3.0; // assume 3.0
|
||||
$format_version = 3.0; // assume 3.0
|
||||
}
|
||||
|
||||
if ($mime_version <= 2.0) {
|
||||
// read Kolab 2.0 format
|
||||
$handler = class_exists('Horde_Kolab_Format') ? Horde_Kolab_Format::factory('XML', $xmltype, array('subtype' => $subtype)) : null;
|
||||
if (!is_object($handler) || is_a($handler, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
// get Kolab format handler for the given type
|
||||
$format = kolab_format::factory($object_type, $format_version);
|
||||
|
||||
// XML-to-array
|
||||
$object = $handler->load($xml);
|
||||
$format->fromkolab2($object);
|
||||
}
|
||||
else {
|
||||
// load Kolab 3 format using libkolabxml
|
||||
$format->load($xml);
|
||||
}
|
||||
if (is_a($format, 'PEAR_Error'))
|
||||
return false;
|
||||
|
||||
// load Kolab object from XML part
|
||||
$format->load($xml);
|
||||
|
||||
if ($format->is_valid()) {
|
||||
$object = $format->to_array();
|
||||
|
@ -563,14 +551,24 @@ class kolab_storage_folder
|
|||
unset($object['_attachments'][$key]);
|
||||
}
|
||||
// load photo.attachment from old Kolab2 format to be directly embedded in xcard block
|
||||
else if ($key == 'photo.attachment' && !isset($object['photo']) && !$object['_attachments'][$key]['content'] && $att['id']) {
|
||||
$object['photo'] = $this->get_attachment($object['_msguid'], $att['id'], $object['_mailbox']);
|
||||
else if ($type == 'contact' && ($key == 'photo.attachment' || $key == 'kolab-picture.png') && $att['id']) {
|
||||
if (!isset($object['photo']))
|
||||
$object['photo'] = $this->get_attachment($object['_msguid'], $att['id'], $object['_mailbox']);
|
||||
unset($object['_attachments'][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse attachments
|
||||
// save contact photo to attachment for Kolab2 format
|
||||
if (kolab_storage::$version == 2.0 && $object['photo'] && !$existing_photo) {
|
||||
$attkey = 'kolab-picture.png'; // this file name is hard-coded in libkolab/kolabformatV2/contact.cpp
|
||||
$object['_attachments'][$attkey] = array(
|
||||
'mimetype'=> rc_image_content_type($object['photo']),
|
||||
'content' => preg_match('![^a-z0-9/=+-]!i', $object['photo']) ? $object['photo'] : base64_decode($object['photo']),
|
||||
);
|
||||
}
|
||||
|
||||
// process attachments
|
||||
if (is_array($object['_attachments'])) {
|
||||
$numatt = count($object['_attachments']);
|
||||
foreach ($object['_attachments'] as $key => $attachment) {
|
||||
|
@ -719,13 +717,13 @@ class kolab_storage_folder
|
|||
|
||||
// create new kolab_format instance
|
||||
if (!$format)
|
||||
$format = kolab_format::factory($type);
|
||||
$format = kolab_format::factory($type, kolab_storage::$version);
|
||||
|
||||
if (PEAR::isError($format))
|
||||
return false;
|
||||
|
||||
$format->set($object);
|
||||
$xml = $format->write();
|
||||
$xml = $format->write(kolab_storage::$version);
|
||||
$object['uid'] = $format->uid; // read UID from format
|
||||
$object['_formatobj'] = $format;
|
||||
|
||||
|
@ -744,7 +742,7 @@ class kolab_storage_folder
|
|||
}
|
||||
$headers['Date'] = date('r');
|
||||
$headers['X-Kolab-Type'] = kolab_format::KTYPE_PREFIX . $type;
|
||||
$headers['X-Kolab-Mime-Version'] = kolab_format::VERSION;
|
||||
$headers['X-Kolab-Mime-Version'] = kolab_storage::$version;
|
||||
$headers['Subject'] = $object['uid'];
|
||||
// $headers['Message-ID'] = $rcmail->gen_message_id();
|
||||
$headers['User-Agent'] = $rcmail->config->get('useragent');
|
||||
|
@ -754,8 +752,9 @@ class kolab_storage_folder
|
|||
. 'To view this object you will need an email client that understands the Kolab Groupware format. '
|
||||
. "For a list of such email clients please visit http://www.kolab.org/\n\n");
|
||||
|
||||
$ctype = kolab_storage::$version == 2.0 ? $format->CTYPEv2 : $format->CTYPE;
|
||||
$mime->addAttachment($xml, // file
|
||||
$format->CTYPE, // content-type
|
||||
$ctype, // content-type
|
||||
'kolab.xml', // filename
|
||||
false, // is_file
|
||||
'8bit', // encoding
|
||||
|
|
|
@ -548,7 +548,8 @@ class tasklist_kolab_driver extends tasklist_driver
|
|||
// convert from DateTime to internal date format
|
||||
if (is_a($record['due'], 'DateTime')) {
|
||||
$task['date'] = $record['due']->format('Y-m-d');
|
||||
$task['time'] = $record['due']->format('h:i');
|
||||
if (!$record['due']->_dateonly)
|
||||
$task['time'] = $record['due']->format('h:i');
|
||||
}
|
||||
// convert from DateTime to internal date format
|
||||
if (is_a($record['start'], 'DateTime')) {
|
||||
|
|
Loading…
Add table
Reference in a new issue