Merged several SyncML fixes from trunk:

- 24518: prevent deleting of accounts via SyncML and to read private flag from the DB if missing
- 24531: Merge SyncML patches from Patrick Bihan-Faou plus some other SyncML fixes from trunk
- 24555: fixed bug introduced by my commit r24522 (included in 24531): egw could not deal with LDAP Ids
- 24571: fixed typo reported by Thomas Hoth on the developer list, causing calendar sync in trunk to fail if the clients wants categories
- 24591: diverse vCard improvments, incl. rudimentary vCard3 support
This commit is contained in:
Ralf Becker 2007-10-29 19:46:46 +00:00
parent ef95e5527c
commit 517bdb9f65
23 changed files with 1267 additions and 418 deletions

View File

@ -392,9 +392,10 @@ class bocontacts extends socontacts
* deletes contact in db
*
* @param mixed &$contact contact array with key id or (array of) id(s)
* @param boolean $deny_account_delete=true if true never allow to delete accounts
* @return boolean true on success or false on failiure
*/
function delete($contact)
function delete($contact,$deny_account_delete=true)
{
if (is_array($contact) && isset($contact['id']))
{
@ -413,7 +414,7 @@ class bocontacts extends socontacts
{
$id = is_array($c) ? $c['id'] : $c;
if ($this->check_perms(EGW_ACL_DELETE,$c) && parent::delete($id))
if ($this->check_perms(EGW_ACL_DELETE,$c,$deny_account_delete) && parent::delete($id))
{
$GLOBALS['egw']->link->unlink(0,'addressbook',$id);
$GLOBALS['egw']->contenthistory->updateTimeStamp('contacts', $id, 'delete', time());
@ -438,11 +439,14 @@ class bocontacts extends socontacts
// remember if we add or update a entry
if (($isUpdate = $contact['id']))
{
if (!isset($contact['owner'])) // owner not set on update, eg. SyncML
if (!isset($contact['owner']) || !isset($contact['private'])) // owner/private not set on update, eg. SyncML
{
if (($old = $this->read($contact['id']))) // --> try reading the old entry and set it from there
{
$contact['owner'] = $old['owner'];
if(!isset($contact['owner']))
{
$contact['owner'] = $old['owner'];
}
if(!isset($contact['private']))
{
$contact['private'] = $old['private'];
@ -544,14 +548,15 @@ class bocontacts extends socontacts
*
* @param int $needed necessary ACL right: EGW_ACL_{READ|EDIT|DELETE}
* @param mixed $contact contact as array or the contact-id
* @return boolean true permission granted or false for permission denied
* @param boolean $deny_account_delete=false if true never allow to delete accounts
* @return boolean true permission granted, false for permission denied, null for contact does not exist
*/
function check_perms($needed,$contact)
function check_perms($needed,$contact,$deny_account_delete=false)
{
if ((!is_array($contact) || !isset($contact['owner'])) &&
!($contact = parent::read(is_array($contact) ? $contact['id'] : $contact)))
{
return false;
return null;
}
$owner = $contact['owner'];
@ -561,7 +566,7 @@ class bocontacts extends socontacts
return true;
}
// dont allow to delete own account (as admin handels it too)
if (!$owner && $needed == EGW_ACL_DELETE && $contact['account_id'] == $this->user)
if (!$owner && $needed == EGW_ACL_DELETE && ($deny_account_delete || $contact['account_id'] == $this->user))
{
return false;
}
@ -1108,4 +1113,86 @@ class bocontacts extends socontacts
//echo "<p>bocontacts::addr_format_by_country('$country'='$code') = '$adr_format'</p>\n";
return $adr_format;
}
var $app_cat;
var $glob_cat;
function find_or_add_categories($catname_list)
{
if (!is_object($this->glob_cat))
{
if (!is_object($GLOBALS['egw']->categories))
{
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$this->owner,'phpgw');
}
$this->glob_cat =& $GLOBALS['egw']->categories;
}
if (!is_object($this->app_cat))
{
$this->app_cat =& CreateObject('phpgwapi.categories',$this->owner,'addressbook');
}
$cat_id_list = array();
foreach($catname_list as $cat_name)
{
$cat_name = trim($cat_name);
if (!($cat_id = $this->glob_cat->name2id($cat_name))
&& !($cat_id = $this->app_cat->name2id($cat_name)))
{
$cat_id = $this->app_cat->add( array('name' => $cat_name,'descr' => $cat_name ));
}
$cat_id_list[] = $cat_id;
}
if (count($cat_id_list) > 1)
{
sort($cat_id_list, SORT_NUMERIC);
}
return $cat_id_list;
}
function get_categories($cat_id_list)
{
if (!is_object($this->glob_cat))
{
if (!is_object($GLOBALS['egw']->categories))
{
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$this->owner,'phpgw');
}
$this->glob_cat =& $GLOBALS['egw']->categories;
}
if (!is_object($this->app_cat))
{
$this->app_cat =& CreateObject('phpgwapi.categories',$this->owner,'addressbook');
}
$cat_list = array();
foreach(explode(',',$cat_id_list) as $cat_id)
{
if ( ($cat_data = $this->glob_cat->return_single($cat_id))
|| ($cat_data = $this->app_cat->return_single($cat_id)) )
{
$cat_list[] = $cat_data[0]['name'];
}
}
return $cat_list;
}
function fixup_contact(&$contact)
{
if (!isset($contact['n_fn']) || empty($contact['n_fn']))
{
$contact['n_fn'] = $this->fullname($contact);
}
if (!isset($contact['n_fileas']) || empty($contact['n_fileas']))
{
$contact['n_fileas'] = $this->fileas($contact);
}
}
}

View File

@ -135,28 +135,18 @@ class sifaddressbook extends bocontacts
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8');
switch($key) {
case 'cat_id':
if(!empty($value)) {
$isAdmin = $GLOBALS['egw']->acl->check('run',1,'admin');
$egwCategories =& CreateObject('phpgwapi.categories', $GLOBALS['egw_info']['user']['account_id'], 'addressbook');
$categories = explode(';',$value);
// add missing categories as personal categories as needed
foreach($categories as $categorieName) {
$cat_id = false;
$categorieName = trim($categorieName);
if(!($cat_id = $egwCategories->name2id($categorieName))) {
$cat_id = $egwCategories->add(array('name' => $categorieName, 'descr' => lang('added by synchronisation')));
error_log("added $cat_id => $categorieName");
}
if($cat_id) {
if(!empty($finalContact[$key])) $finalContact[$key] .= ',';
$finalContact[$key] .= $cat_id;
}
}
if(!empty($value))
{
$finalContact[$key] = implode(",", $this->find_or_add_categories(explode(';', $value)));
}
else
{
$finalContact[$key] = '';
}
break;
case 'private':
$finalContact[$key] = (int) $value > 0; // eGW private is 0 (public) or 1 (private), SIF seems to use 0 and 2
$finalContact[$key] = (int) ($value > 0); // eGW private is 0 (public) or 1 (private), SIF seems to use 0 and 2
break;
default:
@ -164,6 +154,8 @@ class sifaddressbook extends bocontacts
break;
}
}
$this->fixup_contact($finalContact);
return $finalContact;
}
@ -179,6 +171,9 @@ class sifaddressbook extends bocontacts
{
return false;
}
// patch from Di Guest says: we need to ignore the n_fileas
unset($contact['n_fileas']);
// we probably need to ignore even more as we do in vcaladdressbook
if(($foundContacts = bocontacts::search($contact)))
{
@ -231,6 +226,9 @@ class sifaddressbook extends bocontacts
#error_log(print_r($entry,true));
$sysCharSet = $GLOBALS['egw']->translation->charset();
// fillup some defaults such as n_fn and n_fileas is needed
$this->fixup_contact($entry);
foreach($this->sifMapping as $sifField => $egwField)
{
if(empty($egwField)) continue;
@ -245,16 +243,8 @@ class sifaddressbook extends bocontacts
// TODO handle multiple categories
case 'Categories':
if(!empty($value)) {
$egwCategories =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'addressbook');
$categories = explode(',',$value);
$value = '';
foreach($categories as $cat_id) {
if(($catData = $egwCategories->return_single($cat_id)))
{
if(!empty($value)) $value .= '; ';
$value .= $catData[0]['name'];
}
}
$value = implode("; ", $this->get_categories($value));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
$sifContact .= "<$sifField>$value</$sifField>";
break;
@ -270,7 +260,7 @@ class sifaddressbook extends bocontacts
break;
default:
$sifContact .= "<$sifField>$value</$sifField>";
$sifContact .= "<$sifField>".trim($value)."</$sifField>";
break;
}
}

View File

@ -58,51 +58,82 @@ class vcaladdressbook extends bocontacts
return false;
}
foreach($this->supportedFields as $vcardField => $databaseFields) {
$this->fixup_contact($entry);
foreach($this->supportedFields as $vcardField => $databaseFields)
{
$values = array();
$options = array();
$value = '';
foreach($databaseFields as $databaseField) {
$tempVal = ';';
if(!empty($databaseField)) {
$tempVal = trim($entry[$databaseField]).';';
$hasdata = 0;
foreach($databaseFields as $databaseField)
{
$value = "";
if (!empty($databaseField))
{
$value = trim($entry[$databaseField]);
}
$value .= $tempVal;
}
// remove the last ;
$value = substr($value, 0, -1);
switch($vcardField) {
// TODO handle multiple categories
case 'CATEGORIES':
$catData = ExecMethod('phpgwapi.categories.return_single',$value);
$value = $catData[0]['name'];
break;
case 'CLASS':
$value = $value ? 'PRIVATE' : 'PUBLIC';
break;
case 'BDAY':
if(!empty($value)) {
$value = str_replace('-','',$value).'T000000Z';
}
break;
switch($databaseField)
{
case 'private':
$value = $value ? 'PRIVATE' : 'PUBLIC';
$hasdata++;
break;
case 'bday':
if (!empty($value))
{
$value = str_replace('-','',$value).'T000000Z';
$hasdata++;
}
break;
case 'jpegphoto':
if(!empty($value))
{
error_log("PHOTO='".$value."'");
$hasdata++;
}
break;
case 'cat_id':
if (!empty($value))
{
$value = implode(",", $this->get_categories($value));
}
// fall-through to the normal processing of string values
default:
if(!empty($value))
{
$value = $GLOBALS['egw']->translation->convert(trim($value), $sysCharSet, 'utf-8');
$options['CHARSET'] = 'UTF-8';
if(preg_match('/([\000-\012\015\016\020-\037\075])/',$value))
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
$hasdata++;
}
break;
}
if (empty($value))
{
$value = "";
}
$values[] = $value;
}
if ($databaseField != 'jpegphoto') {
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
// don't add the entry if it contains only ';'
// exeptions for mendatory fields
if( ( strlen(str_replace(';','',$value)) != 0 ) || in_array($vcardField,array('FN','ORG','N')) ) {
$vCard->setAttribute($vcardField, $value);
}
if(preg_match('/([\000-\012\015\016\020-\037\075])/',$value)) {
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
if(preg_match('/([\177-\377])/',$value)) {
$options['CHARSET'] = 'UTF-8';
if ($hasdata <= 0)
{
// don't add the entry if there is no data for this field
continue;
}
$vCard->setAttribute($vcardField, implode(';', $values));
$vCard->setParameter($vcardField, $options);
}
@ -170,13 +201,14 @@ class vcaladdressbook extends bocontacts
* - modifier
* - jpegphoto
*/
$defaultFields[0] = array(
$defaultFields[0] = array( // multisync
'ADR' => array('','','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'CATEGORIES' => array('cat_id'),
'CLASS' => array('private'),
'EMAIL' => array('email'),
'N' => array('n_family','n_given','','',''),
'FN' => array('n_fn'),
'NOTE' => array('note'),
'ORG' => array('org_name',''),
'TEL;CELL' => array('tel_cell'),
@ -186,7 +218,7 @@ class vcaladdressbook extends bocontacts
'TITLE' => array('title'),
);
$defaultFields[1] = array(
$defaultFields[1] = array( // all entries, nexthaus corporation, ...
'ADR;WORK' => array('','','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'ADR;HOME' => array('','','adr_two_street','adr_two_locality','adr_two_region',
@ -196,19 +228,24 @@ class vcaladdressbook extends bocontacts
'EMAIL;INTERNET;WORK' => array('email'),
'EMAIL;INTERNET;HOME' => array('email_home'),
'N' => array('n_family','n_given','n_middle','n_prefix','n_suffix'),
'FN' => array('n_fn'),
'NOTE' => array('note'),
'ORG' => array('org_name','org_unit'),
'TEL;CELL;WORK' => array('tel_cell'),
'TEL;CELL;HOME' => array('tel_cell_private'),
'TEL;FAX;WORK' => array('tel_fax'),
'TEL;FAX;HOME' => array('tel_fax_home'),
'TEL;HOME' => array('tel_home'),
'TEL;PAGER;WORK' => array('tel_pager'),
'TEL;WORK' => array('tel_work'),
'TITLE' => array('title'),
'URL;WORK' => array('url'),
'ROLE' => array('role'),
'URL;HOME' => array('url_home'),
'FBURL' => array('freebusy_uri'),
);
$defaultFields[2] = array(
$defaultFields[2] = array( // sony ericson
'ADR;HOME' => array('','','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'BDAY' => array('bday'),
@ -216,6 +253,7 @@ class vcaladdressbook extends bocontacts
'CLASS' => array('private'),
'EMAIL' => array('email'),
'N' => array('n_family','n_given','','',''),
'FN' => array('n_fn'),
'NOTE' => array('note'),
'ORG' => array('org_name',''),
'TEL;CELL;WORK' => array('tel_cell'),
@ -226,7 +264,7 @@ class vcaladdressbook extends bocontacts
'URL;WORK' => array('url'),
);
$defaultFields[3] = array(
$defaultFields[3] = array( // siemens
'ADR;WORK' => array('','','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'ADR;HOME' => array('','','adr_two_street','adr_two_locality','adr_two_region',
@ -235,6 +273,7 @@ class vcaladdressbook extends bocontacts
'EMAIL;INTERNET;WORK' => array('email'),
'EMAIL;INTERNET;HOME' => array('email_home'),
'N' => array('n_family','n_given','','',''),
'FN' => array('n_fn'),
'NOTE' => array('note'),
'ORG' => array('org_name','org_unit'),
'TEL;CELL;WORK' => array('tel_cell'),
@ -246,7 +285,7 @@ class vcaladdressbook extends bocontacts
'URL;WORK' => array('url'),
);
$defaultFields[4] = array(
$defaultFields[4] = array( // nokia 6600
'ADR;WORK' => array('','','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'ADR;HOME' => array('','','adr_two_street','adr_two_locality','adr_two_region',
@ -255,6 +294,7 @@ class vcaladdressbook extends bocontacts
'EMAIL;INTERNET;WORK' => array('email'),
'EMAIL;INTERNET;HOME' => array('email_home'),
'N' => array('n_family','n_given','','',''),
'FN' => array('n_fn'),
'NOTE' => array('note'),
'ORG' => array('org_name',''),
'TEL;CELL;WORK' => array('tel_cell'),
@ -269,7 +309,7 @@ class vcaladdressbook extends bocontacts
'URL;HOME' => array('url_home'),
);
$defaultFields[5] = array(
$defaultFields[5] = array( // nokia e61
'ADR;WORK' => array('','','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'ADR;HOME' => array('','','adr_two_street','adr_two_locality','adr_two_region',
@ -278,6 +318,7 @@ class vcaladdressbook extends bocontacts
'EMAIL;INTERNET;WORK' => array('email'),
'EMAIL;INTERNET;HOME' => array('email_home'),
'N' => array('n_family','n_given','','n_prefix','n_suffix'),
'FN' => array('n_fn'),
'NOTE' => array('note'),
'ORG' => array('org_name',''),
'TEL;CELL;WORK' => array('tel_cell'),
@ -292,33 +333,39 @@ class vcaladdressbook extends bocontacts
'URL;HOME' => array('url_home'),
);
$defaultFields[6] = array(
'ADR;WORK' => array('','','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'ADR;HOME' => array('','','adr_two_street','adr_two_locality','adr_two_region',
'adr_two_postalcode','adr_two_countryname'),
'EMAIL' => array('email'),
'EMAIL;HOME' => array('email_home'),
'N' => array('n_family','n_given','','',''),
'NOTE' => array('note'),
'ORG' => array('org_name','org_unit'),
'TEL;CELL' => array('tel_cell'),
'TEL;HOME;FAX' => array('tel_fax'),
$defaultFields[6] = array( // funambol: fmz-thunderbird-plugin
'ADR;WORK' => array('','','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'ADR;HOME' => array('','','adr_two_street','adr_two_locality','adr_two_region',
'adr_two_postalcode','adr_two_countryname'),
'EMAIL' => array('email'),
'EMAIL;HOME' => array('email_home'),
'N' => array('n_family','n_given','','',''),
'FN' => array('n_fn'),
'NOTE' => array('note'),
'ORG' => array('org_name','org_unit'),
'TEL;CELL' => array('tel_cell'),
'TEL;HOME;FAX' => array('tel_fax'),
'TEL;HOME;VOICE' => array('tel_home'),
'TEL;PAGER' => array('tel_pager'),
'TEL;PAGER' => array('tel_pager'),
'TEL;WORK;VOICE' => array('tel_work'),
'TITLE' => array('title'),
'URL;WORK' => array('url'),
'URL' => array('url_home'),
'TITLE' => array('title'),
'URL;WORK' => array('url'),
'URL' => array('url_home'),
);
//error_log("Client: $_productManufacturer $_productName");
switch(strtolower($_productManufacturer))
{
case 'funambol':
switch(strtolower($_productName))
switch (strtolower($_productName))
{
case 'fmz-thunderbird-plugin':
case 'thunderbird':
$this->supportedFields = $defaultFields[6];
break;
default:
error_log("Funambol product '$_productName', assuming same as thunderbird");
$this->supportedFields = $defaultFields[6];
break;
}
@ -328,7 +375,10 @@ class vcaladdressbook extends bocontacts
switch(strtolower($_productName))
{
case 'syncje outlook edition':
$this->supportedFields = $defaultFields[1];
break;
default:
error_log("Nethaus product '$_productName', assuming same as 'syncje outlook'");
$this->supportedFields = $defaultFields[1];
break;
}
@ -341,7 +391,10 @@ class vcaladdressbook extends bocontacts
$this->supportedFields = $defaultFields[5];
break;
case '6600':
$this->supportedFields = $defaultFields[4];
break;
default:
error_log("Unknown Nokia phone '$_productName', assuming same as '6600'");
$this->supportedFields = $defaultFields[4];
break;
}
@ -363,17 +416,25 @@ class vcaladdressbook extends bocontacts
switch(strtolower($_productName))
{
case 'sx1':
$this->supportedFields = $defaultFields[3];
break;
default:
error_log("Unknown Siemens phone '$_productName', assuming same as 'sx1'");
$this->supportedFields = $defaultFields[3];
break;
}
break;
case 'sonyericsson':
case 'sony ericsson':
switch(strtolower($_productName))
{
case 'd750i':
$this->supportedFields = $defaultFields[2];
break;
case 'p910i':
default:
error_log("unknown Sony Ericsson phone '$_productName', assuming same as 'd750i'");
$this->supportedFields = $defaultFields[2];
break;
}
@ -387,6 +448,7 @@ class vcaladdressbook extends bocontacts
#$this->supportedFields['PHOTO'] = array('jpegphoto');
break;
default:
error_log("Synthesis connector '$_productName', using default fields");
$this->supportedFields = $defaultFields[0];
break;
}
@ -398,7 +460,7 @@ class vcaladdressbook extends bocontacts
// the fallback for SyncML
default:
error_log("Client not found: $_productManufacturer $_productName");
error_log("Client not found: '$_productManufacturer' '$_productName'");
$this->supportedFields = $defaultFields[0];
break;
}
@ -439,27 +501,29 @@ class vcaladdressbook extends bocontacts
{
$rowName .= ";INTERNET";
}
if(isset($vcardRow['params']['CELL']))
$type = strtoupper($vcardRow['params']['TYPE']); // vCard3 sets TYPE={work|home|cell|fax}!
if(isset($vcardRow['params']['CELL']) || $type == 'CELL')
{
$rowName .= ';CELL';
}
if(isset($vcardRow['params']['FAX']))
if(isset($vcardRow['params']['FAX']) || $type == 'FAX')
{
$rowName .= ';FAX';
}
if(isset($vcardRow['params']['PAGER']))
if(isset($vcardRow['params']['PAGER']) || $type == 'PAGER')
{
$rowName .= ';PAGER';
}
if(isset($vcardRow['params']['WORK']))
if(isset($vcardRow['params']['WORK']) || $type == 'WORK')
{
$rowName .= ';WORK';
}
if(isset($vcardRow['params']['HOME']))
if(isset($vcardRow['params']['HOME']) || $type == 'HOME')
{
$rowName .= ';HOME';
}
//error_log("key: $key --> $rowName: name=$vcardRow[name], params=".print_r($vcardRow['params'],true));
$rowNames[$rowName] = $key;
}
@ -500,30 +564,6 @@ class vcaladdressbook extends bocontacts
}
break;
case 'CATEGORIES':
#cat_id = 7,8
$vcardData['category'] = array();
if ($attributes['value'])
{
if (!is_object($this->cat))
{
if (!is_object($GLOBALS['egw']->categories))
{
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$this->owner,'addressbook');
}
$this->cat =& $GLOBALS['egw']->categories;
}
foreach(explode(',',$attributes['value']) as $cat_name)
{
if (!($cat_id = $this->cat->name2id($cat_name)))
{
$cat_id = $this->cat->add( array('name' => $cat_name,'descr' => $cat_name ));
}
$vcardData['category'][] = $cat_id;
}
}
break;
case 'VERSION':
break;
@ -546,38 +586,31 @@ class vcaladdressbook extends bocontacts
{
if(!empty($fieldName))
{
$value = trim($vcardValues[$vcardKey]['values'][$fieldKey]);
//error_log("$fieldName=$vcardKey[$fieldKey]='$value'");
switch($fieldName)
{
case 'bday':
if(!empty($vcardValues[$vcardKey]['values'][$fieldKey])) {
$contact[$fieldName] = date('Y-m-d', $vcardValues[$vcardKey]['values'][$fieldKey]);
if(!empty($value)) {
$contact[$fieldName] = date('Y-m-d', $value);
}
break;
case 'private':
(int)$contact[$fieldName] = $vcardValues[$vcardKey]['values'][$fieldKey] == 'PRIVATE';
$contact[$fieldName] = (int) ($value == 'PRIVATE');
break;
case 'cat_id':
if (!is_object($this->cat)) {
if (!is_object($GLOBALS['egw']->categories)) {
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'addressbook');
}
$this->cat =& $GLOBALS['egw']->categories;
}
foreach(explode(',',$vcardValues[$vcardKey]['values'][$fieldKey]) as $cat_name) {
if (!($cat_id = $this->cat->name2id($cat_name))) {
$cat_id = $this->cat->add( array('name' => $cat_name, 'descr' => $cat_name ));
}
$contact[$fieldName] = $cat_id;
}
$contact[$fieldName] = implode(',',$this->find_or_add_categories(explode(',',$value)));
break;
case 'note':
// note may contain ','s but maybe this needs to be fixed in vcard parser...
$contact[$fieldName] = trim($vcardValues[$vcardKey]['value']);
break;
//$contact[$fieldName] = trim($vcardValues[$vcardKey]['value']);
//break;
default:
$contact[$fieldName] = trim($vcardValues[$vcardKey]['values'][$fieldKey]);
$contact[$fieldName] = $value;
break;
}
}
@ -585,7 +618,7 @@ class vcaladdressbook extends bocontacts
}
}
$contact['n_fn'] = trim($contact['n_given'].' '.$contact['n_family']);
$this->fixup_contact($contact);
return $contact;
}

View File

@ -121,6 +121,21 @@ class bocalupdate extends bocal
!$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner'])) &&
!$this->check_perms(EGW_ACL_ADD,0,$event['owner']))
{
// Just update the status, if the user is in the event already
// is user is in both original and updated event
$egw_event = $this->read($event['id']);
if ( isset($egw_event['participants'][$this->user])
&& isset($event['participants'][$this->user]))
{
// Update their status in the event and say we're done.
// Admittedly, this is false, it's dropping any changes on the floor,
// But this will work better than dropping -everything- silently on
// the floor
$this->set_status($event['id'],'u',$this->user,$event['participants'][$this->user],0);
unset($egw_event);
return $event['id'];
}
return false;
}
// check for conflicts only happens !$ignore_conflicts AND if start + end date are given
@ -1017,4 +1032,65 @@ class bocalupdate extends bocal
}
return $this->so->delete_alarm($id);
}
var $app_cat;
var $glob_cat;
function find_or_add_categories($catname_list)
{
if (!is_object($this->glob_cat))
{
$this->glob_cat =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'phpgw');
}
if (!is_object($this->app_cat))
{
$this->app_cat =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'calendar');
}
$cat_id_list = array();
foreach($catname_list as $cat_name)
{
$cat_name = trim($cat_name);
if (!($cat_id = $this->glob_cat->name2id($cat_name))
&& !($cat_id = $this->app_cat->name2id($cat_name)))
{
$cat_id = $this->app_cat->add( array('name' => $cat_name,'descr' => $cat_name ));
}
$cat_id_list[] = $cat_id;
}
if (count($cat_id_list) > 1)
{
sort($cat_id_list, SORT_NUMERIC);
}
return $cat_id_list;
}
function get_categories($cat_id_list)
{
if (!is_object($this->glob_cat))
{
$this->glob_cat =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'phpgw');
}
if (!is_object($this->app_cat))
{
$this->app_cat =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'calendar');
}
$cat_list = array();
foreach(explode(',',$cat_id_list) as $cat_id)
{
if ( ($cat_data = $this->glob_cat->return_single($cat_id))
|| ($cat_data = $this->app_cat->return_single($cat_id)) )
{
$cat_list[] = $cat_data[0]['name'];
}
}
return $cat_list;
}
}

View File

@ -117,24 +117,38 @@
function &exportVCal($events,$version='1.0', $method='PUBLISH')
{
$egwSupportedFields = array(
'CLASS' => array('dbName' => 'public'),
'SUMMARY' => array('dbName' => 'title'),
'CLASS' => array('dbName' => 'public'),
'SUMMARY' => array('dbName' => 'title'),
'DESCRIPTION' => array('dbName' => 'description'),
'LOCATION' => array('dbName' => 'location'),
'DTSTART' => array('dbName' => 'start'),
'DTEND' => array('dbName' => 'end'),
'ORGANIZER' => array('dbName' => 'owner'),
'ATTENDEE' => array('dbName' => 'participants'),
'RRULE' => array('dbName' => 'recur_type'),
'EXDATE' => array('dbName' => 'recur_exception'),
'PRIORITY' => array('dbName' => 'priority'),
'TRANSP' => array('dbName' => 'non_blocking'),
'LOCATION' => array('dbName' => 'location'),
'DTSTART' => array('dbName' => 'start'),
'DTEND' => array('dbName' => 'end'),
'ORGANIZER' => array('dbName' => 'owner'),
'ATTENDEE' => array('dbName' => 'participants'),
'RRULE' => array('dbName' => 'recur_type'),
'EXDATE' => array('dbName' => 'recur_exception'),
'PRIORITY' => array('dbName' => 'priority'),
'TRANSP' => array('dbName' => 'non_blocking'),
'CATEGORIES' => array('dbName' => 'category'),
);
if(!is_array($this->supportedFields))
{
$this->setSupportedFields();
}
if($this->productManufacturer == '' )
{ // syncevolution is broken
$version = "2.0";
}
$palm_enddate_workaround=False;
if($this->productManufacturer == 'Synthesis AG'
&& strpos($this->productName, "PalmOS") )
{
// This workaround adds 1 day to the recur_enddate if it exists, to fix a palm bug
$palm_enddate_workaround=True;
}
$vcal = &new Horde_iCalendar;
$vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
@ -188,7 +202,22 @@
// PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED|COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm.
$status = $this->status_egw2ical[$status];
// CUTYPE={INDIVIDUAL|GROUP|RESOURCE|ROOM|UNKNOWN}
$cutype = $GLOBALS['egw']->accounts->get_type($uid) == 'g' ? 'GROUP' : 'INDIVIDUAL';
switch (is_numeric($uid) ? $GLOBALS['egw']->accounts->get_type($uid) : $uid{0})
{
case 'g':
$cutype = 'GROUP';
break;
case 'r':
$cutype = 'RESOURCE';
break;
case 'u':
$cutype = 'INDIVIDUAL';
break;
default:
$cutype = 'UNKNOWN';
$cutype = 'INDIVIDUAL';
break;
};
$parameters['ATTENDEE'][] = array(
'CN' => $cn,
'ROLE' => $role,
@ -245,7 +274,20 @@
$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
break;
}
$rrule['UNTIL'] = ($event['recur_enddate']) ? date('Ymd',$event['recur_enddate']).'T'.date('His',$event['start']) : '#0';
if ($event['recur_enddate'])
{
$recur_enddate = (int)$event['recur_enddate'];
if ($palm_enddate_workaround)
{
$recur_enddate += 86400;
}
$rrule['UNTIL'] = date('Ymd',$recur_enddate);
}
else
{
$rrule['UNTIL'] = '#0';
}
$attributes['RRULE'] = $rrule['FREQ'].' '.$rrule['UNTIL'];
} else {
@ -292,7 +334,7 @@
{
$days[] = date('Ymd',$day);
}
$attributes['EXDATE'] = implode(';',$days);
$attributes['EXDATE'] = implode(',',$days);
$parameters['EXDATE']['VALUE'] = 'DATE';
}
break;
@ -312,9 +354,10 @@
case 'CATEGORIES':
if ($event['category'])
{
$attributes['CATEGORIES'] = implode(',',$this->categories($event['category'],$nul));
$attributes['CATEGORIES'] = implode(',',$this->get_categories($event['category']));
}
break;
default:
if ($event[$egwFieldInfo['dbName']]) // dont write empty fields
{
@ -397,6 +440,13 @@
}
//echo "supportedFields="; _debug_array($this->supportedFields);
$syncevo_enddate_fix = False;
if( $this->productManufacturer == '' && $this->productName == '' )
{
// syncevolution needs an adjusted recur_enddate
$syncevo_enddate_fix = True;
}
$Ok = false; // returning false, if file contains no components
foreach($vcal->getComponents() as $component)
{
@ -406,7 +456,10 @@
#$event = array('participants' => array());
$event = array();
$alarms = array();
$vcardData = array('recur_type' => 0);
$vcardData = array(
'recur_type' => MCAL_RECUR_NONE,
'recur_exception' => array(),
);
// lets see what we can get from the vcard
foreach($component->_attributes as $attributes)
@ -432,6 +485,11 @@
$alarms[$alarmTime] = array(
'time' => $alarmTime
);
} elseif (preg_match('/(........T......)$/',$attributes['value'],$matches)) {
$alarmTime = $vcal->_parseDateTime($attributes['value']);
$alarms[$alarmTime] = array(
'time' => $alarmTime
);
}
break;
case 'CLASS':
@ -443,7 +501,7 @@
case 'DTEND':
$dtend_ts = is_numeric($attributes['value']) ? $attributes['value'] : $this->date2ts($attributes['value']);
if(date('H:i:s',$dtend_ts) == '00:00:00') {
$dtend_ts--;
$dtend_ts -= 60;
}
$vcardData['end'] = $dtend_ts;
break;
@ -461,9 +519,14 @@
{
$vcardData['recur_enddate'] = $vcal->_parseDateTime($matches[1]);
}
elseif (preg_match('/COUNT=([0-9]+)/',$recurence,$matches))
{
$vcardData['recur_count'] = (int)$matches[1];
}
if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches))
{
$vcardData['recur_interval'] = (int) $matches[1];
// 1 is invalid,, egw uses 0 for interval
$vcardData['recur_interval'] = (int) $matches[1] != 0 ? (int) $matches[1] : 0;
}
$vcardData['recur_data'] = 0;
switch($type)
@ -495,6 +558,14 @@
}
$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
}
if (!empty($vcardData['recur_count']))
{
$vcardData['recur_enddate'] = mktime(0,0,0,
date('m',$vcardData['start']),
date('d',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)*7),
date('Y',$vcardData['start']));
}
break;
case 'D': // 1.0
@ -521,6 +592,14 @@
// fall-through
case 'DAILY': // 2.0
$vcardData['recur_type'] = MCAL_RECUR_DAILY;
if (!empty($vcardData['recur_count']))
{
$vcardData['recur_enddate'] = mktime(0,0,0,
date('m',$vcardData['start']),
date('d',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)),
date('Y',$vcardData['start']));
}
break;
case 'M':
@ -554,6 +633,14 @@
case 'MONTHLY':
$vcardData['recur_type'] = strpos($recurence,'BYDAY') !== false ?
MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY;
if (!empty($vcardData['recur_count']))
{
$vcardData['recur_enddate'] = mktime(0,0,0,
date('m',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)),
date('d',$vcardData['start']),
date('Y',$vcardData['start']));
}
break;
case 'Y': // 1.0
@ -580,11 +667,24 @@
// fall-through
case 'YEARLY': // 2.0
$vcardData['recur_type'] = MCAL_RECUR_YEARLY;
if (!empty($vcardData['recur_count']))
{
$vcardData['recur_enddate'] = mktime(0,0,0,
date('m',$vcardData['start']),
date('d',$vcardData['start']),
date('Y',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)));
}
break;
}
if( $syncevo_enddate_fix && $vcardData['recur_enddate'] )
{
// Does syncevolution need to adjust recur_enddate
$vcardData['recur_enddate'] = (int)$vcardData['recur_enddate'] + 86400;
}
break;
case 'EXDATE':
$vcardData['recur_exception'] = $attributes['value'];
$vcardData['recur_exception'] = array_merge($vcardData['recur_exception'],$attributes['value']);
break;
case 'SUMMARY':
$vcardData['title'] = $attributes['value'];
@ -608,26 +708,14 @@
$vcardData['priority'] = (int) $this->priority_ical2egw[$attributes['value']];
break;
case 'CATEGORIES':
$vcardData['category'] = array();
if ($attributes['value'])
{
if (!is_object($this->cat))
{
if (!is_object($GLOBALS['egw']->categories))
{
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$this->owner,'calendar');
}
$this->cat =& $GLOBALS['egw']->categories;
}
foreach(explode(',',$attributes['value']) as $cat_name)
{
if (!($cat_id = $this->cat->name2id($cat_name)))
{
$cat_id = $this->cat->add( array('name' => $cat_name,'descr' => $cat_name ));
}
$vcardData['category'][] = $cat_id;
}
$vcardData['category'] = $this->find_or_add_categories(explode(',',$attributes['value']));
}
else
{
$vcardData['category'] = array();
}
break;
case 'ATTENDEE':
if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
@ -737,6 +825,38 @@
$event['participants'] = array($GLOBALS['egw_info']['user']['account_id'] => 'A');
}
// If this is an updated meeting, and the client doesn't support
// participants, add them back
if( $cal_id >0 && !isset($this->supportedFields['participants']))
{
$egw_event = $this->read($cal_id);
if ($egw_event)
{
$event['participants'] = $egw_event['participants'];
$event['participant_types'] = $egw_event['participant_types'];
}
}
// Check for resources, and don't remove them
if( $cal_id > 0 )
{
// for each existing participant:
$egw_event = $this->read($cal_id);
if ( $egw_event )
{
foreach( $egw_event['participants'] as $uid => $status )
{
// Is it a resource?
if ( preg_match("/^r(.*)/", $uid, $matches) )
{
// Add it back in
$event['participants'][$uid] = 'A';
$event['participant_types']['r'][$matches[1]] = 'A';
}
}
}
}
#error_log('ALARMS');
#error_log(print_r($event, true));
@ -776,28 +896,55 @@
$this->productManufacturer = $_productManufacturer;
$this->productName = $_productName;
$defaultFields[0] = array('public' => 'public', 'description' => 'description', 'end' => 'end',
'start' => 'start', 'location' => 'location', 'recur_type' => 'recur_type',
'recur_interval' => 'recur_interval', 'recur_data' => 'recur_data', 'recur_enddate' => 'recur_enddate',
'title' => 'title', 'priority' => 'priority', 'alarms' => 'alarms',
$defaultFields['minimal'] = array(
'public' => 'public',
'description' => 'description',
'end' => 'end',
'start' => 'start',
'location' => 'location',
'recur_type' => 'recur_type',
'recur_interval' => 'recur_interval',
'recur_data' => 'recur_data',
'recur_enddate' => 'recur_enddate',
'title' => 'title',
'alarms' => 'alarms',
);
$defaultFields[1] = array('public' => 'public', 'description' => 'description', 'end' => 'end',
'start' => 'start', 'location' => 'location', 'recur_type' => 'recur_type',
'recur_interval' => 'recur_interval', 'recur_data' => 'recur_data', 'recur_enddate' => 'recur_enddate',
'title' => 'title', 'alarms' => 'alarms',
$defaultFields['basic'] = $defaultFields['minimal'] + array(
'recur_exception' => 'recur_exception',
'priority' => 'priority',
);
$defaultFields['nexthaus'] = $defaultFields['basic'] + array(
'participants' => 'participants',
);
$defaultFields['synthesis'] = $defaultFields['basic'] + array(
'non_blocking' => 'non_blocking',
'category' => 'category',
);
$defaultFields['evolution'] = $defaultFields['basic'] + array(
'participants' => 'participants',
'owner' => 'owner',
'category' => 'category',
);
$defaultFields['full'] = $defaultFields['basic'] + array(
'participants' => 'participants',
'owner' => 'owner',
'category' => 'category',
'non_blocking' => 'non_blocking',
);
switch(strtolower($_productManufacturer))
{
case 'nexthaus corporation':
switch(strtolower($_productName))
{
default:
$this->supportedFields = $defaultFields[0] + array('participants' => 'participants');
#$this->supportedFields = $defaultFields;
$this->supportedFields = $defaultFields['nexthaus'];
break;
}
break;
@ -808,7 +955,7 @@
switch(strtolower($_productName))
{
default:
$this->supportedFields = $defaultFields[0];
$this->supportedFields = $defaultFields['basic'];
break;
}
break;
@ -817,18 +964,26 @@
switch(strtolower($_productName))
{
case 'e61':
$this->supportedFields = $defaultFields['minimal'];
break;
default:
$this->supportedFields = $defaultFields[1];
error_log("Unknown Nokia phone '$_productName', assuming E61");
$this->supportedFields = $defaultFields['minimal'];
break;
}
break;
case 'sonyericsson':
case 'sony ericsson':
switch(strtolower($_productName))
{
case 'd750i':
case 'p910i':
$this->supportedFields = $defaultFields['basic'];
break;
default:
$this->supportedFields = $defaultFields[0];
error_log("Unknown Sony Ericsson phone '$_productName' assuming d750i");
$this->supportedFields = $defaultFields['basic'];
break;
}
break;
@ -837,41 +992,35 @@
switch(strtolower($_productName))
{
default:
$this->supportedFields = $defaultFields[0] + array(
'recur_exception' => 'recur_exception',
'non_blocking' => 'non_blocking',
);
$this->supportedFields = $defaultFields['synthesis'];
break;
}
break;
//Syncevolution compatibility
case 'patrick ohly':
$this->supportedFields = $defaultFields[1] + array(
'participants' => 'participants',
'owner' => 'owner',
'category' => 'category',
);
$this->supportedFields = $defaultFields['evolution'];
break;
case '': // seems syncevolution 0.5 doesn't send a manufacturer
error_log("No vendor name, assuming syncevolution 0.5");
$this->supportedFields = $defaultFields['evolution'];
break;
case 'file': // used outside of SyncML, eg. by the calendar itself ==> all possible fields
$this->supportedFields = $defaultFields[0] + array(
'participants' => 'participants',
'owner' => 'owner',
'non_blocking' => 'non_blocking',
'category' => 'category',
);
$this->supportedFields = $defaultFields['full'];
break;
// the fallback for SyncML
default:
error_log("Client not found: $_productManufacturer $_productName");
$this->supportedFields = $defaultFields[0];
error_log("Unknown calendar SyncML client: manufacturer='$_productManufacturer' product='$_productName'");
$this->supportedFields = $defaultFields['full'];
break;
}
}
function icaltoegw($_vcalData) {
function icaltoegw($_vcalData)
{
// our (patched) horde classes, do NOT unfold folded lines, which causes a lot trouble in the import
$_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData);
@ -1046,26 +1195,14 @@
}
break;
case 'CATEGORIES':
$vcardData['category'] = array();
if ($attributes['value'])
{
if (!is_object($this->cat))
{
if (!is_object($GLOBALS['egw']->categories))
{
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$this->owner,'calendar');
}
$this->cat =& $GLOBALS['egw']->categories;
}
foreach(explode(',',$attributes['value']) as $cat_name)
{
if (!($cat_id = $this->cat->name2id($cat_name)))
{
$cat_id = $this->cat->add( array('name' => $cat_name,'descr' => $cat_name ));
}
$vcardData['category'][] = $cat_id;
}
}
$vcardData['category'] = $this->find_or_add_categories(explode(',',$attributes['value']));
}
else
{
$vcardData['category'] = array();
}
break;
case 'ATTENDEE':
if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
@ -1117,6 +1254,10 @@
{
switch($fieldName)
{
case 'recur_interval':
case 'recur_enddate':
case 'recur_data':
case 'recur_exception':
case 'alarms':
// not handled here
break;
@ -1132,17 +1273,12 @@
}
}
}
unset($supportedFields['recur_type']);
unset($supportedFields['recur_interval']);
unset($supportedFields['recur_enddate']);
unset($supportedFields['recur_data']);
break;
default:
if (isset($vcardData[$fieldName]))
{
$event[$fieldName] = $vcardData[$fieldName];
}
unset($supportedFields[$fieldName]);
break;
}
}
@ -1215,20 +1351,24 @@
{
$vfreebusy->setAttribute($attr, $value, $parameters[$name]);
}
foreach(parent::search(array(
'start' => $this->now_su,
'end' => $end,
'users' => $user,
'date_format' => 'server',
'show_rejected' => false,
)) as $event)
$fbdata = parent::search(array(
'start' => $this->now_su,
'end' => $end,
'users' => $user,
'date_format' => 'server',
'show_rejected' => false,
));
if (is_array($fbdata))
{
if ($event['non_blocking']) continue;
foreach ($fbdata as $event)
{
if ($event['non_blocking']) continue;
$vfreebusy->setAttribute('FREEBUSY',array(array(
'start' => $event['start'],
'end' => $event['end'],
)));
$vfreebusy->setAttribute('FREEBUSY',array(array(
'start' => $event['start'],
'end' => $event['end'],
)));
}
}
$vcal->addComponent($vfreebusy);

