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:  * Turba directory driver implementation for the Horde_Db database abstraction
  4:  * layer.
  5:  *
  6:  * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file LICENSE for license information (ASL).  If you did
  9:  * did not receive this file, see http://www.horde.org/licenses/apache.
 10:  *
 11:  * @author   Jon Parise <jon@csh.rit.edu>
 12:  * @author   Michael J. Rubinsky <mrubinsk@horde.org>
 13:  * @category Horde
 14:  * @license  http://www.horde.org/licenses/apache ASL
 15:  * @package  Turba
 16:  */
 17: class Turba_Driver_Sql extends Turba_Driver
 18: {
 19:     /**
 20:      * What can this backend do?
 21:      *
 22:      * @var array
 23:      */
 24:     protected $_capabilities = array(
 25:         'delete_addressbook' => true,
 26:         'delete_all' => true
 27:     );
 28: 
 29:     /**
 30:      * count() cache.
 31:      *
 32:      * @var array
 33:      */
 34:     protected $_countCache = array();
 35: 
 36:     /**
 37:      * Handle for the current database connection.
 38:      *
 39:      * @var Horde_Db_Adapter
 40:      */
 41:     protected $_db;
 42: 
 43:     /**
 44:      * Constructor.
 45:      *
 46:      * @param string $name   The source name
 47:      * @param array $params  Additional parameters needed:
 48:      * <pre>
 49:      * 'db' - (Horde_Db_Adapter) A DB Adapter object.
 50:      * </pre>
 51:      */
 52:     public function __construct($name = '', array $params = array())
 53:     {
 54:         if (empty($params['db'])) {
 55:             throw new InvalidArgumentException('Missing required Horde_Db_Adapter object');
 56:         }
 57:         $this->_db = $params['db'];
 58:         unset($params['db']);
 59: 
 60:         parent::__construct($name, $params);
 61:     }
 62: 
 63:     /**
 64:      * Returns the number of contacts of the current user in this address book.
 65:      *
 66:      * @return integer  The number of contacts that the user owns.
 67:      */
 68:     public function count()
 69:     {
 70:         $test = $this->getContactOwner();
 71:         if (!isset($this->_countCache[$test])) {
 72:             /* Build up the full query. */
 73:             $query = 'SELECT COUNT(*) FROM ' . $this->_params['table'] .
 74:                      ' WHERE ' . $this->toDriver('__owner') . ' = ?';
 75:             $values = array($test);
 76: 
 77:             /* Run query. */
 78:             try {
 79:                 $this->_countCache[$test] = $this->_db->selectValue($query, $values);
 80:             } catch (Horde_Db_Exception $e) {
 81:                 $this->_countCache[$test] = 0;
 82:             }
 83:         }
 84: 
 85:         return $this->_countCache[$test];
 86:     }
 87: 
 88:     /**
 89:      * Searches the SQL database with the given criteria and returns a
 90:      * filtered list of results. If the criteria parameter is an empty array,
 91:      * all records will be returned.
 92:      *
 93:      * @param array $criteria       Array containing the search criteria.
 94:      * @param array $fields         List of fields to return.
 95:      * @param array $blobFields     TODO
 96:      * @param boolean $count_only   Only return the count of matching entries,
 97:      *                              not the entries themselves.
 98:      *
 99:      * @return array  Hash containing the search results.
100:      * @throws Turba_Exception
101:      */
102:     protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only = false)
103:     {
104:         return $this->_internalSearch($criteria, $fields, $blobFields, array(), $count_only);
105:     }
106: 
107:     /**
108:      * Searches the SQL database with the given criteria and returns a
109:      * filtered list of results. If the criteria parameter is an empty array,
110:      * all records will be returned.
111:      *
112:      * @param array $criteria        Array containing the search criteria.
113:      * @param array $fields          List of fields to return.
114:      * @param array $blobFields      TODO
115:      * @param array $appendWhere     An additional where clause to append.
116:      *                               Array should contain 'sql' and 'params'
117:      *                               params are used as bind parameters.
118:      * @param boolean $count_only   Only return the count of matching entries,
119:      *                              not the entries themselves.
120:      *
121:      * @return mixed array|integer  Hash containing the search results or the
122:      *                              count of matching entries.
123:      * @throws Turba_Exception
124:      */
125:     protected function _internalSearch(array $criteria, array $fields, $blobFields = array(), $appendWhere = array(), $count_only = false)
126:     {
127:         /* Build the WHERE clause. */
128:         $where = '';
129:         $values = array();
130: 
131:         if (count($criteria) || !empty($this->_params['filter'])) {
132:             foreach ($criteria as $key => $vals) {
133:                 if ($key == 'OR' || $key == 'AND') {
134:                     if (!empty($where)) {
135:                         $where .= ' ' . $key . ' ';
136:                     }
137:                     $binds = $this->_buildSearchQuery($key, $vals);
138:                     $where .= '(' . $binds[0] . ')';
139:                     $values += $binds[1];
140:                 }
141:             }
142:             $where = ' WHERE ' . $where;
143:             if (count($criteria) && !empty($this->_params['filter'])) {
144:                 $where .= ' AND ';
145:             }
146:             if (!empty($this->_params['filter'])) {
147:                 $where .= $this->_params['filter'];
148:             }
149:             if (count($appendWhere)) {
150:                 $where .= ' AND ' . $appendWhere['sql'];
151:                 $values = array_merge($values, $appendWhere['params']);
152:             }
153:         } elseif (count($appendWhere)) {
154:             $where = ' WHERE ' . $appendWhere['sql'];
155:             $values = array_merge($values, $appendWhere['params']);
156:         }
157: 
158:         /* Build up the full query. */
159:         if ($count_only) {
160:             $query = 'SELECT COUNT(*) FROM ' . $this->_params['table'] . $where;
161:             try {
162:                 return $this->_db->selectValue($query, $values);
163:             } catch (Horde_Db_Exception $e) {
164:                 throw new Turba_Exception($e);
165:             }
166:         } else {
167:             $query = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $this->_params['table'] . $where;
168:             try {
169:                return $this->_parseRead($blobFields, $this->_db->selectAll($query, $values));
170:             } catch (Horde_Db_Exception $e) {
171:                 throw new Turba_Exception($e);
172:             }
173:         }
174:     }
175: 
176:     protected function _parseRead($blobFields, $result)
177:     {
178:         $results = array();
179:         $columns = $this->_db->columns($this->_params['table']);
180: 
181:         foreach ($result as $row) {
182:             $entry = array();
183: 
184:             foreach ($row as $field => $val) {
185:                 if (isset($blobFields[$field])) {
186:                     $entry[$field] = $columns[$field]->binaryToString($val);
187:                 } else {
188:                     $entry[$field] = $this->_convertFromDriver($val);
189:                 }
190:             }
191: 
192:             $results[] = $entry;
193:         }
194: 
195:         return $results;
196:     }
197: 
198:     /**
199:      * Prepares field lists for searchDuplicates().
200:      *
201:      * @param array $array  A list of field names.
202:      *
203:      * @return array  A prepared list of field names.
204:      */
205:     protected function _buildFields($array)
206:     {
207:         foreach ($array as &$entry) {
208:             $entry = is_array($entry)
209:                 ? implode(',', $this->_buildFields($entry))
210:                 : 'a1.' . $entry;
211:         }
212: 
213:         return $array;
214:     }
215: 
216:     /**
217:      * Builds the WHERE conditions for searchDuplicates().
218:      *
219:      * @param array $array  A list of field names.
220:      *
221:      * @return array  A list of WHERE conditions.
222:      */
223:     protected function _buildWhere($array)
224:     {
225:         foreach ($array as &$entry) {
226:             if (is_array($entry)) {
227:                 $entry = reset($entry);
228:             }
229:             $entry = 'a1.' . $entry . ' IS NOT NULL AND a1.' . $entry . ' <> \'\'';
230:         }
231: 
232:         return $array;
233:     }
234: 
235:     /**
236:      * Builds the JOIN conditions for searchDuplicates().
237:      *
238:      * @param array $array  A list of field names.
239:      *
240:      * @return array  A list of JOIN conditions.
241:      */
242:     protected function _buildJoin($array)
243:     {
244:         foreach ($array as &$entry) {
245:             $entry = is_array($entry)
246:                 ? implode(' AND ', $this->_buildJoin($entry))
247:                 : 'a1.' . $entry . ' = a2.' . $entry;
248:         }
249: 
250:         return $array;
251:     }
252: 
253:     /**
254:      * Searches the current address book for duplicate entries.
255:      *
256:      * Duplicates are determined by comparing email and name or last name and
257:      * first name values.
258:      *
259:      * @return array  A hash with the following format:
260:      *                <code>
261:      *                array('name' => array('John Doe' => Turba_List, ...), ...)
262:      *                </code>
263:      * @throws Turba_Exception
264:      */
265:     public function searchDuplicates()
266:     {
267:         $owner = $this->getContactOwner();
268:         $fields = array();
269:         if (is_array($this->map['name'])) {
270:             if (in_array('lastname', $this->map['name']['fields']) &&
271:                 isset($this->map['lastname'])) {
272:                 $field = array($this->map['lastname']);
273:                 if (in_array('firstname', $this->map['name']['fields']) &&
274:                     isset($this->map['firstname'])) {
275:                     $field[] = $this->map['firstname'];
276:                 }
277:                 $fields[] = $field;
278:             }
279:         } else {
280:             $fields[] = $this->map['name'];
281:         }
282:         if (isset($this->map['email'])) {
283:             $fields[] = $this->map['email'];
284:         }
285:         $nameFormat = $GLOBALS['prefs']->getValue('name_format');;
286:         if ($nameFormat != 'first_last' && $nameFormat != 'last_first') {
287:             $nameFormat = 'first_last';
288:         }
289: 
290:         $order = $this->_buildFields($fields);
291:         $joins = $this->_buildJoin($fields);
292:         $where = $this->_buildWhere($fields);
293: 
294:         $duplicates = array();
295:         for ($i = 0; $i < count($joins); $i++) {
296:             /* Build up the full query. */
297:             $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',
298:                              $this->map['__key'],
299:                              $order[$i],
300:                              $this->_params['table'],
301:                              $this->_params['table'],
302:                              $joins[$i],
303:                              $this->map['__key'],
304:                              $this->map['__key'],
305:                              $this->map['__owner'],
306:                              $this->map['__owner'],
307:                              $where[$i],
308:                              $order[$i]);
309: 
310:             /* Run query. */
311:             try {
312:                 $ids = $this->_db->selectValues($query, array($owner, $owner));
313:             } catch (Horde_Db_Exception $e) {
314:                 throw new Turba_Exception($e);
315:             }
316: 
317:             $field = ($i == 0)
318:                 ? 'name'
319:                 : array_search($fields[$i], $this->map);
320: 
321:             $contacts = array();
322:             foreach ($ids as $id) {
323:                 $contact = $this->getObject($id);
324:                 $value = $contact->getValue($field);
325:                 if ($field == 'name') {
326:                     $value = Turba::formatName($contact, $nameFormat);
327:                 }
328:                 /* HACK! */
329:                 if ($field == 'email') {
330:                     $value = Horde_String::lower($value);
331:                 }
332:                 if (!isset($contacts[$value])) {
333:                     $contacts[$value] = new Turba_List();
334:                 }
335:                 $contacts[$value]->insert($contact);
336:             }
337:             if ($contacts) {
338:                 $duplicates[$field] = $contacts;
339:             }
340:         }
341: 
342:         return $duplicates;
343:     }
344: 
345:     /**
346:      * Reads the given data from the SQL database and returns the results.
347:      *
348:      * @param string $key        The primary key field to use.
349:      * @param mixed $ids         The ids of the contacts to load.
350:      * @param string $owner      Only return contacts owned by this user.
351:      * @param array $fields      List of fields to return.
352:      * @param array $blobFields  Array of fields containing binary data.
353:      *
354:      * @return array  Hash containing the search results.
355:      * @throws Turba_Exception
356:      */
357:     protected function _read($key, $ids, $owner, array $fields,
358:                              array $blobFields = array())
359:     {
360:         $values = array();
361: 
362:         $in = '';
363:         if (is_array($ids)) {
364:             if (!count($ids)) {
365:                 return array();
366:             }
367: 
368:             foreach ($ids as $id) {
369:                 $in .= empty($in) ? '?' : ', ?';
370:                 $values[] = $this->_convertToDriver($id);
371:             }
372:             $where = $key . ' IN (' . $in . ')';
373:         } else {
374:             $where = $key . ' = ?';
375:             $values[] = $this->_convertToDriver($ids);
376:         }
377:         if (isset($this->map['__owner'])) {
378:             $where .= ' AND ' . $this->map['__owner'] . ' = ?';
379:             $values[] = $this->_convertToDriver($owner);
380:         }
381:         if (!empty($this->_params['filter'])) {
382:             $where .= ' AND ' . $this->_params['filter'];
383:         }
384: 
385:         $query  = 'SELECT ' . implode(', ', $fields) . ' FROM '
386:             . $this->_params['table'] . ' WHERE ' . $where;
387: 
388:         try {
389:             return $this->_parseRead($blobFields, $this->_db->selectAll($query, $values));
390:         } catch (Horde_Db_Exception $e) {
391:             throw new Turba_Exception($e);
392:         }
393:     }
394: 
395:     /**
396:      * Adds the specified object to the SQL database.
397:      *
398:      * TODO
399:      *
400:      * @throws Turba_Exception
401:      */
402:     protected function _add(array $attributes, array $blob_fields = array())
403:     {
404:         list($fields, $values) = $this->_prepareWrite($attributes, $blob_fields);
405:         $query  = 'INSERT INTO ' . $this->_params['table']
406:             . ' (' . implode(', ', $fields) . ')'
407:             . ' VALUES (' . str_repeat('?, ', count($values) - 1) . '?)';
408: 
409:         try {
410:             $this->_db->insert($query, $values);
411:         } catch (Horde_Db_Exception $e) {
412:             throw new Turba_Exception($e);
413:         }
414:     }
415: 
416:     protected function _prepareWrite($attributes, $blob_fields)
417:     {
418:         $fields = $values = array();
419: 
420:         foreach ($attributes as $field => $value) {
421:             $fields[] = $field;
422: 
423:             if (!empty($value) && isset($blob_fields[$field])) {
424:                 $values[] = new Horde_Db_Value_Binary($value);
425:             } else {
426:                 $values[] = $this->_convertToDriver($value);
427:             }
428:         }
429: 
430:         return array($fields, $values);
431:     }
432: 
433:     /**
434:      * TODO
435:      */
436:     protected function _canAdd()
437:     {
438:         return true;
439:     }
440: 
441:     /**
442:      * Deletes the specified object from the SQL database.
443:      *
444:      * @throws Turba_Exception
445:      */
446:     protected function _delete($object_key, $object_id)
447:     {
448:         $query = 'DELETE FROM ' . $this->_params['table'] .
449:                  ' WHERE ' . $object_key . ' = ?';
450:         $values = array($object_id);
451: 
452:         try {
453:             $this->_db->delete($query, $values);
454:         } catch (Horde_Db_Exception $e) {
455:             throw new Turba_Exception($e);
456:         }
457:     }
458: 
459:     /**
460:      * Deletes all contacts from a specific address book.
461:      *
462:      * @throws Turba_Exception
463:      */
464:     protected function _deleteAll($sourceName = null)
465:     {
466:         if (!$GLOBALS['registry']->getAuth()) {
467:             throw new Turba_Exception('Permission denied');
468:         }
469: 
470:         /* Get owner id */
471:         $values = empty($sourceName)
472:             ? array($GLOBALS['registry']->getAuth())
473:             : array($sourceName);
474: 
475:         /* Need a list of UIDs so we can notify History */
476:         $query = 'SELECT '. $this->map['__uid'] . ' FROM ' . $this->_params['table'] . ' WHERE owner_id = ?';
477: 
478:         try {
479:             $ids = $this->_db->selectValues($query, $values);
480:         } catch (Horde_Db_Exception $e) {
481:             throw new Turba_Exception($e);
482:         }
483: 
484:         /* Do the deletion */
485:         $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE owner_id = ?';
486: 
487:         try {
488:             $this->_db->delete($query, $values);
489:         } catch (Horde_Db_Exception $e) {
490:             throw new Turba_Exception($e);
491:         }
492: 
493:         /* Update Horde_History */
494:         $history = $GLOBALS['injector']->getInstance('Horde_History');
495:         try {
496:             foreach ($ids as $id) {
497:                 // This is slightly hackish, but it saves us from having to
498:                 // create and save an array of Turba_Objects before we delete
499:                 // them, just to be able to calculate this using
500:                 // Turba_Object#getGuid
501:                 $guid = 'turba:' . $this->getName() . ':' . $id;
502:                 $history->log($guid, array('action' => 'delete'), true);
503:             }
504:         } catch (Exception $e) {
505:             Horde::logMessage($e, 'ERR');
506:         }
507:     }
508: 
509:     /**
510:      * Saves the specified object in the SQL database.
511:      *
512:      * @param Turba_Object $object  The object to save.
513:      *
514:      * @return string  The object id, possibly updated.
515:      * @throws Turba_Exception
516:      */
517:     function _save(Turba_Object $object)
518:     {
519:         list($object_key, $object_id) = each($this->toDriverKeys(array('__key' => $object->getValue('__key'))));
520:         $attributes = $this->toDriverKeys($object->getAttributes());
521:         $blob_fields = $this->toDriverKeys($this->getBlobs());
522: 
523:         $where = $object_key . ' = ?';
524:         unset($attributes[$object_key]);
525: 
526:         list($fields, $values) = $this->_prepareWrite($attributes, $blob_fields);
527: 
528:         $values[] = $object_id;
529: 
530:         $query = 'UPDATE ' . $this->_params['table'] . ' SET ' . implode(' = ?, ', $fields) . ' = ? WHERE ' . $where;
531: 
532:         try {
533:             $this->_db->update($query, $values);
534:         } catch (Horde_Db_Exception $e) {
535:             throw new Turba_Exception($e);
536:         }
537: 
538:         return $object_id;
539:     }
540: 
541:     /**
542:      * Creates an object key for a new object.
543:      *
544:      * @param array $attributes  The attributes (in driver keys) of the
545:      *                           object being added.
546:      *
547:      * @return string  A unique ID for the new object.
548:      */
549:     protected function _makeKey(array $attributes)
550:     {
551:         return strval(new Horde_Support_Randomid());
552:     }
553: 
554:     /**
555:      * Builds a piece of a search query.
556:      *
557:      * @param string $glue      The glue to join the criteria (OR/AND).
558:      * @param array  $criteria  The array of criteria.
559:      *
560:      * @return array  An SQL fragment and a list of values suitable for binding
561:      *                as an array.
562:      */
563:     protected function _buildSearchQuery($glue, array $criteria)
564:     {
565:         $clause = '';
566:         $values = array();
567: 
568:         foreach ($criteria as $key => $vals) {
569:             if (!empty($vals['OR']) || !empty($vals['AND'])) {
570:                 if (!empty($clause)) {
571:                     $clause .= ' ' . $glue . ' ';
572:                 }
573:                 $binds = $this->_buildSearchQuery(!empty($vals['OR']) ? 'OR' : 'AND', $vals);
574:                 $clause .= '(' . $binds[0] . ')';
575:                 $values = array_merge($values, $binds[1]);
576:             } else {
577:                 if (isset($vals['field'])) {
578:                     if (!empty($clause)) {
579:                         $clause .= ' ' . $glue . ' ';
580:                     }
581:                     $rhs = $this->_convertToDriver($vals['test']);
582:                     $binds = $this->_db->buildClause($vals['field'], $vals['op'], $rhs, true, array('begin' => !empty($vals['begin'])));
583:                     if (is_array($binds)) {
584:                         $clause .= $binds[0];
585:                         $values = array_merge($values, $binds[1]);
586:                     } else {
587:                         $clause .= $binds;
588:                     }
589:                 } else {
590:                     foreach ($vals as $test) {
591:                         if (!empty($test['OR']) || !empty($test['AND'])) {
592:                             if (!empty($clause)) {
593:                                 $clause .= ' ' . $glue . ' ';
594:                             }
595:                             $binds = $this->_buildSearchQuery(!empty($vals['OR']) ? 'OR' : 'AND', $test);
596:                             $clause .= '(' . $binds[0] . ')';
597:                             $values = array_merge($values, $binds[1]);
598:                         } else {
599:                             if (!empty($clause)) {
600:                                 $clause .= ' ' . $key . ' ';
601:                             }
602:                             $rhs = $this->_convertToDriver($test['test']);
603:                             if ($rhs == '' && $test['op'] == '=') {
604:                                 $clause .= '(' . $this->_db->buildClause($test['field'], '=', $rhs) . ' OR ' . $test['field'] . ' IS NULL)';
605:                             } else {
606:                                 $binds = $this->_db->buildClause($test['field'], $test['op'], $rhs, true, array('begin' => !empty($test['begin'])));
607:                                 if (is_array($binds)) {
608:                                     $clause .= $binds[0];
609:                                     $values = array_merge($values, $binds[1]);
610:                                 } else {
611:                                     $clause .= $binds;
612:                                 }
613:                             }
614:                         }
615:                     }
616:                 }
617:             }
618:         }
619: 
620:         return array($clause, $values);
621:     }
622: 
623:     /**
624:      * Converts a value from the driver's charset to the default charset.
625:      *
626:      * @param mixed $value  A value to convert.
627:      *
628:      * @return mixed  The converted value.
629:      */
630:     protected function _convertFromDriver($value)
631:     {
632:         return Horde_String::convertCharset($value, $this->_db->getOption('charset'), 'UTF-8');
633:     }
634: 
635:     /**
636:      * Converts a value from the default charset to the driver's charset.
637:      *
638:      * @param mixed $value  A value to convert.
639:      *
640:      * @return mixed  The converted value.
641:      */
642:     protected function _convertToDriver($value)
643:     {
644:         return Horde_String::convertCharset($value, 'UTF-8', $this->_db->getOption('charset'));
645:     }
646: 
647:     /**
648:      * Remove all entries owned by the specified user.
649:      *
650:      * @param string $user  The user's data to remove.
651:      *
652:      * @throws Horde_Exception_PermissionDenied
653:      */
654:     public function removeUserData($user)
655:     {
656:         // Make sure we are being called by an admin.
657:         if (!$GLOBALS['registry']->isAdmin()) {
658:             throw new Horde_Exception_PermissionDenied(_("Permission denied"));
659:         }
660: 
661:         $this->_deleteAll($user);
662:     }
663: 
664:     /**
665:      * Obtain Turba_List of items to get TimeObjects out of.
666:      *
667:      * @param Horde_Date $start  The starting date.
668:      * @param Horde_Date $end    The ending date.
669:      * @param string $field      The address book field containing the
670:      *                           timeObject information (birthday, anniversary)
671:      *
672:      * @return Turba_List  Object list.
673:      * @throws Turba_Exception
674:      */
675:     public function getTimeObjectTurbaList(Horde_Date $start, Horde_Date $end, $field)
676:     {
677:         $t_object = $this->toDriver($field);
678:         $criteria = $this->makesearch(
679:             array('__owner' => $this->getContactOwner()),
680:             'AND',
681:             array($this->toDriver('__owner') => true),
682:             false);
683: 
684:         // Limit to entries that actually contain a birthday and that are in the
685:         // date range we are looking for.
686:         $criteria['AND'][] = array(
687:             'field' => $t_object,
688:             'op' => '<>',
689:             'test' => ''
690:         );
691: 
692:         if ($start->year == $end->year) {
693:             $start = sprintf('%02d-%02d', $start->month, $start->mday);
694:             $end = sprintf('%02d-%02d', $end->month, $end->mday);
695:             $where = array('sql' => $t_object . ' IS NOT NULL AND SUBSTR('
696:                            . $t_object . ', 6, 5) BETWEEN ? AND ?',
697:                            'params' => array($start, $end));
698:         } else {
699:             $months = array();
700:             $diff = ($end->month + 12) - $start->month;
701:             $newDate = new Horde_Date(array(
702:                 'month' => $start->month,
703:                 'mday' => $start->mday,
704:                 'year' => $start->year
705:             ));
706:             for ($i = 0; $i <= $diff; ++$i) {
707:                 $months[] = sprintf('%02d', $newDate->month++);
708:             }
709:             $where = array('sql' => $t_object . ' IS NOT NULL AND SUBSTR('
710:                            . $t_object . ', 6, 2) IN ('
711:                            . str_repeat('?,', count($months) - 1) . '?)',
712:                            'params' => $months);
713:         }
714: 
715:         $fields_pre = array(
716:             '__key', '__type', '__owner', 'name', 'birthday', 'category',
717:             'anniversary'
718:         );
719: 
720:         $fields = array();
721:         foreach ($fields_pre as $field) {
722:             $result = $this->toDriver($field);
723:             if (is_array($result)) {
724:                 foreach ($result as $composite_field) {
725:                     $composite_result = $this->toDriver($composite_field);
726:                     if ($composite_result) {
727:                         $fields[] = $composite_result;
728:                     }
729:                 }
730:             } elseif ($result) {
731:                 $fields[] = $result;
732:             }
733:         }
734: 
735:         return $this->_toTurbaObjects($this->_internalSearch($criteria, $fields, array(), $where));
736:     }
737: 
738: }
739: 
API documentation generated by ApiGen