Apply Addressbook SyncML and GroupDAV changes

This commit is contained in:
Jörg Lehrke 2010-03-07 12:48:10 +00:00
parent 7bb92a86af
commit f499955670
6 changed files with 368 additions and 160 deletions

View File

@ -18,12 +18,6 @@
*/
class addressbook_bo extends addressbook_so
{
/**
* @var int $tz_offset_s offset in secconds between user and server-time,
* it need to be add to a server-time to get the user-time or substracted from a user-time to get the server-time
*/
var $tz_offset_s;
/**
* @var int $now_su actual user (!) time
*/
@ -95,6 +89,13 @@ class addressbook_bo extends addressbook_so
var $business_contact_fields = array();
var $home_contact_fields = array();
/**
* Set Logging
*
* @var boolean
*/
var $log = false;
/**
* Number and message of last error or false if no error, atm. only used for saving
*
@ -129,9 +130,7 @@ class addressbook_bo extends addressbook_so
function __construct($contact_app='addressbook')
{
parent::__construct($contact_app);
$this->tz_offset_s = 3600 * $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'];
$this->now_su = time() + $this->tz_offset_s;
$this->now_su = egw_time::to('now','ts');
$this->prefs =& $GLOBALS['egw_info']['user']['preferences']['addressbook'];
// get the default addressbook from the users prefs
@ -402,7 +401,7 @@ class addressbook_bo extends addressbook_so
*/
function set_all_fileas($fileas_type,$all=false,&$errors=null,$ignore_acl=false)
{
if ($type != '' && !in_array($type,$this->fileas_types))
if ($fileas_type != '' && !in_array($fileas_type, $this->fileas_types))
{
return false;
}
@ -574,15 +573,18 @@ class addressbook_bo extends addressbook_so
* This function needs to be reimplemented in the derived class
*
* @param array $data
* @param $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, 'array'=array or string with date-format
*
* @return array updated data
*/
function db2data($data)
function db2data($data, $date_format='ts')
{
// convert timestamps from server-time in the db to user-time
foreach($this->timestamps as $name)
foreach ($this->timestamps as $name)
{
if(isset($data[$name]))
if (isset($data[$name]))
{
$data[$name] += $this->tz_offset_s;
$data[$name] = egw_time::server2user($data[$name], $date_format);
}
}
$data['photo'] = $this->photo_src($data['id'],$data['jpegphoto']);
@ -620,15 +622,18 @@ class addressbook_bo extends addressbook_so
* this needs to be reimplemented in the derived class
*
* @param array $data
* @param $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, 'array'=array or string with date-format
*
* @return array upated data
*/
function data2db($data)
function data2db($data, $date_format='ts')
{
// convert timestamps from user-time to server-time in the db
foreach($this->timestamps as $name)
foreach ($this->timestamps as $name)
{
if(isset($data[$name]))
if (isset($data[$name]))
{
$data[$name] -= $this->tz_offset_s;
$data[$name] = egw_time::server2user($data[$name], $date_format);
}
}
return $data;
@ -730,11 +735,11 @@ class addressbook_bo extends addressbook_so
$contact['modifier'] = $this->user;
$contact['modified'] = $this->now_su;
// set full name and fileas from the content
if (strlen($fullname = $this->fullname($contact)) > 0 && (!isset($contact['n_fn']) || $contact['n_fn'] != $fullname)) {
$contact['n_fn'] = $fullname;
if (!isset($contact['n_fn']))
{
$contact['n_fn'] = $this->fullname($contact);
if (isset($contact['org_name'])) $contact['n_fileas'] = $this->fileas($contact);
}
unset($fullname);
$to_write = $contact;
// (non-admin) user editing his own account, make sure he does not change fields he is not allowed to (eg. via SyncML or xmlrpc)
if (!$ignore_acl && !$contact['owner'] && !$this->is_admin($contact))
@ -773,8 +778,8 @@ class addressbook_bo extends addressbook_so
/**
* reads contacts matched by key and puts all cols in the data array
*
* @param int/string $contact_id
* @return array/boolean array with contact data, null if not found or false on no view perms
* @param int|string $contact_id
* @return array|boolean array with contact data, null if not found or false on no view perms
*/
function read($contact_id)
{
@ -1649,38 +1654,55 @@ class addressbook_bo extends addressbook_so
*
* @param array $contact the contact data we try to find
* @param boolean $relax=false if asked to relax, we only match against some key fields
* @return the contact_id of the matching entry or false (if none matches)
* @return array od matching contact_ids
*/
function find_contact($contact, $relax=false)
{
if (!empty($contact['uid']))
if ($this->log)
{
// Try the given UID first
Horde::logMessage('Addressbook find UID: '. $contact['uid'],
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$criteria = array ('contact_uid' => $contact['uid']);
if (($found = parent::search($criteria))
&& ($uidmatch = array_shift($found)))
{
return $uidmatch['contact_id'];
}
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '('. ($relax ? 'RELAX': 'EXACT') . ')[ContactData]:'
. array2string($contact));
}
unset($contact['uid']);
$matchingContacts = array();
if ($contact['id'] && ($found = $this->read($contact['id'])))
{
// We only do a simple consistency check
Horde::logMessage('Addressbook find ID: '. $contact['id'],
__FILE__, __LINE__, PEAR_LOG_DEBUG);
if ((empty($found['n_family']) || $found['n_family'] == $contact['n_family'])
&& (empty($found['n_given']) || $found['n_given'] == $contact['n_given'])
&& (empty($found['org_name']) || $found['org_name'] == $contact['org_name']))
if ($this->log)
{
return $found['id'];
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '()[ContactID]: ' . $contact['id']);
}
// We only do a simple consistency check
if (!$relax || ((empty($found['n_family']) || $found['n_family'] == $contact['n_family'])
&& (empty($found['n_given']) || $found['n_given'] == $contact['n_given'])
&& (empty($found['org_name']) || $found['org_name'] == $contact['org_name'])))
{
return array($found['id']);
}
}
unset($contact['id']);
if (!$relax && !empty($contact['uid']))
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '()[ContactUID]: ' . $contact['uid']);
}
// Try the given UID first
$criteria = array ('contact_uid' => $contact['uid']);
if (($foundContacts = parent::search($criteria)))
{
foreach ($foundContacts as $egwContact)
{
$matchingContacts[] = $egwContact['id'];
}
}
return $matchingContacts;
}
unset($contact['uid']);
$columns_to_search = array('n_family', 'n_given', 'n_middle', 'n_prefix', 'n_suffix',
'bday', 'org_name', 'org_unit', 'title', 'role',
'email', 'email_home');
@ -1801,28 +1823,47 @@ class addressbook_bo extends addressbook_so
}
}
}
Horde::logMessage("Addressbook find step 1:\n" . print_r($criteria, true) .
"filter:\n" . print_r($filter, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '()[Addressbook FIND Step 1]: '
. 'FILTER:' . array2string($filter)
. 'CRITERIA' . array2string($criteria));
}
// first try full match
if (($foundContacts = parent::search($criteria, true, '', '', '', False, 'AND', false, $filter)))
{
$result = $foundContacts[0]['id'];
foreach ($foundContacts as $egwContact)
{
$matchingContacts[] = $egwContact['id'];
}
}
// No need for more searches for relaxed matching
if (!$relax && !$result && !$this->all_empty($contact, $addr_one_fields)
if (!$relax || count($matchingContacts)) return $matchingContacts;
if (!$this->all_empty($contact, $addr_one_fields)
&& $this->all_empty($contact, $addr_two_fields))
{
// try given address and ignore the second one in EGW
$filter = array_diff($filter, $no_addr_two);
Horde::logMessage("Addressbook find step 2:\n" . print_r($criteria, true) .
"filter:\n" . print_r($filter, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '()[Addressbook FIND Step 2]: '
. 'FILTER:' . array2string($filter)
. 'CRITERIA' . array2string($criteria));
}
if (($foundContacts = parent::search($criteria, true, '', '', '', False, 'AND', false, $filter)))
{
$result = $foundContacts[0]['id'];
foreach ($foundContacts as $egwContact)
{
$matchingContacts[] = $egwContact['id'];
}
}
else
{
@ -1847,16 +1888,25 @@ class addressbook_bo extends addressbook_so
$filter = $filter + $no_addr_one;
Horde::logMessage("Addressbook find step 3:\n" . print_r($criteria, true) .
"filter:\n" . print_r($filter, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '()[Addressbook FIND Step 3]: '
. 'FILTER:' . array2string($filter)
. 'CRITERIA' . array2string($criteria));
}
if (($foundContacts = parent::search($criteria, true, '', '', '', False, 'AND', false, $filter)))
{
$result = $foundContacts[0]['id'];
foreach ($foundContacts as $egwContact)
{
$matchingContacts[] = $egwContact['id'];
}
}
}
}
elseif (!$relax && !$result)
{ // No more searches for relaxed matching, try again after address swap
else
{ // try again after address swap
$filter = $empty_columns;
@ -1895,15 +1945,26 @@ class addressbook_bo extends addressbook_so
$filter[] = "(" . $db_col . " IS NULL OR " . $db_col . " = '')";
}
}
Horde::logMessage("Addressbook find step 4:\n" . print_r($criteria, true) .
"filter:\n" . print_r($filter, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '()[Addressbook FIND Step 4]: '
. 'FILTER:' . array2string($filter)
. 'CRITERIA' . array2string($criteria));
}
if(($foundContacts = parent::search($criteria, true, '', '', '', False, 'AND', false, $filter)))
{
$result = $foundContacts[0]['id'];
foreach ($foundContacts as $egwContact)
{
$matchingContacts[] = $egwContact['id'];
}
}
}
return $result;
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '()[FOUND]:' . array2string($matchingContacts));
}
return $matchingContacts;
}
}