View File

@ -71,7 +71,7 @@
}
function endElement($_parser, $_tag) {
#error_log($_tag .' => '. $this->sifData);
//error_log('endElem: ' . $_tag .' => '. trim($this->sifData));
if(!empty($this->sifMapping[$_tag])) {
$this->event[$this->sifMapping[$_tag]] = trim($this->sifData);
}
@ -128,19 +128,7 @@
case 'category':
if(!empty($value)) {
$egwCategories =& CreateObject('phpgwapi.categories', $GLOBALS['egw_info']['user']['account_id'], 'calendar');
$categories = explode(';',$value);
foreach($categories as $categorieName) {
$cat_id = false;
$categorieName = trim($categorieName);
if(!($cat_id = $egwCategories->name2id($categorieName))) {
$cat_id = $egwCategories->add(array('name' => $categorieName, 'descr' => lang('added by synchronisation')));
}
if($cat_id) {
if(!empty($finalEvent[$key])) $finalEvent[$key] .= ',';
$finalEvent[$key] .= $cat_id;
}
}
$finalEvent[$key] = implode(',',$this->find_or_add_categories(explode(';', $value)));
}
break;
@ -148,6 +136,7 @@
case 'start':
if($this->event['alldayevent'] < 1) {
$finalEvent[$key] = $vcal->_parseDateTime($value);
error_log("event ".$key." val=".$value.", parsed=".$finalEvent[$key]);
}
break;
@ -348,15 +337,8 @@
{
case 'Categories':
if(!empty($value)) {
$egwCategories =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'calendar');
$categories = explode(',',$value);
$value = '';
foreach($categories as $cat_id) {
if($catData = $egwCategories->return_single($cat_id)) {
if(!empty($value)) $value .= '; ';
$value .= $GLOBALS['egw']->translation->convert($catData[0]['name'], $sysCharSet, 'utf-8');
}
}
$value = implode('; ', $this->get_categories(explode(',',$value)));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
$sifEvent .= "<$sifField>$value</$sifField>";
break;

View File

@ -1270,6 +1270,74 @@ class boinfolog
return $icons;
}
var $app_cat;
var $glob_cat;
function find_or_add_categories($catname_list)
{
if (!is_object($this->glob_cat))
{
if (!is_object($GLOBALS['egw']->categories))
{
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'phpgw');
}
$this->glob_cat =& $GLOBALS['egw']->categories;
}
if (!is_object($this->app_cat))
{
$this->app_cat =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'infolog');
}
$cat_id_list = array();
foreach($catname_list as $cat_name)
{
$cat_name = trim($cat_name);
if (!($cat_id = $this->glob_cat->name2id($cat_name))
&& !($cat_id = $this->app_cat->name2id($cat_name)))
{
$cat_id = $this->app_cat->add( array('name' => $cat_name,'descr' => $cat_name ));
}
$cat_id_list[] = $cat_id;
}
if (count($cat_id_list) > 1)
{
sort($cat_id_list, SORT_NUMERIC);
}
return $cat_id_list;
}
function get_categories($cat_id_list)
{
if (!is_object($this->glob_cat))
{
if (!is_object($GLOBALS['egw']->categories))
{
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'phpgw');
}
$this->glob_cat =& $GLOBALS['egw']->categories;
}
if (!is_object($this->app_cat))
{
$this->app_cat =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'infolog');
}
$cat_list = array();
foreach(explode(',',$cat_id_list) as $cat_id)
{
if ( ($cat_data = $this->glob_cat->return_single($cat_id))
|| ($cat_data = $this->app_cat->return_single($cat_id)) )
{
$cat_list[] = $cat_data[0]['name'];
}
}
return $cat_list;
}
/**
* Send all async infolog notification
*
@ -1351,3 +1419,4 @@ class boinfolog
$GLOBALS['egw_info']['user']['preferences'] = $save_prefs;
}
}

View File

@ -21,12 +21,24 @@
// array containing the current mappings(task or note)
var $_currentSIFMapping;
var $_sifNoteMapping = array(
'Body' => 'info_des',
'Categories' => 'info_cat',
'Color' => '',
'Date' => 'info_startdate',
'Height' => '',
'Left' => '',
'Subject' => 'info_subject',
'Top' => '',
'Width' => '',
);
// mappings for SIFTask to InfologTask
var $_sifTaskMapping = array(
'ActualWork' => '',
'BillingInformation' => '',
'Body' => 'info_des',
'Categories' => '',
'Categories' => 'info_cat',
'Companies' => '',
'Complete' => '',
'DateCompleted' => 'info_datecompleted',
@ -61,8 +73,9 @@
}
function endElement($_parser, $_tag) {
error_log("infolog: tag=$_tag data=".trim($this->sifData));
if(!empty($this->_currentSIFMapping[$_tag])) {
$this->_extractedSIFData[$this->_currentSIFMapping[$_tag]] = $this->sifData;
$this->_extractedSIFData[$this->_currentSIFMapping[$_tag]] = trim($this->sifData);
}
unset($this->sifData);
}
@ -81,7 +94,18 @@
#fwrite($handle, $sifData);
#fclose($handle);
$this->_currentSIFMapping = $this->_sifTaskMapping;
switch ($_sifType)
{
case 'note':
$this->_currentSIFMapping = $this->_sifNoteMapping;
break;
case 'task':
default:
$this->_currentSIFMapping = $this->_sifTaskMapping;
break;
}
$this->xml_parser = xml_parser_create('UTF-8');
xml_set_object($this->xml_parser, $this);
xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, false);
@ -108,6 +132,8 @@
foreach($this->_extractedSIFData as $key => $value) {
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
error_log("infolog key=$key => value=$value");
switch($key) {
case 'info_access':
$taskData[$key] = ((int)$value > 0) ? 'private' : 'public';
@ -127,6 +153,13 @@
break;
case 'info_cat':
if (!empty($value)) {
$categories = $this->find_or_add_categories(explode(';', $value));
$taskData['info_cat'] = $categories[0];
}
break;
case 'info_priority':
$taskData[$key] = (int)$value;
break;
@ -156,11 +189,52 @@
$taskData[$key] = $value;
break;
}
error_log("infolog task key=$key => value=".$taskData[$key]);
}
return $taskData;
break;
case 'note':
$noteData = array();
$noteData['info_type'] = 'note';
$vcal = &new Horde_iCalendar;
foreach($this->_extractedSIFData as $key => $value)
{
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
error_log("infolog client key=$key => value=".$value);
switch ($key)
{
case 'info_startdate':
if(!empty($value)) {
$noteData[$key] = $vcal->_parseDateTime($value);
// somehow the client always deliver a timestamp about 3538 seconds, when no startdate set.
if($noteData[$key] < 10000)
$noteData[$key] = '';
} else {
$noteData[$key] = '';
}
break;
case 'info_cat':
if (!empty($value)) {
$categories = $this->find_or_add_categories(explode(';', $value));
$taskData['info_cat'] = $categories[0];
}
break;
default:
$noteData[$key] = $value;
break;
}
error_log("infolog note key=$key => value=".$noteData[$key]);
}
return $noteData;
break;
default:
return false;
}
@ -251,6 +325,15 @@
$sifTask .= "<$sifField>$value</$sifField>";
break;
case 'Categories':
if (!empty($value))
{
$value = implode('; ', $this->get_categories(array($value)));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
$sifTask .= "<$sifField>$value</$sifField>";
break;
default:
$sifTask .= "<$sifField>$value</$sifField>";
break;
@ -296,6 +379,52 @@
}
break;
case 'note':
if($taskData = $this->read($_id)) {
$sysCharSet = $GLOBALS['egw']->translation->charset();
$vcal = &new Horde_iCalendar;
$sifNote = '<note>';
foreach($this->_sifNoteMapping as $sifField => $egwField)
{
if(empty($egwField)) continue;
$value = $GLOBALS['egw']->translation->convert($taskData[$egwField], $sysCharSet, 'utf-8');
switch($sifField) {
case 'Date':
if(!empty($value)) {
$value = $vcal->_exportDateTime($value);
}
$sifNote .= "<$sifField>$value</$sifField>";
break;
case 'Body':
$value = $GLOBALS['egw']->translation->convert($taskData['info_subject'], $sysCharSet, 'utf-8') . "\n" . $value;
$sifNote .= "<$sifField>$value</$sifField>";
break;
case 'Categories':
if (!empty($value))
{
$value = implode('; ', $this->get_categories(array($value)));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
$sifNote .= "<$sifField>$value</$sifField>";
break;
default:
$sifNote .= "<$sifField>$value</$sifField>";
break;
}
}
return base64_encode($sifNote);
}
break;
default;
return false;
}

View File

@ -186,7 +186,7 @@ class soinfolog // DB-Layer
*/
function aclFilter($filter = False)
{
preg_match('/(my|responsible|delegated|own|privat|all|none|user)([0-9]*)/',$filter_was=$filter,$vars);
preg_match('/(my|responsible|delegated|own|privat|private|all|none|user)([0-9]*)/',$filter_was=$filter,$vars);
$filter = $vars[1];
$f_user = intval($vars[2]);
@ -230,7 +230,7 @@ class soinfolog // DB-Layer
$filtermethod .= " OR (".$this->responsible_filter($this->user)." AND info_access='public')";
// private: own entries plus the one user is responsible for
if ($filter == 'private' || $filter == 'own')
if ($filter == 'private' || $filter == 'privat' || $filter == 'own')
{
$filtermethod .= " OR (".$this->responsible_filter($this->user).
($filter == 'own' && count($public_user_list) ? // offer's should show up in own, eg. startpage, but need read-access

View File

@ -86,6 +86,12 @@
$this->status2vtodo[$taskData['info_status']] : 'NEEDS-ACTION');
$vevent->setAttribute('PRIORITY',$this->egw_priority2vcal_priority[$taskData['info_priority']]);
if (!empty($taskData['info_cat']))
{
$cats = $this->get_categories(array($taskData['info_cat']));
$vevent->setAttribute('CATEGORIES', $cats[0]);
}
#$vevent->setAttribute('TRANSP','OPAQUE');
# status
# ATTENDEE
@ -122,7 +128,8 @@
return $this->write($taskData);
}
function searchVTODO($_vcalData) {
function searchVTODO($_vcalData)
{
if(!$egwData = $this->vtodotoegw($_vcalData)) {
return false;
}
@ -140,7 +147,8 @@
return false;
}
function vtodotoegw($_vcalData) {
function vtodotoegw($_vcalData)
{
$vcal = &new Horde_iCalendar;
if(!$vcal->parsevCalendar($_vcalData)) {
return FALSE;
@ -196,6 +204,13 @@
case 'SUMMARY':
$taskData['info_subject'] = $attributes['value'];
break;
case 'CATEGORIES':
{
$cats = $this->find_or_add_categories(explode(',', $attributes['value']));
$taskData['info_cat'] = $cats[0];
}
break;
}
}
# the horde ical class does already convert in parsevCalendar
@ -207,4 +222,147 @@
}
return FALSE;
}
function exportVNOTE($_noteID, $_type)
{
$note = $this->read($_noteID);
$note = $GLOBALS['egw']->translation->convert($note, $GLOBALS['egw']->translation->charset(), 'UTF-8');
switch($_type)
{
case 'text/plain':
$txt = $note['info_subject']."\n\n".$note['info_des'];
return $txt;
break;
case 'text/x-vnote':
$noteGUID = $GLOBALS['egw']->common->generate_uid('infolog_note',$_noteID);
$vnote = &new Horde_iCalendar_vnote();
$vNote->setAttribute('VERSION', '1.1');
$vnote->setAttribute('SUMMARY',$note['info_subject']);
$vnote->setAttribute('BODY',$note['info_des']);
if($note['info_startdate'])
$vnote->setAttribute('DCREATED',$note['info_startdate']);
$vnote->setAttribute('DCREATED',$GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'add'));
$vnote->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'modify'));
if (!empty($note['info_cat']))
{
$cats = $this->get_categories(array($note['info_cat']));
$vnote->setAttribute('CATEGORIES', $cats[0]);
}
#$vnote->setAttribute('UID',$noteGUID);
#$vnote->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE');
#$options = array('CHARSET' => 'UTF-8','ENCODING' => 'QUOTED-PRINTABLE');
#$vnote->setParameter('SUMMARY', $options);
#$vnote->setParameter('DESCRIPTION', $options);
return $vnote->exportvCalendar();
break;
}
return false;
}
function importVNOTE(&$_vcalData, $_type, $_noteID = -1)
{
if(!$note = $this->vnotetoegw($_vcalData, $_type))
{
return false;
}
if($_noteID > 0)
{
$note['info_id'] = $_noteID;
}
if(empty($note['info_status'])) {
$note['info_status'] = 'done';
}
#_debug_array($taskData);exit;
return $this->write($note);
}
function searchVNOTE($_vcalData, $_type)
{
if(!$note = $this->vnotetoegw($_vcalData)) {
return false;
}
$filter = array('col_filter' => $egwData);
if($foundItems = $this->search($filter)) {
if(count($foundItems) > 0) {
$itemIDs = array_keys($foundItems);
return $itemIDs[0];
}
}
return false;
}
function vnotetoegw($_data, $_type)
{
switch($_type)
{
case 'text/plain':
$note = array();
$note['info_type'] = 'note';
$botranslation =& CreateObject('phpgwapi.translation');
$txt = $botranslation->convert($_data, 'utf-8');
$txt = str_replace("\r\n", "\n", $txt);
if (preg_match("/^(^\n)\n\n(.*)$/", $txt, $match))
{
$note['info_subject'] = $match[0];
$note['info_des'] = $match[1];
}
else
{
$note['info_des'] = $txt;
}
return $note;
break;
case 'text/x-vnote':
$vnote = &new Horde_iCalendar;
if (!$vcal->parsevCalendar($_data))
{
return FALSE;
}
$components = $vnote->getComponent();
if(count($components) > 0)
{
$component = $components[0];
if(is_a($component, 'Horde_iCalendar_vnote'))
{
$note = array();
$note['info_type'] = 'note';
foreach($component->_attributes as $attribute)
{
switch ($attribute['name'])
{
case 'BODY':
$note['info_des'] = $attribute['value'];
break;
case 'SUMMARY':
$note['info_subject'] = $attribute['value'];
break;
case 'CATEGORIES':
{
$cats = $this->find_or_add_categories(explode(',', $attribute['value']));
$note['info_cat'] = $cats[0];
}
break;
}
}
}
return $note;
}
}
return FALSE;
}
}

