- 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
* @author Cornelius Weiss <egw@von-und-zu-weiss.de>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @package addressbook
* @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>
@ -441,8 +442,13 @@ class addressbook_bo extends addressbook_so
*/
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();
foreach(array('n_prefix','n_given','n_middle','n_family','n_suffix') as $n)
foreach($cpart as $n)
{
if ($contact[$n]) $parts[] = $contact[$n];
}
@ -611,8 +617,7 @@ class addressbook_bo extends addressbook_so
$contact['modifier'] = $this->user;
$contact['modified'] = $this->now_su;
// 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);
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_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)
@ -1474,15 +1479,273 @@ class addressbook_bo extends addressbook_so
function fixup_contact(&$contact)
{
if (!isset($contact['n_fn']) || empty($contact['n_fn']))
if (empty($contact['n_fn']))
{
$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);
}
}
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()
{
$handler = new addressbook_vcal();
$handler = new addressbook_vcal('addressbook','text/vcard');
$handler->setSupportedFields('GroupDAV',$this->agent);
return $handler;
@ -358,4 +358,4 @@ class addressbook_groupdav extends groupdav_handler
{
return $this->bo->check_perms($acl,$contact);
}
}
}

View File

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

File diff suppressed because it is too large Load Diff