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 * deletes contact in db
* *
* @param mixed &$contact contact array with key id or (array of) id(s) * @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 * @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'])) if (is_array($contact) && isset($contact['id']))
{ {
@ -413,7 +414,7 @@ class bocontacts extends socontacts
{ {
$id = is_array($c) ? $c['id'] : $c; $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']->link->unlink(0,'addressbook',$id);
$GLOBALS['egw']->contenthistory->updateTimeStamp('contacts', $id, 'delete', time()); $GLOBALS['egw']->contenthistory->updateTimeStamp('contacts', $id, 'delete', time());
@ -438,11 +439,14 @@ class bocontacts extends socontacts
// remember if we add or update a entry // remember if we add or update a entry
if (($isUpdate = $contact['id'])) 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 if (($old = $this->read($contact['id']))) // --> try reading the old entry and set it from there
{
if(!isset($contact['owner']))
{ {
$contact['owner'] = $old['owner']; $contact['owner'] = $old['owner'];
}
if(!isset($contact['private'])) if(!isset($contact['private']))
{ {
$contact['private'] = $old['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 int $needed necessary ACL right: EGW_ACL_{READ|EDIT|DELETE}
* @param mixed $contact contact as array or the contact-id * @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'])) && if ((!is_array($contact) || !isset($contact['owner'])) &&
!($contact = parent::read(is_array($contact) ? $contact['id'] : $contact))) !($contact = parent::read(is_array($contact) ? $contact['id'] : $contact)))
{ {
return false; return null;
} }
$owner = $contact['owner']; $owner = $contact['owner'];
@ -561,7 +566,7 @@ class bocontacts extends socontacts
return true; return true;
} }
// dont allow to delete own account (as admin handels it too) // 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; return false;
} }
@ -1108,4 +1113,86 @@ class bocontacts extends socontacts
//echo "<p>bocontacts::addr_format_by_country('$country'='$code') = '$adr_format'</p>\n"; //echo "<p>bocontacts::addr_format_by_country('$country'='$code') = '$adr_format'</p>\n";
return $adr_format; 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'); $value = $GLOBALS['egw']->translation->convert($value, 'utf-8');
switch($key) { switch($key) {
case 'cat_id': case 'cat_id':
if(!empty($value)) { if(!empty($value))
$isAdmin = $GLOBALS['egw']->acl->check('run',1,'admin'); {
$egwCategories =& CreateObject('phpgwapi.categories', $GLOBALS['egw_info']['user']['account_id'], 'addressbook'); $finalContact[$key] = implode(",", $this->find_or_add_categories(explode(';', $value)));
$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;
}
} }
else
{
$finalContact[$key] = '';
} }
break; break;
case 'private': 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; break;
default: default:
@ -164,6 +154,8 @@ class sifaddressbook extends bocontacts
break; break;
} }
} }
$this->fixup_contact($finalContact);
return $finalContact; return $finalContact;
} }
@ -179,6 +171,9 @@ class sifaddressbook extends bocontacts
{ {
return false; 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))) if(($foundContacts = bocontacts::search($contact)))
{ {
@ -231,6 +226,9 @@ class sifaddressbook extends bocontacts
#error_log(print_r($entry,true)); #error_log(print_r($entry,true));
$sysCharSet = $GLOBALS['egw']->translation->charset(); $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) foreach($this->sifMapping as $sifField => $egwField)
{ {
if(empty($egwField)) continue; if(empty($egwField)) continue;
@ -245,16 +243,8 @@ class sifaddressbook extends bocontacts
// TODO handle multiple categories // TODO handle multiple categories
case 'Categories': case 'Categories':
if(!empty($value)) { if(!empty($value)) {
$egwCategories =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'addressbook'); $value = implode("; ", $this->get_categories($value));
$categories = explode(',',$value); $value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
$value = '';
foreach($categories as $cat_id) {
if(($catData = $egwCategories->return_single($cat_id)))
{
if(!empty($value)) $value .= '; ';
$value .= $catData[0]['name'];
}
}
} }
$sifContact .= "<$sifField>$value</$sifField>"; $sifContact .= "<$sifField>$value</$sifField>";
break; break;
@ -270,7 +260,7 @@ class sifaddressbook extends bocontacts
break; break;
default: default:
$sifContact .= "<$sifField>$value</$sifField>"; $sifContact .= "<$sifField>".trim($value)."</$sifField>";
break; break;
} }
} }

View File

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

View File

@ -121,6 +121,21 @@ class bocalupdate extends bocal
!$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner'])) && !$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner'])) &&
!$this->check_perms(EGW_ACL_ADD,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; return false;
} }
// check for conflicts only happens !$ignore_conflicts AND if start + end date are given // 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); 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

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

