- merged SyncML-1.2 branch with trunk:

svn merge ^/trunk/addressbook@27378 ^/branches/SyncML-1.2/addressbook .
- re-added to trunk commits, which were somehow not in SyncML-1.2 branch:
svn merge -c 26581 ^/trunk/addressbook
svn merge -c 26582 ^/trunk/addressbook
This commit is contained in:
Ralf Becker 2009-07-15 19:44:09 +00:00
parent e667e168b4
commit d333605510
5 changed files with 987 additions and 301 deletions

View File

@ -5,6 +5,7 @@
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @author Cornelius Weiss <egw@von-und-zu-weiss.de> * @author Cornelius Weiss <egw@von-und-zu-weiss.de>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @package addressbook * @package addressbook
* @copyright (c) 2005-8 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2005-8 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2005/6 by Cornelius Weiss <egw@von-und-zu-weiss.de> * @copyright (c) 2005/6 by Cornelius Weiss <egw@von-und-zu-weiss.de>
@ -441,8 +442,13 @@ class addressbook_bo extends addressbook_so
*/ */
function fullname($contact) function fullname($contact)
{ {
if (empty($contact['n_family']) && empty($contact['n_given'])) {
$cpart = array('org_name');
} else {
$cpart = array('n_prefix','n_given','n_middle','n_family','n_suffix');
}
$parts = array(); $parts = array();
foreach(array('n_prefix','n_given','n_middle','n_family','n_suffix') as $n) foreach($cpart as $n)
{ {
if ($contact[$n]) $parts[] = $contact[$n]; if ($contact[$n]) $parts[] = $contact[$n];
} }
@ -611,8 +617,7 @@ class addressbook_bo extends addressbook_so
$contact['modifier'] = $this->user; $contact['modifier'] = $this->user;
$contact['modified'] = $this->now_su; $contact['modified'] = $this->now_su;
// set full name and fileas from the content // set full name and fileas from the content
if (isset($contact['n_family']) && isset($contact['n_given'])) if (!isset($contact['n_fn'])) {
{
$contact['n_fn'] = $this->fullname($contact); $contact['n_fn'] = $this->fullname($contact);
if (isset($contact['org_name'])) $contact['n_fileas'] = $this->fileas($contact); if (isset($contact['org_name'])) $contact['n_fileas'] = $this->fileas($contact);
} }
@ -1432,7 +1437,7 @@ class addressbook_bo extends addressbook_so
{ {
$cat_name = substr($cat_name, 2); $cat_name = substr($cat_name, 2);
} }
$cat_id = $this->categories->add(array('name' => $cat_name,'descr' => $cat_name)); $cat_id = $this->categories->add(array('name' => $cat_name, 'descr' => $cat_name, 'access' => 'private'));
} }
if ($cat_id) if ($cat_id)
@ -1474,15 +1479,273 @@ class addressbook_bo extends addressbook_so
function fixup_contact(&$contact) function fixup_contact(&$contact)
{ {
if (!isset($contact['n_fn']) || empty($contact['n_fn'])) if (empty($contact['n_fn']))
{ {
$contact['n_fn'] = $this->fullname($contact); $contact['n_fn'] = $this->fullname($contact);
} }
if (!isset($contact['n_fileas']) || empty($contact['n_fileas'])) if (empty($contact['n_fileas']))
{ {
$contact['n_fileas'] = $this->fileas($contact); $contact['n_fileas'] = $this->fileas($contact);
} }
} }
function all_empty(&$_contact, &$fields)
{
$retval = true;
foreach ($fields as $field) {
if (isset($_contact[$field]) && !empty($_contact[$field])) {
$retval = false;
break;
}
}
return $retval;
}
/**
* Try to find a matching db entry
*
* @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)
*/
function find_contact($contact, $relax=false)
{
if ($contact['id'] && ($found = $this->read($contact['id'])))
{
// We only do a simple consistency check
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']))
{
return $found['id'];
}
}
unset($contact['id']);
$columns_to_search = array('contact_id',
'n_family', 'n_given', 'n_middle', 'n_prefix', 'n_suffix',
'bday', 'org_name', 'org_unit', 'title', 'role',
'email', 'email_home', 'url', 'url_home');
$tolerance_fields = array('contact_id',
'n_middle', 'n_prefix', 'n_suffix',
'org_unit', 'role',
'bday',
'email', 'email_home',
'url', 'url_home');
$addr_one_fields = array('adr_one_street', 'adr_one_street2',
'adr_one_locality', 'adr_one_region',
'adr_one_postalcode', 'adr_one_countryname');
$addr_two_fields = array('adr_two_street', 'adr_two_street2',
'adr_two_locality', 'adr_two_region',
'adr_two_postalcode', 'adr_two_countryname');
$no_addr_one = array();
$no_addr_two = array();
$backend =& $this->get_backend();
// define filter for empty address one
foreach ($addr_one_fields as $field)
{
if (!($db_col = array_search($field, $backend->db_cols)))
{
$db_col = $field;
}
$no_addr_one[] = "(" . $db_col . " IS NULL OR " . $db_col . " = '')";
}
// define filter for empty address two
foreach ($addr_two_fields as $field)
{
if (!($db_col = array_search($field, $backend->db_cols)))
{
$db_col = $field;
}
$no_addr_two[] = "(" . $db_col . " IS NULL OR " . $db_col . " = '')";
}
$result = false;
$criteria = array();
$empty_columns = array();
foreach ($columns_to_search as $field)
{
if (!isset($contact[$field]) || empty($contact[$field])) {
// Not every device supports all fields
if (!in_array($field, $tolerance_fields))
{
if (!($db_col = array_search($field, $backend->db_cols)))
{
$db_col = $field;
}
$empty_columns[] = "(" . $db_col . " IS NULL OR " . $db_col . " = '')";
}
}
else
{
if (!$relax || !in_array($field, $tolerance_fields))
{
$criteria[$field] = $contact[$field];
}
}
}
$filter = $empty_columns;
if (!$relax)
{
// We use addresses only for strong matching
if ($this->all_empty($contact, $addr_one_fields))
{
$filter = $filter + $no_addr_one;
}
else
{
foreach ($addr_one_fields as $field)
{
if (!isset($contact[$field]) || empty($contact[$field]))
{
if (!($db_col = array_search($field, $backend->db_cols)))
{
$db_col = $field;
}
$filter[] = "(" . $db_col . " IS NULL OR " . $db_col . " = '')";
}
else
{
$criteria[$field] = $contact[$field];
}
}
}
if ($this->all_empty($contact, $addr_two_fields))
{
$filter = $filter + $no_addr_two;
}
else
{
foreach ($addr_two_fields as $field)
{
if (!isset($contact[$field]) || empty($contact[$field]))
{
if (!($db_col = array_search($field, $backend->db_cols)))
{
$db_col = $field;
}
$filter[] = "(" . $db_col . " IS NULL OR " . $db_col . " = '')";
}
else
{
$criteria[$field] = $contact[$field];
}
}
}
}
Horde::logMessage("Addressbook find step 1:\n" . print_r($criteria, true) .
"filter:\n" . print_r($filter, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
// first try full match
if (($foundContacts = parent::search($criteria, true, '', '', '', False, 'AND', false, $filter)))
{
$result = $foundContacts[0]['id'];
}
// No need for more searches for relaxed matching
if (!$relax && !$result && !$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 (($foundContacts = parent::search($criteria, true, '', '', '', False, 'AND', false, $filter)))
{
$result = $foundContacts[0]['id'];
}
else
{
// try address as home address -- some devices don't qualify addresses
$filter = $empty_columns;
foreach ($addr_two_fields as $key => $field)
{
if (isset($criteria[$addr_one_fields[$key]]))
{
$criteria[$field] = $criteria[$addr_one_fields[$key]];
unset($criteria[$addr_one_fields[$key]]);
}
else
{
if (!($db_col = array_search($field,$backend->db_cols)))
{
$db_col = $field;
}
$filter[] = "(" . $db_col . " IS NULL OR " . $db_col . " = '')";
}
}
$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 (($foundContacts = parent::search($criteria, true, '', '', '', False, 'AND', false, $filter)))
{
$result = $foundContacts[0]['id'];
}
}
}
elseif (!$relax && !$result)
{ // No more searches for relaxed matching, try again after address swap
$filter = $empty_columns;
foreach ($addr_one_fields as $key => $field)
{
$_temp_set = false;
if (isset($criteria[$field]))
{
$_temp = $criteria[$field];
$_temp_set = true;
unset($criteria[$field]);
}
if (isset($criteria[$addr_two_fields[$key]]))
{
$criteria[$field] = $criteria[$addr_two_fields[$key]];
unset($criteria[$addr_two_fields[$key]]);
}
else
{
if (!($db_col = array_search($field,$backend->db_cols)))
{
$db_col = $field;
}
$filter[] = "(" . $db_col . " IS NULL OR " . $db_col . " = '')";
}
if ($_temp_set)
{
$criteria[$addr_two_fields[$key]] = $_temp;
}
else
{
if (!($db_col = array_search($addr_two_fields[$key],$backend->db_cols)))
{
$db_col = $field;
}
$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(($foundContacts = parent::search($criteria, true, '', '', '', False, 'AND', false, $filter)))
{
$result = $foundContacts[0]['id'];
}
}
return $result;
}
} }

View File

@ -310,7 +310,7 @@ class addressbook_groupdav extends groupdav_handler
*/ */
private function _get_handler() private function _get_handler()
{ {
$handler = new addressbook_vcal(); $handler = new addressbook_vcal('addressbook','text/vcard');
$handler->setSupportedFields('GroupDAV',$this->agent); $handler->setSupportedFields('GroupDAV',$this->agent);
return $handler; return $handler;

View File

@ -5,6 +5,7 @@
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @author Lars Kneschke <lkneschke@egroupware.org> * @author Lars Kneschke <lkneschke@egroupware.org>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @package addressbook * @package addressbook
* @subpackage export * @subpackage export
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
@ -94,6 +95,10 @@ class addressbook_sif extends addressbook_bo
'Folder' => '', 'Folder' => '',
); );
// standard headers
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) {
} }
@ -108,13 +113,12 @@ class addressbook_sif extends addressbook_bo
$this->sifData .= $_data; $this->sifData .= $_data;
} }
function siftoegw($_sifdata) { function siftoegw($sifData) {
$sifData = base64_decode($_sifdata);
#$tmpfname = tempnam('/tmp/sync/contents','sifc_'); #$tmpfname = tempnam('/tmp/sync/contents','sifc_');
#$handle = fopen($tmpfname, "w"); #$handle = fopen($tmpfname, "w");
#fwrite($handle, $sifdata); #fwrite($handle, $sifData);
#fclose($handle); #fclose($handle);
$this->xml_parser = xml_parser_create('UTF-8'); $this->xml_parser = xml_parser_create('UTF-8');
@ -131,6 +135,7 @@ class addressbook_sif extends addressbook_bo
} }
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'); $value = $GLOBALS['egw']->translation->convert($value, 'utf-8');
switch($key) { switch($key) {
case 'cat_id': case 'cat_id':
@ -164,25 +169,17 @@ class addressbook_sif extends addressbook_bo
* @param string $_sifdata * @param string $_sifdata
* @return boolean/int/string contact-id or false, if not found * @return boolean/int/string contact-id or false, if not found
*/ */
function search($_sifdata,$contentID=null) function search($_sifdata, $contentID=null, $relax=false)
{ {
if(!$contact = $this->siftoegw($_sifdata)) $result = false;
{
return false;
}
if ($contentID) {
$contact['contact_id'] = $contentID;
}
// 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 = addressbook_bo::search($contact))) if($contact = $this->siftoegw($_sifdata)) {
{ if ($contentID) {
error_log(print_r($foundContacts,true)); $contact['contact_id'] = $contentID;
return $foundContacts[0]['id']; }
$result = $this->find_contact($contact, $relax);
} }
return false; return $result;
} }
/** /**
@ -191,8 +188,9 @@ class addressbook_sif extends addressbook_bo
* @return int contact id * @return int contact id
* @param string $_vcard the vcard * @param string $_vcard the vcard
* @param int/string $_abID=null the internal addressbook id or !$_abID for a new enty * @param int/string $_abID=null the internal addressbook id or !$_abID for a new enty
* @param boolean $merge=false merge data with existing entry
*/ */
function addSIF($_sifdata, $_abID=null) function addSIF($_sifdata, $_abID=null, $merge=false)
{ {
#error_log('ABID: '.$_abID); #error_log('ABID: '.$_abID);
#error_log(base64_decode($_sifdata)); #error_log(base64_decode($_sifdata));
@ -209,24 +207,25 @@ class addressbook_sif extends addressbook_bo
} }
/** /**
* return a vcard * return a sifc
* *
* @param int $_id the id of the contact * @param int $_id the id of the contact
* @param int $_vcardProfile profile id for mapping from vcard values to egw addressbook
* @return string containing the vcard * @return string containing the vcard
*/ */
function getSIF($_id) function getSIF($_id)
{ {
$sysCharSet = $GLOBALS['egw']->translation->charset();
$fields = array_unique(array_values($this->sifMapping)); $fields = array_unique(array_values($this->sifMapping));
sort($fields); sort($fields);
if(!($entry = $this->read($_id))) if(!($entry = $this->read($_id))) {
{
return false; return false;
} }
$sifContact = '<contact>';
$sifContact = self::xml_decl . "\n<contact>" . self::SIF_decl;
#error_log(print_r($entry,true)); #error_log(print_r($entry,true));
$sysCharSet = $GLOBALS['egw']->translation->charset();
// fillup some defaults such as n_fn and n_fileas is needed // fillup some defaults such as n_fn and n_fileas is needed
$this->fixup_contact($entry); $this->fixup_contact($entry);
@ -242,15 +241,6 @@ class addressbook_sif extends addressbook_bo
switch($sifField) switch($sifField)
{ {
// TODO handle multiple categories
case 'Categories':
if(!empty($value)) {
$value = implode("; ", $this->get_categories($value));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
}
$sifContact .= "<$sifField>$value</$sifField>";
break;
case 'Sensitivity': case 'Sensitivity':
$value = 2 * $value; // eGW private is 0 (public) or 1 (private) $value = 2 * $value; // eGW private is 0 (public) or 1 (private)
$sifContact .= "<$sifField>$value</$sifField>"; $sifContact .= "<$sifField>$value</$sifField>";
@ -261,13 +251,22 @@ class addressbook_sif extends addressbook_bo
#$sifContact .= "<$sifField>/</$sifField>"; #$sifContact .= "<$sifField>/</$sifField>";
break; break;
case 'Categories':
if(!empty($value)) {
$value = implode("; ", $this->get_categories($value));
$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
} else {
break;
}
default: default:
$sifContact .= "<$sifField>".trim($value)."</$sifField>"; $value = @htmlspecialchars(trim($value), ENT_QUOTES, 'utf-8');
$sifContact .= "<$sifField>$value</$sifField>";
break; break;
} }
} }
$sifContact .= "</contact>"; $sifContact .= "</contact>";
return base64_encode($sifContact); return $sifContact;
} }
} }