View File

@ -51,10 +51,11 @@ class addressbook_groupdav extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/
function __construct($app,$debug=null,$base_uri=null)
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{
parent::__construct($app,$debug,$base_uri);
parent::__construct($app,$debug,$base_uri,$principalURL);
$this->bo = new addressbook_bo();
}
@ -67,7 +68,7 @@ class addressbook_groupdav extends groupdav_handler
*/
static function get_path($contact)
{
return '/addressbook/'.$contact[self::PATH_ATTRIBUTE].'.vcf';
return $contact[self::PATH_ATTRIBUTE].'.vcf';
}
/**
@ -95,8 +96,9 @@ class addressbook_groupdav extends groupdav_handler
}
if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user,$id) filter=".array2string($filter));
// check if we have to return the full calendar data or just the etag's
if (!($filter['address_data'] = $options['props'] == 'all' && $options['root']['ns'] == groupdav::CARDDAV) && is_array($options['props']))
// check if we have to return the full contact data or just the etag's
if (!($filter['address_data'] = $options['props'] == 'all' &&
$options['root']['ns'] == groupdav::CARDDAV) && is_array($options['props']))
{
foreach($options['props'] as $prop)
{
@ -108,7 +110,7 @@ class addressbook_groupdav extends groupdav_handler
}
}
// return iterator, calling ourself to return result in chunks
$files['files'] = new groupdav_propfind_iterator($this,$filter,$files['files']);
$files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']);
return true;
}
@ -116,11 +118,12 @@ class addressbook_groupdav extends groupdav_handler
/**
* Callback for profind interator
*
* @param string $path
* @param array $filter
* @param array|boolean $start=false false=return all or array(start,num)
* @return array with "files" array with values for keys path and props
*/
function &propfind_callback(array $filter,$start=false)
function &propfind_callback($path,array $filter,$start=false)
{
$starttime = microtime(true);
@ -129,7 +132,6 @@ class addressbook_groupdav extends groupdav_handler
$handler = self::_get_handler();
}
unset($filter['address_data']);
$files = array();
// we query etag and modified, as LDAP does not have the strong sql etag
if (($contacts =& $this->bo->search(array(),$address_data ? false : array('id','uid','etag','modified'),'contact_id','','',False,'AND',$start,$filter)))
@ -138,27 +140,27 @@ class addressbook_groupdav extends groupdav_handler
{
$props = array(
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($contact)),
HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/x-vcard'),
HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'),
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $contact['modified']),
);
if ($address_data)
{
$content = $handler->getVCard($contact,$this->charset,false);
$content = $handler->getVCard($contact['id'],$this->charset,false);
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content);
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content,true);
}
else
{
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it
}
$files[] = array(
'path' => self::get_path($contact),
'path' => $path.self::get_path($contact),
'props' => $props,
);
}
}
if ($this->debug) error_log(__METHOD__.'('.array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items');
if ($this->debug) error_log(__METHOD__."($path,".array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items');
return $files;
}
@ -265,22 +267,70 @@ class addressbook_groupdav extends groupdav_handler
*/
function put(&$options,$id,$user=null)
{
$ok = $this->_common_get_put_delete('PUT',$options,$id);
if (!is_null($ok) && !is_array($ok))
{
return $ok;
}
$handler = self::_get_handler();
$contact = $handler->vcardtoegw($options['content']);
if ($this->debug) error_log(__METHOD__.'('.array2string($options).",$id,$user)");
if (!is_null($ok))
$oldContact = $this->_common_get_put_delete('PUT',$options,$id);
if (!is_null($oldContact) && !is_array($oldContact))
{
$contact['id'] = $ok['id'];
// dont allow the client to overwrite certain values
$contact['uid'] = $ok['uid'];
$contact['owner'] = $ok['owner'];
$contact['private'] = $ok['private'];
return $oldContact;
}
$handler = self::_get_handler();
$vCard = htmlspecialchars_decode($options['content']);
if (is_array($oldContact))
{
$contactId = $oldContact['id'];
$retval = true;
}
else
{
// new entry?
if (($foundContacts = $handler->search($vCard)))
{
if (($contactId = array_shift($foundContacts)) &&
($oldContact = $this->bo->read($contactId)))
{
$retval = '301 Moved Permanently';
}
else
{
// to be safe
$contactId = -1;
$retval = '201 Created';
}
}
else
{
// new entry
$contactId = -1;
$retval = '201 Created';
}
}
$contact = $handler->vcardtoegw($vCard);
if (is_array($contact['category']))
{
$contact['category'] = implode(',',$this->bo->find_or_add_categories($contact['category'], $contactId));
}
elseif ($contactId > 0)
{
$contact['category'] = $oldContact['category'];
}
if (is_array($oldContact))
{
$contact['id'] = $oldContact['id'];
// dont allow the client to overwrite certain values
$contact['uid'] = $oldContact['uid'];
$contact['owner'] = $oldContact['owner'];
$contact['private'] = $oldContact['private'];
}
else
{
$contact['owner'] = $user;
}
if ($this->http_if_match) $contact['etag'] = self::etag2value($this->http_if_match);
if (!($save_ok = $this->bo->save($contact)))
@ -292,24 +342,26 @@ class addressbook_groupdav extends groupdav_handler
}
return false;
}
if (!isset($contact['etag']))
{
$contact = $this->read($contact['id']);
$contact = $this->read($save_ok);
}
header('ETag: '.$this->get_etag($contact));
if (is_null($ok))
if ($retval !== true)
{
header($h='Location: '.$this->base_uri.self::get_path($contact));
if ($this->debug) error_log(__METHOD__."($method,,$id) header('$h'): 201 Created");
return '201 Created';
$path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']);
header($h='Location: '.$this->base_uri.$path.self::get_path($contact));
if ($this->debug) error_log(__METHOD__."($method,,$id) header('$h'): $retval");
return $retval;
}
return true;
}
/**
* Query ctag for addressbook
*
*
* @return string
*/
public function getctag($path,$user)
@ -319,10 +371,40 @@ class addressbook_groupdav extends groupdav_handler
if ($user && $path != '/addressbook/') $filter['contact_owner'] = $user;
// should we hide the accounts addressbook
if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null;
$result = $this->bo->search(array(),'MAX(contact_modified) AS contact_modified','','','','','',$filter);
return '"'.$result[0]['modified'].'"';
$ctag = 'EGw-'.$result[0]['modified'].'-wGE';
return $ctag;
}
/**
* Add the privileges of the current user
*
* @param array $props=array() regular props by the groupdav handler
* @return array
*/
static function current_user_privilege_set(array $props=array())
{
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::DAV,'current-user-privilege-set',
array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'privilege',
array(//HTTP_WebDAV_Server::mkprop(groupdav::DAV,'all',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read',''),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'read-free-busy',''),
//HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read-current-user-privilege-set',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'bind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'unbind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-properties',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-content',''),
))));
return $props;
}
/**
@ -342,12 +424,19 @@ class addressbook_groupdav extends groupdav_handler
* </supported-report>
* </D:supported-report-set>
* @link http://www.mail-archive.com/calendarserver-users@lists.macosforge.org/msg01156.html
*
*
* @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @param string $base_uri=null base url of handler
* @return array
*/
static function extra_properties(array $props=array())
static function extra_properties(array $props=array(), $displayname, $base_uri=null)
{
// addressbook description
$displayname = $GLOBALS['egw']->translation->convert(lang('Addressbook of') . ' ' .
$displayname,
$GLOBALS['egw']->translation->charset(),'utf-8');
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-description',$displayname);
// supported reports (required property for CardDAV)
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
@ -357,6 +446,7 @@ class addressbook_groupdav extends groupdav_handler
HTTP_WebDAV_Server::mkprop('report',
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-multiget','')))),
));
//$props = self::current_user_privilege_set($props);
return $props;
}
@ -390,7 +480,7 @@ class addressbook_groupdav extends groupdav_handler
{
return '412 Precondition Failed';
}
return $ok;
//return $ok;
}
/**

View File

@ -99,21 +99,26 @@ class addressbook_sif extends addressbook_bo
const xml_decl = '<?xml version="1.0" encoding="UTF-8"?>';
const SIF_decl = '<SIFVersion>1.1</SIFVersion>';
function startElement($_parser, $_tag, $_attributes) {
function startElement($_parser, $_tag, $_attributes)
{
}
function endElement($_parser, $_tag) {
if(!empty($this->sifMapping[$_tag])) {
function endElement($_parser, $_tag)
{
if (!empty($this->sifMapping[$_tag]))
{
$this->contact[$this->sifMapping[$_tag]] = trim($this->sifData);
}
unset($this->sifData);
}
function characterData($_parser, $_data) {
function characterData($_parser, $_data)
{
$this->sifData .= $_data;
}
function siftoegw($sifData, $_abID=null) {
function siftoegw($sifData, $_abID=null)
{
#$tmpfname = tempnam('/tmp/sync/contents','sifc_');
@ -129,24 +134,26 @@ class addressbook_sif extends addressbook_bo
xml_set_element_handler($this->xml_parser, "startElement", "endElement");
xml_set_character_data_handler($this->xml_parser, "characterData");
$this->strXmlData = xml_parse($this->xml_parser, $sifData);
if(!$this->strXmlData) {
if (!$this->strXmlData)
{
error_log(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->xml_parser)),
xml_get_current_line_number($this->xml_parser)));
return false;
}
foreach($this->contact as $key => $value) {
foreach ($this->contact as $key => $value)
{
$value = preg_replace('/<\!\[CDATA\[(.+)\]\]>/Usim', '$1', $value);
$value = $GLOBALS['egw']->translation->convert($value, 'utf-8');
switch($key) {
switch ($key) {
case 'cat_id':
if(!empty($value))
if (!empty($value))
{
$categories1 = explode(',', $value);
$categories2 = explode(';', $value);
$categories = count($categories1) > count($categories2) ? $categories1 : $categories2;
$finalContact[$key] = implode(",", $this->find_or_add_categories($categories, $_abID));
$finalContact[$key] = implode(',', $this->find_or_add_categories($categories, $_abID));
}
else
{
@ -159,7 +166,7 @@ class addressbook_sif extends addressbook_bo
break;
default:
$finalContact[$key] = $value;
$finalContact[$key] = str_replace("\r\n", "\n", $value);
break;
}
}
@ -173,18 +180,20 @@ class addressbook_sif extends addressbook_bo
* Search an exactly matching entry (used for slow sync)
*
* @param string $_sifdata
* @return boolean/int/string contact-id or false, if not found
* @return array of matching contact-ids
*/
function search($_sifdata, $contentID=null, $relax=false)
{
$result = false;
$result = array();
if($contact = $this->siftoegw($_sifdata, $contentID)) {
if ($contentID) {
$contact['contact_id'] = $contentID;
}
$result = $this->find_contact($contact, $relax);
}
if (($contact = $this->siftoegw($_sifdata, $contentID)))
{
if ($contentID)
{
$contact['contact_id'] = $contentID;
}
$result = $this->find_contact($contact, $relax);
}
return $result;
}
@ -198,22 +207,44 @@ class addressbook_sif extends addressbook_bo
*/
function addSIF($_sifdata, $_abID=null, $merge=false)
{
#error_log('ABID: '.$_abID);
#error_log(base64_decode($_sifdata));
if(!$contact = $this->siftoegw($_sifdata, $_abID)) {
if (!$contact = $this->siftoegw($_sifdata, $_abID))
{
return false;
}
if($_abID) {
if ($_abID)
{
if (($old_contact = $this->read($_abID)))
{
if ($merge)
{
foreach ($contact as $key => $value)
{
if (!empty($old_contact[$key]))
{
$contact[$key] = $old_contact[$key];
}
}
}
else
{
if (isset($old_contact['account_id']))
{
$contact['account_id'] = $old_contact['account_id'];
}
}
}
// update entry
$contact['id'] = $_abID;
}
elseif (array_key_exists('filter_addressbook', $GLOBALS['egw_info']['user']['preferences']['syncml']))
{
$contact['owner'] = (int) $GLOBALS['egw_info']['user']['preferences']['syncml']['filter_addressbook'];
if ($contact['owner'] == -1)
{
$contact['owner'] = $GLOBALS['egw_info']['user']['account_primary_group'];
}
}
return $this->save($contact);
}
@ -230,9 +261,7 @@ class addressbook_sif extends addressbook_bo
$fields = array_unique(array_values($this->sifMapping));
sort($fields);
if(!($entry = $this->read($_id))) {
return false;
}
if (!($entry = $this->read($_id))) return false;
$sifContact = self::xml_decl . "\n<contact>" . self::SIF_decl;
@ -241,16 +270,16 @@ class addressbook_sif extends addressbook_bo
// 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;
#error_log("$sifField => $egwField");
#error_log('VALUE1: '.$entry[0][$egwField]);
$value = $GLOBALS['egw']->translation->convert($entry[$egwField], $sysCharSet, 'utf-8');
#error_log('VALUE2: '.$value);
switch($sifField)
switch ($sifField)
{
case 'Sensitivity':
$value = 2 * $value; // eGW private is 0 (public) or 1 (private)
@ -263,7 +292,7 @@ class addressbook_sif extends addressbook_bo
break;
case 'Categories':
if(!empty($value)) {
if (!empty($value)) {
$value = implode(", ", $this->get_categories($value));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
} else {

View File

@ -122,11 +122,11 @@ class addressbook_tracking extends bo_tracking
{
return lang('New contact submitted by %1 at %2',
$GLOBALS['egw']->common->grab_owner_name($data['creator']),
$this->datetime($data['created']-$this->tracker->tz_offset_s));
$this->datetime($data['created']));
}
return lang('Contact modified by %1 at %2',
$GLOBALS['egw']->common->grab_owner_name($data['modifier']),
$this->datetime($data['modified']-$this->tracker->tz_offset_s));
$this->datetime($data['modified']));
}
/**
@ -149,9 +149,7 @@ class addressbook_tracking extends bo_tracking
* Get the details of an entry
*
* @param array $data
* @param string $datetime_format of user to notify, eg. 'Y-m-d H:i'
* @param int $tz_offset_s offset in sec to be add to server-time to get the user-time of the user to notify
* @return array of details as array with values for keys 'label','value','type'
* return array of details as array with values for keys 'label','value','type'
*/
function get_details($data)
{
@ -167,7 +165,7 @@ class addressbook_tracking extends bo_tracking
case 'created': case 'modified':
$details[$name] = array(
'label' => $label,
'value' => $this->datetime($data[$name]-$this->contacts->tz_offset_s),
'value' => $this->datetime($data[$name]),
);
break;
case 'bday':

View File

@ -1083,7 +1083,7 @@ class addressbook_ui extends addressbook_bo
// hide region for address format 'postcode_city'
if (($row['addr_format'] = $this->addr_format_by_country($row['adr_one_countryname']))=='postcode_city') unset($row['adr_one_region']);
if (($row['addr_format2'] = $this->addr_format_by_country($row['adr_two_countryname']))=='postcode_city') unset($row['adr_two_region']);
// respect category permissions
if(!empty($row['cat_id']))
{
@ -1222,6 +1222,12 @@ class addressbook_ui extends addressbook_bo
{
$old_org_entry = $this->read($content['id']);
}
if (isset($content['n_family']) && isset($content['n_given'])
&& $content['n_family'] != $old_org_entry['n_family']
&& $content['n_given'] != $old_org_entry['n_given'])
{
unset($content['n_fn']);
}
if ($this->save($content))
{
$content['msg'] = lang('Contact saved');
@ -1618,7 +1624,7 @@ class addressbook_ui extends addressbook_bo
$content[$key.'2'] = $content[$key];
}
}
// respect category permissions
if(!empty($content['cat_id']))
{
@ -2025,7 +2031,7 @@ class addressbook_ui extends addressbook_bo
}
common::egw_footer();
}
/**
* Cleanup all contacts of all users (called by Admin >> Addressbook >> Site configuration (Admin only)
*

View File

@ -123,17 +123,13 @@ class addressbook_vcal extends addressbook_bo
*/
function addVCard($_vcard, $_abID=null, $merge=false)
{
if(!$contact = $this->vcardtoegw($_vcard, $_abID))
{
return false;
}
if (!($contact = $this->vcardtoegw($_vcard))) return false;
if($_abID)
if ($_abID)
{
if ($merge)
if (($old_contact = $this->read($_abID)))
{
$old_contact = $this->read($_abID);
if ($old_contact)
if ($merge)
{
foreach ($contact as $key => $value)
{
@ -143,6 +139,22 @@ class addressbook_vcal extends addressbook_bo
}
}
}
else
{
if (isset($old_contact['account_id']))
{
$contact['account_id'] = $old_contact['account_id'];
}
if (is_array($contact['category']))
{
$contact['category'] = implode(',',$this->find_or_add_categories($contact['category'], $_abID));
}
else
{
// restore from orignal
$contact['category'] = $old_contact['category'];
}
}
}
// update entry
$contact['id'] = $_abID;
@ -150,6 +162,14 @@ class addressbook_vcal extends addressbook_bo
elseif (array_key_exists('filter_addressbook', $GLOBALS['egw_info']['user']['preferences']['syncml']))
{
$contact['owner'] = (int) $GLOBALS['egw_info']['user']['preferences']['syncml']['filter_addressbook'];
if ($contact['owner'] == -1)
{
$contact['owner'] = $GLOBALS['egw_info']['user']['account_primary_group'];
}
if (is_array($contact['category']))
{
$contact['category'] = implode(',',$this->find_or_add_categories($contact['category'], -1));
}
}
return $this->save($contact);
}
@ -242,7 +262,7 @@ class addressbook_vcal extends addressbook_bo
$value = trim($entry[$databaseField]);
}
switch($databaseField)
switch ($databaseField)
{
case 'private':
$value = $value ? 'PRIVATE' : 'PUBLIC';
@ -443,10 +463,15 @@ class addressbook_vcal extends addressbook_bo
function search($_vcard, $contentID=null, $relax=false)
{
$result = false;
$result = array();
if (($contact = $this->vcardtoegw($_vcard, $contentID)))
if (($contact = $this->vcardtoegw($_vcard)))
{
if (is_array($contact['category']))
{
$contact['category'] = implode(',',$this->find_or_add_categories($contact['category'],
$contentID ? $contentID : -1));
}
if ($contentID)
{
$contact['id'] = $contentID;
@ -465,7 +490,7 @@ class addressbook_vcal extends addressbook_bo
if (is_array($_supportedFields)) $this->supportedFields = $_supportedFields;
}
function vcardtoegw($_vcard, $_abID=null)
function vcardtoegw($_vcard)
{
// the horde class does the charset conversion. DO NOT CONVERT HERE.
// be as flexible as possible
@ -661,24 +686,24 @@ class addressbook_vcal extends addressbook_bo
}
}
if($rowName == 'EMAIL')
if ($rowName == 'EMAIL')
{
$rowName .= ';X-egw-Ref' . $email++;
}
if(($rowName == 'TEL;CELL') ||
if (($rowName == 'TEL;CELL') ||
($rowName == 'TEL;CELL;VOICE'))
{
$rowName = 'TEL;CELL;X-egw-Ref' . $cell++;
}
if(($rowName == 'TEL') ||
if (($rowName == 'TEL') ||
($rowName == 'TEL;VOICE'))
{
$rowName = 'TEL;X-egw-Ref' . $tel++;
}
if($rowName == 'URL')
if ($rowName == 'URL')
{
$rowName = 'URL;X-egw-Ref' . $url++;
}
@ -701,7 +726,7 @@ class addressbook_vcal extends addressbook_bo
foreach ($rowNames as $vcardKey => $rowName)
{
switch($rowName)
switch ($rowName)
{
case 'VERSION':
break;
@ -879,7 +904,7 @@ class addressbook_vcal extends addressbook_bo
break;
case 'cat_id':
$contact[$fieldName] = implode(',',$this->find_or_add_categories($vcardValues[$vcardKey]['values'], $_abID));
$contact[$fieldName] = $vcardValues[$vcardKey]['values'];
break;
case 'jpegphoto':
@ -887,8 +912,7 @@ class addressbook_vcal extends addressbook_bo
break;
case 'note':
// note may contain ','s but maybe this needs to be fixed in vcard parser...
$contact[$fieldName] = $vcardValues[$vcardKey]['value'];
$contact[$fieldName] = str_replace("\r\n", "\n", $vcardValues[$vcardKey]['value']);
break;
case 'uid':