View File

@ -71,7 +71,7 @@
} }
function endElement($_parser, $_tag) { function endElement($_parser, $_tag) {
#error_log($_tag .' => '. $this->sifData); //error_log('endElem: ' . $_tag .' => '. trim($this->sifData));
if(!empty($this->sifMapping[$_tag])) { if(!empty($this->sifMapping[$_tag])) {
$this->event[$this->sifMapping[$_tag]] = trim($this->sifData); $this->event[$this->sifMapping[$_tag]] = trim($this->sifData);
} }
@ -128,19 +128,7 @@
case 'category': case 'category':
if(!empty($value)) { if(!empty($value)) {
$egwCategories =& CreateObject('phpgwapi.categories', $GLOBALS['egw_info']['user']['account_id'], 'calendar'); $finalEvent[$key] = implode(',',$this->find_or_add_categories(explode(';', $value)));
$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;
}
}
} }
break; break;
@ -148,6 +136,7 @@
case 'start': case 'start':
if($this->event['alldayevent'] < 1) { if($this->event['alldayevent'] < 1) {
$finalEvent[$key] = $vcal->_parseDateTime($value); $finalEvent[$key] = $vcal->_parseDateTime($value);
error_log("event ".$key." val=".$value.", parsed=".$finalEvent[$key]);
} }
break; break;
@ -348,15 +337,8 @@
{ {
case 'Categories': case 'Categories':
if(!empty($value)) { if(!empty($value)) {
$egwCategories =& CreateObject('phpgwapi.categories',$GLOBALS['egw_info']['user']['account_id'],'calendar'); $value = implode('; ', $this->get_categories(explode(',',$value)));
$categories = explode(',',$value); $value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
$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');
}
}
} }
$sifEvent .= "<$sifField>$value</$sifField>"; $sifEvent .= "<$sifField>$value</$sifField>";
break; break;

View File

@ -1270,6 +1270,74 @@ class boinfolog
return $icons; 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 * Send all async infolog notification
* *
@ -1351,3 +1419,4 @@ class boinfolog
$GLOBALS['egw_info']['user']['preferences'] = $save_prefs; $GLOBALS['egw_info']['user']['preferences'] = $save_prefs;
} }
} }

View File

@ -21,12 +21,24 @@
// array containing the current mappings(task or note) // array containing the current mappings(task or note)
var $_currentSIFMapping; 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 // mappings for SIFTask to InfologTask
var $_sifTaskMapping = array( var $_sifTaskMapping = array(
'ActualWork' => '', 'ActualWork' => '',
'BillingInformation' => '', 'BillingInformation' => '',
'Body' => 'info_des', 'Body' => 'info_des',
'Categories' => '', 'Categories' => 'info_cat',
'Companies' => '', 'Companies' => '',
'Complete' => '', 'Complete' => '',
'DateCompleted' => 'info_datecompleted', 'DateCompleted' => 'info_datecompleted',
@ -61,8 +73,9 @@
} }
function endElement($_parser, $_tag) { function endElement($_parser, $_tag) {
error_log("infolog: tag=$_tag data=".trim($this->sifData));
if(!empty($this->_currentSIFMapping[$_tag])) { if(!empty($this->_currentSIFMapping[$_tag])) {
$this->_extractedSIFData[$this->_currentSIFMapping[$_tag]] = $this->sifData; $this->_extractedSIFData[$this->_currentSIFMapping[$_tag]] = trim($this->sifData);
} }
unset($this->sifData); unset($this->sifData);
} }
@ -81,7 +94,18 @@
#fwrite($handle, $sifData); #fwrite($handle, $sifData);
#fclose($handle); #fclose($handle);
switch ($_sifType)
{
case 'note':
$this->_currentSIFMapping = $this->_sifNoteMapping;
break;
case 'task':
default:
$this->_currentSIFMapping = $this->_sifTaskMapping; $this->_currentSIFMapping = $this->_sifTaskMapping;
break;
}
$this->xml_parser = xml_parser_create('UTF-8'); $this->xml_parser = xml_parser_create('UTF-8');
xml_set_object($this->xml_parser, $this); xml_set_object($this->xml_parser, $this);
xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, false); xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, false);
@ -108,6 +132,8 @@
foreach($this->_extractedSIFData as $key => $value) { foreach($this->_extractedSIFData as $key => $value) {
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet); $value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
error_log("infolog key=$key => value=$value");
switch($key) { switch($key) {
case 'info_access': case 'info_access':
$taskData[$key] = ((int)$value > 0) ? 'private' : 'public'; $taskData[$key] = ((int)$value > 0) ? 'private' : 'public';
@ -127,6 +153,13 @@
break; break;
case 'info_cat':
if (!empty($value)) {
$categories = $this->find_or_add_categories(explode(';', $value));
$taskData['info_cat'] = $categories[0];
}
break;
case 'info_priority': case 'info_priority':
$taskData[$key] = (int)$value; $taskData[$key] = (int)$value;
break; break;
@ -156,11 +189,52 @@
$taskData[$key] = $value; $taskData[$key] = $value;
break; break;
} }
error_log("infolog task key=$key => value=".$taskData[$key]);
} }
return $taskData; return $taskData;
break; 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: default:
return false; return false;
} }
@ -251,6 +325,15 @@
$sifTask .= "<$sifField>$value</$sifField>"; $sifTask .= "<$sifField>$value</$sifField>";
break; 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: default:
$sifTask .= "<$sifField>$value</$sifField>"; $sifTask .= "<$sifField>$value</$sifField>";
break; break;
@ -296,6 +379,52 @@
} }
break; 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; default;
return false; return false;
} }

