* array('name' => array('John Doe' => Turba_List, ...), ...)
*
* @throws Turba_Exception
*/
public function searchDuplicates()
{
return $this->_driver->searchDuplicates();
}
/**
* Obtains a Turba_List to get TimeObjects out of.
*
* @param Horde_Date $start The starting date.
* @param Horde_Date $end The ending date.
* @param string $field The address book field containing the
* timeObject information (birthday,
* anniversary).
*
* @return Turba_List A list of objects.
* @throws Turba_Exception
*/
public function getTimeObjectTurbaList(Horde_Date $start, Horde_Date $end, $field)
{
return $this->_driver->getTimeObjectTurbaList($start, $end, $field);
}
/**
* Checks if the current user has the requested permissions on this
* address book.
*
* @param integer $perm The permission to check for.
*
* @return boolean True if the user has permission, otherwise false.
*/
public function hasPermission($perm)
{
return $this->_share->hasPermission($GLOBALS['registry']->getAuth(), $perm);
}
/**
* Return the name of this address book.
*
* @string Address book name
*/
public function getName()
{
$share_parts = explode(':', $this->_share->getName());
return array_pop($share_parts);
}
/**
* Return the owner to use when searching or creating contacts in
* this address book.
*
* @return string TODO
* @throws Turba_Exception
*/
protected function _getContactOwner()
{
$params = @unserialize($this->_share->get('params'));
if (!empty($params['name'])) {
return $params['name'];
}
throw new Turba_Exception(_("Unable to find contact owner."));
}
/**
* Runs any actions after setting a new default tasklist.
*
* @param string $share The default share ID.
*/
public function setDefaultShare($share)
{
$this->_driver->setDefaultShare($share);
}
/**
* Creates an object key for a new object.
*
* @param array $attributes The attributes (in driver keys) of the
* object being added.
*
* @return string A unique ID for the new object.
*/
protected function _makeKey(array $attributes)
{
return $this->_driver->_makeKey($attributes);
}
/**
* Creates an object UID for a new object.
*
* @return string A unique ID for the new object.
*/
protected function _makeUid()
{
return $this->_driver->_makeUid();
}
/**
* Searches the address book with the given criteria and returns a
* filtered list of results. If the criteria parameter is an empty array,
* all records will be returned.
*
* @param array $criteria Array containing the search criteria.
* @param array $fields List of fields to return.
* @param array $blobFields Array of fields containing binary data.
* @param boolean $count_only Only return the count of matching entries,
* not the entries themselves.
*
* @return array Hash containing the search results.
* @throws Turba_Exception
*/
protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only = false)
{
return $this->_driver->_search($criteria, $fields, $blobFields, $count_only);
}
/**
* Reads the given data from the address book and returns the results.
*
* @param string $key The primary key field to use.
* @param mixed $ids The ids of the contacts to load.
* @param string $owner Only return contacts owned by this user.
* @param array $fields List of fields to return.
* @param array $blobFields Array of fields containing binary data.
* @param array $dateFields Array of fields containing date data.
* @since 4.2.0
*
* @return array Hash containing the search results.
* @throws Turba_Exception
*/
protected function _read($key, $ids, $owner, array $fields,
array $blobFields = array(),
array $dateFields = array())
{
return $this->_driver->_read($key, $ids, $owner, $fields, $blobFields, $dateFields);
}
/**
* Adds the specified contact to the addressbook.
*
* @param array $attributes The attribute values of the contact.
* @param array $blob_fields Fields that represent binary data.
* @param array $date_fields Fields that represent dates. @since 4.2.0
*
* @throws Turba_Exception
*/
protected function _add(array $attributes, array $blob_fields = array(), array $date_fields = array())
{
return $this->_driver->_add($attributes, $blob_fields, $date_fields);
}
/**
* Returns ability of the backend to add new contacts.
*
* @return boolean Can backend add?
*/
protected function _canAdd()
{
return $this->_driver->_canAdd();
}
/**
* Deletes the specified contact from the addressbook.
*
* @param string $object_key TODO
* @param string $object_id TODO
*
* @throws Turba_Exception
*/
protected function _delete($object_key, $object_id)
{
return $this->_driver->_delete($object_key, $object_id);
}
/**
* Deletes all contacts from a specific address book.
*
* @param string $sourceName The source to remove all contacts from.
*
* @return array An array of UIDs
* @throws Turba_Exception
*/
protected function _deleteAll($sourceName = null)
{
if (is_null($sourceName)) {
$sourceName = $this->getContactOwner();
}
return $this->_driver->_deleteAll($sourceName);
}
/**
* Saves the specified object in the SQL database.
*
* @param Turba_Object $object The object to save
*
* @return string The object id, possibly updated.
* @throws Turba_Exception
*/
protected function _save(Turba_Object $object)
{
return $this->_driver->_save($object);
}
/**
* Remove all data for a specific user.
*
* @param string $user The user to remove all data for.
*/
public function removeUserData($user)
{
// Make sure we are being called by an admin.
if (!$GLOBALS['registry']->isAdmin()) {
throw new Horde_Exception_PermissionDenied(_("Permission denied"));
}
$this->_deleteAll();
$GLOBALS['injector']
->getInstance('Turba_Shares')
->removeShare($this->_share);
unset($this->_share);
}
}
turba-4.2.12/lib/Driver/Sql.php 0000664 0001750 0001750 00000064220 12654115107 014300 0 ustar jan jan
* @author Michael J. Rubinsky * 'db' - (Horde_Db_Adapter) A DB Adapter object. **/ public function __construct($name = '', array $params = array()) { if (empty($params['db'])) { throw new InvalidArgumentException('Missing required Horde_Db_Adapter object'); } $this->_db = $params['db']; unset($params['db']); parent::__construct($name, $params); } /** * Returns the number of contacts of the current user in this address book. * * @return integer The number of contacts that the user owns. */ public function count() { $test = $this->getContactOwner(); if (!isset($this->_countCache[$test])) { /* Build up the full query. */ $query = 'SELECT COUNT(*) FROM ' . $this->_params['table'] . ' WHERE ' . $this->toDriver('__owner') . ' = ?'; $values = array($test); /* Run query. */ try { $this->_countCache[$test] = $this->_db->selectValue($query, $values); } catch (Horde_Db_Exception $e) { $this->_countCache[$test] = 0; } } return $this->_countCache[$test]; } /** * Searches the SQL database with the given criteria and returns a * filtered list of results. If the criteria parameter is an empty array, * all records will be returned. * * @param array $criteria Array containing the search criteria. * @param array $fields List of fields to return. * @param array $blobFields TODO * @param boolean $count_only Only return the count of matching entries, * not the entries themselves. * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only = false) { return $this->_internalSearch($criteria, $fields, $blobFields, array(), $count_only); } /** * Searches the SQL database with the given criteria and returns a * filtered list of results. If the criteria parameter is an empty array, * all records will be returned. * * @param array $criteria Array containing the search criteria. * @param array $fields List of fields to return. * @param array $blobFields TODO * @param array $appendWhere An additional where clause to append. * Array should contain 'sql' and 'params' * params are used as bind parameters. * @param boolean $count_only Only return the count of matching entries, * not the entries themselves. * * @return mixed array|integer Hash containing the search results or the * count of matching entries. * @throws Turba_Exception */ protected function _internalSearch(array $criteria, array $fields, $blobFields = array(), $appendWhere = array(), $count_only = false) { /* Build the WHERE clause. */ $where = ''; $values = array(); if (count($criteria) || !empty($this->_params['filter'])) { foreach ($criteria as $key => $vals) { if ($key == 'OR' || $key == 'AND') { if (!empty($where)) { $where .= ' ' . $key . ' '; } $binds = $this->_buildSearchQuery($key, $vals); $where .= '(' . $binds[0] . ')'; $values += $binds[1]; } } $where = ' WHERE ' . $where; if (count($criteria) && !empty($this->_params['filter'])) { $where .= ' AND '; } if (!empty($this->_params['filter'])) { $where .= $this->_params['filter']; } if (count($appendWhere)) { $where .= ' AND ' . $appendWhere['sql']; $values = array_merge($values, $appendWhere['params']); } } elseif (count($appendWhere)) { $where = ' WHERE ' . $appendWhere['sql']; $values = array_merge($values, $appendWhere['params']); } /* Build up the full query. */ try { if ($count_only) { return $this->_db->selectValue( 'SELECT COUNT(*) FROM ' . $this->_params['table'] . $where, $values ); } return $this->_parseRead( $blobFields, $this->_db->selectAll( 'SELECT ' . implode(', ', $fields) . ' FROM ' . $this->_params['table'] . $where, $values ) ); } catch (Horde_Db_Exception $e) { throw new Turba_Exception(_("Server error when performing search.")); } } protected function _parseRead($blobFields, $result, $dateFields = array()) { $results = array(); foreach ($result as $row) { $entry = array(); foreach ($row as $field => $val) { if (isset($blobFields[$field])) { if (!isset($columns)) { $columns = $this->_db->columns($this->_params['table']); } $entry[$field] = $columns[$field]->binaryToString($val); } elseif (isset($dateFields[$field]) && !empty($val)) { $d = new Horde_Date($val); $entry[$field] = $this->_convertFromDriver( $d->strftime($GLOBALS['attributes'][array_search($field, $this->map)]['params']['format_in']) ); } else { $entry[$field] = $this->_convertFromDriver($val); } } $results[] = $entry; } return $results; } /** * Prepares field lists for searchDuplicates(). * * @param array $array A list of field names. * * @return array A prepared list of field names. */ protected function _buildFields($array) { foreach ($array as &$entry) { $entry = is_array($entry) ? implode(',', $this->_buildFields($entry)) : 'a1.' . $entry; } return $array; } /** * Builds the WHERE conditions for searchDuplicates(). * * @param array $array A list of field names. * * @return array A list of WHERE conditions. */ protected function _buildWhere($array) { foreach ($array as &$entry) { if (is_array($entry)) { $entry = reset($entry); } $entry = 'a1.' . $entry . ' IS NOT NULL AND a1.' . $entry . ' <> \'\''; } return $array; } /** * Builds the JOIN conditions for searchDuplicates(). * * @param array $array A list of field names. * * @return array A list of JOIN conditions. */ protected function _buildJoin($array) { foreach ($array as &$entry) { $entry = is_array($entry) ? implode(' AND ', $this->_buildJoin($entry)) : 'a1.' . $entry . ' = a2.' . $entry; } return $array; } /** * Searches the current address book for duplicate entries. * * Duplicates are determined by comparing email and name or last name and * first name values. * * @return array A hash with the following format: *
* array('name' => array('John Doe' => Turba_List, ...), ...)
*
* @throws Turba_Exception
*/
public function searchDuplicates()
{
$owner = $this->getContactOwner();
$fields = array();
if (is_array($this->map['name'])) {
if (in_array('lastname', $this->map['name']['fields']) &&
isset($this->map['lastname'])) {
$field = array($this->map['lastname']);
if (in_array('firstname', $this->map['name']['fields']) &&
isset($this->map['firstname'])) {
$field[] = $this->map['firstname'];
}
$fields[] = $field;
}
} else {
$fields[] = $this->map['name'];
}
if (isset($this->map['email'])) {
$fields[] = $this->map['email'];
}
$nameFormat = $GLOBALS['prefs']->getValue('name_format');;
if ($nameFormat != 'first_last' && $nameFormat != 'last_first') {
$nameFormat = 'first_last';
}
$order = $this->_buildFields($fields);
$joins = $this->_buildJoin($fields);
$where = $this->_buildWhere($fields);
$duplicates = array();
for ($i = 0; $i < count($joins); $i++) {
/* Build up the full query. */
$values = array();
$query = sprintf('SELECT DISTINCT a1.%s, %s FROM %s a1 JOIN %s a2 ON %s AND a1.%s <> a2.%s WHERE',
$this->map['__key'],
$order[$i],
$this->_params['table'],
$this->_params['table'],
$joins[$i],
$this->map['__key'],
$this->map['__key']);
if (isset($this->map['__owner'])) {
$query .= sprintf(' a1.%s = ? AND a2.%s = ? AND',
$this->map['__owner'],
$this->map['__owner']);
$values = array($owner, $owner);
}
$query .= sprintf(' %s ORDER BY %s',
$where[$i],
$order[$i]);
/* Run query. */
try {
$ids = $this->_db->selectValues($query, $values);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception(_("Server error when performing search."));
}
$field = ($i == 0)
? 'name'
: array_search($fields[$i], $this->map);
$contacts = array();
foreach ($ids as $id) {
$contact = $this->getObject($id);
$value = $contact->getValue($field);
if ($field == 'name') {
$value = Turba::formatName($contact, $nameFormat);
}
/* HACK! */
if ($field == 'email') {
$value = Horde_String::lower($value);
}
if (!isset($contacts[$value])) {
$contacts[$value] = new Turba_List();
}
$contacts[$value]->insert($contact);
}
if ($contacts) {
$duplicates[$field] = $contacts;
}
}
return $duplicates;
}
/**
* Reads the given data from the SQL database and returns the results.
*
* @param string $key The primary key field to use.
* @param mixed $ids The ids of the contacts to load.
* @param string $owner Only return contacts owned by this user.
* @param array $fields List of fields to return.
* @param array $blobFields Array of fields containing binary data.
* @param array $dateFields Array of fields containing date data.
* @since 4.2.0
*
* @return array Hash containing the search results.
* @throws Turba_Exception
*/
protected function _read($key, $ids, $owner, array $fields,
array $blobFields = array(), array $dateFields = array())
{
$values = array();
$in = '';
if (is_array($ids)) {
if (!count($ids)) {
return array();
}
foreach ($ids as $id) {
$in .= empty($in) ? '?' : ', ?';
$values[] = $this->_convertToDriver($id);
}
$where = $key . ' IN (' . $in . ')';
} else {
$where = $key . ' = ?';
$values[] = $this->_convertToDriver($ids);
}
if (isset($this->map['__owner'])) {
$where .= ' AND ' . $this->map['__owner'] . ' = ?';
$values[] = $this->_convertToDriver($owner);
}
if (!empty($this->_params['filter'])) {
$where .= ' AND ' . $this->_params['filter'];
}
$query = 'SELECT ' . implode(', ', $fields) . ' FROM '
. $this->_params['table'] . ' WHERE ' . $where;
try {
return $this->_parseRead($blobFields, $this->_db->selectAll($query, $values), $dateFields);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception(_("Server error when performing search."));
}
}
/**
* Adds the specified object to the SQL database.
*
* @param array $attributes The attribute values of the contact.
* @param array $blob_fields Fields that represent binary data.
* @param array $date_fields Fields that represent dates. @since 4.2.0
*
* @throws Turba_Exception
*/
protected function _add(array $attributes, array $blob_fields = array(),
array $date_fields = array())
{
list($fields, $values) = $this->_prepareWrite(
$attributes,
$blob_fields,
$date_fields
);
try {
$this->_db->insertBlob(
$this->_params['table'],
array_combine($fields, $values)
);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception(_("Server error when adding data."));
}
}
protected function _prepareWrite($attributes, $blob_fields, $date_fields)
{
$fields = $values = array();
foreach ($attributes as $field => $value) {
$fields[] = $field;
if (!empty($value) && isset($blob_fields[$field])) {
$values[] = new Horde_Db_Value_Binary($value);
} elseif (!empty($value) && isset($date_fields[$field])) {
$d = new Horde_Date($value);
$values[] = $d->strftime('%Y-%m-%d');
} else {
$values[] = $this->_convertToDriver($value);
}
}
return array($fields, $values);
}
/**
* TODO
*/
protected function _canAdd()
{
return true;
}
/**
* Deletes the specified object from the SQL database.
*
* @throws Turba_Exception
*/
protected function _delete($object_key, $object_id)
{
$query = 'DELETE FROM ' . $this->_params['table'] .
' WHERE ' . $object_key . ' = ?';
$values = array($object_id);
try {
$this->_db->delete($query, $values);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception(_("Server error when deleting data."));
}
}
/**
* Deletes all contacts from a specific address book.
*
* @param string $sourceName The source to remove all contacts from.
*
* @return array An array of UIDs
* @throws Turba_Exception
*/
protected function _deleteAll($sourceName = null)
{
if (!$GLOBALS['registry']->getAuth()) {
throw new Turba_Exception('Permission denied');
}
/* Get owner id */
$values = empty($sourceName)
? array($GLOBALS['registry']->getAuth())
: array($sourceName);
if (empty($this->map['__owner'])) {
throw new Turba_Exception('Unable to find __owner field. Cannot delete.');
}
$owner_field = $this->map['__owner'];
/* Need a list of UIDs so we can notify History */
$query = sprintf('SELECT %s FROM %s WHERE %s = ?',
$this->map['__uid'], $this->_params['table'], $owner_field);
try {
$ids = $this->_db->selectValues($query, $values);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception(_("Server error when deleting data."));
}
/* Do the deletion */
$query = sprintf('DELETE FROM %s WHERE %s = ?', $this->_params['table'], $owner_field);
try {
$this->_db->delete($query, $values);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception(_("Server error when deleting data."));
}
return $ids;
}
/**
* Saves the specified object in the SQL database.
*
* @param Turba_Object $object The object to save.
*
* @return string The object id, possibly updated.
* @throws Turba_Exception
*/
protected function _save(Turba_Object $object)
{
list($object_key, $object_id) = each(
$this->toDriverKeys(array('__key' => $object->getValue('__key')))
);
$attributes = $this->toDriverKeys($object->getAttributes());
$blob_fields = $this->toDriverKeys($this->getBlobs());
$date_fields = $this->toDriverKeys($this->getDateFields());
unset($attributes[$object_key]);
list($fields, $values) = $this->_prepareWrite(
$attributes,
$blob_fields,
$date_fields
);
try {
$this->_db->updateBlob(
$this->_params['table'],
array_combine($fields, $values),
array($object_key . ' = ?', array($object_id))
);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception(_("Server error when saving data."));
}
return $object_id;
}
/**
* Creates an object key for a new object.
*
* @param array $attributes The attributes (in driver keys) of the
* object being added.
*
* @return string A unique ID for the new object.
*/
protected function _makeKey(array $attributes)
{
return strval(new Horde_Support_Randomid());
}
/**
* Builds a piece of a search query.
*
* @param string $glue The glue to join the criteria (OR/AND).
* @param array $criteria The array of criteria.
*
* @return array An SQL fragment and a list of values suitable for binding
* as an array.
*/
protected function _buildSearchQuery($glue, array $criteria)
{
$clause = '';
$values = array();
foreach ($criteria as $key => $vals) {
if (!empty($vals['OR']) || !empty($vals['AND'])) {
if (!empty($clause)) {
$clause .= ' ' . $glue . ' ';
}
$binds = $this->_buildSearchQuery(!empty($vals['OR']) ? 'OR' : 'AND', $vals);
$clause .= '(' . $binds[0] . ')';
$values = array_merge($values, $binds[1]);
} else {
if (isset($vals['field'])) {
if (!empty($clause)) {
$clause .= ' ' . $glue . ' ';
}
$rhs = $this->_convertToDriver($vals['test']);
$binds = $this->_db->buildClause($vals['field'], $vals['op'], $rhs, true, array('begin' => !empty($vals['begin'])));
if (is_array($binds)) {
$clause .= $binds[0];
$values = array_merge($values, $binds[1]);
} else {
$clause .= $binds;
}
} else {
foreach ($vals as $test) {
if (!empty($test['OR']) || !empty($test['AND'])) {
if (!empty($clause)) {
$clause .= ' ' . $glue . ' ';
}
$binds = $this->_buildSearchQuery(!empty($vals['OR']) ? 'OR' : 'AND', $test);
$clause .= '(' . $binds[0] . ')';
$values = array_merge($values, $binds[1]);
} else {
if (!empty($clause)) {
$clause .= ' ' . $key . ' ';
}
$rhs = $this->_convertToDriver($test['test']);
if ($rhs == '' && $test['op'] == '=') {
$clause .= '(' . $this->_db->buildClause($test['field'], '=', $rhs) . ' OR ' . $test['field'] . ' IS NULL)';
} else {
$binds = $this->_db->buildClause($test['field'], $test['op'], $rhs, true, array('begin' => !empty($test['begin'])));
if (is_array($binds)) {
$clause .= $binds[0];
$values = array_merge($values, $binds[1]);
} else {
$clause .= $binds;
}
}
}
}
}
}
}
return array($clause, $values);
}
/**
* Converts a value from the driver's charset to the default charset.
*
* @param mixed $value A value to convert.
*
* @return mixed The converted value.
*/
protected function _convertFromDriver($value)
{
return Horde_String::convertCharset($value, $this->_db->getOption('charset'), 'UTF-8');
}
/**
* Converts a value from the default charset to the driver's charset.
*
* @param mixed $value A value to convert.
*
* @return mixed The converted value.
*/
protected function _convertToDriver($value)
{
return Horde_String::convertCharset($value, 'UTF-8', $this->_db->getOption('charset'));
}
/**
* Remove all entries owned by the specified user.
*
* @param string $user The user's data to remove.
*
* @throws Horde_Exception_PermissionDenied
*/
public function removeUserData($user)
{
// Make sure we are being called by an admin.
if (!$GLOBALS['registry']->isAdmin()) {
throw new Horde_Exception_PermissionDenied(_("Permission denied"));
}
$this->_deleteAll($user);
}
/**
* Obtain Turba_List of items to get TimeObjects out of.
*
* @param Horde_Date $start The starting date.
* @param Horde_Date $end The ending date.
* @param string $field The address book field containing the
* timeObject information (birthday, anniversary)
*
* @return Turba_List Object list.
* @throws Turba_Exception
*/
public function getTimeObjectTurbaList(Horde_Date $start, Horde_Date $end, $field)
{
$t_object = $this->toDriver($field);
$criteria = $this->makesearch(
array('__owner' => $this->getContactOwner()),
'AND',
array($this->toDriver('__owner') => true),
false);
// Limit to entries that actually contain a birthday and that are in the
// date range we are looking for.
$criteria['AND'][] = array(
'field' => $t_object,
'op' => '<>',
'test' => ''
);
$criteria['AND'][] = array(
'field' => $t_object,
'op' => '<>',
'test' => '0000-00-00'
);
if ($start->year == $end->year) {
$start = sprintf('%02d-%02d', $start->month, $start->mday);
$end = sprintf('%02d-%02d', $end->month, $end->mday);
$where = array('sql' => $t_object . ' IS NOT NULL AND SUBSTR('
. $t_object . ', 6, 5) BETWEEN ? AND ?',
'params' => array($start, $end));
} else {
$months = array();
$diff = ($end->month + 12) - $start->month;
$newDate = new Horde_Date(array(
'month' => $start->month,
'mday' => $start->mday,
'year' => $start->year
));
for ($i = 0; $i <= $diff; ++$i) {
$months[] = sprintf('%02d', $newDate->month++);
}
$where = array('sql' => $t_object . ' IS NOT NULL AND SUBSTR('
. $t_object . ', 6, 2) IN ('
. str_repeat('?,', count($months) - 1) . '?)',
'params' => $months);
}
$fields_pre = array(
'__key', '__type', '__owner', 'name', 'birthday', 'anniversary'
);
$fields = array();
foreach ($fields_pre as $field) {
$result = $this->toDriver($field);
if (is_array($result)) {
foreach ($result as $composite_field) {
$composite_result = $this->toDriver($composite_field);
if ($composite_result) {
$fields[] = $composite_result;
}
}
} elseif ($result) {
$fields[] = $result;
}
}
return $this->_toTurbaObjects($this->_internalSearch($criteria, $fields, array(), $where));
}
}
turba-4.2.12/lib/Driver/Vbook.php 0000664 0001750 0001750 00000011735 12654115107 014624 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/apache ASL
* @package Turba
*/
class Turba_Driver_Vbook extends Turba_Driver
{
/**
* Search type for this virtual address book.
*
* @var string
*/
public $searchType;
/**
* The search criteria that defines this virtual address book.
*
* @var array
*/
public $searchCriteria;
/**
*
* @see Turba_Driver::__construct
* @throws Turba_Exception
*/
public function __construct($name = '', array $params = array())
{
parent::__construct($name, $params);
/* Grab a reference to the share for this vbook. */
$this->_share = $this->_params['share'];
/* Load the underlying driver. */
$this->_driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($this->_params['source']);
$this->searchCriteria = empty($this->_params['criteria'])
? array()
: $this->_params['criteria'];
$this->searchType = (count($this->searchCriteria) > 1)
? 'advanced'
: 'basic';
}
/**
* Return the owner to use when searching or creating contacts in
* this address book.
*
* @return string
*/
protected function _getContactOwner()
{
return $this->_driver->getContactOwner();
}
/**
* Return all entries matching the combined searches represented by
* $criteria and the vitural address book's search criteria.
*
* @param array $criteria Array containing the search criteria.
* @param array $fields List of fields to return
* @param array $blobFileds Array of fields that contain
*
* @return array Hash containing the search results.
* @throws Turba_Exception
*/
protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only = false)
{
/* Add the passed in search criteria to the vbook criteria
* (which need to be mapped from turba fields to
* driver-specific fields). */
$new_criteria = array();
if (empty($criteria['AND'])) {
$new_criteria['AND'] = array(
$criteria,
$this->makeSearch($this->searchCriteria, 'AND', array())
);
} else {
$new_criteria = $criteria;
$new_criteria['AND'][] = $this->makeSearch($this->searchCriteria, 'AND', array());
}
$results = $this->_driver->_search($new_criteria, $fields, $blobFields);
return $count_only ? count($results) : $results;
}
/**
* Reads the requested entries from the underlying source.
*
* @param string $key The primary key field to use.
* @param mixed $ids The ids of the contacts to load.
* @param string $owner Only return contacts owned by this user.
* @param array $fields List of fields to return.
* @param array $blobFields Array of fields containing binary data.
* @param array $dateFields Array of fields containing date data.
* @since 4.2.0
*
* @return array Hash containing the search results.
* @throws Turba_Exception
*/
protected function _read($key, $ids, $owner, array $fields,
array $blobFields = array(),
array $dateFields = array())
{
return $this->_driver->_read($key, $ids, $owner, $fields, $blobFields, $dateFields);
}
/**
* Adds the specified contact to the addressbook.
*
* @param array $attributes The attribute values of the contact.
* @param array $blob_fields TODO
*
* @throws Turba_Exception
*/
protected function _add(array $attributes, array $blob_fields = array())
{
throw new Turba_Exception(_("You cannot add new contacts to a virtual address book"));
}
/**
* Not supported for virtual address books.
*
* @see Turba_Driver::_delete
* @throws Turba_Exception
*/
protected function _delete($object_key, $object_id)
{
throw new Turba_Exception(_("You cannot delete contacts from a virtual address book"));
}
/**
* @see Turba_Driver::_save
*/
protected function _save(Turba_Object $object)
{
return $this->_driver->save($object);
}
/**
* Check to see if the currently logged in user has requested permissions.
*
* @param integer $perm The permissions to check against.
*
* @return boolean True or False.
*/
public function hasPermission($perm)
{
return $this->_driver->hasPermission($perm);
}
}
turba-4.2.12/lib/Exception/NotSupported.php 0000664 0001750 0001750 00000000115 12654115107 016703 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/apache ASL
* @package Turba
*/
class Turba_Exception_ObjectExists extends Turba_Exception
{
}
turba-4.2.12/lib/Factory/Driver.php 0000664 0001750 0001750 00000011406 12654115107 015146 0 ustar jan jan
* @category Horde
* @license http://www.horde.org/licenses/apl.html APL
* @link http://pear.horde.org/index.php?package=Turba
* @package Turba
*/
/**
* A Horde_Injector:: based Turba_Driver:: factory.
*
* Copyright 2010-2016 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (APL). If you
* did not receive this file, see http://www.horde.org/licenses/apl.html.
*
* @author Michael Slusarz ' . _("No matching contacts") . '
'; } } /** * Renders the list contents into an HTML view. * * @param integer $numDisplayed Output parameter - the number of rows * rendered. * @param integer $min Minimum number of rows to display. * @param integer $max Maximum number of rows to display. * @param string $page The currently displayed page. * * @return string HTML to echo. */ public function getPage(&$numDisplayed, $min = 0, $max = null, $page = 0) { if (is_null($max)) { $max = count($this); } return $this->_get($numDisplayed, new Turba_View_List_PageFilter($min, $max), $page); } /** * Renders the list contents that match $alpha into an HTML view. * * @param integer $numDisplayed This will be set to the number of contacts * in the view. * @param string $alpha The letter to display. * * @return string HTML of the list. */ public function getAlpha(&$numDisplayed, $alpha) { return $this->_get($numDisplayed, new Turba_View_List_AlphaFilter($alpha), $alpha); } /** * Retrieves a column's name * * @param integer $i The zero-basd index of the column * * @return string */ public function getColumnName($i) { return Turba::getColumnName($i, $this->columns); } /** * @param integer $i The zero-based index of the column */ public function getSortInfoForColumn($i) { $sortorder = Turba::getPreferredSortOrder(); $column_name = $this->getColumnName($i); $i = 0; foreach ($sortorder as $sortfield) { if ($column_name == $sortfield['field']) { return array_merge($sortfield, array('rank' => $i)); } $i++; } return null; } /** * @param integer $i * @param string $title * * @return string */ public function getColumnSortImage($i, $title = null) { if (is_null($title)) { $title = _("Sort Direction"); } $sortdir = $this->getColumnSortDirection($i); if ($this->isPrimarySortColumn($i)) { return Horde_Themes_Image::tag($sortdir ? 'za.png' : 'az.png', array( 'alt' => $title )); } return Horde_Themes_Image::tag($sortdir ? 'za_secondary.png' : 'az_secondary.png', array( 'alt' => _("Sort Direction") )); } /** * Retrieves a natural language description of the sort order * * @return string */ public function getSortOrderDescription() { $description = array(); $sortorder = Turba::getPreferredSortOrder(); foreach ($sortorder as $elt) { $field = $elt['field']; if (!strlen($field) || ($field == 'lastname')) { $field = 'name'; } $description[] = $GLOBALS['attributes'][$field]['label']; } return join(', ', $description); } /** * @param integer $i The zero-based index of the column */ public function getColumnSortDirection($i) { $result = $this->getSortInfoForColumn($i); if (is_null($result)) { return null; } return $result['ascending'] ? 0 : 1; } /** * Determines whether we are sorting on the specified column * * @param integer $i The zero-based column index * * @return boolean */ public function isSortColumn($i) { return !is_null($this->getSortInfoForColumn($i)); } /** * Determines whether this is the first column to sort by * * @param integer $i The zero-based column index * * @return boolean */ public function isPrimarySortColumn($i) { $result = $this->getSortInfoForColumn($i); if (is_null($result)) { return false; } return ($result['rank'] == 0); } /** * @param integer $numDisplayed Set to the number of displayed contacts. * @param object $filter A Turba_View_List filter object. * @param string $page The currently displayed page. * * @return string */ protected function _get(&$numDisplayed, $filter, $page) { ob_start(); $width = floor(90 / (count($this->columns) + 1)); $own = $GLOBALS['prefs']->getValue('own_contact'); if (strpos($own, ';')) { list($own_source, $own_id) = explode(';', $own); } else { $own_source = $own_id = null; } $vars = Horde_Variables::getDefaultVariables(); include TURBA_TEMPLATES . '/browse/column_headers.inc'; $numDisplayed = 0; $this->list->reset(); while ($ob = $this->list->next()) { if ($filter->skip($ob)) { continue; } include TURBA_TEMPLATES . '/browse/row.inc'; $numDisplayed++; } include TURBA_TEMPLATES . '/browse/column_footers.inc'; return ob_get_clean(); } /** */ public function getAddSources() { global $addSources; // Create list of lists for Add to. $addToList = array(); $addToListSources = array(); foreach ($addSources as $src => $srcConfig) { if (!empty($srcConfig['map']['__type'])) { $addToListSources[] = array('key' => '', 'name' => ' ' . htmlspecialchars($srcConfig['title']), 'source' => htmlspecialchars($src)); $srcDriver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($src); try { $listList = $srcDriver->search( array('__type' => 'Group'), array( array( 'field' => 'name', 'ascending' => true ) ), 'AND', array('name') ); $listList->reset(); $currentList = Horde_Util::getFormData('key'); while ($listObject = $listList->next()) { if ($listObject->getValue('__key') != $currentList) { $addToList[] = array( 'name' => htmlspecialchars($listObject->getValue('name')), 'source' => htmlspecialchars($src), 'key' => htmlspecialchars($listObject->getValue('__key')) ); } } } catch (Turba_Exception $e) { $GLOBALS['notification']->push($e, 'horde.error'); } } } if ($addToListSources) { if ($addToList) { array_unshift($addToList, '- - - - - - - - -'); } $addToList = array_merge(array(_("Create a new Contact List in:")), $addToListSources, $addToList); $addToListSources = null; } return array($addToList, $addToListSources); } /* Countable methods. */ /** * Returns the number of Turba_Objects that are in the list. Use this to * hide internal implementation details from client objects. * * @return integer The number of objects in the list. */ public function count() { return $this->list->count(); } } turba-4.2.12/lib/.htaccess 0000664 0001750 0001750 00000000174 12654115107 013371 0 ustar jan jan* 'title' - The title for this resource. * 'desc' - A terse description of this resource. * 'view_url' - The URL to view this resource. * 'app' - The Horde application this resource belongs to. * 'icon' - URL to an image. **/ public function searchTags($names, $max = 10, $from = 0, $resource_type = '', $user = null, $raw = false) { global $injector, $registry; $results = $injector ->getInstance('Turba_Tagger') ->search( $names, array('user' => $user)); // Check for error or if we requested the raw data array. if ($raw) { return $results; } $return = array(); foreach ($results as $contact_uid) { try { $driver = $injector->getInstance('Turba_Factory_Driver'); foreach ($this->_getSources($sources) as $source) { $sdriver = $driver->create($source); if (!$sdriver->hasPermission(Horde_Perms::READ)) { continue; } $result = $sdriver->search(array('__uid' => $contact_uid)); if (count($result) == 0) { continue; } elseif (count($result) > 1) { throw new Turba_Exception(sprintf("Internal Horde Error: multiple Turba objects with same objectId %s.", $uid)); } foreach ($result->objects as $obj) { $return[] = array( 'title' => $obj->getValue('name'), 'desc' => $obj->getValue('name'), 'view_url' => $obj->url, 'app' => 'turba', 'icon' => $this->_getContactImageUrl($obj) ); } } } catch (Exception $e) { } } return $return; } protected function _getContactImageUrl($obj) { if ($photo = $obj->getValue('photo')) { try { $img = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Image')->create(); $img->loadString($photo['load']['data']); $img->resize(50, 50, true); $data = $img->raw(true); $type = $img->getContentType(); } catch (Horde_Image_Exception $e) { $data = $photo['load']['data']; $type = $obj->getValue('phototype'); } return Horde_Url_Data::create($type, $data); } } } turba-4.2.12/lib/Application.php 0000664 0001750 0001750 00000102654 12654115107 014555 0 ustar jan jan true, 'modseq' => true, ); /** */ public $version = 'H5 (4.2.12)'; /** */ protected function _bootstrap() { /* Add Turba-specific factories. */ $factories = array( 'Turba_Shares' => 'Turba_Factory_Shares', 'Turba_Tagger' => 'Turba_Factory_Tagger' ); foreach ($factories as $key => $val) { $GLOBALS['injector']->bindFactory($key, $val, 'create'); } } /** * Global variables defined: * $addSources - TODO * $attributes - (array) Attribute data from the config/attributes.php * file. * $browse_source_count - TODO * $cfgSources - TODO * $copymoveSources - TODO */ protected function _init() { global $conf, $injector, $registry, $session; if ($conf['tags']['enabled']) { /* For now, autoloading the Content_* classes depend on there being * a registry entry for the 'content' application that contains at * least the fileroot entry. */ $injector->getInstance('Horde_Autoloader') ->addClassPathMapper( new Horde_Autoloader_ClassPathMapper_Prefix('/^Content_/', $registry->get('fileroot', 'content') . '/lib/')); if (!class_exists('Content_Tagger')) { throw new Horde_Exception(_("The Content_Tagger class could not be found. Make sure the Content application is installed.")); } } // Turba source and attribute configuration. $attributes = $registry->loadConfigFile('attributes.php', 'attributes', 'turba')->config['attributes']; $cfgSources = Turba::availableSources(); /* UGLY UGLY UGLY - we should NOT be using this as a global * variable all over the place. */ $GLOBALS['cfgSources'] = &$cfgSources; // See if any of our sources are configured to use Horde_Share. foreach ($cfgSources as $key => $cfg) { if (!empty($cfg['use_shares'])) { // Create a share instance. $session->set('turba', 'has_share', true); $cfgSources = Turba::getConfigFromShares($cfgSources); break; } } $GLOBALS['attributes'] = $attributes; $cfgSources = Turba::permissionsFilter($cfgSources); // Build the directory sources select widget. if (empty(Turba::$source)) { if (!(Turba::$source = $session->get('turba', 'source'))) { Turba::$source = Turba::getDefaultAddressbook(); } Turba::$source = Horde_Util::getFormData('source', Turba::$source); } $GLOBALS['browse_source_count'] = 0; foreach (Turba::getAddressBooks() as $key => $curSource) { if (!empty($curSource['browse'])) { $GLOBALS['browse_source_count']++; if (empty(Turba::$source)) { Turba::$source = $key; } } } if (empty($cfgSources[Turba::$source]['browse'])) { Turba::$source = Turba::getDefaultAddressbook(); } $session->set('turba', 'source', Turba::$source); $GLOBALS['addSources'] = Turba::getAddressBooks(Horde_Perms::EDIT, array('require_add' => true)); $GLOBALS['copymoveSources'] = $GLOBALS['addSources']; unset($GLOBALS['copymoveSources'][Turba::$source]); } /** */ public function perms() { $cfgSources = Turba::availableSources(); $perms = array( 'sources' => array( 'title' => _("Sources") ) ); // Run through every contact source. foreach ($cfgSources as $source => $curSource) { $perms['sources:' . $source] = array( 'title' => $curSource['title'] ); $perms['sources:' . $source . ':max_contacts'] = array( 'title' => _("Maximum Number of Contacts"), 'type' => 'int' ); } return $perms; } /** */ public function getInitialPage() { global $registry; switch ($registry->getView()) { case $registry::VIEW_SMARTMOBILE: return strval(Horde::url('smartmobile.php')->setRaw(true)); break; default: return null; } } /** */ public function menu($menu) { if ($GLOBALS['browse_source_count']) { $menu->add(Horde::url('browse.php'), _("_Browse"), 'turba-browse', null, null, null, (($GLOBALS['prefs']->getValue('initial_page') == 'browse.php' && basename($_SERVER['PHP_SELF']) == 'index.php' && basename(dirname($_SERVER['PHP_SELF'])) != 'addressbooks') || (basename($_SERVER['PHP_SELF']) == 'browse.php' && Horde_Util::getFormData('key') != '**search')) ? 'current' : '__noselection'); } $menu->add(Horde::url('search.php'), _("_Search"), 'turba-search', null, null, null, (($GLOBALS['prefs']->getValue('initial_page') == 'search.php' && basename($_SERVER['PHP_SELF']) == 'index.php' && strpos($_SERVER['PHP_SELF'], 'addressbooks/index.php') === false) || (basename($_SERVER['PHP_SELF']) == 'browse.php' && Horde_Util::getFormData('key') == '**search')) ? 'current' : null); /* Import/Export */ if ($GLOBALS['conf']['menu']['import_export']) { $menu->add(Horde::url('data.php'), _("_Import/Export"), 'horde-data'); } } /** * Add additional items to the sidebar. * * @param Horde_View_Sidebar $sidebar The sidebar object. */ public function sidebar($sidebar) { if (count($GLOBALS['addSources'])) { $sidebar->addNewButton(_("_New Contact"), Horde::url('add.php')); } $user = $GLOBALS['registry']->getAuth(); $edit = Horde::url('addressbooks/edit.php'); $url = Horde::url(''); $sidebar->containers['my'] = array( 'header' => array( 'id' => 'turba-toggle-my', 'label' => _("My Address Books"), 'collapsed' => false, ), ); if ($GLOBALS['registry']->getAuth() && $GLOBALS['session']->get('turba', 'has_share') && !empty($GLOBALS['conf']['shares']['source'])) { $create = true; $sidebar->containers['my']['header']['add'] = array( 'url' => Horde::url('addressbooks/create.php'), 'label' => _("Create a new Address Book"), ); } $shares = array(); $shared = array(); foreach (Turba::listShares(false, Horde_Perms::SHOW) as $id => $abook) { $row = array( 'selected' => $id == Turba::$source, 'url' => $url->copy()->add('source', $id), 'label' => $abook->get('name'), 'edit' => $edit->copy()->add('a', $abook->getName()), 'type' => 'radiobox', ); if ($abook->get('owner') && $abook->get('owner') == $user) { $sidebar->addRow($row, 'my'); if ($row['selected']) { $sidebar->containers['my']['header']['collapsed'] = false; } } else { if ($abook->get('owner')) { $row['label'] .= ' [' . $GLOBALS['registry']->convertUsername($abook->get('owner'), false) . ']'; } $shared[] = $row; } $shares[$id] = true; } if (!empty($create) || count($shared)) { $sidebar->containers['shared'] = array( 'header' => array( 'id' => 'turba-toggle-shared', 'label' => _("Shared Address Books"), 'collapsed' => true, ), ); foreach ($shared as $row) { $sidebar->addRow($row, 'shared'); if ($row['selected']) { $sidebar->containers['shared']['header']['collapsed'] = false; } } } $sidebar->containers['other'] = array( 'header' => array( 'id' => 'turba-toggle-other', 'label' => _("Other Address Books"), 'collapsed' => true, ), ); foreach (Turba::getAddressBooks(Horde_Perms::SHOW) as $id => $abook) { if (isset($shares[$id])) { continue; } $row = array( 'selected' => $id == Turba::$source, 'url' => $url->copy()->add('source', $id), 'label' => $abook['title'], 'type' => 'radiobox', ); $sidebar->addRow($row, 'other'); if ($row['selected']) { $sidebar->containers['other']['header']['collapsed'] = false; } } } /** * Returns values for
* array('name' => array('John Doe' => Turba_List, ...), ...)
*
* @throws Turba_Exception
*/
public function searchDuplicates()
{
return array();
}
/**
* Takes an array of object hashes and returns a Turba_List
* containing the correct Turba_Objects
*
* @param array $objects An array of object hashes (keyed to backend).
* @param array $sort_order Array of hashes describing sort fields. Each
* hash has the following fields:
* * ascending - (boolean) Indicating sort direction. * field - (string) Sort field. ** * @return Turba_List A list object. */ protected function _toTurbaObjects(array $objects, array $sort_order = null) { $list = new Turba_List(); foreach ($objects as $object) { /* Translate the driver-specific fields in the result back to the * more generalized common Turba attributes using the map. */ $object = $this->toTurbaKeys($object); $done = false; if (!empty($object['__type']) && ucwords($object['__type']) != 'Object') { $class = 'Turba_Object_' . ucwords($object['__type']); if (class_exists($class)) { $list->insert(new $class($this, $object, $this->_objectOptions)); $done = true; } } if (!$done) { $list->insert(new Turba_Object($this, $object, $this->_objectOptions)); } } $list->sort($sort_order); /* Return the filtered (sorted) results. */ return $list; } /** * Returns a list of birthday or anniversary hashes from this source for a * certain period. * * @param Horde_Date $start The start date of the valid period. * @param Horde_Date $end The end date of the valid period. * @param string $category The timeObjects category to return. * * @return array A list of timeObject hashes. * @throws Turba Exception */ public function listTimeObjects(Horde_Date $start, Horde_Date $end, $category) { try { $res = $this->getTimeObjectTurbaList($start, $end, $category); } catch (Turba_Exception $e) { /* Try the default implementation before returning an error */ $res = $this->_getTimeObjectTurbaListFallback($start, $end, $category); } $t_objects = array(); while ($ob = $res->next()) { $t_object = $ob->getValue($category); if (empty($t_object)) { continue; } try { $t_object = new Horde_Date($t_object); } catch (Horde_Date_Exception $e) { continue; } if ($t_object->compareDate($end) > 0) { continue; } $t_object_end = new Horde_Date($t_object); ++$t_object_end->mday; $key = $ob->getValue('__key'); // Calculate the age of the time object if ($start->year == $end->year || $end->year == 9999) { $age = $start->year - $t_object->year; } elseif ($t_object->month <= $end->month) { // t_object must be in later year $age = $end->year - $t_object->year; } else { // t_object must be in earlier year $age = $start->year - $t_object->year; } $title = sprintf(_("%d. %s of %s"), $age, $GLOBALS['attributes'][$category]['label'], $ob->getValue('name')); $t_objects[] = array( 'id' => $key, 'title' => $title, 'start' => sprintf('%d-%02d-%02dT00:00:00', $t_object->year, $t_object->month, $t_object->mday), 'end' => sprintf('%d-%02d-%02dT00:00:00', $t_object_end->year, $t_object_end->month, $t_object_end->mday), 'recurrence' => array('type' => Horde_Date_Recurrence::RECUR_YEARLY_DATE, 'interval' => 1), 'params' => array('source' => $this->_name, 'key' => $key), 'link' => Horde::url('contact.php', true)->add(array('source' => $this->_name, 'key' => $key))->setRaw(true) ); } return $t_objects; } /** * Default implementation for obtaining a Turba_List to get TimeObjects * out of. * * @param Horde_Date $start The starting date. * @param Horde_Date $end The ending date. * @param string $field The address book field containing the * timeObject information (birthday, * anniversary). * * @return Turba_List A list of objects. * @throws Turba_Exception */ public function getTimeObjectTurbaList(Horde_Date $start, Horde_Date $end, $field) { return $this->_getTimeObjectTurbaListFallback($start, $end, $field); } /** * Default implementation for obtaining a Turba_List to get TimeObjects * out of. * * @param Horde_Date $start The starting date. * @param Horde_Date $end The ending date. * @param string $field The address book field containing the * timeObject information (birthday, * anniversary). * * @return Turba_List A list of objects. * @throws Turba_Exception */ protected function _getTimeObjectTurbaListFallback(Horde_Date $start, Horde_Date $end, $field) { return $this->search(array(), null, 'AND', array('name', $field)); } /** * Retrieves a set of objects from the source. * * @param array $objectIds The unique ids of the objects to retrieve. * * @return array The array of retrieved objects (Turba_Objects). * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function getObjects(array $objectIds) { $objects = $this->_read($this->map['__key'], $objectIds, $this->getContactOwner(), array_values($this->fields), $this->toDriverKeys($this->getBlobs()), $this->toDriverKeys($this->getDateFields())); if (!is_array($objects)) { throw new Horde_Exception_NotFound(); } $results = array(); foreach ($objects as $object) { $object = $this->toTurbaKeys($object); $done = false; if (!empty($object['__type']) && ucwords($object['__type']) != 'Object') { $class = 'Turba_Object_' . ucwords($object['__type']); if (class_exists($class)) { $results[] = new $class($this, $object, $this->_objectOptions); $done = true; } } if (!$done) { $results[] = new Turba_Object($this, $object, $this->_objectOptions); } } return $results; } /** * Retrieves one object from the source. * * @param string $objectId The unique id of the object to retrieve. * * @return Turba_Object The retrieved object. * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function getObject($objectId) { $result = $this->getObjects(array($objectId)); if (empty($result[0])) { throw new Horde_Exception_NotFound(); } $result = $result[0]; if (!isset($this->map['__owner'])) { $result->attributes['__owner'] = $this->getContactOwner(); } return $result; } /** * Adds a new entry to the contact source. * * @param array $attributes The attributes of the new object to add. * * @return string The new __key value on success. * @throws Turba_Exception */ public function add(array $attributes) { /* Only set __type and __owner if they are not already set. */ if (!isset($attributes['__type'])) { $attributes['__type'] = 'Object'; } if (isset($this->map['__owner']) && !isset($attributes['__owner'])) { $attributes['__owner'] = $this->getContactOwner(); } if (!isset($attributes['__uid'])) { $attributes['__uid'] = $this->_makeUid(); } $key = $attributes['__key'] = $this->_makeKey($this->toDriverKeys($attributes)); $uid = $attributes['__uid']; /* Remember any tags, since toDriverKeys will remove them. (They are not stored in the Turba backend so have no mapping). */ $tags = isset($attributes['__tags']) ? $attributes['__tags'] : false; $attributes = $this->toDriverKeys($attributes); $this->_add($attributes, $this->toDriverKeys($this->getBlobs()), $this->toDriverKeys($this->getDateFields())); /* Add tags. */ if ($tags !== false) { $GLOBALS['injector']->getInstance('Turba_Tagger')->tag( $uid, $tags, $GLOBALS['registry']->getAuth(), 'contact' ); } /* Log the creation of this item in the history log. */ try { $GLOBALS['injector']->getInstance('Horde_History') ->log('turba:' . $this->getName() . ':' . $uid, array('action' => 'add'), true); } catch (Exception $e) { Horde::log($e, 'ERR'); } return $key; } /** * Returns ability of the backend to add new contacts. * * @return boolean Can backend add? */ public function canAdd() { return $this->_canAdd(); } /** * Returns ability of the backend to add new contacts. * * @return boolean Can backend add? */ protected function _canAdd() { return false; } /** * Deletes the specified entry from the contact source. * * @param string $object_id The ID of the object to delete. * * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function delete($object_id) { $object = $this->getObject($object_id); if (!$object->hasPermission(Horde_Perms::DELETE)) { throw new Turba_Exception(_("Permission denied")); } $this->_delete($this->toDriver('__key'), $object_id); $own_contact = $GLOBALS['prefs']->getValue('own_contact'); if (!empty($own_contact)) { @list(,$id) = explode(';', $own_contact); if ($id == $object_id) { $GLOBALS['prefs']->setValue('own_contact', ''); } } /* Log the deletion of this item in the history log. */ if ($object->getValue('__uid')) { try { $GLOBALS['injector']->getInstance('Horde_History')->log($object->getGuid(), array('action' => 'delete'), true); } catch (Exception $e) { Horde::log($e, 'ERR'); } } /* Remove any CalDAV mappings. */ try { $davStorage = $GLOBALS['injector'] ->getInstance('Horde_Dav_Storage'); try { $davStorage ->deleteInternalObjectId($object_id, $this->_name); } catch (Horde_Exception $e) { Horde::log($e); } } catch (Horde_Exception $e) { } /* Remove tags */ $GLOBALS['injector']->getInstance('Turba_Tagger') ->replaceTags($object->getValue('__uid'), array(), $this->getContactOwner(), 'contact'); /* Might have tags disabled, hence no Content_* objects autoloadable. */ try { /* Tell content we removed the object */ $GLOBALS['injector']->getInstance('Content_Objects_Manager') ->delete(array($object->getValue('__uid')), 'contact'); } catch (Horde_Exception $e) {} } /** * Deletes all contacts from an address book. * * @param string $sourceName The identifier of the address book to * delete. If omitted, will clear the current * user's 'default' address book for this * source type. * * @throws Turba_Exception */ public function deleteAll($sourceName = null) { if (!$this->hasCapability('delete_all')) { throw new Turba_Exception('Not supported'); } $ids = $this->_deleteAll($sourceName); // Update Horde_History and Tagger $history = $GLOBALS['injector']->getInstance('Horde_History'); try { foreach ($ids as $uid) { // This is slightly hackish, but it saves us from having to // create and save an array of Turba_Objects before we delete // them, just to be able to calculate this using // Turba_Object#getGuid $guid = 'turba:' . $this->getName() . ':' . $uid; $history->log($guid, array('action' => 'delete'), true); // Remove tags. $GLOBALS['injector']->getInstance('Turba_Tagger') ->replaceTags($uid, array(), $this->getContactOwner(), 'contact'); /* Tell content we removed the object */ $GLOBALS['injector']->getInstance('Content_Objects_Manager') ->delete(array($uid), 'contact'); } } catch (Exception $e) { Horde::log($e, 'ERR'); } } /** * Deletes all contacts from an address book. * * @param string $sourceName The source to remove all contacts from. * * @return array An array of UIDs that have been deleted. * @throws Turba_Exception */ protected function _deleteAll() { return array(); } /** * Modifies an existing entry in the contact source. * * @param Turba_Object $object The object to update. * * @return string The object id, possibly updated. * @throws Turba_Exception */ public function save(Turba_Object $object) { $object_id = $this->_save($object); if ($uid = $object->getValue('__uid')) { /* Update tags. */ if (!is_null($tags = $object->getValue('__tags'))) { $GLOBALS['injector']->getInstance('Turba_Tagger')->replaceTags( $uid, $tags, $this->getContactOwner(), 'contact' ); } /* Log the modification of this item in the history log. */ try { $GLOBALS['injector']->getInstance('Horde_History')->log($object->getGuid(), array('action' => 'modify'), true); } catch (Exception $e) { Horde::log($e, 'ERR'); } } return $object_id; } /** * Returns the criteria available for this source except '__key'. * * @return array An array containing the criteria. */ public function getCriteria() { return array_diff_key($this->map, array('__key' => true)); } /** * Returns all non-composite fields for this source. Useful for importing * and exporting data, etc. * * @return array The field list. */ public function getFields() { return array_flip($this->fields); } /** * Exports a given Turba_Object as an iCalendar vCard. * * @param Turba_Object $object Turba_Object. * @param string $version The vcard version to produce. * @param array $fields Hash of field names and * Horde_SyncMl_Property properties with the * requested fields. * @param boolean $skipEmpty Whether to skip empty fields. * * @return Horde_Icalendar_Vcard A vcard object. */ public function tovCard(Turba_Object $object, $version = '2.1', array $fields = null, $skipEmpty = false) { global $injector; $hash = $object->getAttributes(); $attributes = array_keys($this->map); $vcard = new Horde_Icalendar_Vcard($version); $formattedname = false; $charset = ($version == '2.1') ? array('CHARSET' => 'UTF-8') : array(); $hooks = $injector->getInstance('Horde_Core_Hooks'); $decode_hook = $hooks->hookExists('decode_attribute', 'turba'); // Tags are stored externally to Turba, so they don't appear in the // source map. $attributes[] = '__tags'; foreach ($attributes as $key) { $val = $object->getValue($key); if ($skipEmpty && !is_array($val) && !strlen($val)) { continue; } if ($decode_hook) { try { $val = $hooks->callHook( 'decode_attribute', 'turba', array($key, $val, $object) ); } catch (Turba_Exception $e) {} } switch ($key) { case '__uid': $vcard->setAttribute('UID', $val); break; case 'name': if ($fields && !isset($fields['FN'])) { break; } $vcard->setAttribute('FN', $val, Horde_Mime::is8bit($val) ? $charset : array()); $formattedname = true; break; case 'nickname': case 'alias': $params = Horde_Mime::is8bit($val) ? $charset : array(); if (!$fields || isset($fields['NICKNAME'])) { $vcard->setAttribute('NICKNAME', $val, $params); } if (!$fields || isset($fields['X-EPOCSECONDNAME'])) { $vcard->setAttribute('X-EPOCSECONDNAME', $val, $params); } break; case 'homeAddress': if ($fields && (!isset($fields['LABEL']) || (isset($fields['LABEL']->Params['TYPE']) && !$this->_hasValEnum($fields['LABEL']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if ($version == '2.1') { $vcard->setAttribute('LABEL', $val, array('HOME' => null)); } else { $vcard->setAttribute('LABEL', $val, array('TYPE' => 'HOME')); } break; case 'workAddress': if ($fields && (!isset($fields['LABEL']) || (isset($fields['LABEL']->Params['TYPE']) && !$this->_hasValEnum($fields['LABEL']->Params['TYPE']->ValEnum, 'WORK')))) { break; } if ($version == '2.1') { $vcard->setAttribute('LABEL', $val, array('WORK' => null)); } else { $vcard->setAttribute('LABEL', $val, array('TYPE' => 'WORK')); } break; case 'otherAddress': if ($fields && !isset($fields['LABEL'])) { break; } $vcard->setAttribute('LABEL', $val); break; case 'phone': if ($fields && !isset($fields['TEL'])) { break; } $vcard->setAttribute('TEL', $val); break; case 'homePhone': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('HOME' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('HOME', 'VOICE'))); } break; case 'workPhone': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('WORK' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('WORK', 'VOICE'))); } break; case 'cellPhone': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('CELL' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('CELL', 'VOICE'))); } break; case 'homeCellPhone': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL')) { if ($version == '2.1') { $parameters['CELL'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('CELL', 'VOICE'); } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('HOME', 'VOICE'); } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('CELL' => null, 'HOME' => null, 'VOICE' => null); } else { $parameters = array('TYPE' => array('CELL', 'HOME', 'VOICE')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workCellPhone': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL')) { if ($version == '2.1') { $parameters['CELL'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('CELL', 'VOICE'); } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('WORK', 'VOICE'); } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('CELL' => null, 'WORK' => null, 'VOICE' => null); } else { $parameters = array('TYPE' => array('CELL', 'WORK', 'VOICE')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'videoCall': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('VIDEO' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'VIDEO')); } break; case 'homeVideoCall': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO')) { if ($version == '2.1') { $parameters['VIDEO'] = null; } else { $parameters['TYPE'] = 'VIDEO'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; } else { $parameters['TYPE'] = 'HOME'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('VIDEO' => null, 'HOME' => null); } else { $parameters = array('TYPE' => array('VIDEO', 'HOME')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workVideoCall': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO')) { if ($version == '2.1') { $parameters['VIDEO'] = null; } else { $parameters['TYPE'] = 'VIDEO'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; } else { $parameters['TYPE'] = 'WORK'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('VIDEO' => null, 'WORK' => null); } else { $parameters = array('TYPE' => array('VIDEO', 'WORK')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'sip': if ($fields && !isset($fields['X-SIP'])) { break; } $vcard->setAttribute('X-SIP', $val); break; case 'ptt': if ($fields && (!isset($fields['X-SIP']) || (isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'POC')))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('POC' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'POC')); } break; case 'voip': if ($fields && (!isset($fields['X-SIP']) || (isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'VOIP')))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('VOIP' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'VOIP')); } break; case 'shareView': if ($fields && (!isset($fields['X-SIP']) || (isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'SWIS')))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('SWIS' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'SWIS')); } break; case 'imaddress': if ($fields && !isset($fields['X-WV-ID'])) { break; } $vcard->setAttribute('X-WV-ID', $val); break; case 'fax': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('FAX' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'FAX')); } break; case 'homeFax': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX')) { if ($version == '2.1') { $parameters['FAX'] = null; } else { $parameters['TYPE'] = 'FAX'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; } else { $parameters['TYPE'] = 'HOME'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('FAX' => null, 'HOME' => null); } else { $parameters = array('TYPE' => array('FAX', 'HOME')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workFax': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX')) { if ($version == '2.1') { $parameters['FAX'] = null; } else { $parameters['TYPE'] = 'FAX'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; } else { $parameters['TYPE'] = 'WORK'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('FAX' => null, 'WORK' => null); } else { $parameters = array('TYPE' => array('FAX', 'WORK')); } } $vcard->setAttribute('TEL', $val, $parameters); if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('FAX' => null, 'WORK' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('FAX', 'WORK'))); } break; case 'pager': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'PAGER')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('PAGER' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'PAGER')); } break; case 'email': if ($fields && !isset($fields['EMAIL'])) { break; } if ($version == '2.1') { $vcard->setAttribute( 'EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('INTERNET' => null)); } else { $vcard->setAttribute( 'EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'INTERNET')); } break; case 'homeEmail': if ($fields && (!isset($fields['EMAIL']) || (isset($fields['EMAIL']->Params['TYPE']) && !$this->_hasValEnum($fields['EMAIL']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if ($version == '2.1') { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('HOME' => null)); } else { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'HOME')); } break; case 'workEmail': if ($fields && (!isset($fields['EMAIL']) || (isset($fields['EMAIL']->Params['TYPE']) && !$this->_hasValEnum($fields['EMAIL']->Params['TYPE']->ValEnum, 'WORK')))) { break; } if ($version == '2.1') { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('WORK' => null)); } else { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'WORK')); } break; case 'emails': if ($fields && !isset($fields['EMAIL'])) { break; } $emails = explode(',', $val); foreach ($emails as $email) { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($email)); } break; case 'title': if ($fields && !isset($fields['TITLE'])) { break; } $vcard->setAttribute('TITLE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case 'role': if ($fields && !isset($fields['ROLE'])) { break; } $vcard->setAttribute('ROLE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case 'notes': if ($fields && !isset($fields['NOTE'])) { break; } $vcard->setAttribute('NOTE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case '__tags': $val = $injector->getInstance('Turba_Tagger')->split($val); case 'businessCategory': // No CATEGORIES in vCard 2.1 if ($version == '2.1' || ($fields && !isset($fields['CATEGORIES']))) { break; } $vcard->setAttribute('CATEGORIES', null, array(), true, $val); break; case 'anniversary': if (!$fields || isset($fields['X-ANNIVERSARY'])) { $vcard->setAttribute('X-ANNIVERSARY', $val); } break; case 'spouse': if (!$fields || isset($fields['X-SPOUSE'])) { $vcard->setAttribute('X-SPOUSE', $val); } break; case 'children': if (!$fields || isset($fields['X-CHILDREN'])) { $vcard->setAttribute('X-CHILDREN', $val); } break; case 'website': if ($fields && !isset($fields['URL'])) { break; } $vcard->setAttribute('URL', $val); break; case 'homeWebsite': if ($fields && (!isset($fields['URL']) || (isset($fields['URL']->Params['TYPE']) && !$this->_hasValEnum($fields['URL']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if ($version == '2.1') { $vcard->setAttribute('URL', $val, array('HOME' => null)); } else { $vcard->setAttribute('URL', $val, array('TYPE' => 'HOME')); } break; case 'workWebsite': if ($fields && (!isset($fields['URL']) || (isset($fields['URL']->Params['TYPE']) && !$this->_hasValEnum($fields['URL']->Params['TYPE']->ValEnum, 'WORK')))) { break; } if ($version == '2.1') { $vcard->setAttribute('URL', $val, array('WORK' => null)); } else { $vcard->setAttribute('URL', $val, array('TYPE' => 'WORK')); } break; case 'freebusyUrl': if ($version == '2.1' || ($fields && !isset($fields['FBURL']))) { break; } $vcard->setAttribute('FBURL', $val); break; case 'birthday': if ($fields && !isset($fields['BDAY'])) { break; } $vcard->setAttribute('BDAY', $val); break; case 'timezone': if ($fields && !isset($fields['TZ'])) { break; } $vcard->setAttribute('TZ', $val, array('VALUE' => 'text')); break; case 'latitude': if ($fields && !isset($fields['GEO'])) { break; } if (isset($hash['longitude'])) { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['longitude'])); } break; case 'homeLatitude': if ($fields && (!isset($fields['GEO']) || (isset($fields['GEO']->Params['TYPE']) && !$this->_hasValEnum($fields['GEO']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if (isset($hash['homeLongitude'])) { if ($version == '2.1') { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['homeLongitude']), array('HOME' => null)); } else { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['homeLongitude']), array('TYPE' => 'HOME')); } } break; case 'workLatitude': if ($fields && (!isset($fields['GEO']) || (isset($fields['GEO']->Params['TYPE']) && !$this->_hasValEnum($fields['GEO']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if (isset($hash['workLongitude'])) { if ($version == '2.1') { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['workLongitude']), array('WORK' => null)); } else { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['workLongitude']), array('TYPE' => 'WORK')); } } break; case 'photo': case 'logo': $name = Horde_String::upper($key); $params = array(); if (strlen($hash[$key])) { $params['ENCODING'] = 'b'; } if (isset($hash[$key . 'type'])) { $params['TYPE'] = $hash[$key . 'type']; } if ($fields && (!isset($fields[$name]) || (isset($params['TYPE']) && isset($fields[$name]->Params['TYPE']) && !$this->_hasValEnum($fields[$name]->Params['TYPE']->ValEnum, $params['TYPE'])))) { break; } $vcard->setAttribute($name, base64_encode($hash[$key]), $params); break; } } // No explicit firstname/lastname in data source: we have to guess. if (!isset($hash['lastname']) && isset($hash['name'])) { $this->_guessName($hash); } $a = array( Horde_Icalendar_Vcard::N_FAMILY => isset($hash['lastname']) ? $hash['lastname'] : '', Horde_Icalendar_Vcard::N_GIVEN => isset($hash['firstname']) ? $hash['firstname'] : '', Horde_Icalendar_Vcard::N_ADDL => isset($hash['middlenames']) ? $hash['middlenames'] : '', Horde_Icalendar_Vcard::N_PREFIX => isset($hash['namePrefix']) ? $hash['namePrefix'] : '', Horde_Icalendar_Vcard::N_SUFFIX => isset($hash['nameSuffix']) ? $hash['nameSuffix'] : '', ); $val = implode(';', $a); if (!$fields || isset($fields['N'])) { $vcard->setAttribute('N', $val, Horde_Mime::is8bit($val) ? $charset : array(), false, $a); } if (!$formattedname && (!$fields || isset($fields['FN']))) { if ($object->getValue('name')) { $val = $object->getValue('name'); } elseif (!empty($this->alternativeName) && isset($hash[$this->alternativeName])) { $val = $hash[$this->alternativeName]; } else { $val = ''; } $vcard->setAttribute('FN', $val, Horde_Mime::is8bit($val) ? $charset : array()); } $org = array(); if (!empty($hash['company']) || (!$skipEmpty && array_key_exists('company', $hash))) { $org[] = $hash['company']; } if (!empty($hash['department']) || (!$skipEmpty && array_key_exists('department', $hash))) { $org[] = $hash['department']; } if (count($org) && (!$fields || isset($fields['ORG']))) { $val = implode(';', $org); $vcard->setAttribute('ORG', $val, Horde_Mime::is8bit($val) ? $charset : array(), false, $org); } if ((!$fields || isset($fields['ADR'])) && (!empty($hash['commonAddress']) || !empty($hash['commonStreet']) || !empty($hash['commonPOBox']) || !empty($hash['commonExtended']) || !empty($hash['commonCity']) || !empty($hash['commonProvince']) || !empty($hash['commonPostalCode']) || !empty($hash['commonCountry']) || (!$skipEmpty && (array_key_exists('commonAddress', $hash) || array_key_exists('commonStreet', $hash) || array_key_exists('commonPOBox', $hash) || array_key_exists('commonExtended', $hash) || array_key_exists('commonCity', $hash) || array_key_exists('commonProvince', $hash) || array_key_exists('commonPostalCode', $hash) || array_key_exists('commonCountry', $hash))))) { /* We can't know if this particular Turba source uses a single * address field or multiple for * street/city/province/postcode/country. Try to deal with * both. */ if (isset($hash['commonAddress']) && !isset($hash['commonStreet'])) { $hash['commonStreet'] = $hash['commonAddress']; } $a = array( Horde_Icalendar_Vcard::ADR_POB => isset($hash['commonPOBox']) ? $hash['commonPOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['commonExtended']) ? $hash['commonExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['commonStreet']) ? $hash['commonStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['commonCity']) ? $hash['commonCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['commonProvince']) ? $hash['commonProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['commonPostalCode']) ? $hash['commonPostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['commonCountry']) ? Horde_Nls::getCountryISO($hash['commonCountry']) : '', ); $val = implode(';', $a); if ($version == '2.1') { $params = array(); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => ''); } $vcard->setAttribute('ADR', $val, $params, true, $a); } if ((!$fields || (isset($fields['ADR']) && (!isset($fields['ADR']->Params['TYPE']) || $this->_hasValEnum($fields['ADR']->Params['TYPE']->ValEnum, 'HOME')))) && (!empty($hash['homeAddress']) || !empty($hash['homeStreet']) || !empty($hash['homePOBox']) || !empty($hash['homeExtended']) || !empty($hash['homeCity']) || !empty($hash['homeProvince']) || !empty($hash['homePostalCode']) || !empty($hash['homeCountry']) || (!$skipEmpty && (array_key_exists('homeAddress', $hash) || array_key_exists('homeStreet', $hash) || array_key_exists('homePOBox', $hash) || array_key_exists('homeExtended', $hash) || array_key_exists('homeCity', $hash) || array_key_exists('homeProvince', $hash) || array_key_exists('homePostalCode', $hash) || array_key_exists('homeCountry', $hash))))) { if (isset($hash['homeAddress']) && !isset($hash['homeStreet'])) { $hash['homeStreet'] = $hash['homeAddress']; } $a = array( Horde_Icalendar_Vcard::ADR_POB => isset($hash['homePOBox']) ? $hash['homePOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['homeExtended']) ? $hash['homeExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['homeStreet']) ? $hash['homeStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['homeCity']) ? $hash['homeCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['homeProvince']) ? $hash['homeProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['homePostalCode']) ? $hash['homePostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['homeCountry']) ? Horde_Nls::getCountryISO($hash['homeCountry']) : '', ); $val = implode(';', $a); if ($version == '2.1') { $params = array('HOME' => null); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => 'HOME'); } $vcard->setAttribute('ADR', $val, $params, true, $a); } if ((!$fields || (isset($fields['ADR']) && (!isset($fields['ADR']->Params['TYPE']) || $this->_hasValEnum($fields['ADR']->Params['TYPE']->ValEnum, 'WORK')))) && (!empty($hash['workAddress']) || !empty($hash['workStreet']) || !empty($hash['workPOBox']) || !empty($hash['workExtended']) || !empty($hash['workCity']) || !empty($hash['workProvince']) || !empty($hash['workPostalCode']) || !empty($hash['workCountry']) || (!$skipEmpty && (array_key_exists('workAddress', $hash) || array_key_exists('workStreet', $hash) || array_key_exists('workPOBox', $hash) || array_key_exists('workExtended', $hash) || array_key_exists('workCity', $hash) || array_key_exists('workProvince', $hash) || array_key_exists('workPostalCode', $hash) || array_key_exists('workCountry', $hash))))) { if (isset($hash['workAddress']) && !isset($hash['workStreet'])) { $hash['workStreet'] = $hash['workAddress']; } $a = array( Horde_Icalendar_Vcard::ADR_POB => isset($hash['workPOBox']) ? $hash['workPOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['workExtended']) ? $hash['workExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['workStreet']) ? $hash['workStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['workCity']) ? $hash['workCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['workProvince']) ? $hash['workProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['workPostalCode']) ? $hash['workPostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['workCountry']) ? Horde_Nls::getCountryISO($hash['workCountry']) : '', ); $val = implode(';', $a); if ($version == '2.1') { $params = array('WORK' => null); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => 'WORK'); } $vcard->setAttribute('ADR', $val, $params, true, $a); } return $vcard; } /** * Returns whether a ValEnum entry from a DevInf object contains a certain * type. * * @param array $valEnum A ValEnum hash. * @param string $type A requested attribute type. * * @return boolean True if $type exists in $valEnum. */ protected function _hasValEnum($valEnum, $type) { foreach (array_keys($valEnum) as $key) { if (in_array($type, explode(',', $key))) { return true; } } return false; } /** * Function to convert a Horde_Icalendar_Vcard object into a Turba * Object Hash with Turba attributes suitable as a parameter for add(). * * @see add() * * @param Horde_Icalendar_Vcard $vcard The Horde_Icalendar_Vcard object * to parse. * * @return array A Turba attribute hash. */ public function toHash(Horde_Icalendar_Vcard $vcard) { $hash = array(); $attr = $vcard->getAllAttributes(); foreach ($attr as $item) { switch ($item['name']) { case 'UID': $hash['__uid'] = $item['value']; break; case 'FN': $hash['name'] = $item['value']; break; case 'N': $name = $item['values']; if (!empty($name[Horde_Icalendar_Vcard::N_FAMILY])) { $hash['lastname'] = $name[Horde_Icalendar_Vcard::N_FAMILY]; } if (!empty($name[Horde_Icalendar_Vcard::N_GIVEN])) { $hash['firstname'] = $name[Horde_Icalendar_Vcard::N_GIVEN]; } if (!empty($name[Horde_Icalendar_Vcard::N_ADDL])) { $hash['middlenames'] = $name[Horde_Icalendar_Vcard::N_ADDL]; } if (!empty($name[Horde_Icalendar_Vcard::N_PREFIX])) { $hash['namePrefix'] = $name[Horde_Icalendar_Vcard::N_PREFIX]; } if (!empty($name[Horde_Icalendar_Vcard::N_SUFFIX])) { $hash['nameSuffix'] = $name[Horde_Icalendar_Vcard::N_SUFFIX]; } break; case 'NICKNAME': case 'X-EPOCSECONDNAME': $hash['nickname'] = $item['value']; $hash['alias'] = $item['value']; break; // We use LABEL but also support ADR. case 'LABEL': if (isset($item['params']['HOME']) && !isset($hash['homeAddress'])) { $hash['homeAddress'] = $item['value']; } elseif (isset($item['params']['WORK']) && !isset($hash['workAddress'])) { $hash['workAddress'] = $item['value']; } elseif (!isset($hash['commonAddress'])) { $hash['commonAddress'] = $item['value']; } break; case 'ADR': if (isset($item['params']['TYPE'])) { if (!is_array($item['params']['TYPE'])) { $item['params']['TYPE'] = array($item['params']['TYPE']); } } else { $item['params']['TYPE'] = array(); if (isset($item['params']['WORK'])) { $item['params']['TYPE'][] = 'WORK'; } if (isset($item['params']['HOME'])) { $item['params']['TYPE'][] = 'HOME'; } if (count($item['params']['TYPE']) == 0) { $item['params']['TYPE'][] = 'COMMON'; } } $address = $item['values']; foreach ($item['params']['TYPE'] as $adr) { switch (Horde_String::upper($adr)) { case 'HOME': $prefix = 'home'; break; case 'WORK': $prefix = 'work'; break; default: $prefix = 'common'; } if (isset($hash[$prefix . 'Address'])) { continue; } $hash[$prefix . 'Address'] = ''; if (!empty($address[Horde_Icalendar_Vcard::ADR_STREET])) { $hash[$prefix . 'Street'] = $address[Horde_Icalendar_Vcard::ADR_STREET]; $hash[$prefix . 'Address'] .= $hash[$prefix . 'Street'] . "\n"; } if (!empty($address[Horde_Icalendar_Vcard::ADR_EXTEND])) { $hash[$prefix . 'Extended'] = $address[Horde_Icalendar_Vcard::ADR_EXTEND]; $hash[$prefix . 'Address'] .= $hash[$prefix . 'Extended'] . "\n"; } if (!empty($address[Horde_Icalendar_Vcard::ADR_POB])) { $hash[$prefix . 'POBox'] = $address[Horde_Icalendar_Vcard::ADR_POB]; $hash[$prefix . 'Address'] .= $hash[$prefix . 'POBox'] . "\n"; } if (!empty($address[Horde_Icalendar_Vcard::ADR_LOCALITY])) { $hash[$prefix . 'City'] = $address[Horde_Icalendar_Vcard::ADR_LOCALITY]; $hash[$prefix . 'Address'] .= $hash[$prefix . 'City']; } if (!empty($address[Horde_Icalendar_Vcard::ADR_REGION])) { $hash[$prefix . 'Province'] = $address[Horde_Icalendar_Vcard::ADR_REGION]; $hash[$prefix . 'Address'] .= ', ' . $hash[$prefix . 'Province']; } if (!empty($address[Horde_Icalendar_Vcard::ADR_POSTCODE])) { $hash[$prefix . 'PostalCode'] = $address[Horde_Icalendar_Vcard::ADR_POSTCODE]; $hash[$prefix . 'Address'] .= ' ' . $hash[$prefix . 'PostalCode']; } if (!empty($address[Horde_Icalendar_Vcard::ADR_COUNTRY])) { include 'Horde/Nls/Countries.php'; $country = array_search($address[Horde_Icalendar_Vcard::ADR_COUNTRY], $countries); if ($country === false) { $country = $address[Horde_Icalendar_Vcard::ADR_COUNTRY]; } $hash[$prefix . 'Country'] = $country; $hash[$prefix . 'Address'] .= "\n" . $address[Horde_Icalendar_Vcard::ADR_COUNTRY]; } $hash[$prefix . 'Address'] = trim($hash[$prefix . 'Address']); } break; case 'TZ': // We only support textual timezones. if (!isset($item['params']['VALUE']) || Horde_String::lower($item['params']['VALUE']) != 'text') { break; } $timezones = explode(';', $item['value']); $available_timezones = Horde_Nls::getTimezones(); foreach ($timezones as $timezone) { $timezone = trim($timezone); if (isset($available_timezones[$timezone])) { $hash['timezone'] = $timezone; break 2; } } break; case 'GEO': if (isset($item['params']['HOME'])) { $hash['homeLatitude'] = $item['value']['latitude']; $hash['homeLongitude'] = $item['value']['longitude']; } elseif (isset($item['params']['WORK'])) { $hash['workLatitude'] = $item['value']['latitude']; $hash['workLongitude'] = $item['value']['longitude']; } else { $hash['latitude'] = $item['value']['latitude']; $hash['longitude'] = $item['value']['longitude']; } break; case 'TEL': if (isset($item['params']['FAX'])) { if (isset($item['params']['WORK']) && !isset($hash['workFax'])) { $hash['workFax'] = $item['value']; } elseif (isset($item['params']['HOME']) && !isset($hash['homeFax'])) { $hash['homeFax'] = $item['value']; } elseif (!isset($hash['fax'])) { $hash['fax'] = $item['value']; } } elseif (isset($item['params']['PAGER']) && !isset($hash['pager'])) { $hash['pager'] = $item['value']; } elseif (isset($item['params']['TYPE'])) { if (!is_array($item['params']['TYPE'])) { $item['params']['TYPE'] = array($item['params']['TYPE']); } foreach ($item['params']['TYPE'] as &$type) { $type = Horde_String::upper($type); } // For vCard 3.0. if (in_array('CELL', $item['params']['TYPE'])) { if (in_array('HOME', $item['params']['TYPE']) && !isset($hash['homeCellPhone'])) { $hash['homeCellPhone'] = $item['value']; } elseif (in_array('WORK', $item['params']['TYPE']) && !isset($hash['workCellPhone'])) { $hash['workCellPhone'] = $item['value']; } elseif (!isset($hash['cellPhone'])) { $hash['cellPhone'] = $item['value']; } } elseif (in_array('FAX', $item['params']['TYPE'])) { if (in_array('HOME', $item['params']['TYPE']) && !isset($hash['homeFax'])) { $hash['homeFax'] = $item['value']; } elseif (in_array('WORK', $item['params']['TYPE']) && !isset($hash['workFax'])) { $hash['workFax'] = $item['value']; } elseif (!isset($hash['fax'])) { $hash['fax'] = $item['value']; } } elseif (in_array('VIDEO', $item['params']['TYPE'])) { if (in_array('HOME', $item['params']['TYPE']) && !isset($hash['homeVideoCall'])) { $hash['homeVideoCall'] = $item['value']; } elseif (in_array('WORK', $item['params']['TYPE']) && !isset($hash['workVideoCall'])) { $hash['workVideoCall'] = $item['value']; } elseif (!isset($hash['videoCall'])) { $hash['videoCall'] = $item['value']; } } elseif (in_array('PAGER', $item['params']['TYPE']) && !isset($hash['pager'])) { $hash['pager'] = $item['value']; } elseif (in_array('WORK', $item['params']['TYPE']) && !isset($hash['workPhone'])) { $hash['workPhone'] = $item['value']; } elseif (in_array('HOME', $item['params']['TYPE']) && !isset($hash['homePhone'])) { $hash['homePhone'] = $item['value']; } elseif (!isset($hash['phone'])) { $hash['phone'] = $item['value']; } } elseif (isset($item['params']['CELL'])) { if (isset($item['params']['WORK']) && !isset($hash['workCellPhone'])) { $hash['workCellPhone'] = $item['value']; } elseif (isset($item['params']['HOME']) && !isset($hash['homeCellPhone'])) { $hash['homeCellPhone'] = $item['value']; } elseif (!isset($hash['cellPhone'])) { $hash['cellPhone'] = $item['value']; } } elseif (isset($item['params']['VIDEO'])) { if (isset($item['params']['WORK']) && !isset($hash['workVideoCall'])) { $hash['workVideoCall'] = $item['value']; } elseif (isset($item['params']['HOME']) && !isset($hash['homeVideoCall'])) { $hash['homeVideoCall'] = $item['value']; } elseif (!isset($hash['videoCall'])) { $hash['videoCall'] = $item['value']; } } else { if (isset($item['params']['WORK']) && !isset($hash['workPhone'])) { $hash['workPhone'] = $item['value']; } elseif (isset($item['params']['HOME']) && !isset($hash['homePhone'])) { $hash['homePhone'] = $item['value']; } else { $hash['phone'] = $item['value']; } } break; case 'EMAIL': $email_set = false; if (isset($item['params']['HOME']) && (!isset($hash['homeEmail']) || isset($item['params']['PREF']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['homeEmail'] = $e ? $e : ''; $email_set = true; } elseif (isset($item['params']['WORK']) && (!isset($hash['workEmail']) || isset($item['params']['PREF']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['workEmail'] = $e ? $e : ''; $email_set = true; } elseif (isset($item['params']['TYPE'])) { if (!is_array($item['params']['TYPE'])) { $item['params']['TYPE'] = array($item['params']['TYPE']); } foreach ($item['params']['TYPE'] as &$type) { $type = Horde_String::upper($type); } if (in_array('HOME', $item['params']['TYPE']) && (!isset($hash['homeEmail']) || in_array('PREF', $item['params']['TYPE']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['homeEmail'] = $e ? $e : ''; $email_set = true; } elseif (in_array('WORK', $item['params']['TYPE']) && (!isset($hash['workEmail']) || in_array('PREF', $item['params']['TYPE']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['workEmail'] = $e ? $e : ''; $email_set = true; } } if (!$email_set && (!isset($hash['email']) || isset($item['params']['PREF']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['email'] = $e ? $e : ''; } if (!isset($hash['emails'])) { $hash['emails'] = ''; } if ($e = Horde_Icalendar_Vcard::getBareEmail($item['value'])) { if (strlen($hash['emails'])) { $hash['emails'] .= ','; } $hash['emails'] .= $e; } break; case 'TITLE': $hash['title'] = $item['value']; break; case 'ROLE': $hash['role'] = $item['value']; break; case 'ORG': // The VCARD 2.1 specification requires the presence of two // SEMI-COLON separated fields: Organizational Name and // Organizational Unit. Additional fields are optional. $hash['company'] = !empty($item['values'][0]) ? $item['values'][0] : ''; $hash['department'] = !empty($item['values'][1]) ? $item['values'][1] : ''; break; case 'NOTE': $hash['notes'] = $item['value']; break; case 'CATEGORIES': $hash['businessCategory'] = $item['value']; $hash['__tags'] = $item['values']; break; case 'URL': if (isset($item['params']['HOME']) && !isset($hash['homeWebsite'])) { $hash['homeWebsite'] = $item['value']; } elseif (isset($item['params']['WORK']) && !isset($hash['workWebsite'])) { $hash['workWebsite'] = $item['value']; } elseif (!isset($hash['website'])) { $hash['website'] = $item['value']; } break; case 'BDAY': if (empty($item['value'])) { $hash['birthday'] = null; } else { $hash['birthday'] = $item['value']['year'] . '-' . $item['value']['month'] . '-' . $item['value']['mday']; } break; case 'PHOTO': case 'LOGO': if (isset($item['params']['VALUE']) && Horde_String::lower($item['params']['VALUE']) == 'uri') { // No support for URIs yet. break; } if (!isset($item['params']['ENCODING']) || (Horde_String::lower($item['params']['ENCODING']) != 'b' && Horde_String::upper($item['params']['ENCODING']) != 'BASE64')) { // Invalid property. break; } $type = Horde_String::lower($item['name']); $hash[$type] = base64_decode($item['value']); if (isset($item['params']['TYPE'])) { $hash[$type . 'type'] = $item['params']['TYPE']; } break; case 'X-SIP': if (isset($item['params']['POC']) && !isset($hash['ptt'])) { $hash['ptt'] = $item['value']; } elseif (isset($item['params']['VOIP']) && !isset($hash['voip'])) { $hash['voip'] = $item['value']; } elseif (isset($item['params']['SWIS']) && !isset($hash['shareView'])) { $hash['shareView'] = $item['value']; } elseif (!isset($hash['sip'])) { $hash['sip'] = $item['value']; } break; case 'X-WV-ID': $hash['imaddress'] = $item['value']; break; case 'X-ANNIVERSARY': if (empty($item['value'])) { $hash['anniversary'] = null; } else { $hash['anniversary'] = $item['value']['year'] . '-' . $item['value']['month'] . '-' . $item['value']['mday']; } break; case 'X-CHILDREN': $hash['children'] = $item['value']; break; case 'X-SPOUSE': $hash['spouse'] = $item['value']; break; } } /* Ensure we have a valid name field. */ if (empty($hash['name'])) { /* If name is a composite field, it won't be present in the * $this->fields array, so check for that as well. */ if (isset($this->map['name']) && is_array($this->map['name']) && !empty($this->map['name']['attribute'])) { $fieldarray = array(); foreach ($this->map['name']['fields'] as $mapfields) { $fieldarray[] = isset($hash[$mapfields]) ? $hash[$mapfields] : ''; } $hash['name'] = Turba::formatCompositeField($this->map['name']['format'], $fieldarray); } else { $hash['name'] = isset($hash['firstname']) ? $hash['firstname'] : ''; if (!empty($hash['lastname'])) { $hash['name'] .= ' ' . $hash['lastname']; } $hash['name'] = trim($hash['name']); } } return $hash; } /** * Convert the contact to an ActiveSync contact message * * @param Turba_Object $object The turba object to convert * @param array $options Options: * - protocolversion: (float) The EAS version to support * DEFAULT: 2.5 * - bodyprefs: (array) A BODYPREFERENCE array. * DEFAULT: none (No body prefs enforced). * - truncation: (integer) Truncate event body to this length * DEFAULT: none (No truncation). * - device: (Horde_ActiveSync_Device) The device object. * * @return Horde_ActiveSync_Message_Contact */ public function toASContact(Turba_Object $object, array $options = array()) { global $injector; $message = new Horde_ActiveSync_Message_Contact(array( 'logger' => $injector->getInstance('Horde_Log_Logger'), 'protocolversion' => $options['protocolversion'], 'device' => !empty($options['device']) ? $options['device'] : null )); $hash = $object->getAttributes(); if (!isset($hash['lastname']) && isset($hash['name'])) { $this->_guessName($hash); } // Ensure we have at least a good guess as to separate address fields. // Not ideal, but EAS does not have a single "address" field so we must // map "common" to either home or work. I choose home since // work/non-personal installs will be more likely to have separated // address fields. if (!empty($hash['commonAddress'])) { if (!isset($hash['commonStreet'])) { $hash['commonStreet'] = $hash['commonHome']; } foreach (array('Address', 'Street', 'POBox', 'Extended', 'City', 'Province', 'PostalCode', 'Country') as $field) { $hash['home' . $field] = $hash['common' . $field]; } } else { if (isset($hash['homeAddress']) && !isset($hash['homeStreet'])) { $hash['homeStreet'] = $hash['homeAddress']; } if (isset($hash['workAddress']) && !isset($hash['workStreet'])) { $hash['workStreet'] = $hash['workAddress']; } } $hooks = $injector->getInstance('Horde_Core_Hooks'); $decode_hook = $hooks->hookExists('decode_attribute', 'turba'); foreach ($hash as $field => $value) { if ($decode_hook) { try { $value = $hooks->callHook( 'decode_attribute', 'turba', array($field, $value, $object) ); } catch (Turba_Exception $e) { Horde::log($e); } } if (isset(self::$_asMap[$field])) { try { $message->{self::$_asMap[$field]} = $value; } catch (InvalidArgumentException $e) { } continue; } switch ($field) { case 'photo': $message->picture = base64_encode($value); break; case 'homeCountry': $message->homecountry = !empty($hash['homeCountryFree']) ? $hash['homeCountryFree'] : !empty($hash['homeCountry']) ? Horde_Nls::getCountryISO($hash['homeCountry']) : null; break; case 'otherCountry': $message->othercountry = !empty($hash['otherCountryFree']) ? $hash['otherCountryFree'] : !empty($hash['otherCountry']) ? Horde_Nls::getCountryISO($hash['otherCountry']) : null; break; case 'workCountry': $message->businesscountry = !empty($hash['workCountryFree']) ? $hash['workCountryFree'] : !empty($hash['workCountry']) ? Horde_Nls::getCountryISO($hash['workCountry']) : null; break; case 'email': $message->email1address = $value; break; case 'homeEmail': $message->email2address = $value; break; case 'workEmail': $message->email3address = $value; break; case 'emails': $address = 1; foreach (explode(',', $value) as $email) { while ($address <= 3 && $message->{'email' . $address . 'address'}) { $address++; } if ($address > 3) { break; } $message->{'email' . $address . 'address'} = $email; $address++; } break; case 'children': // Children FROM horde are a simple string value. Even though EAS // uses an array stucture to pass them, we pass as a single // string since we can't assure what delimter the user will // use and (at least in some languages) a comma can be used // within a full name. $message->children = array($value); break; case 'notes': if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWOFIVE) { $bp = $options['bodyprefs']; $note = new Horde_ActiveSync_Message_AirSyncBaseBody(); // No HTML supported in Turba's notes. Always use plaintext. $note->type = Horde_ActiveSync::BODYPREF_TYPE_PLAIN; if (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'])) { if (Horde_String::length($value) > $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']) { $note->data = Horde_String::substr($value, 0, $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']); $note->truncated = 1; } else { $note->data = $value; } $note->estimateddatasize = Horde_String::length($value); } $message->airsyncbasebody = $note; } else { // EAS 2.5 $message->body = $value; $message->bodysize = strlen($message->body); $message->bodytruncated = 0; } break; case 'birthday': case 'anniversary': if (!empty($value) && $value != '0000-00-00') { try { $date = new Horde_Date($value); } catch (Horde_Date_Exception $e) { $message->$field = null; } // Some sanity checking to make sure the date was // successfully parsed. if ($date->month != 0) { $message->$field = $date; } else { $message->$field = null; } } else { $message->$field = null; } break; } } /* Get tags. */ $message->categories = $injector->getInstance('Turba_Tagger') ->split($object->getValue('__tags')); if (empty($this->fileas)) { $message->fileas = Turba::formatName($object); } return $message; } /** * Convert an ActiveSync contact message into a hash suitable for * importing via self::add(). * * @param Horde_ActiveSync_Message_Contact $message The contact message * object. * * @return array A contact hash. */ public function fromASContact(Horde_ActiveSync_Message_Contact $message) { $hash = array(); foreach (self::$_asMap as $turbaField => $asField) { if (!$message->isGhosted($asField)) { try { $hash[$turbaField] = $message->{$asField}; } catch (InvalidArgumentException $e) { } } } /* Requires special handling */ try { if ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_TWELVE) { if (!empty($message->airsyncbasebody)) { $hash['notes'] = $message->airsyncbasebody->data; } } else { $hash['notes'] = $message->body; } } catch (InvalidArgumentException $e) {} // picture ($message->picture *should* already be base64 encdoed) if (!$message->isGhosted('picture')) { $hash['photo'] = base64_decode($message->picture); } /* Email addresses */ $hash['emails'] = array(); if (!$message->isGhosted('email1address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email1address); $hash['emails'][] = $hash['email'] = $e ? $e : ''; } if (!$message->isGhosted('email2address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email2address); $hash['emails'][] = $hash['homeEmail'] = $e ? $e : ''; } if (!$message->isGhosted('email3address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email3address); $hash['emails'][] = $hash['workEmail'] = $e ? $e : ''; } $hash['emails'] = implode(',', $hash['emails']); /* Categories */ if (is_array($message->categories) && count($message->categories)) { $hash['__tags'] = $message->categories; } /* Children */ if (is_array($message->children) && count($message->children)) { // We use a comma as incoming delimiter as it's the most // common even though it might be used withing a name string. $hash['children'] = implode(', ', $message->children); } elseif (!$message->isGhosted('children')) { $hash['children'] = ''; } /* Birthday and Anniversary */ if (!empty($message->birthday)) { $bday = new Horde_Date($message->birthday); $bday->setTimezone(date_default_timezone_get()); $hash['birthday'] = $bday->format('Y-m-d'); } elseif (!$message->isGhosted('birthday')) { $hash['birthday'] = ''; } if (!empty($message->anniversary)) { $anniversary = new Horde_Date($message->anniversary); $anniversary->setTimezone(date_default_timezone_get()); $hash['anniversary'] = $anniversary->format('Y-m-d'); } elseif (!$message->isGhosted('anniversary')) { $hash['anniversary'] = ''; } /* Countries */ include 'Horde/Nls/Countries.php'; if (!empty($message->homecountry)) { if (!empty($this->map['homeCountryFree'])) { $hash['homeCountryFree'] = $message->homecountry; } else { $country = array_search($message->homecountry, $countries); if ($country === false) { $country = $message->homecountry; } $hash['homeCountry'] = $country; } } elseif (!$message->isGhosted('homecountry')) { $hash['homeCountry'] = ''; } if (!empty($message->businesscountry)) { if (!empty($this->map['workCountryFree'])) { $hash['workCountryFree'] = $message->businesscountry; } else { $country = array_search($message->businesscountry, $countries); if ($country === false) { $country = $message->businesscountry; } $hash['workCountry'] = $country; } } elseif (!$message->isGhosted('businesscountry')) { $hash['workCountry'] = ''; } if (!empty($message->othercountry)) { if (!empty($this->map['otherCountryFree'])) { $hash['otherCountryFree'] = $message->othercountry; } else { $country = array_search($message->othercountry, $countries); if ($country === false) { $country = $message->othercountry; } $hash['otherCountry'] = $country; } } elseif (!$message->isGhosted('othercountry')) { $hash['otherCountry'] = ''; } return $hash; } /** * Checks if the current user has the requested permissions on this * address book. * * @param integer $perm The permission to check for. * * @return boolean True if the user has permission, otherwise false. */ public function hasPermission($perm) { $perms = $GLOBALS['injector']->getInstance('Horde_Perms'); return $perms->exists('turba:sources:' . $this->_name) ? $perms->hasPermission('turba:sources:' . $this->_name, $GLOBALS['registry']->getAuth(), $perm) // Assume we have permissions if they're not explicitly set. : true; } /** * Return the name of this address book. * (This is the key into the cfgSources array) * * @string Address book name */ public function getName() { return $this->_name; } /** * Return the owner to use when searching or creating contacts in * this address book. * * @return string Contact owner. */ public function getContactOwner() { return empty($this->_contact_owner) ? $this->_getContactOwner() : $this->_contact_owner; } /** * Override the contactOwner setting for this driver. * * @param string $owner The contact owner. */ public function setContactOwner($owner) { $this->_contact_owner = $owner; } /** * Override the name setting for this driver. * * @param string $name The source name. This is the key into the * $cfgSources array. */ public function setSourceName($name) { $this->_name = $name; } /** * Return the owner to use when searching or creating contacts in * this address book. * * @return string Contact owner. */ protected function _getContactOwner() { return $GLOBALS['registry']->getAuth(); } /** * Creates a new Horde_Share for this source type. * * @param string $share_name The share name * @param array $params The params for the share. * * @return Horde_Share The share object. */ public function createShare($share_name, array $params) { // If the raw address book name is not set, use the share name if (empty($params['params']['name'])) { $params['params']['name'] = $share_name; } return Turba::createShare($share_name, $params); } /** * Runs any actions after setting a new default tasklist. * * @param string $share The default share ID. */ public function setDefaultShare($share) { } /** * Creates an object key for a new object. * * @param array $attributes The attributes (in driver keys) of the * object being added. * * @return string A unique ID for the new object. */ protected function _makeKey(array $attributes) { return hash('md5', mt_rand()); } /** * Creates an object UID for a new object. * * @return string A unique ID for the new object. */ protected function _makeUid() { return strval(new Horde_Support_Guid()); } /** * Initialize the driver. * * @throws Turba_Exception */ protected function _init() { } /** * Searches the address book with the given criteria and returns a * filtered list of results. If the criteria parameter is an empty array, * all records will be returned. * * @param array $criteria Array containing the search criteria. * @param array $fields List of fields to return. * @param array $blobFields Array of fields containing binary data. * @param boolean $count_only Only return the count of matching entries, * not the entries themselves. * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only = false) { throw new Turba_Exception(_("Searching is not available.")); } /** * Reads the given data from the address book and returns the results. * * @param string $key The primary key field to use. * @param mixed $ids The ids of the contacts to load. * @param string $owner Only return contacts owned by this user. * @param array $fields List of fields to return. * @param array $blobFields Array of fields containing binary data. * @param array $dateFields Array of fields containing date data. * @since 4.2.0 * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _read($key, $ids, $owner, array $fields, array $blobFields = array(), array $dateFields = array()) { throw new Turba_Exception(_("Reading contacts is not available.")); } /** * Adds the specified contact to the addressbook. * * @param array $attributes The attribute values of the contact. * @param array $blob_fields Fields that represent binary data. * @param array $date_fields Fields that represent dates. @since 4.2.0 * * @throws Turba_Exception */ protected function _add(array $attributes, array $blob_fields = array(), array $date_fields = array()) { throw new Turba_Exception(_("Adding contacts is not available.")); } /** * Deletes the specified contact from the addressbook. * * @param string $object_key TODO * @param string $object_id TODO * * @throws Turba_Exception */ protected function _delete($object_key, $object_id) { throw new Turba_Exception(_("Deleting contacts is not available.")); } /** * Saves the specified object in the SQL database. * * @param Turba_Object $object The object to save * * @return string The object id, possibly updated. * @throws Turba_Exception */ protected function _save(Turba_Object $object) { throw new Turba_Exception(_("Saving contacts is not available.")); } /** * Remove all entries owned by the specified user. * * @param string $user The user's data to remove. * * @throws Turba_Exception */ public function removeUserData($user) { throw new Turba_Exception_NotSupported(_("Removing user data is not supported in the current address book storage driver.")); } /** * Check if the passed in share is the default share for this source. * * @param Horde_Share_Object $share The share object. * @param array $srcconfig The cfgSource entry for the share. * * @return boolean TODO */ public function checkDefaultShare(Horde_Share_Object $share, array $srcconfig) { $params = @unserialize($share->get('params')); if (!isset($params['default'])) { $params['default'] = ($params['name'] == $GLOBALS['registry']->getAuth()); $share->set('params', serialize($params)); $share->save(); } return $params['default']; } /* Countable methods. */ /** * Returns the number of contacts of the current user in this address book. * * @return integer The number of contacts that the user owns. * @throws Turba_Exception */ public function count() { if (is_null($this->_count)) { $this->_count = count( $this->_search(array('AND' => array( array('field' => $this->toDriver('__owner'), 'op' => '=', 'test' => $this->getContactOwner()))), array($this->toDriver('__key'))) ); } return $this->_count; } /** * Helper function for guessing name parts from a single name string. * * @param array $hash The attributes array. */ protected function _guessName(&$hash) { if (($pos = strpos($hash['name'], ',')) !== false) { // Assume Last, First $hash['lastname'] = Horde_String::substr($hash['name'], 0, $pos); $hash['firstname'] = trim(Horde_String::substr($hash['name'], $pos + 1)); } elseif (($pos = Horde_String::rpos($hash['name'], ' ')) !== false) { // Assume everything after last space as lastname $hash['lastname'] = trim(Horde_String::substr($hash['name'], $pos + 1)); $hash['firstname'] = Horde_String::substr($hash['name'], 0, $pos); } else { $hash['lastname'] = $hash['name']; $hash['firstname'] = ''; } } } turba-4.2.12/lib/Exception.php 0000664 0001750 0001750 00000000704 12654115107 014241 0 ustar jan jan * @category Horde * @license http://www.horde.org/licenses/apache ASL * @package Turba */ class Turba_Exception extends Horde_Exception_Wrapped { } turba-4.2.12/lib/List.php 0000664 0001750 0001750 00000016421 12654115107 013221 0 ustar jan jan * @author Jon Parise
* ascending - (boolean) Sort direction. * field - (string) Sort field. **/ public function sort($order = null) { global $attributes, $prefs; if (!$order) { $order = array( array( 'ascending' => true, 'field' => 'lastname' ) ); } $need_lastname = $need_firstname = false; $name_format = $prefs->getValue('name_format'); $name_sort = $prefs->getValue('name_sort'); foreach ($order as &$field) { if ($field['field'] == 'name') { if ($name_sort == 'last_first') { $field['field'] = 'lastname'; } elseif ($name_sort == 'first_last') { $field['field'] = 'firstname'; } } if ($field['field'] == 'lastname') { $field['field'] = '__lastname'; $need_lastname = true; break; } if ($field['field'] == 'firstname') { $field['field'] = '__firstname'; $need_firstname = true; break; } } if ($need_firstname || $need_lastname) { $sorted_objects = array(); foreach ($this->objects as $key => $object) { $name = $object->getValue('name'); $firstname = $object->getValue('firstname'); $lastname = $object->getValue('lastname'); if (!$lastname) { $lastname = Turba::guessLastname($name); } if (!$firstname) { switch ($name_format) { case 'last_first': $firstname = preg_replace('/' . preg_quote($lastname, '/') . ',\s*/', '', $name); break; case 'first_last': $firstname = preg_replace('/\s+' . preg_quote($lastname, '/') . '/', '', $name); break; default: $firstname = preg_replace('/\s*' . preg_quote($lastname, '/') . '(,\s*)?/', '', $name); break; } } $object->setValue('__lastname', $lastname); $object->setValue('__firstname', $firstname); $sorted_objects[$key] = $object; } } else { $sorted_objects = $this->objects; } // Set the comparison type based on the type of attribute we're // sorting by. foreach ($order as &$val) { $sm = 'text'; if (isset($attributes[$val['field']])) { $f = $attributes[$val['field']]; if (!empty($f['cmptype'])) { $sm = $f['cmptype']; } elseif (in_array($f['type'], array('int', 'intlist', 'number'))) { $sm = 'int'; } } $val['sortmethod'] = $sm; } $this->_usortCriteria = $order; /* Exceptions thrown inside a sort incorrectly cause an error. See * Bug #9202. */ @usort($sorted_objects, array($this, '_cmp')); $this->objects = $sorted_objects; } /** * Usort helper function. * * Compares two Turba_Objects based on the member variable * $_usortCriteria, taking care to sort numerically if it is an integer * field. * * @param Turba_Object $a The first Turba_Object to compare. * @param Turba_Object $b The second Turba_Object to compare. * * @return integer Comparison of the two field values. */ protected function _cmp(Turba_Object $a, Turba_Object $b) { foreach ($this->_usortCriteria as $field) { $f = $field['field']; switch ($field['sortmethod']) { case 'int': $result = ($a->getValue($f) > $b->getValue($f)) ? 1 : -1; break; case 'text': if (!isset($a->sortValue[$f])) { $a->sortValue[$f] = Horde_String::lower($a->getValue($f), true, 'UTF-8'); } if (!isset($b->sortValue[$f])) { $b->sortValue[$f] = Horde_String::lower($b->getValue($f), true, 'UTF-8'); } // Use strcoll for locale-safe comparisons. $result = strcoll($a->sortValue[$f], $b->sortValue[$f]); break; } if ($result != 0) { return (($field['ascending'] ? 1 : -1) * $result); } } return 0; } /* Countable methods. */ public function count() { return count($this->objects); } } turba-4.2.12/lib/Object.php 0000664 0001750 0001750 00000046614 12654115107 013523 0 ustar jan jan * @author Jon Parise
* - user: (array) Only include objects owned by these users. * - list: (array) Restrict to contacts contained in these address * books. ** * @return array A hash of results. */ public function search($tags, $filter = array()) { global $injector; $args = array(); $tagger = $injector->getInstance('Content_Tagger'); // These filters are mutually exclusive if (array_key_exists('user', $filter)) { // Items owned by specific user(s) $args['userId'] = $filter['user']; } elseif (!empty($filter['list'])) { // Only events located in specific address book(s) if (!is_array($filter['list'])) { $filter['list'] = array($filter['list']); } $args['listId'] = $filter['list']; } // Add the tags to the search $args['tagId'] = $tagger->ensureTags($tags); $args['typeId'] = $this->_type_ids['contact']; return array_values($tagger->getObjects($args)); } } turba-4.2.12/lib/Test.php 0000664 0001750 0001750 00000010761 12654115107 013226 0 ustar jan jan * @package Turba */ class Turba_Test extends Horde_Test { /** * The module list * * @var array */ protected $_moduleList = array(); /** * PHP settings list. * * @var array */ protected $_settingsList = array(); /** * PEAR modules list. * * @var array */ protected $_pearList = array(); /** * Inter-Horde application dependencies. * * @var array */ protected $_appList = array(); /** */ public function __construct() { parent::__construct(); $this->_fileList += array( 'config/attributes.php' => null, 'config/backends.php' => null, 'config/mime_drivers.php' => null, 'config/prefs.php' => null ); } /** * Any application specific tests that need to be done. * * @return string HTML output. */ public function appTests() { $ret = '
unable to bind as ' . htmlspecialchars($params['user']) . ' to LDAP server
'; ldap_close($ldap); $ldap = ''; } elseif (empty($params['user']) && !ldap_bind($ldap)) { $ret .= "unable to bind anonymously to LDAP server
\n"; ldap_close($ldap); $ldap = ''; } if ($ldap) { $result = ldap_search($ldap, $params['basedn'], $params['filter']); if ($result) { $ret .= 'search returned ' . ldap_count_entries($ldap, $result) . " entries
\n"; $info = ldap_get_entries($ldap, $result); for ($i = 0; $i < $info['count']; ++$i) { $ret .= 'dn is: ' . $info[$i]['dn'] . '
' .
'first cn entry is: ' . $info[$i]['cn'][0] . '
' .
'first mail entry is: ' . $info[$i]['mail'][0] . '
(only first 10 entries displayed)
'; break; } } } else { $ret .= 'unable to search LDAP server
'; } } } else { $ret .= 'unable to connect to LDAP server
'; } return $ret; } } turba-4.2.12/lib/Turba.php 0000664 0001750 0001750 00000060652 12654115107 013370 0 ustar jan jan * @author Jon Parise