Overview

Packages

  • Horde
    • Data
  • None
  • Turba

Classes

  • Turba
  • Turba_Api
  • Turba_Driver
  • Turba_Driver_Facebook
  • Turba_Driver_Favourites
  • Turba_Driver_Group
  • Turba_Driver_Imsp
  • Turba_Driver_Kolab
  • Turba_Driver_Ldap
  • Turba_Driver_Prefs
  • Turba_Driver_Share
  • Turba_Driver_Sql
  • Turba_Driver_Vbook
  • Turba_Exception
  • Turba_Factory_Driver
  • Turba_Form_AddContact
  • Turba_Form_Contact
  • Turba_Form_ContactBase
  • Turba_Form_CreateAddressBook
  • Turba_Form_DeleteAddressBook
  • Turba_Form_EditAddressBook
  • Turba_Form_EditContact
  • Turba_Form_EditContactGroup
  • Turba_List
  • Turba_LoginTasks_SystemTask_Upgrade
  • Turba_Object
  • Turba_Object_Group
  • Turba_Test
  • Turba_View_Browse
  • Turba_View_Contact
  • Turba_View_DeleteContact
  • Turba_View_Duplicates
  • Turba_View_EditContact
  • Turba_View_List
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Horde Turba driver for the Kolab IMAP Server.
  4:  *
  5:  * Copyright 2004-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * See the enclosed file LICENSE for license information (ASL).  If you
  8:  * did not receive this file, see http://www.horde.org/licenses/apache.
  9:  *
 10:  * @author   Thomas Jarosch <thomas.jarosch@intra2net.com>
 11:  * @author   Gunnar Wrobel <wrobel@pardus.de>
 12:  * @author   Stuart Binge <omicron@mighty.co.za>
 13:  * @category Horde
 14:  * @license  http://www.horde.org/licenses/apache ASL
 15:  * @package  Turba
 16:  */
 17: class Turba_Driver_Kolab extends Turba_Driver
 18: {
 19:     /**
 20:      * The Kolab_Storage backend.
 21:      *
 22:      * @var Horde_Kolab_Storage
 23:      */
 24:     protected $_kolab;
 25: 
 26:     /**
 27:      * Indicates if the driver has been connected to a specific addressbook or
 28:      * not.
 29:      *
 30:      * @var boolean
 31:      */
 32:     protected $_connected = false;
 33: 
 34:     /**
 35:      * The current addressbook.
 36:      *
 37:      * @var Horde_Kolab_Storage_Data
 38:      */
 39:     private $_data;
 40: 
 41:     /**
 42:      * The current addressbook represented as share.
 43:      *
 44:      * @var Horde_Share_Object
 45:      */
 46:     private $_share;
 47: 
 48:     /**
 49:      * What can this backend do?
 50:      *
 51:      * @var array
 52:      */
 53:     protected $_capabilities = array(
 54:         'delete_addressbook' => true,
 55:         'delete_all' => true,
 56:     );
 57: 
 58:     /**
 59:      * Attempts to open a Kolab Groupware folder.
 60:      */
 61:     public function __construct($name = '', $params = array())
 62:     {
 63:         if (empty($params['storage'])) {
 64:             throw new InvalidArgumentException('Missing required storage handler.');
 65:         }
 66:         $this->_kolab = $params['storage'];
 67:         unset($params['storage']);
 68: 
 69:         if (isset($params['share'])) {
 70:             $this->_share = $params['share'];
 71:         }
 72:         if (isset($params['name'])) {
 73:             $name = $params['name'];
 74:         }
 75: 
 76:         parent::__construct($name, $params);
 77:     }
 78: 
 79:     /**
 80:      * Return the Kolab data handler for the current notepad.
 81:      *
 82:      * @return Horde_Kolab_Storage_Date The data handler.
 83:      */
 84:     private function _getData()
 85:     {
 86:         if ($this->_data === null) {
 87:             if (!empty($this->_share)) {
 88:                 $this->_data = $this->_kolab->getData(
 89:                     $this->_share->get('folder'),
 90:                     'contact'
 91:                 );
 92:                 $this->setContactOwner($this->_share->get('owner'));
 93:             } else if (empty($this->_name)) {
 94:                 throw new Turba_Exception(
 95:                     'The addressbook has been left undefined but is required!'
 96:                 );
 97:             } else {
 98:                 $this->_data = $this->_getDataForAddressbook($this->_name);
 99:             }
100:         }
101:         return $this->_data;
102:     }
103: 
104:     /**
105:      * Return the Kolab data handler for the specified addressbook.
106:      *
107:      * @param string $addressbook The addressbook name.
108:      *
109:      * @return Horde_Kolab_Storage_Date The data handler.
110:      */
111:     private function _getDataForAddressbook($addressbook)
112:     {
113:         $share = $GLOBALS['turba_shares']->getShare($addressbook);
114:         $this->setContactOwner($share->get('owner'));
115:         return $this->_kolab->getData($share->get('folder'), 'contact');
116:     }
117: 
118:     /**
119:      * Connect to the Kolab backend.
120:      *
121:      * @throws Turba_Exception
122:      */
123:     function connect()
124:     {
125:         /* Fetch the contacts first */
126:         $raw_contacts = $this->_getData()->getObjects();
127:         if (!$raw_contacts) {
128:             $raw_contacts = array();
129:         }
130:         $contacts = array();
131:         foreach ($raw_contacts as $id => $contact) {
132:             if (isset($contact['email'])) {
133:                 unset($contact['email']);
134:             }
135:             if (isset($contact['picture'])) {
136:                 $name = $contact['picture'];
137:                 if (isset($contact['_attachments'][$name])) {
138:                     $contact['photo'] =  $this->_getData()->getAttachment($contact['_attachments'][$name]['key']);
139:                     $contact['phototype'] = $contact['_attachments'][$name]['type'];
140:                 }
141:             }
142:             if (isset($contact['name'])) {
143:                 foreach ($contact['name'] as $detail => $value) {
144:                     $contact[$detail] = $value;
145:                 }
146:                 unset($contact['name']);
147:             }
148:             $contacts[$id] = $contact;
149:         }
150: 
151:         /* Now we retrieve distribution-lists */
152:         $groups = array();
153:         //@todo: group support
154:         /* $result = $this->_store->setObjectType('distribution-list'); */
155:         /* $groups = $this->_store->getObjects(); */
156:         /* if (!$groups) { */
157:         /*     $groups = array(); */
158:         /* } */
159: 
160:         /* Store the results in our cache */
161:         $this->_contacts_cache = array_merge($contacts, $groups);
162:     }
163: 
164:     /**
165:      * Searches the address book with the given criteria and returns a
166:      * filtered list of results. If the criteria parameter is an empty array,
167:      * all records will be returned.
168:      *
169:      * @param array $criteria    Array containing the search criteria.
170:      * @param array $fields      List of fields to return.
171:      * @param array $blobFields  Array of fields containing binary data.
172:      *
173:      * @return array  Hash containing the search results.
174:      * @throws Turba_Exception
175:      */
176:     protected function _search(array $criteria, array $fields,
177:                                array $blobFields = array(), $count_only = false)
178:     {
179:         $this->connect();
180: 
181:         if (!count($criteria)) {
182:             return $this->_contacts_cache;
183:         }
184: 
185:         // keep only entries matching criteria
186:         $ids = array();
187:         foreach ($criteria as $key => $criteria) {
188:             $ids[] = $this->_doSearch($criteria, strval($key), $this->_contacts_cache);
189:         }
190:         $ids = $this->_removeDuplicated($ids);
191: 
192:         /* Now we have a list of names, get the rest. */
193:         $this->_read('uid', $ids, null, $fields);
194: 
195:         Horde::logMessage(sprintf('Kolab returned %s results',
196:                                   count($result)), 'DEBUG');
197: 
198:         return $count_only ? count($result) : array_values($result);
199:     }
200: 
201:     /**
202:      * Applies the filter criteria to a list of entries
203:      *
204:      * @param array $criteria  Array containing the search criteria.
205:      * @param array $fields    List of fields to return.
206:      *
207:      * @return array  Array containing the ids of the selected entries.
208:      */
209:     protected function _doSearch($criteria, $glue, &$entries)
210:     {
211:         $ids = array();
212: 
213:         foreach ($criteria as $key => $vals) {
214:             if (!empty($vals['OR'])) {
215:                 $ids[] = $this->_doSearch($vals['OR'], 'OR', $entries);
216:             } elseif (!empty($vals['AND'])) {
217:                 $ids[] = $this->_doSearch($vals['AND'], 'AND', $entries);
218:             } else {
219:                 /* If we are here, and we have a ['field'] then we
220:                  * must either do the 'AND' or the 'OR' search. */
221:                 if (isset($vals['field'])) {
222:                     $ids[] = $this->_selectEntries($vals, $entries);
223:                 } else {
224:                     foreach ($vals as $test) {
225:                         if (!empty($test['OR'])) {
226:                             $ids[] = $this->_doSearch($test['OR'], 'OR');
227:                         } elseif (!empty($test['AND'])) {
228:                             $ids[] = $this->_doSearch($test['AND'], 'AND');
229:                         } else {
230:                             $ids[] = $this->_doSearch(array($test), $glue);
231:                         }
232:                     }
233:                 }
234:             }
235:         }
236: 
237:         if ($glue == 'AND') {
238:             $ids = $this->_getAND($ids);
239:         } elseif ($glue == 'OR') {
240:             $ids = $this->_removeDuplicated($ids);
241:         }
242: 
243:         return $ids;
244:     }
245: 
246:     /**
247:      * Applies one filter criterium to a list of entries
248:      *
249:      * @param $test          Test criterium
250:      * @param &$entries       List of fields to return.
251:      *
252:      * @return array  Array containing the ids of the selected entries
253:      */
254:     protected function _selectEntries($test, &$entries)
255:     {
256:         $ids = array();
257: 
258:         if (!isset($test['field'])) {
259:             Horde::logMessage('Search field not set. Returning all entries.', 'DEBUG');
260:             foreach ($entries as $entry) {
261:                 $ids[] = $entry['uid'];
262:             }
263:         } else {
264:             $field = $test['field'];
265:             $value = isset($test['test'])
266:                 ? $test['test']
267:                 : '';
268: 
269:             // Special emails hack
270:             if ($field == 'email') {
271:                 $field = 'emails';
272:                 $test['op'] = 'LIKE';
273:                 $test['begin'] = false;
274:             }
275:             if (!isset($test['op']) || $test['op'] == '=') {
276:                 foreach ($entries as $entry) {
277:                     if (isset($entry[$field]) && $entry[$field] == $value) {
278:                         $ids[] = $entry['uid'];
279:                     }
280:                 }
281:             } else {
282:                 // 'op' is LIKE
283:                 foreach ($entries as $entry) {
284:                     if (empty($value) ||
285:                         (isset($entry[$field]) &&
286:                          !empty($test['begin']) &&
287:                          (($pos = stripos($entry[$field], $value)) !== false) &&
288:                          ($pos == 0))) {
289:                         $ids[] = $entry['uid'];
290:                     }
291:                 }
292:             }
293:         }
294: 
295:         return $ids;
296:     }
297: 
298:     /**
299:      * Returns only those names that are duplicated in $ids
300:      *
301:      * @param array $ids  A nested array of arrays containing names
302:      *
303:      * @return array  Array containing the 'AND' of all arrays in $ids
304:      */
305:     protected function _getAND($ids)
306:     {
307:         $matched = $results = array();
308: 
309:         /* If there is only 1 array, simply return it. */
310:         if (count($ids) < 2) {
311:             return $ids[0];
312:         }
313: 
314:         for ($i = 0; $i < count($ids); ++$i) {
315:             if (is_array($ids[$i])) {
316:                 $results = array_merge($results, $ids[$i]);
317:             }
318:         }
319: 
320:         $search = array_count_values($results);
321:         foreach ($search as $key => $value) {
322:             if ($value == count($ids)) {
323:                 $matched[] = $key;
324:             }
325:         }
326: 
327:         return $matched;
328:     }
329: 
330:     /**
331:      * Returns an array with all duplicate names removed.
332:      *
333:      * @param array $ids  Nested array of arrays containing names.
334:      *
335:      * @return array  Array containg the 'OR' of all arrays in $ids.
336:      */
337:     protected function _removeDuplicated($ids)
338:     {
339:         for ($i = 0; $i < count($ids); ++$i) {
340:             if (is_array($ids[$i])) {
341:                 $unames = array_merge($unames, $ids[$i]);
342:             }
343:         }
344: 
345:         return array_unique($unames);
346:     }
347: 
348:     /**
349:      * Reads the given data from the address book and returns the results.
350:      *
351:      * @param string $key        The primary key field to use.
352:      * @param mixed $ids         The ids of the contacts to load.
353:      * @param string $owner      Only return contacts owned by this user.
354:      * @param array $fields      List of fields to return.
355:      * @param array $blobFields  Array of fields containing binary data.
356:      *
357:      * @return array  Hash containing the search results.
358:      * @throws Turba_Exception
359:      */
360:     protected function _read($key, $ids, $owner, array $fields,
361:                              array $blobFields = array())
362:     {
363:         $this->connect();
364: 
365:         $results = array();
366: 
367:         if (!is_array($ids)) {
368:             $ids = array($ids);
369:         }
370: 
371:         $count = count($fields);
372:         foreach ($ids as $id) {
373:             if (in_array($id, array_keys($this->_contacts_cache))) {
374:                 $object = $this->_contacts_cache[$id];
375: 
376:                 if (!isset($object['__type']) || $object['__type'] == 'Object') {
377:                     if ($count) {
378:                         $result = array();
379:                         foreach ($fields as $field) {
380:                             if (isset($object[$field])) {
381:                                 $result[$field] = $object[$field];
382:                             }
383:                         }
384:                         $results[] = $result;
385:                     } else {
386:                         $results[] = $object;
387:                     }
388:                 } else {
389:                     $member_ids = array();
390:                     if (isset($object['member'])) {
391:                         foreach ($object['member'] as $member) {
392:                             if (isset($member['uid'])) {
393:                                 $member_ids[] = $member['uid'];
394:                                 continue;
395:                             }
396:                             $display_name = $member['display-name'];
397:                             $smtp_address = $member['smtp-address'];
398:                             $criteria = array(
399:                                 'AND' => array(
400:                                     array(
401:                                         'field' => 'full-name',
402:                                         'op' => 'LIKE',
403:                                         'test' => $display_name,
404:                                         'begin' => false,
405:                                     ),
406:                                     array(
407:                                         'field' => 'emails',
408:                                         'op' => 'LIKE',
409:                                         'test' => $smtp_address,
410:                                         'begin' => false,
411:                                     ),
412:                                 ),
413:                             );
414:                             $fields = array('uid');
415: 
416:                             // we expect only one result here!!!
417:                             $contacts = $this->_search($criteria, $fields);
418: 
419:                             // and drop everything else except the first search result
420:                             $member_ids[] = $contacts[0]['uid'];
421:                         }
422:                         $object['__members'] = serialize($member_ids);
423:                         unset($object['member']);
424:                     }
425:                     $results[] = $object;;
426:                 }
427:             }
428:         }
429: 
430:         return $results;
431:     }
432: 
433:     /**
434:      * Adds the specified contact to the addressbook.
435:      *
436:      * @param array $attributes  The attribute values of the contact.
437:      * @param array $blob_fields TODO
438:      *
439:      * @throws Turba_Exception
440:      */
441:     protected function _add(array $attributes, array $blob_fields = array())
442:     {
443:         $this->connect();
444: 
445:         if (isset($attributes['last-name'])) {
446:             $attributes['full-name'] = $attributes['last-name'];
447:         }
448:         if (isset($attributes['middle-names'])) {
449:             $attributes['full-name'] = $attributes['middle-names'] . ' ' . $attributes['full-name'];
450:         }
451:         if (isset($attributes['given-name'])) {
452:             $attributes['full-name'] = $attributes['given-name'] . ' ' . $attributes['full-name'];
453:         }
454: 
455:         $attributes['name'] = array(
456:             'last-name' => $attributes['last-name'],
457:         );
458: 
459:         $this->_store($attributes);
460:     }
461: 
462:     protected function _canAdd()
463:     {
464:         return true;
465:     }
466: 
467:     /**
468:      * Removes the specified object from the Kolab message store.
469:      */
470:     protected function _delete($object_key, $object_id)
471:     {
472:         $this->connect();
473: 
474:         if ($object_key != 'uid') {
475:             throw new Turba_Exception(sprintf('Key for saving must be a UID not %s!', $object_key));
476:         }
477: 
478:         if (!in_array($object_id, array_keys($this->_contacts_cache))) {
479:             throw new Turba_Exception(sprintf(_("Object with UID %s does not exist!"), $object_id));
480:         }
481: 
482:         $group = (isset($this->_contacts_cache[$object_id]['__type']) &&
483:                   $this->_contacts_cache[$object_id]['__type'] == 'Group');
484: 
485:         if ($group) {
486:             //@todo: group support
487:             //$result = $this->_store->setObjectType('distribution-list');
488:         }
489: 
490:         $result = $this->_getData()->delete($object_id);
491: 
492:         return $result;
493:     }
494: 
495:     /**
496:      * Deletes all contacts from a specific address book.
497:      *
498:      * @return boolean  True if the operation worked.
499:      */
500:     protected function _deleteAll($sourceName = null)
501:     {
502:         $this->connect();
503: 
504:         /* Delete contacts */
505:         $result = $this->_getData()->deleteAll();
506: 
507:         /* Delete groups */
508:         //@todo: group support
509:         //$result = $this->_store->setObjectType('distribution-list');
510:         //$result = $this->_store->deleteAll();
511:     }
512: 
513:     /**
514:      * Saves the specified object in the SQL database.
515:      *
516:      * @param Turba_Object $object  The object to save
517:      *
518:      * @return string  The object id, possibly updated.
519:      * @throws Turba_Exception
520:      */
521:     protected function _save(Turba_Object $object)
522:     {
523:         $this->connect();
524: 
525:         if ($object_key != 'uid') {
526:             throw new Turba_Exception(sprintf('Key for saving must be \'uid\' not %s!', $object_key));
527:         }
528: 
529:         return $this->_store($attributes, $object_id);
530:     }
531: 
532:     /**
533:      * Stores an object in the Kolab message store.
534:      *
535:      * TODO
536:      *
537:      * @return string  The object id, possibly updated.
538:      * @throws Turba_Exception
539:      */
540:     protected function _store($attributes, $object_id = null)
541:     {
542:         $group = false;
543:         if (isset($attributes['__type']) && $attributes['__type'] == 'Group') {
544:             return;
545:             //@todo: group support
546:             /* $group = true; */
547:             /* $result = $this->_store->setObjectType('distribution-list'); */
548:             /* $this->_convertMembers($attributes); */
549:         }
550: 
551:         if (isset($attributes['photo']) && isset($attributes['phototype'])) {
552:             $attributes['_attachments']['photo.attachment'] = array(
553:                 'type' => $attributes['phototype'],
554:                 'content' => $attributes['photo']
555:             );
556:             $attributes['picture'] = 'photo.attachment';
557:             unset($attributes['photo'], $attributes['phototype']);
558:         }
559: 
560:         if ($object_id === null) {
561:             $result = $this->_getData()->create($attributes);
562:         } else {
563:             $result = $this->_getData()->modify($attributes);
564:         }
565:         if ($group) {
566:             $result = $this->_store->setObjectType('contact');
567:         }
568: 
569:         return $object_id;
570:     }
571: 
572:     /**
573:      * TODO
574:      */
575:     function _convertMembers(&$attributes)
576:     {
577:         if (isset($attributes['__members'])) {
578:             $member_ids = unserialize($attributes['__members']);
579:             $attributes['member'] = array();
580:             foreach ($member_ids as $member_id) {
581:                 if (isset($this->_contacts_cache[$member_id])) {
582:                     $member = $this->_contacts_cache[$member_id];
583:                     $mail = array('uid' => $member_id);
584:                     if (!empty($member['full-name'])) {
585:                         $mail['display-name'] = $member['full-name'];
586:                     }
587:                     if (!empty($member['emails'])) {
588:                         $emails = explode(',', $member['emails']);
589:                         $mail['smtp-address'] = trim($emails[0]);
590:                         if (!isset($mail['display-name'])) {
591:                             $mail['display-name'] = $mail['smtp-address'];
592:                         }
593:                     }
594:                     $attributes['member'][] = $mail;
595:                 }
596:             }
597:             unset($attributes['__members']);
598:         }
599:     }
600: 
601: 
602:     /**
603:      * Create an object key for a new object.
604:      *
605:      * @param array $attributes  The attributes (in driver keys) of the
606:      *                           object being added.
607:      *
608:      * @return string  A unique ID for the new object.
609:      */
610:     protected function _makeKey(array $attributes)
611:     {
612:         return isset($attributes['uid'])
613:             ? $attributes['uid']
614:             : $this->_generateUid();
615:     }
616: 
617:     /**
618:      * Creates an object UID for a new object.
619:      *
620:      * @return string  A unique ID for the new object.
621:      */
622:     protected function _makeUid()
623:     {
624:         return $this->_generateUid();
625:     }
626: 
627:     /**
628:      * Create an object key for a new object.
629:      *
630:      * @return string  A unique ID for the new object.
631:      */
632:     private function _generateUid()
633:     {
634:         return $this->_getData()->generateUID();
635:     }
636: 
637:     /**
638:      * Creates a new Horde_Share for this source type.
639:      *
640:      * @param string $share_name  The share name
641:      * @param array  $params      The params for the share.
642:      *
643:      * @return Horde_Share  The share object.
644:      */
645:     public function createShare($share_name, array $params)
646:     {
647:         if (!isset($params['name'])) {
648:             $params['name'] = _('Contacts');
649:         }
650:         return Turba::createShare($share_name, $params);
651:     }
652: 
653:     /**
654:      * Check if the passed in share is the default share for this source.
655:      *
656:      * @param Horde_Share_Object $share  The share object.
657:      * @param array $srcconfig           The cfgSource entry for the share.
658:      *
659:      * @return boolean TODO
660:      */
661:     public function checkDefaultShare(Horde_Share_Object $share, array $srcconfig)
662:     {
663:         $params = @unserialize($share->get('params'));
664:         return isset($params['default'])
665:             ? $params['default']
666:             : false;
667:     }
668: }
669: 
API documentation generated by ApiGen