View File

@ -607,11 +607,23 @@ class addressbook_sql extends so_sql
*/ */
function read($keys,$extra_cols='',$join='') function read($keys,$extra_cols='',$join='')
{ {
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
} else {
$minimum_uid_length = 8;
}
if (!is_array($keys) && !is_numeric($keys)) if (!is_array($keys) && !is_numeric($keys))
{ {
$keys = array('contact_uid' => $keys); $keys = array('contact_uid' => $keys);
} }
return parent::read($keys,$extra_cols,$join); $contact = parent::read($keys,$extra_cols,$join);
// enforce a minium uid strength
if (is_array($contact) && (!isset($contact['uid'])
|| strlen($contact['uid']) < $minimum_uid_length)) {
parent::update(array('uid' => common::generate_uid('addressbook',$contact['id'])));
}
return $contact;
} }
/** /**
@ -623,6 +635,12 @@ class addressbook_sql extends so_sql
*/ */
function save($keys=null) function save($keys=null)
{ {
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
} else {
$minimum_uid_length = 8;
}
if (is_array($keys) && count($keys)) $this->data_merge($keys); if (is_array($keys) && count($keys)) $this->data_merge($keys);
$new_entry = !$this->data['id']; $new_entry = !$this->data['id'];
@ -649,8 +667,8 @@ class addressbook_sql extends so_sql
} }
} }
// enforce a minium uid strength // enforce a minium uid strength
if (!$err && ($new_entry || isset($this->data['uid'])) && (strlen($this->data['uid']) < 20 || is_numeric($this->data['uid']))) if (!$err && (!isset($this->data['uid'])
{ || strlen($this->data['uid']) < $minimum_uid_length)) {
parent::update(array('uid' => common::generate_uid('addressbook',$this->data['id']))); parent::update(array('uid' => common::generate_uid('addressbook',$this->data['id'])));
//echo "<p>set uid={$this->data['uid']}, etag={$this->data['etag']}</p>"; //echo "<p>set uid={$this->data['uid']}, etag={$this->data['etag']}</p>";
} }

File diff suppressed because it is too large Load Diff