View File

@ -215,7 +215,7 @@ class accounts_backend
{
$GLOBALS['egw']->contacts =& CreateObject('phpgwapi.contacts');
}
$GLOBALS['egw']->contacts->delete($contact_id);
$GLOBALS['egw']->contacts->delete($contact_id,false); // false = allow to delete accounts (!)
}
return true;
}

View File

@ -169,8 +169,10 @@
if(empty($_globalUid)) return false;
$globalUidParts = explode('-',$_globalUid);
array_shift($globalUidParts); // remove the app name
array_pop($globalUidParts); // remove the install_id
return $globalUidParts[1];
return implode('-',$globalUidParts); // return the rest, allowing to have dashs in the id, can happen with LDAP!
}
// This is used for searching the access fields

View File

@ -134,6 +134,49 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
switch($element) {
case 'CTType':
$this->_contentType = trim($this->_chars);
if (substr($this->_contentType, 0, 14) == "text/x-s4j-sif")
{
// workaround a little bug in sync4j for mobile v3.1.3 (and possibly others)
// where the content-type is set to just one value regardless of
// the source... this further leads to a failure to send updates
// by the server since it does not know how to convert say tasks to text/x-s4j-sifc
// (it should be text/x-s4j-sift).
switch ($this->_sourceReference)
{
case 'contact':
if ($this->_contentType != "text/x-s4j-sifc")
{
error_log("forcing 'contact' content type to 'text/x-s4j-sifc' instead of '".$this->_contentType."'");
$this->_contentType = "text/x-s4j-sifc";
}
break;
case 'calendar':
case 'appointment':
if ($this->_contentType != "text/x-s4j-sife")
{
error_log("forcing 'calendar' content type to 'text/x-s4j-sife' instead of '".$this->_contentType."'");
$this->_contentType = "text/x-s4j-sife";
}
break;
case 'task':
if ($this->_contentType != "text/x-s4j-sift")
{
error_log("forcing 'task' content type to 'text/x-s4j-sift' instead of '".$this->_contentType."'");
$this->_contentType = "text/x-s4j-sift";
}
break;
case 'note':
if ($this->_contentType != "text/x-s4j-sifn")
{
error_log("forcing 'note' content type to 'text/x-s4j-sifn' instead of '".$this->_contentType."'");
$this->_contentType = "text/x-s4j-sifn";
}
break;
default:
#error_log("Leaving ContentType='".$this->_contentType."' as is for source '".$this->_sourceReference."'");
break;
}
}
break;
case 'SyncType':

View File

@ -40,17 +40,18 @@ class Horde_SyncML_Command_Results extends Horde_SyncML_Command {
break;
case 'DevID':
switch(trim($this->_chars)) {
$devid = trim($this->_chars);
$this->_deviceInfo['deviceID'] = $devid;
switch ($devid)
{
case 'fmz-thunderbird-plugin':
$this->_deviceInfo['deviceID'] = trim($this->_chars);
$this->_deviceInfo['manufacturer'] = 'funambol';
$this->_deviceInfo['model'] = trim($this->_chars);
if (empty($this->_devinceInfo['manufacturer']))
$this->_deviceInfo['manufacturer'] = 'funambol';
if (empty($this->_devinceInfo['model']))
$this->_deviceInfo['model'] = 'thunderbird';
if (empty($this->_devinceInfo['softwareVersion']))
$this->_deviceInfo['softwareVersion'] = '0.3';
break;
default:
$this->_deviceInfo['deviceID'] = trim($this->_chars);
break;
}
break;

View File

@ -109,7 +109,11 @@ class Horde_SyncML_Command_Sync extends Horde_Syncml_Command {
foreach($targets as $target)
{
$sync = $state->getSync($target);
$sync = $state->getSync($target);
// make sure that the state reflects what is currently being done
$state->_currentSourceURI = $sync->_sourceLocURI;
$state->_currentTargetURI = $sync->_targetLocURI;
$output->startElement($state->getURI(), 'Sync', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);

View File

@ -655,28 +655,82 @@ class Horde_SyncML_State {
/**
* This function should use DevINF information.
*/
function getPreferedContentType($type) {
switch($type) {
function adjustContentType($type, $target = null)
{
$ctype;
if (is_array($type))
{
$ctype = $type['ContentType'];
$res = $type;
}
else
{
$ctype = $type;
$res = array();
$res['ContentType'] = $ctype;
}
switch($ctype)
{
case 'text/x-vcard':
case 'text/x-vcalendar':
case 'text/x-vnote':
case 'text/calendar':
case 'text/plain':
$res['mayFragment'] = 1;
break;
case 'text/x-s4j-sifc':
case 'text/x-s4j-sife':
case 'text/x-s4j-sift':
case 'text/x-s4j-sifn':
$res['ContentFormat'] = 'b64';
$res['mayFragment'] = 0;
break;
}
if (!isset($res['mayFragment']))
{
$res['mayFragment'] = 0;
}
if ($target != null)
{
switch($target)
{
case 'calendar':
case 'tasks':
case 'notes':
case 'contacts':
$res['mayFragment'] = 1;
break;
case 'sifcalendar':
case 'siftasks':
case 'sifnotes':
case 'sifcontacts':
case 'scard':
case 'scalendar':
case 'stask':
case 'snote':
default:
$res['mayFragment'] = 0;
break;
}
}
return $res;
}
function getPreferedContentType($type)
{
$_type = str_replace('./','',$type);
switch($_type)
{
case 'contacts':
case './contacts':
return 'text/x-vcard';
break;
case 'sifcalendar':
case './sifcalendar':
return 'text/x-s4j-sife';
break;
case 'sifcontacts':
case './sifcontacts':
return 'text/x-s4j-sifc';
break;
case 'siftasks':
case './siftasks':
return 'text/x-s4j-sift';
break;
case 'notes':
return 'text/x-vnote';
break;
@ -686,12 +740,82 @@ class Horde_SyncML_State {
break;
case 'calendar':
case './calendar':
return 'text/x-vcalendar';
break;
case 'sifcalendar':
case 'scal':
return 'text/x-s4j-sife';
break;
case 'sifcontacts':
case 'scard':
return 'text/x-s4j-sifc';
break;
case 'siftasks':
case 'stask':
return 'text/x-s4j-sift';
break;
case 'sifnotes':
case 'snote':
return 'text/x-s4j-sifn';
break;
}
}
function getHordeType($type)
{
$_type = str_replace('./','',$type);
switch($_type)
{
case 'contacts':
return 'contacts';
break;
case 'notes':
return 'notes';
break;
case 'tasks':
return 'tasks';
break;
case 'calendar':
return 'calendar';
break;
# funambol related types
case 'sifcalendar':
case 'scal':
return 'sifcalendar';
break;
case 'sifcontacts':
case 'scard':
return 'sifcontacts';
break;
case 'siftasks':
case 'stask':
return 'siftasks';
break;
case 'sifnotes':
case 'snote':
return 'sifnotes';
break;
default:
Horde::logMessage("unknown hordeType for type=$type ($_type)", __FILE__, __LINE__, PEAR_LOG_INFO);
return $_type;
break;
}
}
/**
/**
* Returns the preferred contenttype of the client for the given
* sync data type (database).
@ -699,15 +823,21 @@ class Horde_SyncML_State {
* This is passed as an option to the Horde API export functions.
*/
function getPreferedContentTypeClient($_sourceLocURI) {
function getPreferedContentTypeClient($_sourceLocURI, $_targetLocURI = null) {
$deviceInfo = $this->getClientDeviceInfo();
if(isset($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType'])) {
return array('ContentType' => $deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType']);
if(isset($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType']))
{
return $this->adjustContentType($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType'], $_targetLocURI);
}
Horde::logMessage('SyncML: sourceLocURI ' . $_sourceLocURI .' not found', __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($_targetLocURI != null)
{
return $this->adjustContentType($this->getPreferedContentType($_targetLocURI), $_targetLocURI);
}
return PEAR::raiseError(_('sourceLocURI not found'));
}

View File

@ -141,13 +141,14 @@ class Horde_SyncML_Sync {
}
$hordeType = $type = $this->_targetLocURI;
// remove the './' from the beginning
$hordeType = str_replace('./','',$hordeType);
$hordeType = $state->getHordeType($hordeType);
if(!$contentType = $command->getContentType()) {
$contentType = $state->getPreferedContentType($type);
}
if ($this->_targetLocURI == 'calendar' && strpos($command->getContent(), 'BEGIN:VTODO') !== false) {
if (($contentType == 'text/x-vcalendar' || $contentType == 'text/calendar')
&& strpos($command->getContent(), 'BEGIN:VTODO') !== false)
{
$hordeType = 'tasks';
}

View File

@ -36,28 +36,27 @@ class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySy
continue;
}
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI);
if(is_a($contentType, 'PEAR_Error')) {
// Client did not sent devinfo
$contentType = array('ContentType' => $state->getPreferedContentType($this->_targetLocURI));
}
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement();
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
Horde::logMessage("SyncML: slowsync add $guid to client ". print_r($c, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!is_a($c, 'PEAR_Error')) {
$cmd->setContent($c);
if($hordeType == 'sifcalendar' || $hordeType == 'sifcontacts' || $hordeType == 'siftasks') {
$cmd->setContentFormat('b64');
$cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setContentType($contentType['ContentType']);
$cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
// return if we have to much data
if(++$counter >= MAX_ENTRIES && $hordeType != 'sifcalendar' && $hordeType != 'sifcontacts' && $hordeType != 'siftasks') {
if(++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
@ -77,7 +76,7 @@ class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySy
$state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI;
$hordeType = str_replace('./','',$syncType);
$hordeType = $state->getHordeType($syncType);
Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/list', array()));

View File

@ -38,28 +38,27 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
continue;
}
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI);
if(is_a($contentType, 'PEAR_Error')) {
// Client did not sent devinfo
$contentType = array('ContentType' => $state->getPreferedContentType($this->_targetLocURI));
}
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement();
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
#Horde::logMessage("SyncML: slowsync add guid $guid to client ". print_r($c, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!is_a($c, 'PEAR_Error')) {
$cmd->setContent($c);
if($hordeType == 'sifcalendar' || $hordeType == 'sifcontacts' || $hordeType == 'siftasks') {
$cmd->setContentFormat('b64');
$cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setContentType($contentType['ContentType']);
$cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
// return if we have to much data
if(++$counter >= MAX_ENTRIES && $hordeType != 'sifcalendar' && $hordeType != 'sifcontacts' && $hordeType != 'siftasks') {
if(++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
@ -129,9 +128,8 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
return;
}
$hordeType = $type = $this->_targetLocURI;
// remove the './' from the beginning
$hordeType = str_replace('./','',$hordeType);
$type = $this->_targetLocURI;
$hordeType = $state->getHordeType($type);
$syncElementItems = $command->getSyncElementItems();
@ -140,7 +138,9 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$contentType = $state->getPreferedContentType($type);
}
if ($this->_targetLocURI == 'calendar' && strpos($syncItem->getContent(), 'BEGIN:VTODO') !== false) {
if (($contentType == 'text/x-vcalendar' || $contentType == 'text/calendar')
&& strpos($syncItem->getContent(), 'BEGIN:VTODO') !== false)
{
$hordeType = 'tasks';
}
@ -179,7 +179,7 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI;
$hordeType = str_replace('./','',$syncType);
$hordeType = $state->getHordeType($syncType);
Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/list', array()));

View File

@ -27,7 +27,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$syncType = $this->_targetLocURI;
$hordeType = str_replace('./','',$syncType);
$hordeType = $state->getHordeType($syncType);
$refts = $state->getServerAnchorLast($syncType);
$currentCmdID = $this->handleSync($currentCmdID,
@ -79,11 +79,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
}
// Create a replace request for client.
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI);
if(is_a($contentType, 'PEAR_Error')) {
// Client did not sent devinfo
$contentType = array('ContentType' => $state->getPreferedContentType($this->_targetLocURI));
}
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$c = $registry->call($hordeType. '/export',
array('guid' => $guid, 'contentType' => $contentType));
if (!is_a($c, 'PEAR_Error')) {
@ -95,14 +91,19 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$cmd->setSourceURI($guid);
$cmd->setTargetURI($locid);
$cmd->setContentType($contentType['ContentType']);
if($hordeType == 'sifcalendar' || $hordeType == 'sifcontacts' || $hordeType == 'siftasks') {
$cmd->setContentFormat('b64');
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
}
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Replace');
$state->log('Server-Replace');
// return if we have to much data
if(++$counter >= MAX_ENTRIES && $hordeType != 'sifcalendar' && $hordeType != 'sifcontacts' && $hordeType != 'siftasks') {
if (++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
@ -139,8 +140,12 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$state->log('Server-Delete');
$state->removeUID($syncType, $locid);
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
// return if we have to much data
if(++$counter >= MAX_ENTRIES && $hordeType != 'sifcalender' && $hordeType != 'sifcontacts' &&$hordeType != 'siftasks') {
if(++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
@ -180,11 +185,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
Horde::logMessage("SyncML: add: $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Create an Add request for client.
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI);
if(is_a($contentType, 'PEAR_Error')) {
// Client did not sent devinfo
$contentType = array('ContentType' => $state->getPreferedContentType($this->_targetLocURI));
}
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement();
$c = $registry->call($hordeType . '/export',
@ -197,16 +198,20 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
if (!is_a($c, 'PEAR_Error')) {
// Item in history but not in database. Strange, but can happen.
$cmd->setContent($c);
if($hordeType == 'sifcalendar' || $hordeType == 'sifcontacts' || $hordeType == 'siftasks') {
$cmd->setContentFormat('b64');
}
$cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
// return if we have to much data
if(++$counter >= MAX_ENTRIES && $hordeType != 'sifcalendar' && $hordeType != 'sifcontacts' &&$hordeType != 'siftasks') {
if(++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
@ -225,7 +230,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI;
$hordeType = str_replace('./','',$syncType);
$hordeType = $state->getHordeType($syncType);
$refts = $state->getServerAnchorLast($syncType);
Horde::logMessage("SyncML: reading changed items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);

View File

@ -510,18 +510,11 @@ class Horde_iCalendar {
case 'CREATED':
case 'LAST-MODIFIED':
case 'BDAY':
$this->setAttribute($tag, $this->_parseDateTime($value), $params);
break;
case 'DTEND':
case 'DTSTART':
case 'DUE':
case 'RECURRENCE-ID':
if (isset($params['VALUE']) && $params['VALUE'] == 'DATE') {
$this->setAttribute($tag, $this->_parseDate($value), $params);
} else {
$this->setAttribute($tag, $this->_parseDateTime($value), $params);
}
$this->setAttribute($tag, $this->_parseDateTime($value), $params);
break;
case 'RDATE':
@ -902,7 +895,7 @@ class Horde_iCalendar {
if (!$date = $this->_parseDate($text)) {
return $date;
}
return @gmmktime(0, 0, 0, $date['month'], $date['mday'], $date['year']);
return @mktime(0, 0, 0, $date['month'], $date['mday'], $date['year']);
}
if (!$date = $this->_parseDate($dateParts[0])) {
@ -912,6 +905,8 @@ class Horde_iCalendar {
return $time;
}
error_log("parseDateTime: ".$text." => ".print_r($time, true));
if ($time['zone'] == 'UTC') {
return @gmmktime($time['hour'], $time['minute'], $time['second'],
$date['month'], $date['mday'], $date['year']);
@ -1016,7 +1011,12 @@ class Horde_iCalendar {
*/
function _parseDate($text)
{
if (strlen($text) != 8) {
if (strlen($text) == 10)
{
$text = str_replace('-','',$text);
}
if (strlen($text) != 8)
{
return false;
}

View File

@ -18,12 +18,12 @@ $conf['auth']['checkip'] = true;
$conf['auth']['params']['username'] = 'Administrator';
$conf['auth']['params']['requestuser'] = false;
$conf['auth']['driver'] = 'auto';
$conf['log']['priority'] = PEAR_LOG_INFO;
$conf['log']['priority'] = PEAR_LOG_DEBUG;
$conf['log']['ident'] = 'EGWSYNC';
$conf['log']['params'] = array();
$conf['log']['name'] = '/tmp/egroupware_syncml.log';
$conf['log']['params']['append'] = true;
$conf['log']['type'] = 'file';
$conf['log']['type'] = 'error_log';
$conf['log']['enabled'] = true;
$conf['log_accesskeys'] = false;
$conf['prefs']['driver'] = 'none';

View File

@ -77,7 +77,7 @@ $this->applications['egwnotessync'] = array(
'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
'name' => _("Notes"),
'status' => 'active',
'provides' => 'notes',
'provides' => array('notes', 'sifnotes', 'snote'),
'menu_parent' => 'organizing'
);
@ -87,19 +87,19 @@ $this->applications['egwcontactssync'] = array(
'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
'name' => _("Contacts"),
'status' => 'active',
'provides' => 'contacts',
'provides' => array('contacts', 'sifcontacts', 'scard'),
'menu_parent' => 'organizing'
);
$this->applications['egwsifcontactssync'] = array(
'fileroot' => EGW_SERVER_ROOT.'/syncml/sifcontacts',
'webroot' => $this->applications['horde']['webroot'] . '/mnemo',
'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
'name' => _("SIF Contacts"),
'status' => 'active',
'provides' => 'sifcontacts',
'menu_parent' => 'organizing'
);
#$this->applications['egwsifcontactssync'] = array(
# 'fileroot' => EGW_SERVER_ROOT.'/syncml/sifcontacts',
# 'webroot' => $this->applications['horde']['webroot'] . '/mnemo',
# 'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
# 'name' => _("SIF Contacts"),
# 'status' => 'active',
# 'provides' => 'sifcontacts',
# 'menu_parent' => 'organizing'
#);
$this->applications['egwcalendarsync'] = array(
'fileroot' => EGW_SERVER_ROOT.'/syncml/calendar',
@ -107,19 +107,19 @@ $this->applications['egwcalendarsync'] = array(
'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
'name' => _("Calendar"),
'status' => 'active',
'provides' => 'calendar',
'provides' => array('calendar', 'sifcalendar', 'scal'),
'menu_parent' => 'organizing'
);
$this->applications['egwsifcalendarsync'] = array(
'fileroot' => EGW_SERVER_ROOT.'/syncml/sifcalendar',
'webroot' => $this->applications['horde']['webroot'] . '/mnemo',
'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
'name' => _("Calendar"),
'status' => 'active',
'provides' => 'sifcalendar',
'menu_parent' => 'organizing'
);
#$this->applications['egwsifcalendarsync'] = array(
# 'fileroot' => EGW_SERVER_ROOT.'/syncml/sifcalendar',
# 'webroot' => $this->applications['horde']['webroot'] . '/mnemo',
# 'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
# 'name' => _("Calendar"),
# 'status' => 'active',
# 'provides' => 'sifcalendar',
# 'menu_parent' => 'organizing'
#);
$this->applications['egwtaskssync'] = array(
'fileroot' => EGW_SERVER_ROOT.'/syncml/tasks',
@ -127,19 +127,19 @@ $this->applications['egwtaskssync'] = array(
'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
'name' => _("Tasks"),
'status' => 'active',
'provides' => 'tasks',
'provides' => array('tasks', 'siftasks', 'stask'),
'menu_parent' => 'organizing'
);
$this->applications['egwsiftaskssync'] = array(
'fileroot' => EGW_SERVER_ROOT.'/syncml/siftasks',
'webroot' => $this->applications['horde']['webroot'] . '/mnemo',
'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
'name' => _("SIFTasks"),
'status' => 'active',
'provides' => 'siftasks',
'menu_parent' => 'organizing'
);
#$this->applications['egwsiftaskssync'] = array(
# 'fileroot' => EGW_SERVER_ROOT.'/syncml/siftasks',
# 'webroot' => $this->applications['horde']['webroot'] . '/mnemo',
# 'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif',
# 'name' => _("SIFTasks"),
# 'status' => 'active',
# 'provides' => array('siftasks', 'stask'),
# 'menu_parent' => 'organizing'
#);
$this->applications['egwcaltaskssync'] = array(
'fileroot' => EGW_SERVER_ROOT.'/syncml/caltasks',