View File

@ -186,7 +186,7 @@ class soinfolog // DB-Layer
*/ */
function aclFilter($filter = False) 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]; $filter = $vars[1];
$f_user = intval($vars[2]); $f_user = intval($vars[2]);
@ -230,7 +230,7 @@ class soinfolog // DB-Layer
$filtermethod .= " OR (".$this->responsible_filter($this->user)." AND info_access='public')"; $filtermethod .= " OR (".$this->responsible_filter($this->user)." AND info_access='public')";
// private: own entries plus the one user is responsible for // 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). $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 ($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'); $this->status2vtodo[$taskData['info_status']] : 'NEEDS-ACTION');
$vevent->setAttribute('PRIORITY',$this->egw_priority2vcal_priority[$taskData['info_priority']]); $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'); #$vevent->setAttribute('TRANSP','OPAQUE');
# status # status
# ATTENDEE # ATTENDEE
@ -122,7 +128,8 @@
return $this->write($taskData); return $this->write($taskData);
} }
function searchVTODO($_vcalData) { function searchVTODO($_vcalData)
{
if(!$egwData = $this->vtodotoegw($_vcalData)) { if(!$egwData = $this->vtodotoegw($_vcalData)) {
return false; return false;
} }
@ -140,7 +147,8 @@
return false; return false;
} }
function vtodotoegw($_vcalData) { function vtodotoegw($_vcalData)
{
$vcal = &new Horde_iCalendar; $vcal = &new Horde_iCalendar;
if(!$vcal->parsevCalendar($_vcalData)) { if(!$vcal->parsevCalendar($_vcalData)) {
return FALSE; return FALSE;
@ -196,6 +204,13 @@
case 'SUMMARY': case 'SUMMARY':
$taskData['info_subject'] = $attributes['value']; $taskData['info_subject'] = $attributes['value'];
break; 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 # the horde ical class does already convert in parsevCalendar
@ -207,4 +222,147 @@
} }
return FALSE; 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 =& CreateObject('phpgwapi.contacts');
} }
$GLOBALS['egw']->contacts->delete($contact_id); $GLOBALS['egw']->contacts->delete($contact_id,false); // false = allow to delete accounts (!)
} }
return true; return true;
} }

View File

@ -169,8 +169,10 @@
if(empty($_globalUid)) return false; if(empty($_globalUid)) return false;
$globalUidParts = explode('-',$_globalUid); $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 // 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) { switch($element) {
case 'CTType': case 'CTType':
$this->_contentType = trim($this->_chars); $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; break;
case 'SyncType': case 'SyncType':

View File

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

View File

