* array('name' => array('John Doe' => Turba_List, ...), ...)
*
* @throws Turba_Exception
*/
public function searchDuplicates()
{
return $this->_driver->searchDuplicates();
}
/**
* 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.
*
* @return array Hash containing the search results.
* @throws Turba_Exception
*/
protected function _read($key, $ids, $owner, array $fields,
array $blobFields = array())
{
return $this->_driver->_read($key, $ids, $owner, $fields, $blobFields);
}
/**
* 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())
{
return $this->_driver->_add($attributes, $blob_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.1.3/lib/Driver/Sql.php 0000664 0001750 0001750 00000060437 12233762701 014226 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. */ if ($count_only) { $query = 'SELECT COUNT(*) FROM ' . $this->_params['table'] . $where; try { return $this->_db->selectValue($query, $values); } catch (Horde_Db_Exception $e) { throw new Turba_Exception($e); } } else { $query = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $this->_params['table'] . $where; try { return $this->_parseRead($blobFields, $this->_db->selectAll($query, $values)); } catch (Horde_Db_Exception $e) { throw new Turba_Exception($e); } } } protected function _parseRead($blobFields, $result) { $results = array(); $columns = $this->_db->columns($this->_params['table']); foreach ($result as $row) { $entry = array(); foreach ($row as $field => $val) { if (isset($blobFields[$field])) { $entry[$field] = $columns[$field]->binaryToString($val); } 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. */
$query = sprintf('SELECT DISTINCT a1.%s,%s FROM %s a1 JOIN %s a2 ON %s AND a1.%s <> a2.%s WHERE a1.%s = ? AND a2.%s = ? AND %s ORDER BY %s',
$this->map['__key'],
$order[$i],
$this->_params['table'],
$this->_params['table'],
$joins[$i],
$this->map['__key'],
$this->map['__key'],
$this->map['__owner'],
$this->map['__owner'],
$where[$i],
$order[$i]);
/* Run query. */
try {
$ids = $this->_db->selectValues($query, array($owner, $owner));
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception($e);
}
$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.
*
* @return array Hash containing the search results.
* @throws Turba_Exception
*/
protected function _read($key, $ids, $owner, array $fields,
array $blobFields = 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));
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception($e);
}
}
/**
* Adds the specified object to the SQL database.
*
* TODO
*
* @throws Turba_Exception
*/
protected function _add(array $attributes, array $blob_fields = array())
{
list($fields, $values) = $this->_prepareWrite($attributes, $blob_fields);
$query = 'INSERT INTO ' . $this->_params['table']
. ' (' . implode(', ', $fields) . ')'
. ' VALUES (' . str_repeat('?, ', count($values) - 1) . '?)';
try {
$this->_db->insert($query, $values);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception($e);
}
}
protected function _prepareWrite($attributes, $blob_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);
} 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($e);
}
}
/**
* 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);
/* Need a list of UIDs so we can notify History */
$query = 'SELECT '. $this->map['__uid'] . ' FROM '
. $this->_params['table'] . ' WHERE owner_id = ?';
try {
$ids = $this->_db->selectValues($query, $values);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception($e);
}
/* Do the deletion */
$query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE owner_id = ?';
try {
$this->_db->delete($query, $values);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception($e);
}
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
*/
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());
$where = $object_key . ' = ?';
unset($attributes[$object_key]);
list($fields, $values) = $this->_prepareWrite($attributes, $blob_fields);
$values[] = $object_id;
$query = 'UPDATE ' . $this->_params['table'] . ' SET ' . implode(' = ?, ', $fields) . ' = ? WHERE ' . $where;
try {
$this->_db->update($query, $values);
} catch (Horde_Db_Exception $e) {
throw new Turba_Exception($e);
}
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' => ''
);
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', 'category',
'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.1.3/lib/Driver/Vbook.php 0000664 0001750 0001750 00000010757 12233762701 014547 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). */
$criteria['AND'][] = $this->makeSearch($this->searchCriteria, 'AND', array());
$results = $this->_driver->_search($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.
*
* @return array Hash containing the search results.
* @throws Turba_Exception
*/
protected function _read($key, $ids, $owner, array $fields,
array $blobFields = array())
{
return $this->_driver->_read($key, $ids, $owner, $fields, $blobFields);
}
/**
* 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.1.3/lib/Exception/NotSupported.php 0000664 0001750 0001750 00000000115 12233762701 016623 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.1.3/lib/Factory/Driver.php 0000664 0001750 0001750 00000011055 12233762701 015066 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-2013 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 Ouptut 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::img($sortdir ? 'za.png' : 'az.png', $title); } else { return Horde::img($sortdir ? 'za_secondary.png' : 'az_secondary.png', _("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.1.3/lib/.htaccess 0000644 0001750 0001750 00000000016 12233762701 013302 0 ustar jan jan Deny from all turba-4.1.3/lib/Api.php 0000664 0001750 0001750 00000224460 12233762701 012743 0 ustar jan jan * @category Horde * @license http://www.horde.org/licenses/apache ASL * @package Turba */ class Turba_Api extends Horde_Registry_Api { /** * Links. * * @var array */ protected $_links = array( 'show' => '%application%/contact.php?source=|source|&key=|key|&uid=|uid|', 'smartmobile_browse' => '%application%/smartmobile.php#browse' ); /** * The listing of API calls that do not require permissions checking. * * @var array */ protected $_noPerms = array( 'getClientSource', 'getClient', 'getClients', 'searchClients' ); /** * Callback for comment API. * * @param integer $id Internal data identifier. * * @return mixed Name of object on success, false on failure. */ public function commentCallback($id) { if (!$GLOBALS['conf']['comments']['allow']) { return false; } @list($source, $key) = explode('.', $id, 2); if (isset($GLOBALS['cfgSources'][$source]) && $key) { try { return $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($source)->getObject($key)->getValue('name'); } catch (Horde_Exception $e) { } } return false; } /** * Does this API allow comments? * * @return boolean True if API allows comments. */ public function hasComments() { return !empty($GLOBALS['conf']['comments']['allow']); } /** * Returns a list of available sources. * * @param boolean $writeable If true, limits to writeable sources. * @param boolean $sync_only Only include synchable address books. * * @return array An array of the available sources. Keys are source IDs, * values are source titles. */ public function sources($writeable = false, $sync_only = false) { $out = array(); foreach (Turba::getAddressBooks($writeable ? Horde_Perms::EDIT : Horde_Perms::READ) as $key => $val) { $out[$key] = $val['title']; } if ($sync_only) { $syncable = unserialize($GLOBALS['prefs']->getValue('sync_books')); $out = array_intersect_key($out, array_flip($syncable)); } return $out; } /** * Returns a list of fields avaiable in a source. * * @param string $source The source name. * * @return array An array describing the fields. Keys are the field name, * values are arrays with these keys: * - name: (string) Field name. * - label: (string) Field label. * - search: (boolean) Can this field be searched? * - type: (string) See turba/config/attributes.php. * * @throws Turba_Exception */ public function fields($source = null) { global $attributes, $cfgSources; if (is_null($source) || !isset($cfgSources[$source])) { throw new Turba_Exception(_("Invalid address book.")); } $fields = array(); foreach (array_keys($cfgSources[$source]['map']) as $name) { if (substr($name, 0, 2) != '__') { $fields[$name] = array( 'label' => $attributes[$name]['label'], 'name' => $name, 'search' => in_array($name, $cfgSources[$source]['search']), 'type' => $attributes[$name]['type'] ); } } return $fields; } /** * Retrieve the UID for the current user's default Turba share. * * @return string UID. */ public function getDefaultShare() { global $injector, $prefs, $session; // Bring in a clean copy of sources. $cfgSources = Turba::availableSources(); if ($session->get('turba', 'has_share')) { $driver = $injector->getInstance('Turba_Factory_Driver'); foreach (Turba::listShares(true) as $uid => $share) { $params = @unserialize($share->get('params')); if (!empty($params['source'])) { try { if ($driver->create($uid)->checkDefaultShare($share, $cfgSources[$params['source']])) { return $uid; } } catch (Turba_Exception $e) {} } } } // Return Turba's default_dir as default. return $prefs->getValue('default_dir'); } /** * Retrieve the UID for the Global Address List source. * * @return string|boolean The UID or false if none configured. */ public function getGalUid() { return empty($GLOBALS['conf']['gal']['addressbook']) ? false : $GLOBALS['conf']['gal']['addressbook']; } /** * Browses through Turba's object tree. * * @param string $path The path of the tree to browse. * @param array $properties The item properties to return. Defaults to * 'name', 'icon', and 'browseable'. * * @return array Content of the specified path. * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function browse($path = '', $properties = array('name', 'icon', 'browseable')) { global $injector, $session; // Strip off the application name if present if (substr($path, 0, 5) == 'turba') { $path = substr($path, 5); } $path = trim($path, '/'); $results = array(); if (empty($path)) { /* We always provide the "global" folder which contains address * book sources that are shared among all users. Per-user shares * are shown in a folder for each respective user. */ $owners = array( 'global' => true ); foreach (Turba::listShares() as $share) { $owners[$share->get('owner') ? $share->get('owner') : '-system-'] = true; } $now = time(); foreach (array_keys($owners) as $owner) { if (in_array('name', $properties)) { $results['turba/' . $owner]['name'] = $owner; } if (in_array('icon', $properties)) { $results['turba/' . $owner]['icon'] = Horde_Themes::img('turba.png'); } if (in_array('browseable', $properties)) { $results['turba/' . $owner]['browseable'] = true; } if (in_array('contenttype', $properties)) { $results['turba/' . $owner]['contenttype'] = 'httpd/unix-directory'; } if (in_array('contentlength', $properties)) { $results['turba/' . $owner]['contentlength'] = 0; } if (in_array('modified', $properties)) { // @TODO: Get a real modification date $results['turba/' . $owner]['modified'] = $now; } if (in_array('created', $properties)) { // @TODO Get a real creation date $results['turba/' . $owner]['created'] = 0; } } return $results; } $parts = explode('/', $path); if (count($parts) == 1) { /* We should either have the username that is a valid share owner * or 'global'. */ if (empty($parts[0])) { // We need either 'global' or a valid username with shares. return array(); } if ($parts[0] == 'global') { // The client is requesting a list of global address books. $addressbooks = Turba::getAddressBooks(); foreach ($addressbooks as $addressbook => $info) { if ($info['type'] == 'share') { // Ignore address book shares in the 'global' folder unset($addressbooks[$addressbook]); } } } else { /* Assume $parts[0] is a valid username and we need to list * their shared addressbooks. */ if (!$session->get('turba', 'has_share')) { // No backends are configured to provide shares return array(); } $addressbooks = $injector->getInstance('Turba_Shares')->listShares($parts[0], array( 'attributes' => $parts[0], 'perm' => Horde_Perms::READ )); /* The last check returns all addressbooks for the requested * user, but that does not mean the requesting user has access * to them. * Filter out those address books for which the requesting * user has no access. */ $addressbooks = Turba::permissionsFilter($addressbooks); } $curpath = 'turba/' . $parts[0] . '/'; $now = time(); foreach ($addressbooks as $addressbook => $info) { if (in_array('name', $properties)) { $results[$curpath . $addressbook]['name'] = sprintf( _("Contacts from %s"), ($info instanceof Horde_Share_Object) ? $info->get('name') : $info['title'] ); } if (in_array('icon', $properties)) { $results[$curpath . $addressbook]['icon'] = Horde_Themes::img('turba.png'); } if (in_array('browseable', $properties)) { $results[$curpath . $addressbook]['browseable'] = true; } if (in_array('contenttype', $properties)) { $results[$curpath . $addressbook]['contenttype'] = 'httpd/unix-directory'; } if (in_array('contentlength', $properties)) { $results[$curpath . $addressbook]['contentlength'] = 0; } if (in_array('modified', $properties)) { // @TODO: Get a real modification date $results[$curpath . $addressbook]['modified'] = $now; } if (in_array('created', $properties)) { // @TODO Get a real creation date $results[$curpath . $addressbook]['created'] = 0; } } return $results; } if (count($parts) == 2) { /* The client is requesting all contacts from a given * addressbook. */ if (empty($parts[0]) || empty($parts[1])) { /* $parts[0] must be either 'global' or a valid user with * shares; $parts[1] must be an addressbook ID. */ return array(); } $addressbooks = Turba::getAddressBooks(); if (!isset($addressbooks[$parts[1]])) { // We must have a valid addressbook to continue. return array(); } $contacts = $injector->getInstance('Turba_Factory_Driver')->create($parts[1])->search(array()); $contacts->reset(); $curpath = 'turba/' . $parts[0] . '/' . $parts[1] . '/'; while ($contact = $contacts->next()) { $key = $curpath . $contact->getValue('__key'); if (in_array('name', $properties)) { $results[$key]['name'] = Turba::formatName($contact); } if (in_array('icon', $properties)) { $results[$key]['icon'] = Horde_Themes::img('mime/vcard.png'); } if (in_array('browseable', $properties)) { $results[$key]['browseable'] = false; } if (in_array('contenttype', $properties)) { $results[$key]['contenttype'] = 'text/x-vcard'; } if (in_array('contentlength', $properties)) { try { $data = $this->export($contact->getValue('__uid'), 'text/x-vcard', $contact->getSource()); } catch (Turba_Exception $e) { $data = ''; } $results[$key]['contentlength'] = strlen($data); } if (in_array('modified', $properties)) { $results[$key]['modified'] = $this->_modified($contact->getValue('__uid'), $parts[1]); } if (in_array('created', $properties)) { $results[$key]['created'] = $this->getActionTimestamp($contact->getValue('__uid'), 'add', $parts[1]); } } return $results; } if (count($parts) == 3) { /* The client is requesting an individual contact. */ $addressbooks = Turba::getAddressBooks(); if (!isset($addressbooks[$parts[1]])) { // We must have a valid addressbook to continue. return array(); } // Load the Turba driver. $driver = $injector->getInstance('Turba_Factory_Driver')->create($parts[1]); $contact = $driver->getObject($parts[2]); $result = array( 'data' => $this->export($contact->getValue('__uid'), 'text/x-vcard', $contact->getSource()), 'mimetype' => 'text/x-vcard' ); $modified = $this->_modified($contact->getValue('__uid'), $parts[1]); if (!empty($modified)) { $result['mtime'] = $modified; } return $result; } throw new Turba_Exception(_("Malformed request.")); } /** * Deletes a file from the Turba tree. * * @param string $path The path to the file. * * @return string The event's UID. * @throws Turba_Exception */ public function path_delete($path) { // Strip off the application name if present if (substr($path, 0, 5) == 'turba') { $path = substr($path, 5); } $parts = explode('/', trim($path, '/')); if (count($parts) < 3) { // Deletes must be on individual contacts. throw new Turba_Exception(_("Delete denied.")); } if (!array_key_exists($parts[1], Turba::getAddressBooks())) { throw new Turba_Exception(_("Address book does not exist")); } return $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($parts[1])->delete($parts[2]); } /** * Returns an array of UIDs for all contacts that the current user is * authorized to see. * * @param string|array $sources The name(s) of the source(s) to return * contacts of. If empty, the current user's * sync sources or default source are used. * * @return array An array of UIDs for all contacts the user can access. * @throws Turba_Exception */ public function listUids($sources = null) { $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); $uids = array(); foreach ($this->_getSources($sources) as $source) { try { $results = $driver->create($source)->search(array()); } catch (Turba_Exception $e) { throw new Turba_Exception(sprintf(_("Error searching the address book: %s"), $e->getMessage())); } foreach ($results->objects as $o) { if (!$o->isGroup()) { $uids[] = $o->getValue('__uid'); } } } return $uids; } /** * Returns an array of UIDs for contacts that have had a given action * since a certain time. * * @param string $action The action to check for - add, modify, or * delete. * @param integer $timestamp The time to start the search. * @param string|array $sources The source(s) for which to retrieve the * history. * @param integer $end The optional ending timestamp. * @param boolean $isModSeq If true, $timestamp and $end are * modification sequences and not timestamps. * @since 4.1.1 * * @return array An array of UIDs matching the action and time criteria. * * @throws Turba_Exception */ public function listBy($action, $timestamp, $sources = null, $end = null, $isModSeq = false) { $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); $history = $GLOBALS['injector']->getInstance('Horde_History'); $filter = array( array( 'field' => 'action', 'op' => '=', 'value' => $action ) ); $uids = array(); if (!empty($end) && !$isModSeq) { $filter[] = array( 'field' => 'ts', 'op' => '<', 'value' => $end ); } foreach ($this->_getSources($sources) as $source) { $sdriver = $driver->create($source); if (!$isModSeq) { $histories = $history->getByTimestamp( '>', $timestamp, $filter, 'turba:' . $sdriver->getName() ); } else { $histories = $history->getByModSeq( $timestamp, $end, $filter, 'turba:' . $sdriver->getName()); } // Filter out groups $nguids = str_replace( 'turba:' . $sdriver->getName() . ':', '', array_keys($histories) ); $include = array(); foreach ($nguids as $uid) { if ($action != 'delete') { $list = $sdriver->search(array('__uid' => $uid)); if ($list->count()) { $object = $list->next(); if ($object->isGroup()) { continue; } } } $include[] = $uid; } // Strip leading turba:addressbook:. $uids = array_merge($uids, $include); } return $uids; } /** * Method for obtaining all server changes between two timestamps. * Essentially a wrapper around listBy(), but returns an array containing * all adds, edits, and deletions. * * @param integer $start The starting timestamp * @param integer $end The ending timestamp. * @param boolean $isModSeq If true, $start and $end are modification * sequences and not timestamps. @since 4.1.1 * * @return array A hash with 'add', 'modify' and 'delete' arrays. */ public function getChanges($start, $end, $isModSeq = false) { return array( 'add' => $this->listBy('add', $start, null, $end, $isModSeq), 'modify' => $this->listBy('modify', $start, null, $end, $isModSeq), 'delete' => $this->listBy('delete', $start, null, $end, $isModSeq) ); } /** * Return all changes occuring between the specified modification * sequences. * * @param integer $start The starting modseq. * @param integer $end The ending modseq. * * @return array The changes @see getChanges() * @since 4.1.1 */ public function getChangesByModSeq($start, $end) { return $this->getChanges($start, $end, true); } /** * Returns the timestamp of an operation for a given UID and action. * * @param string $uid The UID to look for. * @param string $action The action to check for - add, modify, or * delete. * @param string|array $sources The source(s) for which to retrieve the * history. * @param boolean $modSeq Request a modification sequence instead of * timestamp. @since 4.1.1 * * @return integer The timestamp for this action. * * @throws Turba_Exception */ public function getActionTimestamp($uid, $action, $sources = null, $modSeq = false) { $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); $history = $GLOBALS['injector']->getInstance('Horde_History'); $last = 0; foreach ($this->_getSources($sources) as $source) { if (!$modSeq) { $ts = $history->getActionTimestamp( 'turba:' . $driver->create($source)->getName() . ':' . $uid, $action); } else { $ts = $history->getActionModSeq( 'turba:' . $driver->create($source)->getName() . ':' . $uid, $action); } if (!empty($ts) && $ts > $last) { $last = $ts; } } return $last; } /** * Return the largest modification sequence from the history backend. * * @return integer The modseq. * @since 4.1.1 */ public function getHighestModSeq() { return $GLOBALS['injector']->getInstance('Horde_History')->getHighestModSeq('turba'); } /** * Import a contact represented in the specified contentType. * * @param string $content The content of the contact. * @param string $contentType What format is the data in? Currently * supports array, text/directory, text/vcard, * text/x-vcard, and activesync. * @param string $source The source into which the contact will be * imported. * * @return string The new UID. * * @throws Turba_Exception * @throws Turba_Exception_ObjectExists */ public function import($content, $contentType = 'array', $source = null) { global $cfgSources, $injector, $prefs; /* Get default address book from user preferences. */ if (empty($source) && !($source = $prefs->getValue('default_dir'))) { // On new installations default_dir is not set. Try default // addressbook if it's editable. Otherwise use first editable // addressbook. $edit_sources = Turba::getAddressBooks(Horde_Perms::EDIT); $default_source = Turba::getDefaultAddressbook(); if (isset($edit_sources[$default_source])) { // use default addressbook $source = $default_source; } else { // Use first writable source $source = reset($edit_sources); } } // Check existence of and permissions on the specified source. if (!isset($cfgSources[$source])) { throw new Turba_Exception(sprintf(_("Invalid address book: %s"), $source)); } $driver = $injector ->getInstance('Turba_Factory_Driver') ->create($source); if (!$driver->hasPermission(Horde_Perms::EDIT)) { throw new Turba_Exception(_("Permission denied")); } // Create a category manager. $cManager = new Horde_Prefs_CategoryManager(); $categories = $cManager->get(); if (!($content instanceof Horde_Icalendar_Vcard)) { switch ($contentType) { case 'activesync': $content = $driver->fromASContact($content); break; case 'array': if (!isset($content['emails']) && isset($content['email'])) { $content['emails'] = $content['email']; } break; case 'text/x-vcard': case 'text/vcard': case 'text/directory': $iCal = new Horde_Icalendar(); if (!$iCal->parsevCalendar($content)) { throw new Turba_Exception(_("There was an error importing the iCalendar data.")); } switch ($iCal->getComponentCount()) { case 0: throw new Turba_Exception(_("No vCard data was found.")); case 1: $content = $iCal->getComponent(0); break; default: $ids = array(); foreach ($iCal->getComponents() as $c) { if ($c instanceof Horde_Icalendar_Vcard) { $content = $driver->toHash($c); $result = $driver->search($content); if (count($result)) { continue; } $result = $driver->add($content); if (!empty($content['category']) && !in_array($content['category'], $categories)) { $cManager->add($content['category']); $categories[] = $content['category']; } $ids[] = $result; } } return $ids; } break; default: throw new Turba_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType)); } } if ($content instanceof Horde_Icalendar_Vcard) { $content = $driver->toHash($content); } // Check if the entry already exists in the data source. $result = $driver->search($content); if (count($result)) { throw new Turba_Exception_ObjectExists(_("Already Exists")); } // We can't use $object->setValue() here since that cannot be used // with composite fields. if (Horde::hookExists('encode_attribute', 'turba')) { foreach ($content as $attribute => &$value) { try { $value = Horde::callHook('encode_attribute', array($attribute, $value, null, null), 'turba'); } catch (Turba_Exception $e) {} } } $result = $driver->add($content); if (!empty($content['category']) && !in_array($content['category'], $categories)) { $cManager->add($content['category']); } return $driver->getObject($result)->getValue('__uid'); } /** * Export a contact, identified by UID, in the requested contentType. * * @param string $uid Identify the contact to export. * @param mixed $contentType What format should the data be in? Either * a string with one of: - text/directory - * text/vcard - text/x-vcard The first two * produce a vcard3.0 (rfc2426), the second * produces a vcard in old 2.1 format * defined by imc.org Also supports a raw * array * @param string|array $sources The source(s) from which the contact will * be exported. * @param array $fields Hash of field names and * Horde_SyncMl_Property properties with the * requested fields. * @param array $options Any additional options to be passed to the * exporter. * * @return mixed The requested data. * @throws Turba_Exception */ public function export($uid, $contentType, $sources = null, $fields = null, array $options = array()) { if (empty($uid)) { throw new Turba_Exception(_("Invalid ID")); } $driver = $GLOBALS['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' => $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)); } $version = '3.0'; list($contentType,) = explode(';', $contentType); switch ($contentType) { case 'text/x-vcard': $version = '2.1'; // Fall-through case 'text/vcard': case 'text/directory': $export = ''; foreach ($result->objects as $obj) { $vcard = $sdriver->tovCard($obj, $version, $fields); /* vCards are not enclosed in * BEGIN:VCALENDAR..END:VCALENDAR. Export the individual * cards instead. */ $export .= $vcard->exportvCalendar(); } return $export; case 'array': $attributes = array(); foreach ($result->objects as $object) { foreach (array_keys($GLOBALS['cfgSources'][$source]['map']) as $field) { $attributes[$field] = $object->getValue($field); } } return $attributes; case 'activesync': foreach ($result->objects as $object) { $return = $object; } return $sdriver->toASContact($return, $options); default: throw new Turba_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType)); } } throw new Turba_Exception(sprintf(_("Object %s not found."), $uid)); } /** * Exports the user's own contact as a vCard string. * * @return string The requested vCard data. * @throws Turba_Exception */ public function ownVCard() { $contact = $this->getOwnContactObject(); $vcard = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($contact['source'])->tovCard($contact['contact'], '3.0', null, true); $vcard->setAttribute('VERSION', '3.0'); return $vcard->exportvCalendar(); } /** * Export the user's own contact as a hash. * * @return array The contact hash. * @throws Turba_Exception */ public function ownContact() { $contact = $this->getOwnContactObject(); return $contact['contact']->getAttributes(); } /** * Helper function to return the user's own contact object * * @return array An array containing the following keys: * - contact: (Turba_Object) Object representing the user's own contact. * - source: (string) The source of the user's own contact. * @throws Turba_Exception */ public function getOwnContactObject() { $own_contact = $GLOBALS['prefs']->getValue('own_contact'); if (empty($own_contact)) { throw new Turba_Exception(_("You didn't mark a contact as your own yet.")); } @list($source, $id) = explode(';', $own_contact); if (!isset($GLOBALS['cfgSources'][$source])) { throw new Turba_Exception(_("The address book with your own contact doesn't exist anymore.")); } $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($source); if (!$driver->hasPermission(Horde_Perms::READ)) { throw new Turba_Exception(_("You don't have sufficient permissions to read the address book that contains your own contact.")); } try { $contact = $driver->getObject($id); } catch (Horde_Exception_NotFound $e) { throw new Turba_Exception(_("Your own contact cannot be found in the address book.")); } return array( 'contact' => $contact, 'source'=> $source ); } /** * Deletes a contact identified by UID. * * @param string|array $uid Identify the contact to delete, either a * single UID or an array. * @param string|array $sources The source(s) from which the contact will * be deleted. * * @return boolean Success or failure. * @throws Turba_Exception */ public function delete($uid, $sources = null) { if (empty($uid)) { throw new Turba_Exception(_("Invalid ID")); } // Handle an array of UIDs for convenience of deleting multiple // contacts at once. if (is_array($uid)) { foreach ($uid as $g) { if (!$this->delete($g, $sources)) { return false; } } return true; } $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); foreach ($this->_getSources($sources) as $source) { $sdriver = $driver->create($source); if (!$GLOBALS['registry']->isAdmin() && !$sdriver->hasPermission(Horde_Perms::DELETE)) { continue; } // If the objectId isn't in $source in the first place, just // return true. Otherwise, try to delete it and return success or // failure. $result = $sdriver->search(array('__uid' => $uid)); if (count($result) != 0) { $r = $result->objects[0]; try { $sdriver->delete($r->getValue('__key')); } catch (Turba_Exception $e) { return false; } } } return true; } /** * Replaces the contact identified by UID with the content represented in * the specified contentType. * * @param string $uid Idenfity the contact to replace. * @param mixed $content The content of the contact. * @param string $contentType What format is the data in? Currently * supports array, text/directory, * text/vcard, text/x-vcard and activesync. * @param string|array $sources The source(s) where the contact will be * replaced. * * @return boolean Success or failure. * @throws Turba_Exception */ public function replace($uid, $content, $contentType, $sources = null) { if (empty($uid)) { throw new Turba_Exception(_("Invalid contact unique ID")); } $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); foreach ($this->_getSources($sources) as $source) { $sdriver = $driver->create($source); // Check permissions. if (!$sdriver->hasPermission(Horde_Perms::EDIT)) { continue; } $result = $sdriver->search(array('__uid' => $uid)); if (!count($result)) { continue; } elseif (count($result) > 1) { throw new Turba_Exception(sprintf(_("Multiple contacts found with same unique ID %s."), $uid)); } $object = $result->objects[0]; switch ($contentType) { case 'activesync': $content = $sdriver->fromASContact($content); foreach ($content as $attribute => $value) { if ($attribute != '__key') { $object->setValue($attribute, $value); } } return $object->store(); case 'array': break; case 'text/x-vcard': case 'text/vcard': case 'text/directory': $iCal = new Horde_Icalendar(); if (!$iCal->parsevCalendar($content)) { throw new Turba_Exception(_("There was an error importing the iCalendar data.")); } switch ($iCal->getComponentCount()) { case 0: throw new Turba_Exception(_("No vCard data was found.")); case 1: $content = $sdriver->toHash($iCal->getComponent(0)); break; default: throw new Turba_Exception(_("Only one vcard supported.")); } break; default: throw new Turba_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType)); } foreach ($content as $attribute => $value) { if ($attribute != '__key') { $object->setValue($attribute, $value); } } return $object->store(); } throw new Turba_Exception(sprintf(_("Object %s not found."), $uid)); } /** * Returns a contact search result. * * @param mixed $names The search filter values. * @param array $opts Optional parameters: * - customStrict: (array) An array of fields that must match exactly. * DEFAULT: None * - fields: (array) The fields to search on. * DEFAULT: All fields * - forceSource: (boolean) Whether to use the specified sources, even * if they have been disabled in the preferences? * DEFAULT: false * - matchBegin: (boolean) Match word boundaries only? * DEFAULT: false * - returnFields: Only return these fields. * DEFAULT: Return all fields. * - rfc822Return: Return a Horde_Mail_Rfc822_List object. * DEFAULT: Returns an array of search results. * - sources: (array) The sources to search in. * DEFAULT: Search the user's default address book * - count_only: (boolean) If true, only return the count of matching * results. * DEFAULT: false (Return the full data set). * * @return mixed Either a hash containing the search results or a * Rfc822 List object (if 'rfc822Return' is true). * @throws Turba_Exception */ public function search($names = null, array $opts = array()) { global $attributes, $cfgSources, $injector; $opts = array_merge(array( 'fields' => array(), 'forceSource' => false, 'matchBegin' => false, 'returnFields' => array(), 'rfc822Return' => false, 'sources' => array(), 'customStrict' => array(), 'count_only' => false, ), $opts); $results = !empty($opts['count_only']) ? 0 : (empty($opts['rfc822Return']) ? array() : new Horde_Mail_Rfc822_List()); if (!isset($cfgSources) || !is_array($cfgSources) || !count($cfgSources) || is_null($names)) { return $results; } if (!is_array($names)) { $names = array($names); } if (!$opts['forceSource']) { // Make sure the selected source is activated in Turba. $addressbooks = array_keys(Turba::getAddressBooks()); foreach (array_keys($opts['sources']) as $id) { if (!in_array($opts['sources'][$id], $addressbooks)) { unset($opts['sources'][$id]); } } } // ...and ensure the default source is used as a default. if (!count($opts['sources'])) { $opts['sources'] = array(Turba::getDefaultAddressbook()); } $driver = $injector->getInstance('Turba_Factory_Driver'); foreach ($opts['sources'] as $source) { // Skip invalid sources -or- // skip sources that aren't browseable if the search is empty. if (!isset($cfgSources[$source]) || (empty($cfgSources[$source]['browse']) && (!count($names) || ((count($names) == 1) && empty($names[0]))))) { continue; } $sdriver = $driver->create($source); foreach ($names as $name) { $trimname = trim($name); $out = $criteria = array(); if (strlen($trimname)) { if (isset($opts['fields'][$source])) { foreach ($opts['fields'][$source] as $field) { $criteria[$field] = $trimname; } } if (!count($criteria)) { $criteria['name'] = $trimname; } } $search = $sdriver->search( $criteria, Turba::getPreferredSortOrder(), 'OR', $opts['returnFields'], $opts['customStrict'], $opts['matchBegin'], $opts['count_only'] ); if ($opts['count_only']) { $results += $search; continue; } elseif (!($search instanceof Turba_List)) { continue; } $rfc822 = new Horde_Mail_Rfc822(); while ($ob = $search->next()) { $emails = $seen = array(); if ($ob->isGroup()) { /* Is a distribution list. */ $members = $ob->listMembers(); if (!($members instanceof Turba_List) || !count($members)) { continue; } $listatt = $ob->getAttributes(); $listName = $ob->getValue('name'); while ($ob = $members->next()) { foreach (array_keys($ob->getAttributes()) as $key) { $value = $ob->getValue($key); if (empty($value)) { continue; } $seen_key = trim(Horde_String::lower($ob->getValue('name'))) . trim(Horde_String::lower(is_array($value) ? $value['load']['file'] : $value)); if (isset($attributes[$key]) && ($attributes[$key]['type'] == 'email') && empty($seen[$seen_key])) { $emails[] = $value; $seen[$seen_key] = true; } } } if (empty($opts['rfc822Return'])) { $out[] = array( 'email' => implode(', ', $emails), 'id' => $listatt['__key'], 'name' => $listName, 'source' => $source ); } else { $results->add(new Horde_Mail_Rfc822_Group($listName, $emails)); } } else { /* Not a group. */ $att = array( '__key' => $ob->getValue('__key') ); foreach (array_keys($ob->driver->getCriteria()) as $key) { $att[$key] = $ob->getValue($key); } $email = new Horde_Mail_Rfc822_List(); foreach (array_keys($att) as $key) { if ($ob->getValue($key) && isset($attributes[$key]) && ($attributes[$key]['type'] == 'email')) { // Multiple addresses support $email->add($rfc822->parseAddressList($ob->getValue($key), array( 'limit' => (isset($attributes[$key]['params']) && is_array($attributes[$key]['params']) && !empty($attributes[$key]['params']['allow_multi'])) ? 0 : 1 ))); } } $display_name = ($ob->hasValue('name') || !isset($ob->driver->alternativeName)) ? Turba::formatName($ob) : $ob->getValue($ob->driver->alternativeName); if (count($email)) { foreach ($email as $val) { $seen_key = trim(Horde_String::lower($display_name)) . '/' . Horde_String::lower($val->bare_address); if (empty($seen[$seen_key])) { $seen[$seen_key] = true; if (empty($opts['rfc822Return'])) { $emails[] = $val->bare_address; } else { $val->personal = $display_name; $results->add($val); } } } } elseif (empty($opts['rfc822Return'])) { $emails[] = null; } if (empty($opts['rfc822Return'])) { foreach ($emails as $val) { $out[] = array_merge($att, array( '__type' => 'Object', 'email' => $val, 'id' => $att['__key'], 'name' => $display_name, 'source' => $source )); } } } } if (!empty($out)) { $results[$name] = $out; } } } return $results; } /** * Retrieves a contact. * * @param string $source The source name where the contact is stored * @param string $objectId The unique id of the contact to retrieve * * @return array The retrieved contact. * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function getContact($source = null, $objectId = '') { global $cfgSources; if (!isset($cfgSources) || !is_array($cfgSources) || !isset($cfgSources[$source])) { return array(); } $attributes = array(); $object = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($source)->getObject($objectId); foreach (array_keys($cfgSources[$source]['map']) as $field) { $attributes[$field] = $object->getValue($field); } return $attributes; } /** * Retrieves a set of contacts from a single source. * * @param string $source The source name where the contact is stored * @param array $objectIds The unique ids of the contact to retrieve. * * @return array The retrieved contact. * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function getContacts($source = '', array $objectIds = array()) { global $cfgSources; if (!isset($cfgSources) || !is_array($cfgSources) || !isset($cfgSources[$source])) { return array(); } if (!is_array($objectIds)) { $objectIds = array($objectIds); } $objects = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($source)->getObjects($objectIds); $results = array(); foreach ($objects as $object) { $attributes = array(); foreach (array_keys($cfgSources[$source]['map']) as $field) { $attributes[$field] = $object->getValue($field); } $results[] = $attributes; } return $results; } /** * Retrieves a list of all possible values of a field in specified * source(s). * * @param string $field Field name to check. * @param array $sources Array containing the sources to look in. * * @return array An array of fields and possible values. * @throws Turba_Exception */ public function getAllAttributeValues($field = '', array $sources = array()) { global $cfgSources; if (!isset($cfgSources) || !is_array($cfgSources)) { return array(); } if (!count($sources)) { $sources = array(Turba::getDefaultAddressbook()); } $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); $results = array(); foreach ($sources as $source) { if (isset($cfgSources[$source])) { $res = $driver->create($source)->search(array()); if (!($res instanceof Turba_List)) { throw new Turba_Exception(_("Search failed")); } while ($ob = $res->next()) { if ($ob->hasValue($field)) { $results[$source . ':' . $ob->getValue('__key')] = array( 'email' => $ob->getValue('email'), 'name' => $ob->getValue('name'), $field => $ob->getValue($field) ); } } } } return $results; } /** * Retrieves a list of available time objects categories. * * @return array An array of all configured time object categories. */ public function listTimeObjectCategories() { $categories = array(); foreach ($GLOBALS['attributes'] as $key => $attribute) { if (($attribute['type'] == 'monthdayyear') && !empty($attribute['time_object_label'])) { foreach ($GLOBALS['cfgSources'] as $srcKey => $source) { if (!empty($source['map'][$key])) { $categories[$key . '/'. $srcKey] =array( 'title' => sprintf(_("%s in %s"), $attribute['time_object_label'], $source['title']), 'type' => 'share'); } } } } return $categories; } /** * Lists birthdays and/or anniversaries as time objects. * * @param array $time_categories The time categories (from * listTimeObjectCategories) to list. * @param mixed $start The start date of the period. * @param mixed $end The end date of the period. * * @return array An array of timeObject results. * @throws Turba_Exception */ public function listTimeObjects($time_categories, $start, $end) { $start = new Horde_Date($start); $end = new Horde_Date($end); $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); $objects = array(); foreach ($time_categories as $category) { list($category, $source) = explode('/', $category, 2); $objects = array_merge($objects, $driver->create($source)->listTimeObjects($start, $end, $category)); } return $objects; } /** * Returns the client source name. * * @return string The name of the source to use with the clients api. */ public function getClientSource() { return empty($GLOBALS['conf']['client']['addressbook']) ? false : $GLOBALS['conf']['client']['addressbook']; } /** * Returns the available client fields. * * @return array An array describing the fields. */ public function clientFields() { return $this->fields($GLOBALS['conf']['client']['addressbook']); } /** * Returns a contact from the client source. * * @param string $objectId Client unique ID. * * @return array Array of client data. * @throws Turba_Exception */ public function getClient($objectId = '') { return $this->getContact($GLOBALS['conf']['client']['addressbook'], $objectId); } /** * Returns mulitple contacts from the client source. * * @param array $objectIds client unique ids. * * @return array An array of clients data. * @throws Turba_Exception */ public function getClients($objectIds = array()) { return $this->getContacts($GLOBALS['conf']['client']['addressbook'], $objectIds); } /** * Adds a client to the client source. * * @param array $attributes Array containing the client attributes. * * @return boolean */ public function addClient(array $attributes = array()) { return $this->import($attributes, 'array', $this->getClientSource()); } /** * Updates client data. * * @param string $objectId The unique id of the client. * @param array $attributes An array of client attributes. * * @return boolean */ public function updateClient($objectId = '', array $attributes = array()) { return $this->replace($this->getClientSource() . ':' . $objectId, $attributes, 'array'); } /** * Deletes a client * * @param string $objectId The unique id of the client * * @return boolean */ public function deleteClient($objectId = '') { return $this->delete($this->getClientSource() . ':' . $objectId); } /** * Search for clients. * * @param array $names The search filter values. * @param array $fields The fields to search in. * @param boolean $matchBegin Match word boundaries only. * * @return array A hash containing the search results. * @throws Turba_Exception */ public function searchClients(array $names = array(), array $fields = array(), $matchBegin = false) { $abook = $GLOBALS['conf']['client']['addressbook']; return $this->search( $names, array('sources' => array($abook), 'fields' => array($abook => $fields), 'matchBegin' => $matchBegin, 'forceSource' => true) ); } /** * Sets the value of the specified attribute of a contact * * @param string|array $address Contact email address(es). * @param string $name Contact name. * @param string $field Field to update. * @param string $value Field value to set. * @param string $source Contact source. * * @throws Turba_Exception */ public function addField($address = '', $name = '', $field = '', $value = '', $source = '') { if (is_array($address)) { $e = null; $success = 0; foreach ($address as $tmp) { try { $this->addField($tmp, $name, $field, $value, $source); ++$success; } catch (Exception $e) {} } if ($e) { if ($success) { throw new Turba_Exception(sprintf(ngettext("Added or updated %d contact, but at least one contact failed:", "Added or updated %d contacts, but at least one contact failed:", $success), $success) . ' ' . $e->getMessage()); } else { throw $e; } } } global $cfgSources; if (empty($source) || !isset($cfgSources[$source])) { throw new Turba_Exception(sprintf(_("Invalid address book: %s"), $source)); } if (empty($address)) { throw new Turba_Exception(_("Invalid email")); } if (empty($name)) { throw new Turba_Exception(_("Invalid name")); } if (empty($value)) { throw new Turba_Exception(_("Invalid entry")); } $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($source); if (!$driver->hasPermission(Horde_Perms::EDIT)) { throw new Turba_Exception(_("Permission denied")); } try { $res = $driver->search(array('email' => trim($address)), null, 'AND'); } catch (Turba_Exception $e) { throw new Turba_Exception(sprintf(_("Search failed: %s"), $res->getMessage())); } if (count($res) > 1) { try { $res2 = $driver->search(array('email' => trim($address), 'name' => trim($name)), null, 'AND'); } catch (Turba_Exception $e) { throw new Turba_Exception(sprintf(_("Search failed: %s"), $e->getMessage())); } if (!count($res2)) { throw new Turba_Exception(sprintf(_("Multiple persons with address [%s], but none with name [%s] already exist"), trim($address), trim($name))); } try { $res3 = $driver->search(array('email' => $address, 'name' => $name, $field => $value)); } catch (Turba_Exception $e) { throw new Turba_Exception(sprintf(_("Search failed: %s"), $e->getMessage())); } if (count($res3)) { throw new Turba_Exception(sprintf(_("This person already has a %s entry in the address book"), $field)); } $ob = $res2->next(); $ob->setValue($field, $value); $ob->store(); } elseif (count($res) == 1) { try { $res4 = $driver->search(array('email' => $address, $field => $value)); } catch (Turba_Exception $e) { throw new Turba_Exception(sprintf(_("Search failed: %s"), $e->getMessage())); } if (count($res4)) { throw new Turba_Exception(sprintf(_("This person already has a %s entry in the address book"), $field)); } $ob = $res->next(); $ob->setValue($field, $value); $ob->store(); } else { $driver->add(array('email' => $address, 'name' => $name, $field => $value, '__owner' => $GLOBALS['registry']->getAuth())); } } /** * Returns a field value. * * @param string $address Contact email address. * @param string $field Field to get. * @param array $sources Sources to check. * @param boolean $strict Match the email address strictly. * @param boolean $multiple Return more than one entry if found and true, * return an error if this is false. * * @return array An array of field value(s). * @throws Turba_Exception */ public function getField($address = '', $field = '', $sources = array(), $strict = false, $multiple = false) { global $cfgSources; if (empty($address)) { throw new Turba_Exception(_("Invalid email")); } if (!isset($cfgSources) || !is_array($cfgSources)) { return array(); } if (!count($sources)) { $sources = array(Turba::getDefaultAddressbook()); } $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); $result = array(); foreach ($sources as $source) { if (!isset($cfgSources[$source])) { continue; } $sdriver = $driver->create($source); $criterium = array('email' => $address); if (!isset($sdriver->map['email'])) { if (isset($sdriver->map['emails'])) { $criterium = array('emails' => $address); } else { continue; } } try { $list = $sdriver->search($criterium, null, 'AND', array(), $strict ? array('email') : array()); } catch (Turba_Exception $e) { Horde::logMessage($e, 'ERR'); } if ($list instanceof Turba_List) { while ($ob = $list->next()) { if ($ob->hasValue($field)) { $result[] = $ob->getValue($field); } } } } if (count($result) > 1) { if ($multiple) { return $result; } else { throw new Turba_Exception(_("More than 1 entry found")); } } elseif (empty($result)) { throw new Turba_Exception(sprintf(_("No %s entry found for %s"), $field, $address)); } return reset($result); } /** * Deletes a field value. * * @param string $address Contact email address. * @param string $field Field to delete value for. * @param array $sources Sources to delete value from. * * @throws Turba_Exception */ public function deleteField($address = '', $field = '', $sources = array()) { global $cfgSources; if (!strlen($address)) { throw new Turba_Exception(_("Invalid email")); } if (!isset($cfgSources) || !is_array($cfgSources)) { return; } if (count($sources) == 0) { $sources = array(Turba::getDefaultAddressbook()); } $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver'); $success = false; foreach ($sources as $source) { if (isset($cfgSources[$source])) { $sdriver = $driver->create($source); if (!$sdriver->hasPermission(Horde_Perms::EDIT)) { continue; } $res = $sdriver->search(array('email' => $address)); if ($res instanceof Turba_List) { if (count($res) == 1) { $ob = $res->next(); if (is_object($ob) && $ob->hasValue($field)) { $ob->setValue($field, ''); $ob->store(); $success = true; } } } } } if (!$success) { throw new Turba_Exception(sprintf(_("No %s entry found for %s"), $field, $address)); } } /** * Obtain an array of $cfgSource entries matching the filter criteria. * * @param type $filter A single key -> value hash to filter the sources. * * @return array */ public function getSourcesConfig($filter = array()) { $results = array(); $turba_sources = Horde::loadConfiguration('backends.php', 'cfgSources', 'turba'); foreach ($turba_sources as $key => $source) { if (!empty($filter)) { if (!empty($source[current(array_keys($filter))]) && $source[current(array_keys($filter))] == current($filter)) { $results[$key] = $source; } } } return $results; } /** * Lists all shares the current user has access to. * * @param integer $perms * * @return array of Turba_Share objects. */ public function listShares($perms = Horde_Perms::READ) { return Turba::listShares(true, $perms); } /** * GroupObject API - Lists all turba lists for the current user that can * be treated as Horde_Group objects. * * @return array A hash of all visible groups in the form of * group_id => group_name * @throws Horde_Exception */ public function listUserGroupObjects() { $groups = $owners = array(); // Only turba's SQL based sources can act as Horde_Groups $sources = $this->getSourcesConfig(array('type' => 'sql')); foreach ($sources as $key => $source) { // Each source could have a different database connection $db[$key] = empty($source['params']['sql']) ? $GLOBALS['injector']->getInstance('Horde_Db_Adapter') : $GLOBALS['injector']->getInstance('Horde_Core_Factory_Db')->create('turba', $source['params']['sql']); if ($source['use_shares']) { if (empty($contact_shares)) { $contact_shares = $this->listShares(Horde_Perms::SHOW); } foreach ($contact_shares as $id => $share) { $params = @unserialize($share->get('params')); if ($params['source'] == $key) { $owners[] = $params['name']; } } if (!$owners) { return array(); } } else { $owners = array($GLOBALS['registry']->getAuth()); } $owner_ids = array(); foreach ($owners as $owner) { $owner_ids[] = $db[$key]->quoteString($owner); } $sql = 'SELECT ' . $source['map']['__key'] . ', ' . $source['map'][$source['list_name_field']] . ' FROM ' . $source['params']['table'] . ' WHERE ' . $source['map']['__type'] . ' = \'Group\' AND ' . $source['map']['__owner'] . ' IN (' . implode(',', $owner_ids ) . ')'; try { $results = $db[$key]->selectAssoc($sql); } catch (Horde_Db_Exception $e) { Horde::logMessage($e); throw new Horde_Exception_Wrapped($e); } foreach ($results as $id => $name) { $groups[$key . ':' . $id] = $name; } } return $groups; } /** * Returns all contact groups. * * @return array A list of group hashes. * @throws Turba_Exception */ public function getGroupObjects() { $ret = array(); foreach ($this->getSourcesConfig(array('type' => 'sql')) as $key => $source) { if (empty($source['map']['__type'])) { continue; } list($db, $sql) = $this->_getGroupObject($source, 'Group'); try { $results = $db->selectAll($sql); } catch (Horde_Db_Exception $e) { Horde::logMessage($e); throw new Turba_Exception($e); } foreach ($results as $row) { /* name is a reserved word in Postgresql (at a minimum). */ $row['name'] = $row['lname']; unset($row['lname']); $ret[$key . ':' . $row['id']] = $row; } } return $ret; } /** * Returns all contact groups that the specified user is a member of. * * @param string $user The user. * @param boolean $parentGroups Include user as a member of the any * parent group as well. * * @return array An array of group identifiers that the specified user is * a member of. * @throws Horde_Exception */ public function getGroupMemberships($user, $parentGroups = false) { $memberships = array(); foreach ($this->getGroupObjects() as $id => $list) { if (in_array($user, $this->getGroupMembers($id, $parentGroups))) { $memberships[$id] = $list['name']; } } return $memberships; } /** * Returns a contact group hash. * * @param string $gid The group identifier. * * @return array A hash defining the group. * @throws Turba_Exception */ public function getGroupObject($gid) { if (empty($gid) || (strpos($gid, ':') === false)) { throw new Turba_Exception(sprintf('Unsupported group id: %s', $gid)); } $sources = $this->getSourcesConfig(array('type' => 'sql')); list($source, $id) = explode(':', $gid); if (empty($sources[$source])) { return array(); } list($db, $sql) = $this->_getGroupObject($sources[$source], $id); try { $ret = $db->selectOne($sql); $ret['name'] = $ret['lname']; unset($ret['lname']); return $ret; } catch (Horde_Db_Exception $e) { Horde::logMessage($e); throw new Turba_Exception($e); } } /** */ protected function _getGroupObject($source, $key) { $db = empty($source['params']['sql']) ? $GLOBALS['injector']->getInstance('Horde_Db_Adapter') : $GLOBALS['injector']->getInstance('Horde_Core_Factory_Db')->create('turba', $source['params']['sql']); $sql = 'SELECT ' . $source['map']['__members'] . ' members,' . $source['map']['email'] . ' email,' . $source['map'][$source['list_name_field']] . ' lname FROM ' . $source['params']['table'] . ' WHERE ' . $source['map']['__key'] . ' = ' . $db->quoteString($key); return array($db, $sql); } /** * Returns a list of all members belonging to a contact group. * * @param string $gid The group identifier * @param boolean $subGroups Also include members of any subgroups? * * @return array An array of group members (identified by email address). * @throws Horde_Exception */ public function getGroupMembers($gid, $subGroups = false) { $contact_shares = $this->listShares(Horde_Perms::SHOW); $sources = $this->getSourcesConfig(array('type' => 'sql')); $entry = $this->getGroupObject($gid); if (!$entry) { return array(); } list($source,) = explode(':', $gid); $members = @unserialize($entry['members']); if (!is_array($members)) { return array(); } $db[$source] = empty($sources[$source]['params']['sql']) ? $GLOBALS['injector']->getInstance('Horde_Db_Adapter') : $GLOBALS['injector']->getInstance('Horde_Core_Factory_Db')->create('turba', $sources[$source]['params']['sql']); $users = array(); foreach ($members as $member) { // Is this member from the same source or a different one? if (strpos($member, ':') !== false) { list($newSource, $uid) = explode(':', $member); if (!empty($contact_shares[$newSource])) { $params = @unserialize($contact_shares[$newSource]->get('params')); $newSource = $params['source']; $member = $uid; $db[$newSource] = empty($sources[$newSource]['params']['sql']) ? $GLOBALS['injector']->getInstance('Horde_Db_Adapter') : $GLOBALS['injector']->getInstance('Horde_Core_Factory_Db')->create('turba', $sources[$newSource]['params']['sql']); } elseif (empty($sources[$newSource])) { // Last chance, it's not in one of our non-share sources continue; } } else { // Same source $newSource = $source; } $type = $sources[$newSource]['map']['__type']; $email = $sources[$newSource]['map']['email']; $sql = 'SELECT ' . $email . ', ' . $type . ' FROM ' . $sources[$newSource]['params']['table'] . ' WHERE ' . $sources[$newSource]['map']['__key'] . ' = ' . $db[$newSource]->quoteString($member); try { $results = $db[$newSource]->selectOne($sql); } catch (Horde_Db_Exception $e) { Horde::logMessage($e); throw new Horde_Exception_Wrapped($e); } // Sub-Lists are treated as sub groups the best that we can... if ($subGroups && $results[$type] == 'Group') { $users = array_merge($users, $this->getGroupMembers($newSource . ':' . $member)); } if (strlen($results[$email])) { // use a key to dump dups $users[$results[$email]] = true; } } ksort($users); return array_keys($users); } /* Helper methods. */ /** */ private function _modified($uid, $sources) { $modified = $this->getActionTimestamp($uid, 'modify', $sources); if (empty($modified)) { $modified = $this->getActionTimestamp($uid, 'add', $sources); } return $modified; } /** * @throws Turba_Exception */ private function _getSources($sources) { /* Get default address book from user preferences. */ if (empty($sources)) { $sources = @unserialize($GLOBALS['prefs']->getValue('sync_books')); } elseif (!is_array($sources)) { $sources = array($sources); } if (empty($sources)) { $sources = array(Turba::getDefaultAddressbook()); if (empty($sources)) { throw new Turba_Exception(_("No address book specified")); } } foreach ($sources as $val) { if (!strlen($val) || !isset($GLOBALS['cfgSources'][$val])) { throw new Turba_Exception(sprintf(_("Invalid address book: %s"), $val)); } } return $sources; } } turba-4.1.3/lib/Application.php 0000664 0001750 0001750 00000074704 12233762701 014501 0 ustar jan jan true, 'modseq' => true, ); /** */ public $version = 'H5 (4.1.3)'; /** */ protected function _bootstrap() { /* Add Turba-specific factories. */ $factories = array( 'Turba_Shares' => 'Turba_Factory_Shares' ); 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() { // Turba source and attribute configuration. $attributes = Horde::loadConfiguration('attributes.php', 'attributes', 'turba'); $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. $GLOBALS['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 = $GLOBALS['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(); } $GLOBALS['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 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(); $url = Horde::url(''); $edit = Horde::url('addressbooks/edit.php'); $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')) { $sidebar->containers['my']['header']['add'] = array( 'url' => Horde::url('addressbooks/create.php'), 'label' => _("Create a new Address Book"), ); } $sidebar->containers['shared'] = array( 'header' => array( 'id' => 'turba-toggle-shared', 'label' => _("Shared Address Books"), 'collapsed' => true, ), ); $shares = array(); foreach (Turba::listShares() as $id => $abook) { $row = array( 'selected' => $id == Turba::$source, 'url' => $url->add('source', $id), 'label' => $abook->get('name'), 'edit' => $edit->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) . ']'; } $sidebar->addRow($row, 'shared'); if ($row['selected']) { $sidebar->containers['shared']['header']['collapsed'] = false; } } $shares[$id] = true; } $sidebar->containers['other'] = array( 'header' => array( 'id' => 'turba-toggle-other', 'label' => _("Other Address Books"), 'collapsed' => true, ), ); foreach (Turba::getAddressBooks(Horde_Perms::READ) as $id => $abook) { if (isset($shares[$id])) { continue; } $row = array( 'selected' => $id == Turba::$source, 'url' => $url->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)); $done = true; } } if (!$done) { $list->insert(new Turba_Object($this, $object)); } } $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) || $t_object == '0000-00-00' || !preg_match('/(\d{4})-(\d{2})-(\d{2})/', $t_object, $match)) { continue; } $t_object = new Horde_Date(array( 'mday' => $match[3], 'month' => $match[2], 'year' => $match[1] )); 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), 'category' => $ob->getValue('category'), '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, 'category')); } /** * 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())); 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); $done = true; } } if (!$done) { $results[] = new Turba_Object($this, $object); } } 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']; $attributes = $this->toDriverKeys($attributes); $this->_add($attributes, $this->toDriverKeys($this->getBlobs())); /* 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::logMessage($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::logMessage($e, 'ERR'); } } /* Remove any CalDAV mappings. */ try { $GLOBALS['injector'] ->getInstance('Horde_Dav_Storage') ->deleteInternalObjectId($object_id, $this->_name); } catch (Horde_Exception $e) { Horde::log($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 $history = $GLOBALS['injector']->getInstance('Horde_History'); try { foreach ($ids as $id) { // 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() . ':' . $id; $history->log($guid, array('action' => 'delete'), true); } } catch (Exception $e) { Horde::logMessage($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); /* Log the modification of this item in the history log. */ if ($object->getValue('__uid')) { try { $GLOBALS['injector']->getInstance('Horde_History')->log($object->getGuid(), array('action' => 'modify'), true); } catch (Exception $e) { Horde::logMessage($e, 'ERR'); } } return $object_id; } /** * Returns the criteria available for this source except '__key'. * * @return array An array containing the criteria. */ public function getCriteria() { $criteria = $this->map; unset($criteria['__key']); return $criteria; } /** * 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) { $hash = $object->getAttributes(); $vcard = new Horde_Icalendar_Vcard($version); $formattedname = false; $charset = ($version == '2.1') ? array('CHARSET' => 'UTF-8') : array(); $haveDecodeHook = Horde::hookExists('decode_attribute', 'turba'); foreach ($hash as $key => $val) { if ($skipEmpty && !strlen($val)) { continue; } if ($haveDecodeHook) { try { $val = Horde::callHook( 'decode_attribute', array($key, $val, $object), 'turba'); } 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 'businessCategory': case 'category': if ($fields && !isset($fields['CATEGORIES'])) { break; } $vcard->setAttribute('CATEGORIES', $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 '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($val)) { $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($val), $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']); } // 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']); } 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'])) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['emails'] = $e ? $e : ''; } else { $hash['emails'] .= ',' . Horde_Icalendar_Vcard::getBareEmail($item['value']); } 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'] = $hash['category'] = str_replace('\; ', ';', $item['value']); 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'] = ''; } 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'] = ''; } 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). * * @return Horde_ActiveSync_Message_Contact */ public function toASContact(Turba_Object $object, array $options = array()) { $message = new Horde_ActiveSync_Message_Contact(array( 'logger' => $GLOBALS['injector']->getInstance('Horde_Log_Logger'), 'protocolversion' => $options['protocolversion']) ); $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']; } } $haveDecodeHook = Horde::hookExists('decode_attribute', 'turba'); foreach ($hash as $field => $value) { if ($haveDecodeHook) { try { $value = Horde::callHook( 'decode_attribute', array($field, $value, $object), 'turba'); } catch (Turba_Exception $e) { Horde::logMessage($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 'category': // Categories FROM horde are a simple string value, categories going BACK to horde are an array with 'value' and 'new' keys $message->categories = explode(';', $value); 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; } } 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['category'] = implode(';', $message->categories); } elseif (!$message->isGhosted('categories')) { $hash['category'] = ''; } /* 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. * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _read($key, $ids, $owner, array $fields, array $blobFields = 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 TODO * * @throws Turba_Exception */ protected function _add(array $attributes, array $blob_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.1.3/lib/Exception.php 0000664 0001750 0001750 00000000704 12233762701 014161 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.1.3/lib/List.php 0000664 0001750 0001750 00000016425 12233762702 013146 0 ustar jan jan * @author Jon Parise
* ascending - (boolean) Sort direction. * field - (string) Sort field. **/ public function sort($order = null) { if (!$order) { $order = array( array( 'ascending' => true, 'field' => 'lastname' ) ); } $need_lastname = $need_firstname = false; $name_format = $GLOBALS['prefs']->getValue('name_format'); $name_sort = $GLOBALS['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; } $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) { // Set the comparison type based on the type of attribute we're // sorting by. $sortmethod = 'text'; if (isset($GLOBALS['attributes'][$field['field']])) { $f = $GLOBALS['attributes'][$field['field']]; if (!empty($f['cmptype'])) { $sortmethod = $f['cmptype']; } elseif (in_array($f['type'], array('int', 'intlist', 'number'))) { $sortmethod = 'int'; } } $f = $field['field']; switch ($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 (!$field['ascending']) { $result = -$result; } if ($result != 0) { return $result; } } return 0; } /* Countable methods. */ public function count() { return count($this->objects); } } turba-4.1.3/lib/Object.php 0000664 0001750 0001750 00000036360 12233762702 013441 0 ustar jan jan * @author Jon Parise
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.1.3/lib/Turba.php 0000664 0001750 0001750 00000056756 12233762702 013323 0 ustar jan jan * @author Jon Parise