@ -111,6 +111,10 @@ class Horde_SyncML_Command_Sync extends Horde_Syncml_Command {
{ {
$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(), 'Sync', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs); $output->startElement($state->getURI(), 'CmdID', $attrs);
$output->characters($currentCmdID); $output->characters($currentCmdID);

View File

@ -655,26 +655,80 @@ class Horde_SyncML_State {
/** /**
* This function should use DevINF information. * This function should use DevINF information.
*/ */
function getPreferedContentType($type) { function adjustContentType($type, $target = null)
switch($type) { {
$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': case 'contacts':
case './contacts': $res['mayFragment'] = 1;
return 'text/x-vcard';
break; break;
case 'sifcalendar': case 'sifcalendar':
case './sifcalendar':
return 'text/x-s4j-sife';
break;
case 'sifcontacts':
case './sifcontacts':
return 'text/x-s4j-sifc';
break;
case 'siftasks': case 'siftasks':
case './siftasks': case 'sifnotes':
return 'text/x-s4j-sift'; 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':
return 'text/x-vcard';
break; break;
case 'notes': case 'notes':
@ -686,12 +740,82 @@ class Horde_SyncML_State {
break; break;
case 'calendar': case 'calendar':
case './calendar':
return 'text/x-vcalendar'; return 'text/x-vcalendar';
break; 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 * Returns the preferred contenttype of the client for the given
* sync data type (database). * sync data type (database).
@ -699,15 +823,21 @@ class Horde_SyncML_State {
* This is passed as an option to the Horde API export functions. * This is passed as an option to the Horde API export functions.
*/ */
function getPreferedContentTypeClient($_sourceLocURI) { function getPreferedContentTypeClient($_sourceLocURI, $_targetLocURI = null) {
$deviceInfo = $this->getClientDeviceInfo(); $deviceInfo = $this->getClientDeviceInfo();
if(isset($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType'])) { if(isset($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType']))
return array('ContentType' => $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); 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')); return PEAR::raiseError(_('sourceLocURI not found'));
} }

View File

@ -141,13 +141,14 @@ class Horde_SyncML_Sync {
} }
$hordeType = $type = $this->_targetLocURI; $hordeType = $type = $this->_targetLocURI;
// remove the './' from the beginning $hordeType = $state->getHordeType($hordeType);
$hordeType = str_replace('./','',$hordeType);
if(!$contentType = $command->getContentType()) { if(!$contentType = $command->getContentType()) {
$contentType = $state->getPreferedContentType($type); $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'; $hordeType = 'tasks';
} }

View File

@ -36,28 +36,27 @@ class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySy
continue; continue;
} }
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI); $contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
if(is_a($contentType, 'PEAR_Error')) {
// Client did not sent devinfo
$contentType = array('ContentType' => $state->getPreferedContentType($this->_targetLocURI));
}
$cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement(); $cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement();
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType)); $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); Horde::logMessage("SyncML: slowsync add $guid to client ". print_r($c, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!is_a($c, 'PEAR_Error')) { if (!is_a($c, 'PEAR_Error')) {
$cmd->setContent($c); $cmd->setContent($c);
if($hordeType == 'sifcalendar' || $hordeType == 'sifcontacts' || $hordeType == 'siftasks') { $cmd->setContentType($contentType['ContentType']);
$cmd->setContentFormat('b64'); if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
} }
$cmd->setContentType($contentType['ContentType']);
$cmd->setSourceURI($guid); $cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add'); $currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add'); $state->log('Server-Add');
// return if we have to much data // 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); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; return $currentCmdID;
} }
@ -77,7 +76,7 @@ class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySy
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI; $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); Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/list', array())); $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; continue;
} }
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI); $contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
if(is_a($contentType, 'PEAR_Error')) {
// Client did not sent devinfo
$contentType = array('ContentType' => $state->getPreferedContentType($this->_targetLocURI));
}
$cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement(); $cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement();
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType)); $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); #Horde::logMessage("SyncML: slowsync add guid $guid to client ". print_r($c, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!is_a($c, 'PEAR_Error')) { if (!is_a($c, 'PEAR_Error')) {
$cmd->setContent($c); $cmd->setContent($c);
if($hordeType == 'sifcalendar' || $hordeType == 'sifcontacts' || $hordeType == 'siftasks') { $cmd->setContentType($contentType['ContentType']);
$cmd->setContentFormat('b64'); if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
} }
$cmd->setContentType($contentType['ContentType']);
$cmd->setSourceURI($guid); $cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add'); $currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add'); $state->log('Server-Add');
// return if we have to much data // 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); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; return $currentCmdID;
} }
@ -129,9 +128,8 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
return; return;
} }
$hordeType = $type = $this->_targetLocURI; $type = $this->_targetLocURI;
// remove the './' from the beginning $hordeType = $state->getHordeType($type);
$hordeType = str_replace('./','',$hordeType);
$syncElementItems = $command->getSyncElementItems(); $syncElementItems = $command->getSyncElementItems();
@ -140,7 +138,9 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$contentType = $state->getPreferedContentType($type); $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'; $hordeType = 'tasks';
} }
@ -179,7 +179,7 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI; $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); Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/list', array())); $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; $syncType = $this->_targetLocURI;
$hordeType = str_replace('./','',$syncType); $hordeType = $state->getHordeType($syncType);
$refts = $state->getServerAnchorLast($syncType); $refts = $state->getServerAnchorLast($syncType);
$currentCmdID = $this->handleSync($currentCmdID, $currentCmdID = $this->handleSync($currentCmdID,
@ -79,11 +79,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
} }
// Create a replace request for client. // Create a replace request for client.
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI); $contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
if(is_a($contentType, 'PEAR_Error')) {
// Client did not sent devinfo
$contentType = array('ContentType' => $state->getPreferedContentType($this->_targetLocURI));
}
$c = $registry->call($hordeType. '/export', $c = $registry->call($hordeType. '/export',
array('guid' => $guid, 'contentType' => $contentType)); array('guid' => $guid, 'contentType' => $contentType));
if (!is_a($c, 'PEAR_Error')) { if (!is_a($c, 'PEAR_Error')) {
@ -95,14 +91,19 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$cmd->setSourceURI($guid); $cmd->setSourceURI($guid);
$cmd->setTargetURI($locid); $cmd->setTargetURI($locid);
$cmd->setContentType($contentType['ContentType']); $cmd->setContentType($contentType['ContentType']);
if($hordeType == 'sifcalendar' || $hordeType == 'sifcontacts' || $hordeType == 'siftasks') { if (isset($contentType['ContentFormat']))
$cmd->setContentFormat('b64'); {
$cmd->setContentFormat($contentType['ContentFormat']);
} }
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Replace'); $currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Replace');
$state->log('Server-Replace'); $state->log('Server-Replace');
// return if we have to much data // 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); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; return $currentCmdID;
} }
@ -139,8 +140,12 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$state->log('Server-Delete'); $state->log('Server-Delete');
$state->removeUID($syncType, $locid); $state->removeUID($syncType, $locid);
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
// return if we have to much data // 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); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; 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); Horde::logMessage("SyncML: add: $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Create an Add request for client. // Create an Add request for client.
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI); $contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
if(is_a($contentType, 'PEAR_Error')) {
// Client did not sent devinfo
$contentType = array('ContentType' => $state->getPreferedContentType($this->_targetLocURI));
}
$cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement(); $cmd = &new Horde_SyncML_Command_Sync_ContentSyncElement();
$c = $registry->call($hordeType . '/export', $c = $registry->call($hordeType . '/export',
@ -197,16 +198,20 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
if (!is_a($c, 'PEAR_Error')) { if (!is_a($c, 'PEAR_Error')) {
// Item in history but not in database. Strange, but can happen. // Item in history but not in database. Strange, but can happen.
$cmd->setContent($c); $cmd->setContent($c);
if($hordeType == 'sifcalendar' || $hordeType == 'sifcontacts' || $hordeType == 'siftasks') {
$cmd->setContentFormat('b64');
}
$cmd->setContentType($contentType['ContentType']); $cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setSourceURI($guid); $cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add'); $currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add'); $state->log('Server-Add');
// return if we have to much data // 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); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; return $currentCmdID;
} }
@ -225,7 +230,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI; $syncType = $this->_targetLocURI;
$hordeType = str_replace('./','',$syncType); $hordeType = $state->getHordeType($syncType);
$refts = $state->getServerAnchorLast($syncType); $refts = $state->getServerAnchorLast($syncType);
Horde::logMessage("SyncML: reading changed items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG); 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 'CREATED':
case 'LAST-MODIFIED': case 'LAST-MODIFIED':
case 'BDAY': case 'BDAY':
$this->setAttribute($tag, $this->_parseDateTime($value), $params);
break;
case 'DTEND': case 'DTEND':
case 'DTSTART': case 'DTSTART':
case 'DUE': case 'DUE':
case 'RECURRENCE-ID': 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; break;
case 'RDATE': case 'RDATE':
@ -902,7 +895,7 @@ class Horde_iCalendar {
if (!$date = $this->_parseDate($text)) { if (!$date = $this->_parseDate($text)) {
return $date; 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])) { if (!$date = $this->_parseDate($dateParts[0])) {
@ -912,6 +905,8 @@ class Horde_iCalendar {
return $time; return $time;
} }
error_log("parseDateTime: ".$text." => ".print_r($time, true));
if ($time['zone'] == 'UTC') { if ($time['zone'] == 'UTC') {
return @gmmktime($time['hour'], $time['minute'], $time['second'], return @gmmktime($time['hour'], $time['minute'], $time['second'],
$date['month'], $date['mday'], $date['year']); $date['month'], $date['mday'], $date['year']);
@ -1016,7 +1011,12 @@ class Horde_iCalendar {
*/ */
function _parseDate($text) function _parseDate($text)
{ {
if (strlen($text) != 8) { if (strlen($text) == 10)
{
$text = str_replace('-','',$text);
}
if (strlen($text) != 8)
{
return false; return false;
} }

View File

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

View File

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