Location: PHPKode > scripts > dbiCal > dbiCal-3.0.5/includes/dbiCal.class.php
<?php
/**
 * dbiCal
 * ver 3.0.5
 *
 * The iCal calendar database interface, using PEAR MDB2 package
 *
 * copyright (c) 2011 Kjell-Inge Gustafsson kigkonsult
 * kigkonsult.se/index.php
 * hide@address.com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**/
define( 'DBICALVERSION', 'dbiCal 3.0.5' );
/**
  * @since    3.0.3 - 2011-12-01
**/
/**
 * This class implements the dbiCal class
**/
class dbiCal {
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   public
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2011-01-18
   */
  function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
  }
  /**
   * getCalendars
   *
   * @access   public
   * @param    array  $selectOptions
   * @return   mixed  $res (FALSE or array calendars)
   * @since    1.0 - 2011-01-20
   */
  function getCalendars( $selectOptions=array()) {
    if( $this->_log )
      $this->_log->log( __METHOD__." start selectOptions=".var_export( $selectOptions, TRUE ), PEAR_LOG_INFO );
    $dbiCal_calendar_DAO = dbiCal_calendar_DAO::singleton( $this->_db, $this->_log );
    $calendars = $dbiCal_calendar_DAO->getCalendars( $selectOptions );
    if( PEAR::isError( $calendars ))
      return FALSE;
    return $calendars;
  }
  /**
   * delete
   *
   * @access   public
   * @param    array  $selectOptions
   * @return   bool  $res
   * @since    1.0 - 2011-01-20
   */
  function delete( $selectOptions=array()) {
    if( $this->_log )
      $this->_log->log( __METHOD__." start selectOptions=".var_export( $selectOptions, TRUE ), PEAR_LOG_INFO );
    if( FALSE === $this->transactionBegin())
      return FALSE;
    $dbiCal_calendar_DAO = dbiCal_calendar_DAO::singleton( $this->_db, $this->_log );
    if( isset( $selectOptions['calendar_id'] ))
      $calendars = array( 'calendar_id' => array( 'calendar_id' => $selectOptions['calendar_id'] ));
    else {
      $calendars = $dbiCal_calendar_DAO->getCalendars( $selectOptions, FALSE );
      if( PEAR::isError( $calendars )) {
        $this->transactionRollback();
        return FALSE;
      }
    }
    if( $this->_log )
      $this->_log->log( 'calendars='.var_export( $calendars, TRUE ), PEAR_LOG_DEBUG );
    foreach( $calendars as $calendar ) {
      $res = $dbiCal_calendar_DAO->delete( $calendar['calendar_id']  );
      if( PEAR::isError( $res )) {
        $this->transactionRollback();
        return FALSE;
      }
    }
    if( FALSE === $this->transactionCommit())
      return FALSE;
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    object $calendar
   * @param    array  $metadata
   * @return   mixed  $res (FALSE or calendar-id)
   * @since    1.0 - 2011-01-21
   */
  function insert( & $calendar, $metadata=array()) {
    if( $this->_log )
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
    if( FALSE === $this->transactionBegin())
      return FALSE;
    $data = array();
    if   ( $prop = $calendar->getProperty( 'calscale' ))         $data['CALSCALE']  = $prop;
    if   ( $prop = $calendar->getProperty( 'method'   ))         $data['METHOD']    = $prop;
    if   ( $prop = $calendar->getProperty( 'version'  ))         $data['VERSION']   = $prop;
    $unique_id   = $calendar->getConfig( 'unique_id' );
    if( !in_array( $unique_id, array( 'localhost', '127.0.0.1' )))
                                                                 $data['unique_id'] = $unique_id;
                                                                 $data['filename']  = $calendar->getConfig( 'filename' );
    $filesize    = $calendar->getConfig( 'filesize' );
    if( 0 < $filesize )                                          $data['filesize']  = $filesize;
    foreach( $calendar->components as $compix => & $comp )
      $data[$comp->objName] = isset( $data[$comp->objName] ) ? ( $data[$comp->objName] + 1 ) : 1;
    while( $prop = $calendar->getProperty( FALSE, FALSE, TRUE )) $data['XPROP'][]   = $prop;
    if( !empty( $data['XPROP'] ))
      $data['cnt_xprop'] = count( $data['XPROP'] );
    if( !empty( $metadata )) {
      $data['cnt_metadata'] = count( $metadata );
      $data['metadata']     = $metadata;
    }
    $dbiCal_calendar_DAO = dbiCal_calendar_DAO::singleton( $this->_db, $this->_log );
    $calendar_id = $dbiCal_calendar_DAO->insert( $data );
    if( PEAR::isError( $calendar_id )) {
      $this->transactionRollback();
      return FALSE;
    }
    foreach( $calendar->components as $compix => & $comp ) {
      $data = array( 'ono' => $compix );
      while( $prop = $comp->getProperty( 'attach',           FALSE, TRUE )) $data['ATTACH'][]         = $prop;
      while( $prop = $comp->getProperty( 'attendee',         FALSE, TRUE )) $data['ATTENDEE'][]       = $prop;
      while( $prop = $comp->getProperty( 'categories',       FALSE, TRUE )) $data['CATEGORIES'][]     = $prop;
      if   ( $prop = $comp->getProperty( 'class',            FALSE, TRUE )) $data['CLASS']            = $prop;
      while( $prop = $comp->getProperty( 'comment',          FALSE, TRUE )) $data['COMMENT'][]        = $prop;
      if   ( $prop = $comp->getProperty( 'completed',        FALSE, TRUE )) $data['COMPLETED']        = $prop;
      while( $prop = $comp->getProperty( 'contact',          FALSE, TRUE )) $data['CONTACT'][]        = $prop;
      if   ( $prop = $comp->getProperty( 'created',          FALSE, TRUE )) $data['CREATED']          = $prop;
      while( $prop = $comp->getProperty( 'description',      FALSE, TRUE )) $data['DESCRIPTION'][]    = $prop;
      if   ( $prop = $comp->getProperty( 'dtend',            FALSE, TRUE )) $data['DTEND']            = $prop;
      if   ( $prop = $comp->getProperty( 'dtstamp',          FALSE, TRUE )) $data['DTSTAMP']          = $prop;
      if   ( $prop = $comp->getProperty( 'dtstart',          FALSE, TRUE )) $data['DTSTART']          = $prop;
      if   ( $prop = $comp->getProperty( 'due',              FALSE, TRUE )) $data['DUE']              = $prop;
      if   ( $prop = $comp->getProperty( 'duration',         FALSE, TRUE )) $data['DURATION']         = $prop;
      while( $prop = $comp->getProperty( 'exdate',           FALSE, TRUE )) $data['EXDATE'][]         = $prop;
      while( $prop = $comp->getProperty( 'exrule',           FALSE, TRUE )) $data['EXRULE'][]         = $prop;
      while( $prop = $comp->getProperty( 'freebusy',         FALSE, TRUE )) $data['FREEBUSY'][]       = $prop;
      if   ( $prop = $comp->getProperty( 'geo',              FALSE, TRUE )) $data['GEO']              = $prop;
      if   ( $prop = $comp->getProperty( 'last-modified',    FALSE, TRUE )) $data['LAST-MODIFIED']    = $prop;
      if   ( $prop = $comp->getProperty( 'location',         FALSE, TRUE )) $data['LOCATION']         = $prop;
      if   ( $prop = $comp->getProperty( 'organizer',        FALSE, TRUE )) $data['ORGANIZER']        = $prop;
      if   ( $prop = $comp->getProperty( 'percent-complete', FALSE, TRUE )) $data['PERCENT-COMPLETE'] = $prop;
      if   ( $prop = $comp->getProperty( 'priority',         FALSE, TRUE )) $data['PRIORITY']         = $prop;
      while( $prop = $comp->getProperty( 'rdate',            FALSE, TRUE )) $data['RDATE'][]          = $prop;
      if   ( $prop = $comp->getProperty( 'recurrence-id',    FALSE, TRUE )) $data['RECURRENCE-ID']    = $prop;
      while( $prop = $comp->getProperty( 'related-to',       FALSE, TRUE )) $data['RELATED-TO'][]     = $prop;
      while( $prop = $comp->getProperty( 'request-status',   FALSE, TRUE )) $data['REQUEST-STATUS'][] = $prop;
      while( $prop = $comp->getProperty( 'resources',        FALSE, TRUE )) $data['RESOURCES'][]      = $prop;
      while( $prop = $comp->getProperty( 'rrule',            FALSE, TRUE )) $data['RRULE'][]          = $prop;
      if   ( $prop = $comp->getProperty( 'sequence',         FALSE, TRUE )) $data['SEQUENCE']         = $prop;
      if   ( $prop = $comp->getProperty( 'status',           FALSE, TRUE )) $data['STATUS']           = $prop;
      if   ( $prop = $comp->getProperty( 'summary',          FALSE, TRUE )) $data['SUMMARY']          = $prop;
      if   ( $prop = $comp->getProperty( 'transp',           FALSE, TRUE )) $data['TRANSP']           = $prop;
      if   ( $prop = $comp->getProperty( 'tzid',             FALSE, TRUE )) $data['TZID']             = $prop;
      if   ( $prop = $comp->getProperty( 'tzurl',            FALSE, TRUE )) $data['TZURL']            = $prop;
      if   ( $prop = $comp->getProperty( 'uid',              FALSE, TRUE )) $data['UID']              = $prop;
      if   ( $prop = $comp->getProperty( 'url',              FALSE, TRUE )) $data['URL']              = $prop;
      while( $prop = $comp->getProperty( FALSE,              FALSE, TRUE )) $data['XPROP'][]          = $prop;
      while( $cmp2 = $comp->getComponent()) {
        $subc = array();
        if   ( $prop = $cmp2->getProperty( 'action',         FALSE, TRUE )) $subc['ACTION']           = $prop;
        while( $prop = $cmp2->getProperty( 'attach',         FALSE, TRUE )) $subc['ATTACH'][]         = $prop;
        while( $prop = $cmp2->getProperty( 'attendee',       FALSE, TRUE )) $subc['ATTENDEE'][]       = $prop;
        while( $prop = $cmp2->getProperty( 'comment',        FALSE, TRUE )) $subc['COMMENT'][]        = $prop;
        if   ( $prop = $cmp2->getProperty( 'description',    FALSE, TRUE )) $subc['DESCRIPTION']      = $prop;
        if   ( $prop = $cmp2->getProperty( 'dtstart',        FALSE, TRUE )) $subc['DTSTART']          = $prop;
        if   ( $prop = $cmp2->getProperty( 'duration',       FALSE, TRUE )) $subc['DURATION']         = $prop;
        if   ( $prop = $cmp2->getProperty( 'repeat',         FALSE, TRUE )) $subc['REPEAT']           = $prop;
        while( $prop = $cmp2->getProperty( 'rdate',          FALSE, TRUE )) $subc['RDATE'][]          = $prop;
        while( $prop = $cmp2->getProperty( 'rrule',          FALSE, TRUE )) $subc['RRULE'][]          = $prop;
        if   ( $prop = $cmp2->getProperty( 'summary',        FALSE, TRUE )) $subc['SUMMARY']          = $prop;
        if   ( $prop = $cmp2->getProperty( 'trigger',        FALSE, TRUE )) $subc['TRIGGER']          = $prop;
        while( $prop = $cmp2->getProperty( 'tzname',         FALSE, TRUE )) $subc['TZNAME'][]         = $prop;
        if   ( $prop = $cmp2->getProperty( 'tzoffsetfrom',   FALSE, TRUE )) $subc['TZOFFSETFROM']     = $prop;
        if   ( $prop = $cmp2->getProperty( 'tzoffsetto',     FALSE, TRUE )) $subc['TZOFFSETTO']       = $prop;
        while( $prop = $cmp2->getProperty( FALSE,            FALSE, TRUE )) $subc['XPROP'][]          = $prop;
        $objName = ( 'valarm' == $cmp2->objName ) ? 'ALARM' : strtoupper( $cmp2->objName );
        $data[$objName][] = $subc;
      } // end while( $cmp2 = $comp->getComponent())
      switch( $comp->objName ) {
        case 'vtimezone':
          $dbiCal_timezone_DAO = dbiCal_timezone_DAO::singleton( $this->_db, $this->_log );
          $res = $dbiCal_timezone_DAO->insert( $calendar_id, $data, ( 1 + $compix ));
          if( PEAR::isError( $res )) {
            $this->transactionRollback();
            return FALSE;
          }
          break;
        case 'vevent':
          if( isset( $data['DESCRIPTION'][0] ) &&  !empty( $data['DESCRIPTION'][0] ))
            $data['DESCRIPTION'] = $data['DESCRIPTION'][0];
          $dbiCal_event_DAO = dbiCal_event_DAO::singleton( $this->_db, $this->_log );
          $res = $dbiCal_event_DAO->insert( $calendar_id, $data, ( 1 + $compix ));
          if( PEAR::isError( $res )) {
            $this->transactionRollback();
            return FALSE;
          }
          break;
        case 'vtodo':
          if( isset( $data['DESCRIPTION'] ) &&  !empty( $data['DESCRIPTION'] ))
            $data['DESCRIPTION'] = $data['DESCRIPTION'][0];
          $dbiCal_todo_DAO = dbiCal_todo_DAO::singleton( $this->_db, $this->_log );
          $res = $dbiCal_todo_DAO->insert( $calendar_id, $data, ( 1 + $compix ));
          if( PEAR::isError( $res )) {
            $this->transactionRollback();
            return FALSE;
          }
          break;
        case 'vjournal':
          $dbiCal_journal_DAO = dbiCal_journal_DAO::singleton( $this->_db, $this->_log );
          $res = $dbiCal_journal_DAO->insert( $calendar_id, $data, ( 1 + $compix ));
          if( PEAR::isError( $res )) {
            $this->transactionRollback();
            return FALSE;
          }
          break;
        case 'vfreebusy':
          if( isset( $data['CONTACT'] ) &&  !empty( $data['CONTACT'] ))
            $data['CONTACT'] = $data['CONTACT'][0];
          $dbiCal_freebusy_DAO = dbiCal_freebusy_DAO::singleton( $this->_db, $this->_log );
          $res = $dbiCal_freebusy_DAO->insert( $calendar_id, $data, ( 1 + $compix ));
          if( PEAR::isError( $res )) {
            $this->transactionRollback();
            return FALSE;
          }
          break;
      } // end switch( $comp->objName )
    } // end foreach( $calendar->components as $comp )
    if( FALSE === $this->transactionCommit())
      return FALSE;
    return $calendar_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $calendar_id
   * @param    array  $config
   * @return   mixed  $res (FALSE or calendar object)
   * @since    3.0.2 - 2011-05-01
   */
  function select( $calendar_id, $config = array()) {
    if( $this->_log )
      $this->_log->log( __METHOD__." start calendar_id=$calendar_id config=".var_export( $config, TRUE ), PEAR_LOG_INFO );
    if( FALSE === $this->transactionBegin())
      return FALSE;
    $dbiCal_calendar_DAO = dbiCal_calendar_DAO::singleton( $this->_db, $this->_log );
    $calendarValues = $dbiCal_calendar_DAO->select( $calendar_id );
    if( PEAR::isError( $calendarValues )) {
      $this->transactionRollback();
      return FALSE;
    }
    if( FALSE === $calendarValues ) // not found
      return FALSE;
    if( $this->_log )
      $this->_log->log( var_export( $calendarValues, TRUE ), PEAR_LOG_DEBUG );

    $calendar = new vcalendar( $config );
    if( isset( $calendarValues['filename'] ) && !empty( $calendarValues['filename'] ))
      $calendar->setConfig( 'filename', $calendarValues['filename'] );
    if( isset( $calendarValues['VERSION'] )  && !empty( $calendarValues['VERSION'] ))
      $calendar->setProperty( 'version',  $calendarValues['VERSION'] );
    if( isset( $calendarValues['unique_id'] ) && !empty( $calendarValues['unique_id'] ))
      $calendar->setConfig( 'unique_id', $calendarValues['unique_id'] );
    if( isset( $calendarValues['CALSCALE'] ) && !empty( $calendarValues['CALSCALE'] ))
      $calendar->setProperty( 'calscale', $calendarValues['CALSCALE'] );
    if( isset( $calendarValues['METHOD'] )   && !empty( $calendarValues['METHOD'] ))
      $calendar->setProperty( 'method',   $calendarValues['METHOD'] );
    foreach( $calendarValues as $prop => $propVal ) {
      if( 'X-' == substr( $prop, 0, 2 ))
        $calendar->setProperty( $prop,  $propVal['value'], $propVal['params'] );
    }
    $calendar_data = array();

    if( isset( $calendarValues['calendar_cnt_timezone'] )) {
      $dbiCal_timezone_DAO = dbiCal_timezone_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_timezone_DAO->select( $calendar_id );
      if( PEAR::isError( $res )) {
        $this->transactionRollback();
        return FALSE;
      }
      if (!empty( $res ))
        $calendar_data['vtimezone'] = $res;
    }

    if( isset( $calendarValues['calendar_cnt_event'] )) {
      $dbiCal_event_DAO = dbiCal_event_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_event_DAO->select( $calendar_id );
      if( PEAR::isError( $res )) {
        $this->transactionRollback();
        return FALSE;
      }
      if (!empty( $res ))
        $calendar_data['vevent'] = $res;
    }

    if( isset( $calendarValues['calendar_cnt_freebusy'] )) {
      $dbiCal_freebusy_DAO = dbiCal_freebusy_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_freebusy_DAO->select( $calendar_id );
      if( PEAR::isError( $res )) {
        $this->transactionRollback();
        return FALSE;
      }
      if (!empty( $res ))
        $calendar_data['vfreebusy'] = $res;
    }

    if( isset( $calendarValues['calendar_cnt_journal'] )) {
      $dbiCal_journal_DAO = dbiCal_journal_DAO::singleton( $this->_db, $this->_log );
      $res  = $dbiCal_journal_DAO->select( $calendar_id );
      if( PEAR::isError( $res )) {
        $this->transactionRollback();
        return FALSE;
      }
      if (!empty( $res ))
        $calendar_data['vjournal'] = $res;
    }

    if( isset( $calendarValues['calendar_cnt_todo'] )) {
      $dbiCal_todo_DAO = dbiCal_todo_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_todo_DAO->select( $calendar_id );
      if( PEAR::isError( $res )) {
        $this->transactionRollback();
        return FALSE;
      }
      if (!empty( $res ))
        $calendar_data['vtodo'] = $res;
    }
    unset( $calendarValues );

    $cntcomps = $cntsubcomps = 0;
    foreach( $calendar_data as $compType => $compValues ){
      if( empty( $compValues ))
        continue;
      foreach( $compValues as $compdbix => $compdata ) {
        if( empty( $compdata ))
          continue;
        $compix = ( isset( $compdata['ono'] )) ? $compdata['ono'] : null;
        $comp = new $compType();
        foreach( $compdata as $propname => $propdata ) {
          $propname2 = ( 'X-' == substr( $propname, 0, 2 )) ? 'X-PROP' : $propname;
          if( $this->_log )
            $this->_log->log( "$propname=".PHP_EOL.var_export( $propdata, TRUE ), PEAR_LOG_DEBUG );
          switch( $propname2 ) {
            case 'ono':
              break;
            case 'UID':                           // single ocurrence
            case 'DTSTAMP':
            case 'DTSTART':
            case 'DTEND':
            case 'DUE':
            case 'DURATION':
            case 'CREATED':
            case 'LAST-MODIFIED':
            case 'SUMMARY':
            case 'LOCATION':
            case 'CLASS':
            case 'COMPLETED':
            case 'ORGANIZER':
            case 'PERCENT-COMPLETE':
            case 'PRIORITY':
            case 'RECURRENCE-ID':
            case 'SEQUENCE':
            case 'STATUS':
            case 'TRANSP':
            case 'TZID':
            case 'TZURL':
            case 'URL':
            case 'X-PROP':
              $comp->setProperty( $propname, $propdata['value'], $propdata['params'] );
              break;
            case 'GEO':
              $comp->setProperty( $propname, (float) $propdata['value']['latitude'], (float) $propdata['value']['longitude'], $propdata['params'] );
              break;
            case 'DESCRIPTION':                   // journal allowes multiple ocurrence
            case 'CONTACT':                       // freebusy allowes not multiple ocurrence
              if( isset( $propdata['value'] ))    // explode single occurence to multiple
                $propdata = array( $propdata );
            case 'ATTACH':                        // multiple ocurrence
            case 'ATTENDEE':
            case 'CATEGORIES':
            case 'COMMENT':
            case 'EXDATE':
            case 'EXRULE':
            case 'RDATE':
            case 'RELATED-TO':
            case 'RESOURCES':
            case 'RRULE':
// $this->_log->log( PHP_EOL."$propname=".var_export( $propdata, TRUE ), PEAR_LOG_DEBUG );
              foreach( $propdata as $propdat2 ) {
                if( in_array( $propname, array( 'EXRULE', 'RRULE' )) && isset( $propdat2['value']['rexrule_type'] ))
                  unset( $propdat2['value']['rexrule_type'] );
                $comp->setProperty( $propname, $propdat2['value'], $propdat2['params'] );
              }
              break;
            case 'FREEBUSY':
// if( $this->_log ) $this->_log->log( "$compType, db ix=$compdbix".PHP_EOL.$propname.PHP_EOL.var_export( $propdata, TRUE ), PEAR_LOG_INFO );   // ############################################ test !!!
              foreach( $propdata as $propdat2 ) {
                $fbperiods = array();
                foreach( $propdat2['value'] as $key => $fbvalue ) {
                  if( 'fbtype' !== $key )
                    $fbperiods[] = $fbvalue;
                }
                $comp->setProperty( 'FREEBUSY', $propdat2['value']['fbtype'], $fbperiods, $propdat2['params'] );
              }
              break;
            case 'REQUEST-STATUS':
              foreach( $propdata as $propdat2 )
                $comp->setProperty( $propname, $propdat2['value']['statcode'], $propdat2['value']['text'], $propdat2['value']['extdata'], $propdat2['params'] );
              break;
            case 'ALARM':                         // sub-components
            case 'STANDARD':
            case 'DAYLIGHT':
// $this->_log->log( PHP_EOL."$propname=".var_export( $propdata, TRUE ), PEAR_LOG_DEBUG );
              foreach( $propdata as $sub ) {
                if( 'ALARM' == $propname )
                  $subc = new valarm();
                elseif ( 'STANDARD' == $propname )
                  $subc = new vtimezone( 'STANDARD' );
                elseif( 'DAYLIGHT' == $propname )
                  $subc = new vtimezone( 'DAYLIGHT' );
                foreach( $sub as $subn => $subd ) {  // for each subcomponent, in order
                  $compsubix = ( isset( $sub['ono'] )) ? $sub['ono'] : null;
                  $subn2 = ( 'X-' == substr( $subn, 0, 2 )) ? 'X-PROP' : $subn;
// $this->_log->log( PHP_EOL."$propname($compsubix) $subn=".var_export( $subd, TRUE ), PEAR_LOG_DEBUG );
                  switch( $subn2 ) {
                    case 'ono':
                      break;
                    case 'ACTION':                // single ocurrence
                    case 'DESCRIPTION':
                    case 'DURATION':
                    case 'REPEAT':
                    case 'SUMMARY':
                    case 'TRIGGER':
                    case 'DTSTART':
                    case 'TZOFFSETFROM':
                    case 'TZOFFSETTO':
                    case 'X-PROP':
                      $subc->setProperty( $subn, $subd['value'], $subd['params'] );
                      break;
                    case 'ATTACH':                // multiple ocurrence
                    case 'ATTENDEE':
                    case 'COMMENT':
                    case 'RDATE':
                    case 'RRULE':
                    case 'TZNAME':
                      foreach( $subd as $d2 ) {
                        if( in_array( $subn, array( 'EXRULE', 'RRULE' )) && isset( $d2['value']['rexrule_type'] ))
                          unset( $d2['value']['rexrule_type'] );
                        $rupd = $subc->setProperty( $subn, $d2['value'], $d2['params'] );
// if( !$rupd )  $this->_log->log( "$propname($compsubix) setProperty = FALSE", PEAR_LOG_DEBUG );
                      }
                      break;
                  } // switch( $subn2 )
                } // end foreach( $sub as $subn => $subd )
                $cntsubcomps += 1;
                $comp->setComponent( $subc, $compsubix );
// $this->_log->log( PHP_EOL."compsubix=$compsubix, ant subcomps=".count( $comp->components), PEAR_LOG_DEBUG );   // test ### =====================================
              } // end foreach( $propdata as $sub )
              break;
          } // end switch( $propname )
        } // end foreach( $compdata as $propname => $propdata )
        $cntcomps += 1;
        $calendar->setComponent( $comp, $compix );
// $this->_log->log( PHP_EOL."compix=$compix, ant comps=".count( $calendar->components), PEAR_LOG_DEBUG );   // test ### =====================================
      } // end foreach( $compValues as $compdata )
    } // end foreach( $calendar_data as $compType => $compValues )
    if( $this->_log )
      $this->_log->log( "total $cntcomps components with $cntsubcomps subcomponents ", PEAR_LOG_INFO );
    if( FALSE === $this->transactionCommit())
      return FALSE;
    return $calendar;
  }
  /**
   * transactionBegin
   *
   * @access   public
   * @return   bool
   * @since    1.0 - 2011-01-18
   */
  function transactionBegin() {
    if( !$this->_db->supports( 'transactions' ))
      return TRUE;
    if( $this->_log )
      $this->_log->log( 'transaction Begin', PEAR_LOG_INFO );
    $res = $this->_db->beginTransaction();
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return FALSE;
    }
    return TRUE;
  }
  /**
   * transactionCommit
   *
   * @access   public
   * @return   bool
   * @since    1.0 - 2011-01-18
   */
  function transactionCommit() {
    if( !$this->_db->inTransaction())
      return TRUE;
    if( $this->_log )
      $this->_log->log( 'transaction Commit', PEAR_LOG_INFO );
    if( $this->_db->supports( 'transactions' )) {
      $res = $this->_db->commit();
      if( PEAR::isError( $res )) {
        if( $this->_log )
          $this->_log->log( PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
        return FALSE;
      }
      return TRUE;
    }
    return TRUE;
  }
  /**
   * transactionRollback
   *
   * @access   public
   * @return   bool
   * @since    1.0 - 2011-01-18
   */
  function transactionRollback() {
    if( !$this->_db->inTransaction())
      return TRUE;
    if( $this->_log )
      $this->_log->log( 'transaction Rollback', PEAR_LOG_INFO );
    if( $this->_db->supports( 'transactions' )) {
      $res = $this->_db->rollback();
      if( PEAR::isError( $res )) {
        if( $this->_log )
          $this->_log->log( PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
        return FALSE;
      }
      return TRUE;
    }
    return TRUE;
  }
}
/**
 * This class implements the calendar table DAO
**/
class dbiCal_calendar_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-30
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_calendar_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * getCalendars
   *
   * @access   public
   * @param    array  $selectOptions
   * @param    bool   $fullSearch
   * @return   mixed  $res (PEAR::Error or array calendars)
   * @since    1.0 - 2011-02-08
   */
  function getCalendars( $selectOptions=array(), $fullSearch=TRUE ) {
    if( $this->_log )
      $this->_log->log( __METHOD__.' start selectOptions'.var_export($selectOptions, TRUE )." fullSearch=$fullSearch", PEAR_LOG_INFO );
    $calendars = $cx = $cm = $cc = array();
    $xpropSearch = $metadataSearch = FALSE;
            // selectOption 'calendar_id' overrides all
    if( isset( $selectOptions['calendar_id'] ))
      $calendars = array( $selectOptions['calendar_id'] => array( 'calendar_id' => $selectOptions['calendar_id'] ));
    else {
      foreach( $selectOptions as $xpropName => $xpropValue ) {
        if( 'X-' == strtoupper( substr( $xpropName, 0, 2 ))) {
          $xpropSearch = TRUE;
          continue;
        }
        if( in_array( strtolower( $xpropName ), array( 'calendar_id', 'date', 'filename' )))
          continue;
        $metadataSearch = TRUE;
      }
    }
            // check and fetch calendar_id(-s) from xprop table
    if( $xpropSearch ) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      $cx = $dbiCal_xprop_DAO->getOwners( 'calendar', $selectOptions );
      if( PEAR::isError( $cx ))
        return $cx;
    }
            // check and fetch calendar_id(-s) from metadata table
    if( $metadataSearch ) {
      $dbiCal_metadata_DAO = dbiCal_metadata_DAO::singleton( $this->_db, $this->_log );
      $cm = $dbiCal_metadata_DAO->select( $selectOptions );
      if( PEAR::isError( $cm ))
        return $cm;
    }
            // check and fetch calendar_id(-s) from calendar table
    if( isset( $selectOptions['date'] ) || isset( $selectOptions['filename'] ) || empty( $selectOptions )) {
      $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
      $sql = 'SELECT calendar_id FROM calendar WHERE 1=1';
      if( isset( $selectOptions['date'] ))
        $sql .= ' AND DATE(calendar_create_date) = '.$this->_db->quote( $selectOptions['date'], 'date' );
      if( isset( $selectOptions['filename'] ))
        $sql .= ' AND calendar_filename = '.$this->_db->quote( $selectOptions['filename'], 'text' );
      $sql .= ' ORDER BY 1 DESC';
      $res  = & $this->_db->query( $sql, array( 'integer' ));
      if( PEAR::isError( $res )) {
        if( $this->_log )
          $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
        return $res;
      }
      while( $tablerow = $res->fetchRow())
        $cc[$tablerow['calendar_id']] = array( 'calendar_id' => $tablerow['calendar_id'] );
      $res->free();
      if( $this->_log )
        $this->_log->log( $sql.' result='.implode(',', array_keys( $cc )), PEAR_LOG_DEBUG );
    }
    if( !empty( $cx )) {
      $calendars = $cx;
      if( $this->_log )
        $this->_log->log( 'xprop ids='.implode(',', array_keys( $cx )), PEAR_LOG_DEBUG );
    }
    if( !empty( $cm )) {
      if( $this->_log )
        $this->_log->log( 'meta  ids='.implode(',', array_keys( $cm )), PEAR_LOG_DEBUG );
      if( !empty( $calendars ))
        $calendars = array_intersect_key( $calendars, $cm ); // fix the intersection of arrays
      else
        $calendars = $cm;
    }
    if( !empty( $cc )) {
      if( $this->_log )
        $this->_log->log( 'clndr ids='.implode(',', array_keys( $cc )), PEAR_LOG_DEBUG );
      if( !empty( $calendars ))
        $calendars = array_intersect_key( $calendars, $cc ); // fix the intersection of arrays
      else
        $calendars = $cc;
    }
    if( $this->_log )
      $this->_log->log( 'comb  ids='.implode(',', array_keys( $calendars )), PEAR_LOG_INFO );
    if( !$fullSearch ) {
      krsort( $calendars );
      return $calendars;
    }
    foreach( $calendars as $calendar_id => & $calendar ) {
      $res = $this->select( $calendar_id );
      if( PEAR::isError( $res ))
        return $res;
      foreach( $res as $cname => $cvalue )
        $calendar[$cname] = $cvalue;
    }
    return $calendars;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2011-01-21
   */
  function delete( $calendar_id ) {
    if( $this->_log )
      $this->_log->log( __METHOD__." start calendar_id=$calendar_id", PEAR_LOG_INFO );
    if( empty( $calendar_id ))
      return FALSE;
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere = 'FROM calendar WHERE calendar_id = '.$this->_db->quote( $calendar_id, 'integer' );
    $sql  = "SELECT calendar_cnt_metadata, calendar_cnt_xprop, calendar_cnt_timezone, calendar_cnt_event, calendar_cnt_freebusy, calendar_cnt_journal, calendar_cnt_todo $fromWhere LIMIT 1";
    $res  = & $this->_db->query( $sql, array_fill( 0, 7, 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $calendar_cnts = $res->fetchRow();
    $res->free();
    if( $this->_log )
      $this->_log->log( 'sql='.$sql.PHP_EOL.'result='.var_export( $calendar_cnts, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $calendar_cnts))
      return null;

    if( !empty( $calendar_cnts['calendar_cnt_metadata'] )) {
      $dbiCal_metadata_DAO = dbiCal_metadata_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_metadata_DAO->delete( $calendar_id );
      if( PEAR::isError( $res ))
        return $res;
    }
    if( !empty( $calendar_cnts['calendar_cnt_timezone'] )) {
      $dbiCal_timezone_DAO = dbiCal_timezone_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_timezone_DAO->delete( $calendar_id );
      if( PEAR::isError( $res ))
        return $res;
    }
    if( !empty( $calendar_cnts['calendar_cnt_event'] )) {
      $dbiCal_event_DAO = dbiCal_event_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_event_DAO->delete( $calendar_id );
      if( PEAR::isError( $res ))
        return $res;
    }
    if( !empty( $calendar_cnts['calendar_cnt_todo'] )) {
      $dbiCal_todo_DAO = dbiCal_todo_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_todo_DAO->delete( $calendar_id );
      if( PEAR::isError( $res ))
        return $res;
    }
    if( !empty( $calendar_cnts['calendar_cnt_journal'] )) {
      $dbiCal_journal_DAO = dbiCal_journal_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_journal_DAO->delete( $calendar_id );
      if( PEAR::isError( $res ))
        return $res;
    }
    if( !empty( $calendar_cnts['calendar_cnt_freebusy'] )) {
      $dbiCal_freebusy_DAO = dbiCal_freebusy_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_freebusy_DAO->delete( $calendar_id );
      if( PEAR::isError( $res ))
        return $res;
    }
    if( !empty( $calendar_cnts['calendar_cnt_xprop'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_xprop_DAO->delete( $calendar_id, 'calendar' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    array $calendarValues
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-01-20
   */
  public function insert( & $calendarValues= array() ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql       = 'INSERT INTO calendar (calendar_create_date';
    $values    = ') VALUES (NOW()';
    if( isset( $calendarValues['filename'] )) {
      $sql    .= ', calendar_filename';
      $values .= ', '.$this->_db->quote( $calendarValues['filename'], 'text' );
    }
    if( isset( $calendarValues['VERSION'] )) {
      $sql    .= ', calendar_version';
      $values .= ', '.$this->_db->quote( $calendarValues['VERSION'], 'text' );
    }
    if( isset( $calendarValues['unique_id'] )) {
      $sql    .= ', calendar_unique_id';
      $values .= ', '.$this->_db->quote( $calendarValues['unique_id'], 'text' );
    }
    if( isset( $calendarValues['CALSCALE'] )) {
      $sql    .= ', calendar_calscale';
      $values .= ', '.$this->_db->quote( $calendarValues['CALSCALE'], 'text' );
    }
    if( isset( $calendarValues['METHOD'] )) {
      $sql    .= ', calendar_method';
      $values .= ', '.$this->_db->quote( $calendarValues['METHOD'], 'text' );
    }
    if( isset( $calendarValues['filesize'] )) {
      $sql    .= ', calendar_filesize';
      $values .= ', '.$this->_db->quote( $calendarValues['filesize'], 'integer' );
    }
    if( isset( $calendarValues['cnt_metadata'] )) {
      $sql    .= ', calendar_cnt_metadata';
      $values .= ', '.$this->_db->quote( $calendarValues['cnt_metadata'], 'integer' );
    }
    if( isset( $calendarValues['cnt_xprop'] )) {
      $sql    .= ', calendar_cnt_xprop';
      $values .= ', '.$this->_db->quote( $calendarValues['cnt_xprop'], 'integer' );
    }
    if( isset( $calendarValues['vtimezone'] )) {
      $sql    .= ', calendar_cnt_timezone';
      $values .= ', '.$this->_db->quote( $calendarValues['vtimezone'], 'integer' );
    }
    if( isset( $calendarValues['vevent'] )) {
      $sql    .= ', calendar_cnt_event';
      $values .= ', '.$this->_db->quote( $calendarValues['vevent'], 'integer' );
    }
    if( isset( $calendarValues['vfreebusy'] )) {
      $sql    .= ', calendar_cnt_freebusy';
      $values .= ', '.$this->_db->quote( $calendarValues['vfreebusy'], 'integer' );
    }
    if( isset( $calendarValues['vjournal'] )) {
      $sql    .= ', calendar_cnt_journal';
      $values .= ', '.$this->_db->quote( $calendarValues['vjournal'], 'integer' );
    }
    if( isset( $calendarValues['vtodo'] )) {
      $sql    .= ', calendar_cnt_todo';
      $values .= ', '.$this->_db->quote( $calendarValues['vtodo'], 'integer' );
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $calendar_id = $this->_db->lastInsertID( 'calendar', 'calendar_id' );
    if( PEAR::isError( $calendar_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$calendar_id->getUserInfo().PHP_EOL.$calendar_id->getMessage(), PEAR_LOG_ALERT );
      return $calendar_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'calendar_id='.$calendar_id, PEAR_LOG_INFO );
// if( $this->_log ) $this->_log->log( var_export( $calendarValues['XPROP'], TRUE ), PEAR_LOG_DEBUG );   // test ### ================================================0
    if( isset( $calendarValues['XPROP'] ) && !empty( $calendarValues['XPROP'] ) && is_array( $calendarValues['XPROP'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      foreach( $calendarValues['XPROP'] as $xprop ) {
        if( isset( $xprop[1]['value'] ) && !empty( $xprop[1]['value'] )) {
          $res = $dbiCal_xprop_DAO->insert( $calendar_id, 'calendar', $xprop[0], $xprop[1] );
          if( PEAR::isError( $res ))
            return $res;
        }
      }
    }
    if( isset( $calendarValues['metadata'] ) && !empty( $calendarValues['metadata'] ) && is_array( $calendarValues['metadata'] )) {
      $dbiCal_metadata_DAO = dbiCal_metadata_DAO::singleton( $this->_db, $this->_log );
      foreach( $calendarValues['metadata'] as $metadataKey => $metadataValue ) {
        $res = $dbiCal_metadata_DAO->insert( $calendar_id, $metadataKey, $metadataValue );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $calendar_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-02-15
   */
  function select( $calendar_id ) {
    if( $this->_log )
      $this->_log->log( __METHOD__." start calendar_id=$calendar_id", PEAR_LOG_INFO );
    if( empty( $calendar_id ))
      return FALSE;
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql = 'SELECT * FROM calendar WHERE calendar_id = '.$this->_db->quote( $calendar_id, 'integer' ).' LIMIT 1';
    $types = array( 'integer', 'timestamp', 'text', 'text', 'text', 'text', 'text' );
    $types = array_merge( $types, array_fill( count( $types ), 8, 'integer' ));
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $tablerow = $res->fetchRow();
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql, PEAR_LOG_INFO );
    $result = array();
    if( !isset( $tablerow['calendar_id'] )) // not found
      return FALSE;
    else
      $result['calendar_id'] = $tablerow['calendar_id'];
    if( $this->_log )
      $this->_log->log( var_export( $tablerow, TRUE ), PEAR_LOG_DEBUG );
    if( isset( $tablerow['calendar_create_date'] ))
      $result['create_date'] = $tablerow['calendar_create_date'];
    if( isset( $tablerow['calendar_filename'] ))
      $result['filename']    = $tablerow['calendar_filename'];
    if( isset( $tablerow['calendar_filesize'] ))
      $result['filesize']    = $tablerow['calendar_filesize'];
    if( isset( $tablerow['calendar_version'] ))
      $result['VERSION']     = $tablerow['calendar_version'];
    if( isset( $tablerow['calendar_unique_id'] ))
      $result['unique_id']   = $tablerow['calendar_unique_id'];
    if( isset( $tablerow['calendar_calscale'] ))
      $result['CALSCALE']    = $tablerow['calendar_calscale'];
    if( isset( $tablerow['calendar_method'] ))
      $result['METHOD']      = $tablerow['calendar_method'];
    if( isset( $tablerow['calendar_cnt_metadata'] ) && !empty( $tablerow['calendar_cnt_metadata'] ))
      $result['calendar_cnt_metadata'] = $tablerow['calendar_cnt_metadata'];
    if( isset( $tablerow['calendar_cnt_timezone'] ) && !empty( $tablerow['calendar_cnt_timezone'] ))
      $result['calendar_cnt_timezone'] = $tablerow['calendar_cnt_timezone'];
    if( isset( $tablerow['calendar_cnt_event'] )    && !empty( $tablerow['calendar_cnt_event'] ))
      $result['calendar_cnt_event']    = $tablerow['calendar_cnt_event'];
    if( isset( $tablerow['calendar_cnt_freebusy'] ) && !empty( $tablerow['calendar_cnt_freebusy'] ))
      $result['calendar_cnt_freebusy'] = $tablerow['calendar_cnt_freebusy'];
    if( isset( $tablerow['calendar_cnt_journal'] )  && !empty( $tablerow['calendar_cnt_journal'] ))
      $result['calendar_cnt_journal']  = $tablerow['calendar_cnt_journal'];
    if( isset( $tablerow['calendar_cnt_todo'] )     && !empty( $tablerow['calendar_cnt_todo'] ))
      $result['calendar_cnt_todo']     = $tablerow['calendar_cnt_todo'];
    if( !empty( $tablerow['calendar_cnt_metadata'] )) {
      $dbiCal_metadata_DAO = dbiCal_metadata_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_metadata_DAO->select( array( 'calendar_id' => $calendar_id ));
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $metadata )
          $result['metadata'] = $metadata;
      }
    }
    if( !empty( $tablerow['calendar_cnt_xprop'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      $res = $dbiCal_xprop_DAO->select( $calendar_id, 'calendar' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $propName => $propValue )
          $result[$propName] = $propValue;
      }
    }
    return $result;
  }
}
/**
 * This class implements the metadata table DAO
**/
class dbiCal_metadata_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_metadata_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2011-01-21
   */
  function delete( $owner_id ) {
    if( $this->_log )
      $this->_log->log( __METHOD__." start owner_id=$owner_id", PEAR_LOG_INFO );
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'DELETE FROM metadata WHERE metadata_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
        return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res deleted rows", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $metadataKey
   * @param    array  $metadataValue
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-01-22
   */
  public function insert( $owner_id, $metadataKey, $metadataValue ) {
    if( $this->_log )
      $this->_log->log( __METHOD__." start owner_id=$owner_id, metadataKey=$metadataKey, metadataValue=$metadataValue", PEAR_LOG_INFO );
    $sql       = 'INSERT INTO metadata (metadata_owner_id';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' );
    if( !empty( $metadataKey )) {
      $sql    .= ', metadata_key';
      $values .= ', '.$this->_db->quote( $metadataKey, 'text' );
    }
    if( !empty( $metadataValue )) {
      $sql    .= ', metadata_text';
      $values .= ', '.$this->_db->quote( $metadataValue, 'text' );
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $metadata_id = $this->_db->lastInsertID( 'metadata', 'metadata_id' );
    if( PEAR::isError( $metadata_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$metadata_id->getUserInfo().PHP_EOL.$metadata_id->getMessage(), PEAR_LOG_ALERT );
      return $metadata_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'metadata_id='.$metadata_id, PEAR_LOG_INFO );
    return $metadata_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    array  $selectOptions
   * @return   mixed  $res (PEAR::Error eller array)
   * @since    1.0 - 2011-02-15
   */
  function select( $selectOptions=array()) {
    if( $this->_log )
      $this->_log->log( __METHOD__.' start selectOptions='.var_export($selectOptions, TRUE ), PEAR_LOG_INFO );
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql      = 'SELECT metadata_owner_id AS calendar_id, metadata_key, metadata_text FROM metadata WHERE';
    if( isset( $selectOptions['calendar_id'] ))
      $sql   .= ' metadata_owner_id = '.$this->_db->quote( $selectOptions['calendar_id'], 'integer' );
    else {
      $orsw  = FALSE;
      foreach( $selectOptions as $metadataKey => $metadataValue ) {
        if(( 'X-' == strtoupper( substr( $metadataKey, 0, 2 ))) ||
           ( in_array( strtolower( $metadataKey ), array( 'date', 'filename' ))))
          continue;
        if( $orsw )
          $sql .= ' OR';
        $sql .= ' (LOWER(metadata_key) = '.$this->_db->quote( strtolower( $metadataKey ), 'text' );
        if( !empty( $metadataValue ))
          $sql .= ' AND metadata_text = '.$this->_db->quote( $metadataValue, 'text' );
        $sql .= ')';
        $orsw  = TRUE;
      }
    }
    $sql     .= ' ORDER BY 1 DESC, 2';
    $res      = & $this->_db->query( $sql, array( 'integer', 'text', 'text' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow()) {
      if( !isset( $row['calendar_id'] ) || empty( $row['calendar_id'] ))
        continue;
      if( !isset( $result[$row['calendar_id']] ))
        $result[$row['calendar_id']] = array( 'calendar_id' => $row['calendar_id'] );
      $result[$row['calendar_id']][$row['metadata_key']] = $row['metadata_text'];
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    return $result;
  }
}
/**
 * This class implements the parameter table DAO
**/
class dbiCal_parameter_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-11-04
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-11-04
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_parameter_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    string $ownerProperty
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-13
   */
  function delete( $owner_id, $ownertype, $ownerProperty=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql  = 'DELETE FROM parameter WHERE parameter_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' AND parameter_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    if( $ownerProperty )
      $sql .= ' AND parameter_property = '.$this->_db->quote( $ownerProperty, 'text' );
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    string $ownerProperty
   * @param    string $parameterKey
   * @param    string $parameterValue
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $ownertype, $ownerProperty, $parameterKey, $parameterValue ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql     = 'INSERT INTO parameter (parameter_owner_id, parameter_ownertype, parameter_property';
    $values  = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $ownertype, 'text' );
    $values .= ', '.$this->_db->quote( strtoupper( $ownerProperty ), 'text' );
    $sql    .= ', parameter_key';
    $values .= ', '.$this->_db->quote( $parameterKey, 'text' );
    $sql    .= ', parameter_value';
    if( is_array( $parameterValue ))
      $parameterValue = implode( '|', $parameterValue );
    $values .= ', '.$this->_db->quote( $parameterValue, 'text' );
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $parameter_id = $this->_db->lastInsertID( 'parameter', 'parameter_id' );
    if( PEAR::isError( $parameter_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$parameter_id->getUserInfo().PHP_EOL.$parameter_id->getMessage(), PEAR_LOG_ALERT );
      return $parameter_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'parameter_id='.$parameter_id, PEAR_LOG_INFO );
    return $parameter_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    string $ownerProperty
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-02-20
   */
  function select( $owner_id, $ownertype, $ownerProperty=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM parameter WHERE parameter_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' AND parameter_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $types = array( 'integer', 'integer', 'text', 'text', 'text', 'text' );
    if( $ownerProperty ) {
      $ownerProperty = strtoupper( $ownerProperty );
      $sql .= ' AND parameter_property = '.$this->_db->quote( $ownerProperty, 'text' );
    }
    $sql .= ' ORDER BY parameter_id';
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      if( isset( $tablerow['parameter_key'] ) && isset( $tablerow['parameter_value'] )) {
        if( FALSE !==  strpos( $tablerow['parameter_value'], '|' ))
          $tablerow['parameter_value'] = explode( '|', $tablerow['parameter_value'] );
        if( !$ownerProperty )
          $result[$tablerow['parameter_property']][$tablerow['parameter_key']] = $tablerow['parameter_value'];
        else
          $result[$tablerow['parameter_key']] = $tablerow['parameter_value'];
        if( $this->_log )
          $this->_log->log( $tablerow['parameter_property'].' : '.$tablerow['parameter_key'].' : '.$tablerow['parameter_value'], PEAR_LOG_DEBUG );
      }
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' parameters', PEAR_LOG_INFO );
    return $result;
  }
}
/**
 * This class implements the timezone table DAO
**/
class dbiCal_timezone_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private static $singleProperties;
  /**
   * @access   private
   * @var      object
   */
  private static $multiProperties;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-30
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$singleProperties = array( 'TZID','LAST-MODIFIED', 'TZURL', );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_timezone_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-19
   */
  function delete( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere = 'FROM timezone WHERE timezone_owner_id = '.$this->_db->quote( $calendar_id, 'integer' );
    $sql  = "SELECT timezone_id, timezone_cnt_xprop, timezone_cnt_stddlght $fromWhere";
    $res  = & $this->_db->query( $sql, array_fill( 0, 3, 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      $row = array();
      $row['timezone_cnt_xprop']    = ( isset( $tablerow['timezone_cnt_xprop'] )    && !empty( $tablerow['timezone_cnt_xprop'] ))    ? $tablerow['timezone_cnt_xprop']    : 0;
      $row['timezone_cnt_stddlght'] = ( isset( $tablerow['timezone_cnt_stddlght'] ) && !empty( $tablerow['timezone_cnt_stddlght'] )) ? $tablerow['timezone_cnt_stddlght'] : 0;
      $result[$tablerow['timezone_id']] = $row;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_stddlght_DAO  = dbiCal_stddlght_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_xprop_DAO     = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $timezone_id => $timezone_cnts ) {
      $res = $dbiCal_parameter_DAO->delete( $timezone_id, 'timezone' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $timezone_cnts['timezone_cnt_xprop'] )) {
        $res = $dbiCal_xprop_DAO->delete( $timezone_id, 'timezone' );
        if( PEAR::isError( $res ))
          return $res;
      }
      if( empty( $timezone_cnts['timezone_cnt_stddlght'] ))
        continue;
      $res = $dbiCal_stddlght_DAO->delete( $timezone_id );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $calendar_id
   * @param    array  $timezone_values
   * @param    int    $calendar_order
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $calendar_id, & $timezone_values= array(), $calendar_order=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $cnts = array( 'stddlght' => 0 );
    foreach( $timezone_values as $prop => $propVals ) {
      if( in_array( $prop, self::$singleProperties ) || ( 'ono' == $prop ))
        continue;
      if( in_array( $prop, array( 'STANDARD', 'DAYLIGHT' ) ))
        $cnts['stddlght'] = isset( $cnts['stddlght'] ) ? ( $cnts['stddlght'] + count( $propVals )) : count( $propVals );
      else
        $cnts[$prop]   = isset( $cnts[$prop] ) ? ( $cnts[$prop] + count( $propVals )) : count( $propVals );
    }
    $sql       = 'INSERT INTO timezone (timezone_owner_id';
    $values    = ') VALUES ('.$this->_db->quote( $calendar_id, 'integer' );
    if( isset( $calendar_order )) {
      $sql    .= ', timezone_ono';
      $values .= ', '.$this->_db->quote( $calendar_order, 'integer' );
    }
    if( isset( $timezone_values['TZID']['value'] )) {
      $sql    .= ', timezone_tzid';
      $values .= ', '.$this->_db->quote( $timezone_values['TZID']['value'], 'text' );
    }
    if( isset( $timezone_values['TZURL']['value'] )) {
      $sql    .= ', timezone_url';
      $values .= ', '.$this->_db->quote( $timezone_values['TZURL']['value'], 'text' );
    }
    if( isset( $timezone_values['LAST-MODIFIED']['value'] )) {
      $sql    .= ', timezone_last_modified';
      $value   = sprintf("%04d-%02d-%02d", $timezone_values['LAST-MODIFIED']['value']['year'], $timezone_values['LAST-MODIFIED']['value']['month'], $timezone_values['LAST-MODIFIED']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $timezone_values['LAST-MODIFIED']['value']['hour'], $timezone_values['LAST-MODIFIED']['value']['min'], $timezone_values['LAST-MODIFIED']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    foreach( $cnts as $prop => $cnt ) {
      if( !empty( $cnt )) {
        $sql    .= ', timezone_cnt_'.strtolower( $prop );
        $values .= ', '.$this->_db->quote( $cnt, 'integer' );
      }
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $timezone_id = $this->_db->lastInsertID( 'timezone', 'timezone_id' );
    if( PEAR::isError( $timezone_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$timezone_id->getUserInfo().PHP_EOL.$timezone_id->getMessage(), PEAR_LOG_ALERT );
      return $timezone_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'timezone_id='.$timezone_id, PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $timezone_values as $prop => $pValue ) {
      if( !isset( $pValue['params'] ) || empty( $pValue['params'] ))
        continue;
      if( !in_array( $prop, self::$singleProperties ))
        continue;
      foreach( $pValue['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $timezone_id, 'timezone', $prop, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $timezone_values['XPROP'] ) && !empty( $timezone_values['XPROP'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      foreach( $timezone_values['XPROP'] as $xprop ) {
        if( isset( $xprop[1]['value'] ) && !empty( $xprop[1]['value'] )) {
          $res = $dbiCal_xprop_DAO->insert( $timezone_id, 'timezone', $xprop[0], $xprop[1] );
          if( PEAR::isError( $res ))
            return $res;
        }
      }
    }
    if(( isset( $timezone_values['STANDARD'] ) && !empty( $timezone_values['STANDARD'] )) ||
       ( isset( $timezone_values['DAYLIGHT'] ) && !empty( $timezone_values['DAYLIGHT'] ))) {
      $dbiCal_stddlght_DAO = dbiCal_stddlght_DAO::singleton( $this->_db, $this->_log );
      $subix = 1;
      if( isset( $timezone_values['STANDARD'] )) {
        foreach( $timezone_values['STANDARD'] as $standard ) {
          $res = $dbiCal_stddlght_DAO->insert( $timezone_id, 'STANDARD', $standard, $subix );
          if( PEAR::isError( $res ))
            return $res;
          $subix += 1;
        }
      }
      if( isset( $timezone_values['DAYLIGHT'] )) {
        foreach( $timezone_values['DAYLIGHT'] as $daylight ) {
          $res = $dbiCal_stddlght_DAO->insert( $timezone_id, 'DAYLIGHT', $daylight, $subix );
          if( PEAR::isError( $res ))
            return $res;
          $subix += 1;
        }
      }
    }
    return $timezone_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM timezone WHERE timezone_owner_id = '.$this->_db->quote( $calendar_id, 'integer' ).' ORDER BY timezone_id';
    $types = array( 'integer', 'integer', 'integer', 'text', 'text', 'timestamp' );
    $types = array_merge( $types, array_fill( count( $types ), 3, 'integer' ));
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = $timezone_cnts = array();
    while( $tablerow = $res->fetchRow()) {
      $row = $cnt = array();
      $cnt['timezone_cnt_xprop']    = ( isset( $tablerow['timezone_cnt_xprop'] )    && !empty( $tablerow['timezone_cnt_xprop'] ))    ? $tablerow['timezone_cnt_xprop']    : 0;
      $cnt['timezone_cnt_stddlght'] = ( isset( $tablerow['timezone_cnt_stddlght'] ) && !empty( $tablerow['timezone_cnt_stddlght'] )) ? $tablerow['timezone_cnt_stddlght'] : 0;
      $timezone_cnts[$tablerow['timezone_id']] = $cnt;
      if( isset( $tablerow['timezone_ono'] ))
        $row['ono'] = $tablerow['timezone_ono'];
      if( isset( $tablerow['timezone_tzid'] )           && !empty( $tablerow['timezone_tzid'] ))
        $row['TZID']['value'] = $tablerow['timezone_tzid'];
      if( isset( $tablerow['timezone_last_modified'] )  && !empty( $tablerow['timezone_last_modified'] )) {
        $dt = str_replace( '-', '', $tablerow['timezone_last_modified'] );
        $dt = str_replace( ':', '', $dt );
        $row['LAST-MODIFIED']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['timezone_tzurl'] )          &&  empty( $tablerow['timezone_tzurl'] ))
        $row['TZURL']['value'] = $tablerow['timezone_tzurl'];
      if( $this->_log )
        $this->_log->log( var_export( $row, TRUE ), PEAR_LOG_DEBUG );
      if( !empty( $row ))
        $result[$tablerow['timezone_id']] = $row;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' timezones', PEAR_LOG_INFO );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $timezone_id => $propVal ) {
      foreach( $propVal as $prop => $pValue ) {
        if( 'ono' == $prop )
          continue;
        $res = $dbiCal_parameter_DAO->select( $timezone_id, 'timezone', $prop );
        if( PEAR::isError( $res ))
          return $res;
        $result[$timezone_id][$prop]['params'] = array();
        if( !empty( $res )) {
          foreach( $res as $key => $value )
            $result[$timezone_id][$prop]['params'][$key] = $value;
        }
      }
    }
    $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $timezone_id => $propval ) {
      if( empty( $timezone_cnts[$timezone_id]['timezone_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->select( $timezone_id, 'timezone' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $propName => $propValue )
          $result[$timezone_id][$propName] = $propValue;
      }
    }
    $dbiCal_stddlght_DAO = dbiCal_stddlght_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $timezone_id => & $propval ) {
      if( empty( $timezone_cnts[$timezone_id]['timezone_cnt_stddlght'] ))
        continue;
      $res = $dbiCal_stddlght_DAO->select( $timezone_id );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $stddlghttype => $stddlght ) {
          foreach( $stddlght as $stddlghtix => $stddlghtval )
            $propval[$stddlghttype][$stddlghtix] = $stddlghtval;
        }
      }
    }
    return $result;
  }
}
/**
 * This class implements the stddlght (timezone: standard/daylight) table DAO
**/
class dbiCal_stddlght_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private static $singleProperties;
  /**
   * @access   private
   * @var      object
   */
  private static $mtextProperties;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-11-19
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$singleProperties = array( 'DTSTART', 'TZOFFSETFROM', 'TZOFFSETTO' );
    self::$mtextProperties  = array( // properties using mtext (DAO)
                                     'COMMENT', 'TZNAME' );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-11-13
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_stddlght_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-22
   */
  function delete( $owner_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM stddlght WHERE stddlght_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql  = "SELECT stddlght_id, stddlght_type, stddlght_cnt_mtext, stddlght_cnt_rdate, stddlght_cnt_rrule, stddlght_cnt_xprop $fromWhere";
    $res  = & $this->_db->query( $sql, array( 'integer', 'text', 'integer', 'integer', 'integer', 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      $cnt = array( 'stddlght_type' =>       $tablerow['stddlght_type'] );
      $cnt['stddlght_cnt_mtext']  = ( isset( $tablerow['stddlght_cnt_mtext'] ) && !empty( $tablerow['stddlght_cnt_mtext'] )) ? $tablerow['stddlght_cnt_mtext'] : 0;
      $cnt['stddlght_cnt_rdate']  = ( isset( $tablerow['stddlght_cnt_rdate'] ) && !empty( $tablerow['stddlght_cnt_rdate'] )) ? $tablerow['stddlght_cnt_rdate'] : 0;
      $cnt['stddlght_cnt_rrule']  = ( isset( $tablerow['stddlght_cnt_rrule'] ) && !empty( $tablerow['stddlght_cnt_rrule'] )) ? $tablerow['stddlght_cnt_rrule'] : 0;
      $cnt['stddlght_cnt_xprop']  = ( isset( $tablerow['stddlght_cnt_xprop'] ) && !empty( $tablerow['stddlght_cnt_xprop'] )) ? $tablerow['stddlght_cnt_xprop'] : 0;
      $result[$tablerow['stddlght_id']] = $cnt;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $stddlght_id => $stddlght_cnts ) {
      $res = $dbiCal_parameter_DAO->delete( $stddlght_id, $stddlght_cnts['stddlght_type'] );
      if( PEAR::isError( $res ))
        return $res;
    }
    $multiProps = array( 'mtext', 'rdate', 'rexrule', 'xprop' );
    foreach( $multiProps as $mProp ) {
      foreach( $result as $stddlght_id => $stddlght_cnts ) {
        if(( 'mtext'   == $mProp ) && empty( $stddlght_cnts['stddlght_cnt_mtext'] ))
          continue;
        if(( 'rdate'   == $mProp ) && empty( $stddlght_cnts['stddlght_cnt_rdate'] ))
          continue;
        if(( 'rexrule' == $mProp ) && empty( $stddlght_cnts['stddlght_cnt_rrule'] ))
          continue;
        if(( 'xprop'   == $mProp ) && empty( $stddlght_cnts['stddlght_cnt_xprop'] ))
          continue;
        $prop_DAO_name = 'dbiCal_'.$mProp.'_DAO';
        $prop_DAO = $prop_DAO_name::singleton( $this->_db, $this->_log );
        $res = $prop_DAO->delete( $stddlght_id, $stddlght_cnts['stddlght_type'] );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $sql  = "DELETE  $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $stddlghttype
   * @param    array  $stddlght_values
   * @param    int    $calendar_order
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $stddlghttype, $stddlght_values, $calendar_order=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $cnts = array();
    foreach( $stddlght_values as $prop => $propVals ) {
      if( in_array( $prop, self::$singleProperties ))
        continue;
      if( in_array( $prop, self::$mtextProperties ))
        $cnts['mtext'] = isset( $cnts['mtext'] ) ? ( $cnts['mtext'] + count( $propVals )) : count( $propVals );
      else
        $cnts[$prop]   = isset( $cnts[$prop] ) ? ( $cnts[$prop] + count( $propVals )) : count( $propVals );
    }
    $sql       = 'INSERT INTO stddlght (stddlght_owner_id, stddlght_type';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $stddlghttype, 'text' );
    if( isset( $calendar_order )) {
      $sql    .= ', stddlght_ono';
      $values .= ', '.$this->_db->quote( $calendar_order, 'integer' );
    }
    if( isset( $stddlght_values['DTSTART']['value'] )      && !empty( $stddlght_values['DTSTART']['value'] )) {
      $sql    .= ', stddlght_startdatetime';
      $value   = sprintf("%04d-%02d-%02d", $stddlght_values['DTSTART']['value']['year'], $stddlght_values['DTSTART']['value']['month'], $stddlght_values['DTSTART']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $stddlght_values['DTSTART']['value']['hour'], $stddlght_values['DTSTART']['value']['min'], $stddlght_values['DTSTART']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $stddlght_values['TZOFFSETFROM']['value'] ) && !empty( $stddlght_values['TZOFFSETFROM']['value'] )) {
      $sql    .= ', stddlght_tzoffsetfrom';
      $values .= ', '.$this->_db->quote( $stddlght_values['TZOFFSETFROM']['value'], 'integer' );
    }
    if( isset( $stddlght_values['TZOFFSETTO']['value'] )   && !empty( $stddlght_values['TZOFFSETTO']['value'] )) {
      $sql    .= ', stddlght_tzoffsetto';
      $values .= ', '.$this->_db->quote( $stddlght_values['TZOFFSETTO']['value'], 'integer' );
    }
    foreach( $cnts as $prop => $cnt ) {
      if( !empty( $cnt )) {
        $sql    .= ', stddlght_cnt_'.strtolower( $prop );
        $values .= ', '.$this->_db->quote( $cnt, 'integer' );
      }
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $stddlght_id = $this->_db->lastInsertID( 'stddlght', 'stddlght_id' );
    if( PEAR::isError( $stddlght_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$stddlght_id->getUserInfo().PHP_EOL.$stddlght_id->getMessage(), PEAR_LOG_ALERT );
      return $stddlght_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'stddlght_id='.$stddlght_id, PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $stddlght_values as $prop => $pValue ) {
      if( !isset( $pValue['params'] ) || empty( $pValue['params'] ))
        continue;
      if( in_array( $prop, array( 'RDATE', 'RRULE' )))
        continue;
      if( !in_array( $prop, self::$singleProperties ))
        continue;
      if( in_array( $prop, self::$mtextProperties ) || ( 'X-' == substr( $prop, 0, 2 )))
        continue;
      foreach( $pValue['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $stddlght_id, $stddlghttype, $prop, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $stddlght_values['RRULE'] ) && !empty( $stddlght_values['RRULE'] )) {
      $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
      foreach( $stddlght_values['RRULE'] as $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rexrule_DAO->insert( $stddlght_id, $stddlghttype, 'RRULE', $theProp );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $stddlght_values['RDATE'] ) || !empty( $stddlght_values['RDATE'] )) {
      $dbiCal_rdate_DAO = dbiCal_rdate_DAO::singleton( $this->_db, $this->_log );
      foreach( $stddlght_values['RDATE'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rdate_DAO->insert( $stddlght_id, $stddlghttype, $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( self::$mtextProperties as $mProp ) {
      if( isset( $stddlght_values[$mProp] ) && !empty( $stddlght_values[$mProp] )) {
        foreach( $stddlght_values[$mProp] as $themProp ) {
          if( isset( $themProp['value'] ) && !empty( $themProp['value'] )) {
            $res = $dbiCal_mtext_DAO->insert( $stddlght_id, $stddlghttype, $mProp, $themProp );
            if( PEAR::isError( $res ))
              return $res;
          }
        }
      }
    }
    if( isset( $stddlght_values['XPROP'] ) && !empty( $stddlght_values['XPROP'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      foreach( $stddlght_values['XPROP'] as $xprop ) {
        if( isset( $xprop[1]['value'] ) && !empty( $xprop[1]['value'] )) {
          $res = $dbiCal_xprop_DAO->insert( $stddlght_id, $stddlghttype, $xprop[0], $xprop[1] );
          if( PEAR::isError( $res ))
            return $res;
        }
      }
    }
    return $stddlght_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @return   mixed  $res (PEAR::Error eller result-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $owner_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM stddlght WHERE stddlght_owner_id = '.$this->_db->quote( $owner_id, 'integer' ).' ORDER BY stddlght_id';
    $types = array( 'integer', 'integer', 'text', 'integer', 'timestamp', 'integer', 'integer' );
    $types = array_merge( $types, array_fill( count( $types ), 4, 'integer' ));
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = $stddlght_cnts = array();
    while( $tablerow = $res->fetchRow()) {
      $row = $cnt = array();
      $cnt['stddlght_cnt_mtext'] = ( isset( $tablerow['stddlght_cnt_mtext'] ) && !empty( $tablerow['stddlght_cnt_mtext'] )) ? $tablerow['stddlght_cnt_mtext'] : 0;
      $cnt['stddlght_cnt_rdate'] = ( isset( $tablerow['stddlght_cnt_rdate'] ) && !empty( $tablerow['stddlght_cnt_rdate'] )) ? $tablerow['stddlght_cnt_rdate'] : 0;
      $cnt['stddlght_cnt_rrule'] = ( isset( $tablerow['stddlght_cnt_rrule'] ) && !empty( $tablerow['stddlght_cnt_rrule'] )) ? $tablerow['stddlght_cnt_rrule'] : 0;
      $cnt['stddlght_cnt_xprop'] = ( isset( $tablerow['stddlght_cnt_xprop'] ) && !empty( $tablerow['stddlght_cnt_xprop'] )) ? $tablerow['stddlght_cnt_xprop'] : 0;
      $stddlght_cnts[$tablerow['stddlght_id']] = $cnt;
      if( isset( $tablerow['stddlght_ono'] ))
        $row['ono'] = $tablerow['stddlght_ono'];
      if( isset( $tablerow['stddlght_startdatetime'] ) && !empty( $tablerow['stddlght_startdatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['stddlght_startdatetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTART']['value']      = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
      }
      if( isset( $tablerow['stddlght_tzoffsetfrom'] )  && ( !empty( $tablerow['stddlght_tzoffsetfrom'] ) || ( '0' == $tablerow['stddlght_tzoffsetfrom'] ))) {
        $format = (( 9999 < $tablerow['stddlght_tzoffsetfrom'] ) || ( -9999 > $tablerow['stddlght_tzoffsetfrom'] )) ? "%+07d" : "%+05d";
        $row['TZOFFSETFROM']['value'] = sprintf( $format, $tablerow['stddlght_tzoffsetfrom'] );
      }
      if( isset( $tablerow['stddlght_tzoffsetto'] )    && ( !empty( $tablerow['stddlght_tzoffsetto'] ) || ( '0' == $tablerow['stddlght_tzoffsetto'] ))) {
        $format = (( 9999 < $tablerow['stddlght_tzoffsetto'] ) || ( -9999 > $tablerow['stddlght_tzoffsetto'] )) ? "%+07d" : "%+05d";
        $row['TZOFFSETTO']['value'] = sprintf( $format, $tablerow['stddlght_tzoffsetto'] );
      }
      if( !empty( $row )) {
        $result[$tablerow['stddlght_type']][$tablerow['stddlght_id']] = $row;
        if( $this->_log )
          $this->_log->log( var_export( $row, TRUE ), PEAR_LOG_DEBUG );
      }
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' stddlghts', PEAR_LOG_INFO );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $stddlghttype => $stddlghtVal ) {
      foreach( $stddlghtVal as $stddlght_id => $stddlghtprop ) {
        foreach( $stddlghtprop as $stddlghtpname => $stddlghtpval ) {
          if( 'ono' === $stddlghtpname )
            continue;
          $res = $dbiCal_parameter_DAO->select( $stddlght_id, $stddlghttype, $stddlghtpname );
          if( PEAR::isError( $res ))
            return $res;
          $result[$stddlghttype][$stddlght_id][$stddlghtpname]['params'] = FALSE;
          if( !empty( $res )) {
            foreach( $res as $key => $value )
              $result[$stddlghttype][$stddlght_id][$stddlghtpname]['params'][$key] = $value;
          }
        }
      }
    }
    $dbiCal_rdate_DAO   = dbiCal_rdate_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_xprop_DAO   = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $stddlghttype => $stddlghtVal ) {
      foreach( $stddlghtVal as $stddlght_id => $stddlghtprop ) {
        if( !empty( $stddlght_cnts[$stddlght_id]['stddlght_cnt_rdate'] )) {
          $res = $dbiCal_rdate_DAO->select( $stddlght_id, $stddlghttype );
          if( PEAR::isError( $res ))
            return $res;
          if( !empty( $res ))
            $result[$stddlghttype][$stddlght_id]['RDATE'] = $res;
        }
        if( !empty( $stddlght_cnts[$stddlght_id]['stddlght_cnt_rrule'] )) {
          $res = $dbiCal_rexrule_DAO->select( $stddlght_id, $stddlghttype );
          if( PEAR::isError( $res ))
            return $res;
          if( !empty( $res )) {
            foreach( $res as $propValue )
              $result[$stddlghttype][$stddlght_id][$propValue['value']['rexrule_type']][] = $propValue;
          }
        }
        if( empty( $stddlght_cnts[$stddlght_id]['stddlght_cnt_xprop'] ))
          continue;
        $res = $dbiCal_xprop_DAO->select( $stddlght_id, $stddlghttype );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res )) {
          foreach( $res as $propName => $propValue )
            $result[$stddlghttype][$stddlght_id][$propName] = $propValue;
        }
      }
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $stddlghttype => $stddlghtVal ) {
      foreach( $stddlghtVal as $stddlght_id => $stddlghtprop ) {
        if( empty( $stddlght_cnts[$stddlght_id]['stddlght_cnt_mtext'] ))
          continue;
        $res = $dbiCal_mtext_DAO->select( $stddlght_id, $stddlghttype );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res )) {
          foreach( $res as $mpropname => $mprops )
            $result[$stddlghttype][$stddlght_id][$mpropname] = $mprops;
        }
      }
    }
    return $result;
  }
}
/**
 * This class implements the event table DAO
**/
class dbiCal_event_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private static $singleProperties;
  /**
   * @access   private
   * @var      object
   */
  private static $mtextProperties;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-30
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$singleProperties = array( 'CLASS', 'CREATED', 'DESCRIPTION', 'DTEND', 'DTSTAMP', 'DTSTART'
                             , 'DURATION', 'GEO', 'LAST-MODIFIED', 'LOCATION', 'ORGANIZER'
                             , 'PRIORITY', 'RECURRENCE-ID', 'SEQUENCE'
                             , 'STATUS', 'SUMMARY', 'TRANSP', 'UID', 'URL', );
    self::$mtextProperties  = array( // properties using mtext (DAO)
                               'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'RELATED-TO', 'RESOURCES', 'REQUEST-STATUS' );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_event_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-20
   */
  function delete( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere = 'FROM event WHERE event_owner_id = '.$this->_db->quote( $calendar_id, 'integer' );
    $sql  = "SELECT event_id, event_cnt_mtext, event_cnt_attach, event_cnt_exdate, event_cnt_exrule, event_cnt_rdate, event_cnt_rrule, event_cnt_xprop, event_cnt_alarm $fromWhere";
    $res  = & $this->_db->query( $sql, array_fill( 0, 9, 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      $cnt = array();
      $cnt['event_cnt_mtext']  = ( isset( $tablerow['event_cnt_mtext'] )  && !empty( $tablerow['event_cnt_mtext'] ))  ? $tablerow['event_cnt_mtext']  : 0;
      $cnt['event_cnt_attach'] = ( isset( $tablerow['event_cnt_attach'] ) && !empty( $tablerow['event_cnt_attach'] )) ? $tablerow['event_cnt_attach'] : 0;
      $cnt['event_cnt_exdate'] = ( isset( $tablerow['event_cnt_exdate'] ) && !empty( $tablerow['event_cnt_exdate'] )) ? $tablerow['event_cnt_exdate'] : 0;
      $cnt['event_cnt_exrule'] = ( isset( $tablerow['event_cnt_exrule'] ) && !empty( $tablerow['event_cnt_exrule'] )) ? $tablerow['event_cnt_exrule'] : 0;
      $cnt['event_cnt_rdate']  = ( isset( $tablerow['event_cnt_rdate'] )  && !empty( $tablerow['event_cnt_rdate'] ))  ? $tablerow['event_cnt_rdate']  : 0;
      $cnt['event_cnt_rrule']  = ( isset( $tablerow['event_cnt_rrule'] )  && !empty( $tablerow['event_cnt_rrule'] ))  ? $tablerow['event_cnt_rrule']  : 0;
      $cnt['event_cnt_xprop']  = ( isset( $tablerow['event_cnt_xprop'] )  && !empty( $tablerow['event_cnt_xprop'] ))  ? $tablerow['event_cnt_xprop']  : 0;
      $cnt['event_cnt_alarm']  = ( isset( $tablerow['event_cnt_alarm'] )  && !empty( $tablerow['event_cnt_alarm'] ))  ? $tablerow['event_cnt_alarm']  : 0;
      $result[$tablerow['event_id']] = $cnt;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_alarm_DAO     = dbiCal_alarm_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_xprop_DAO     = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $event_id => $event_cnts ) {
      $res = $dbiCal_parameter_DAO->delete( $event_id, 'event' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $event_cnts['event_cnt_alarm'] )) {
        $res = $dbiCal_alarm_DAO->delete( $event_id, 'event' );
        if( PEAR::isError( $res ))
          return $res;
      }
      if( empty( $event_cnts['event_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->delete( $event_id, 'event' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $multiProps = array( 'mtext', 'attach', 'rexrule', 'exdate', 'rdate' );
    foreach( $multiProps as $mProp ) {
      foreach( $result as $event_id => $event_cnts ) {
        if(( 'mtext'       == $mProp ) && empty( $event_cnts['event_cnt_mtext'] ))
          continue;
        elseif(( 'attach'  == $mProp ) && empty( $event_cnts['event_cnt_attach'] ))
          continue;
        elseif(( 'rexrule' == $mProp ) && empty( $event_cnts['event_cnt_exrule'] ) && empty( $event_cnts['event_cnt_rrule'] ))
          continue;
        elseif(( 'exdate'  == $mProp ) && empty( $event_cnts['event_cnt_exdate'] ))
          continue;
        elseif(( 'rdate'   == $mProp ) && empty( $event_cnts['event_cnt_rdate'] ))
          continue;
        $prop_DAO_name = 'dbiCal_'.$mProp.'_DAO';
        $prop_DAO = $prop_DAO_name::singleton( $this->_db, $this->_log );
        $res = $prop_DAO->delete( $event_id, 'event' );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $calendar_id
   * @param    array  $event_values
   * @param    int    $calendar_order
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
   public function insert( $calendar_id, & $event_values= array(), $calendar_order=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $cnts = array();
    foreach( $event_values as $prop => $propVals ) {
      if( in_array( $prop, self::$singleProperties ) || ( 'ono' == $prop ))
        continue;
      if( in_array( $prop, self::$mtextProperties ))
        $cnts['mtext'] = isset( $cnts['mtext'] ) ? ( $cnts['mtext'] + count( $propVals )) : count( $propVals );
      else
        $cnts[$prop]   = isset( $cnts[$prop] ) ? ( $cnts[$prop] + count( $propVals )) : count( $propVals );
    }
    $sql       = 'INSERT INTO event (event_owner_id';
    $values    = ') VALUES ('.$this->_db->quote( $calendar_id, 'integer' );
    if( isset( $calendar_order )) {
      $sql    .= ', event_ono';
      $values .= ', '.$this->_db->quote( $calendar_order, 'integer' );
    }
    if( isset( $event_values['UID']['value'] )) {
      $sql    .= ', event_uid';
      $values .= ', '.$this->_db->quote( $event_values['UID']['value'], 'text' );
    }
    if( isset( $event_values['DTSTAMP']['value'] )) {
      $sql    .= ', event_dtstamp';
      $value   = sprintf("%04d-%02d-%02d", $event_values['DTSTAMP']['value']['year'], $event_values['DTSTAMP']['value']['month'], $event_values['DTSTAMP']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $event_values['DTSTAMP']['value']['hour'], $event_values['DTSTAMP']['value']['min'], $event_values['DTSTAMP']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $event_values['DTSTART']['value'] )) {
      $value = sprintf("%04d-%02d-%02d", $event_values['DTSTART']['value']['year'], $event_values['DTSTART']['value']['month'], $event_values['DTSTART']['value']['day']);
      if( isset( $event_values['DTSTART']['value']['hour'] )) {
        $sql    .= ', event_startdatetime';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $event_values['DTSTART']['value']['hour'], $event_values['DTSTART']['value']['min'], $event_values['DTSTART']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $event_values['DTSTART']['value']['tz'] ) &&  in_array( $event_values['DTSTART']['value']['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql    .= ', event_startdatetimeutc';
          $values .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql    .= ', event_startdate';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $event_values['DTEND']['value'] )) {
      $value = sprintf("%04d-%02d-%02d", $event_values['DTEND']['value']['year'], $event_values['DTEND']['value']['month'], $event_values['DTEND']['value']['day']);
      if( isset( $event_values['DTEND']['value']['hour'] )) {
        $sql    .= ', event_enddatetime';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $event_values['DTEND']['value']['hour'], $event_values['DTEND']['value']['min'], $event_values['DTEND']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $event_values['DTEND']['value']['tz'] ) &&  in_array( $event_values['DTEND']['value']['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql    .= ', event_enddatetimeutc';
          $values .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql    .= ', event_enddate';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $event_values['DURATION']['value'] )) {
      $sql    .= ', event_duration';
      $value = '';
      foreach( $event_values['DURATION']['value'] as $k => $v )
        $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
      $values .= ', '.$this->_db->quote( $value, 'text' );
    }
    if( isset( $event_values['SUMMARY']['value'] )) {
      $sql    .= ', event_summary';
      $values .= ', '.$this->_db->quote( $event_values['SUMMARY']['value'], 'text' );
    }
    if( isset( $event_values['DESCRIPTION']['value'] )) {
      $sql    .= ', event_description';
      $values .= ', '.$this->_db->quote( $event_values['DESCRIPTION']['value'], 'text' );
    }
    if( isset( $event_values['GEO']['value'] )) {
      $sql    .= ', event_geo';
      $value = '';
      foreach( $event_values['GEO']['value'] as $k => $v )
        $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
      $values .= ', '.$this->_db->quote( $value, 'text' );
    }
    if( isset( $event_values['LOCATION']['value'] )) {
      $sql    .= ', event_location';
      $values .= ', '.$this->_db->quote( $event_values['LOCATION']['value'], 'text' );
    }
    if( isset( $event_values['ORGANIZER']['value'] )) {
      $sql    .= ', event_organizer';
      $values .= ', '.$this->_db->quote( $event_values['ORGANIZER']['value'], 'text' );
    }
    if( isset( $event_values['CLASS']['value'] )) {
      $sql    .= ', event_class';
      $values .= ', '.$this->_db->quote( $event_values['CLASS']['value'], 'text' );
    }
    if( isset( $event_values['TRANSP']['value'] )) {
      $sql    .= ', event_transp';
      $values .= ', '.$this->_db->quote( $event_values['TRANSP']['value'], 'text' );
    }
    if( isset( $event_values['STATUS']['value'] )) {
      $sql    .= ', event_status';
      $values .= ', '.$this->_db->quote( $event_values['STATUS']['value'], 'text' );
    }
    if( isset( $event_values['URL']['value'] )) {
      $sql    .= ', event_url';
      $values .= ', '.$this->_db->quote( $event_values['URL']['value'], 'text' );
    }
    if( isset( $event_values['PRIORITY']['value'] )) {
      $sql    .= ', event_priority';
      $values .= ', '.$this->_db->quote( $event_values['PRIORITY']['value'], 'integer' );
    }
    if( isset( $event_values['RECURRENCE-ID']['value'] )) {
      $value = sprintf("%04d-%02d-%02d", $event_values['RECURRENCE-ID']['value']['year'], $event_values['RECURRENCE-ID']['value']['month'], $event_values['RECURRENCE-ID']['value']['day']);
      if( isset( $event_values['RECURRENCE-ID']['value']['hour'] )) {
        $sql    .= ', event_recurrence_id_dt';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $event_values['RECURRENCE-ID']['value']['hour'], $event_values['RECURRENCE-ID']['value']['min'], $event_values['RECURRENCE-ID']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $event_values['RECURRENCE-ID']['value']['tz'] ) &&  in_array( $event_values['RECURRENCE-ID']['value']['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql    .= ', event_recurrence_idutc';
          $values .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql    .= ', event_recurrence_id';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $event_values['SEQUENCE']['value'] )) {
      $sql    .= ', event_sequence';
      $values .= ', '.$this->_db->quote( $event_values['SEQUENCE']['value'], 'integer' );
    }
    if( isset( $event_values['CREATED']['value'] )) {
      $sql    .= ', event_created';
      $value   = sprintf("%04d-%02d-%02d", $event_values['CREATED']['value']['year'], $event_values['CREATED']['value']['month'], $event_values['CREATED']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $event_values['CREATED']['value']['hour'], $event_values['CREATED']['value']['min'], $event_values['CREATED']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $event_values['LAST-MODIFIED']['value'] )) {
      $sql    .= ', event_last_modified';
      $value   = sprintf("%04d-%02d-%02d", $event_values['LAST-MODIFIED']['value']['year'], $event_values['LAST-MODIFIED']['value']['month'], $event_values['LAST-MODIFIED']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $event_values['LAST-MODIFIED']['value']['hour'], $event_values['LAST-MODIFIED']['value']['min'], $event_values['LAST-MODIFIED']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    foreach( $cnts as $prop => $cnt ) {
      if( !empty( $cnt )) {
        $sql    .= ', event_cnt_'.strtolower( $prop );
        $values .= ', '.$this->_db->quote( $cnt, 'integer' );
      }
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $event_id = $this->_db->lastInsertID( 'event', 'event_id' );
    if( PEAR::isError( $event_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$event_id->getUserInfo().PHP_EOL.$event_id->getMessage(), PEAR_LOG_ALERT );
      return $event_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'event_id='.$event_id, PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $event_values as $prop => $pValue ) {
      if( !isset( $pValue['params'] ) || empty( $pValue['params'] ))
        continue;
      if( in_array( $prop, array( 'ATTACH', 'EXDATE', 'EXRULE', 'RDATE', 'RRULE' )))
        continue;
      if( !in_array( $prop, self::$singleProperties ))
        continue;
      if( in_array( $prop, self::$mtextProperties ) || ( 'X-' == substr( $prop, 0, 2 )))
        continue;
      foreach( $pValue['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $event_id, 'event', $prop, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $event_values['ATTACH'] ) && !empty( $event_values['ATTACH'] )) {
      $dbiCal_attach_DAO = dbiCal_attach_DAO::singleton( $this->_db, $this->_log );
      foreach( $event_values['ATTACH'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_attach_DAO->insert( $event_id, 'event', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $event_values['EXRULE'] ) && !empty( $event_values['EXRULE'] )) {
      $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
      foreach( $event_values['EXRULE'] as $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rexrule_DAO->insert( $event_id, 'event', 'EXRULE', $theProp );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $event_values['RRULE'] ) && !empty( $event_values['RRULE'] )) {
      $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
      foreach( $event_values['RRULE'] as $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rexrule_DAO->insert( $event_id, 'event', 'RRULE', $theProp );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $event_values['EXDATE'] ) && !empty( $event_values['EXDATE'] )) {
      $dbiCal_exdate_DAO = dbiCal_exdate_DAO::singleton( $this->_db, $this->_log );
      foreach( $event_values['EXDATE'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_exdate_DAO->insert( $event_id, 'event', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $event_values['RDATE'] ) && !empty( $event_values['RDATE'] )) {
      $dbiCal_rdate_DAO = dbiCal_rdate_DAO::singleton( $this->_db, $this->_log );
      foreach( $event_values['RDATE'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rdate_DAO->insert( $event_id, 'event', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( self::$mtextProperties as $mProp ) {
      if( isset( $event_values[$mProp] ) && !empty( $event_values[$mProp] )) {
        foreach( $event_values[$mProp] as $themProp ) {
          if( isset( $themProp ) && !empty( $themProp['value'] )) {
            $res = $dbiCal_mtext_DAO->insert( $event_id, 'event', $mProp, $themProp );
            if( PEAR::isError( $res ))
              return $res;
          }
        }
      }
    }
    if( isset( $event_values['XPROP'] ) && !empty( $event_values['XPROP'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      foreach( $event_values['XPROP'] as $xprop ) {
        if( isset( $xprop[1]['value'] ) && !empty( $xprop[1]['value'] )) {
          $res = $dbiCal_xprop_DAO->insert( $event_id, 'event', $xprop[0], $xprop[1] );
          if( PEAR::isError( $res ))
            return $res;
        }
      }
    }
    if( isset( $event_values['ALARM'] ) && !empty( $event_values['ALARM'] )) {
      $dbiCal_alarm_DAO = dbiCal_alarm_DAO::singleton( $this->_db, $this->_log );
      foreach( $event_values['ALARM'] as $aix => $alarmValues ) {
        $res = $dbiCal_alarm_DAO->insert( $event_id, 'event', $alarmValues, ( $aix + 1 ));
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $event_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM event WHERE event_owner_id = '.$this->_db->quote( $calendar_id, 'integer' ).' ORDER BY event_id';
    $types = array( 'integer', 'integer', 'integer', 'text', 'timestamp', 'timestamp', 'boolean', 'date', 'timestamp', 'boolean'
                  , 'date', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'integer', 'timestamp'
                  , 'boolean', 'date', 'integer', 'timestamp', 'timestamp' );
    $types = array_merge( $types, array_fill( count( $types ), 8, 'integer' ));
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = $event_cnts = array();
    while( $tablerow = $res->fetchRow()) {
      $row = $cnt = array();
      $cnt['event_cnt_mtext']  = ( isset( $tablerow['event_cnt_mtext'] )  && !empty( $tablerow['event_cnt_mtext'] ))  ? $tablerow['event_cnt_mtext']  : 0;
      $cnt['event_cnt_attach'] = ( isset( $tablerow['event_cnt_attach'] ) && !empty( $tablerow['event_cnt_attach'] )) ? $tablerow['event_cnt_attach'] : 0;
      $cnt['event_cnt_exdate'] = ( isset( $tablerow['event_cnt_exdate'] ) && !empty( $tablerow['event_cnt_exdate'] )) ? $tablerow['event_cnt_exdate'] : 0;
      $cnt['event_cnt_exrule'] = ( isset( $tablerow['event_cnt_exrule'] ) && !empty( $tablerow['event_cnt_exrule'] )) ? $tablerow['event_cnt_exrule'] : 0;
      $cnt['event_cnt_rdate']  = ( isset( $tablerow['event_cnt_rdate'] )  && !empty( $tablerow['event_cnt_rdate'] ))  ? $tablerow['event_cnt_rdate']  : 0;
      $cnt['event_cnt_rrule']  = ( isset( $tablerow['event_cnt_rrule'] )  && !empty( $tablerow['event_cnt_rrule'] ))  ? $tablerow['event_cnt_rrule']  : 0;
      $cnt['event_cnt_xprop']  = ( isset( $tablerow['event_cnt_xprop'] )  && !empty( $tablerow['event_cnt_xprop'] ))  ? $tablerow['event_cnt_xprop']  : 0;
      $cnt['event_cnt_alarm']  = ( isset( $tablerow['event_cnt_alarm'] )  && !empty( $tablerow['event_cnt_alarm'] ))  ? $tablerow['event_cnt_alarm']  : 0;
      $event_cnts[$tablerow['event_id']] = $cnt;
      if( isset( $tablerow['event_ono'] ))
        $row['ono'] = $tablerow['event_ono'];
      if( isset( $tablerow['event_uid'] )            && !empty( $tablerow['event_uid'] ))
        $row['UID']['value'] = $tablerow['event_uid'];
      if( isset( $tablerow['event_dtstamp'] )        && !empty( $tablerow['event_dtstamp'] )) {
        $dt = str_replace( '-', '', $tablerow['event_dtstamp'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTAMP']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['event_startdatetime'] )  && !empty( $tablerow['event_startdatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['event_startdatetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTART']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['event_startdatetimeutc'] )  && !empty( $tablerow['event_startdatetimeutc'] ))
          $row['DTSTART']['value'] .= 'Z';
      }
      elseif( isset( $tablerow['event_startdate'] ) && !empty( $tablerow['event_startdate'] )) {
        $row['DTSTART']['value'] = str_replace( '-', '', $tablerow['event_startdate'] );
      }
      if( isset( $tablerow['event_enddatetime'] )    && !empty( $tablerow['event_enddatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['event_enddatetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTEND']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['event_enddatetimeutc'] )  && !empty( $tablerow['event_enddatetimeutc'] ))
          $row['DTEND']['value'] .= 'Z';
      }
      elseif( isset( $tablerow['event_enddate'] )   && !empty( $tablerow['event_enddate'] ))
        $row['DTEND']['value'] = str_replace( '-', '', $tablerow['event_enddate'] );
      if( isset( $tablerow['event_duration'] )       && !empty( $tablerow['event_duration'] )) {
        $durParts = explode( '|', $tablerow['event_duration'] );
        $duration = array();
        foreach( $durParts as $durPart ) {
          list( $key, $value ) = explode( '=', $durPart, 2 );
          $duration[$key] = $value;
        }
        $row['DURATION']['value'] = iCalUtilityFunctions::_format_duration( $duration );
      }
      if( isset( $tablerow['event_summary'] )        && !empty( $tablerow['event_summary'] ))
        $row['SUMMARY']['value'] = $tablerow['event_summary'];
      if( isset( $tablerow['event_description'] )    && !empty( $tablerow['event_description'] ))
        $row['DESCRIPTION']['value'] = $tablerow['event_description'];

      if( isset( $tablerow['event_geo'] )            && !empty( $tablerow['event_geo'] )) {
        $geoParts = explode( '|', $tablerow['event_geo'] );
        foreach( $geoParts as $geoPart ) {
          list( $key, $value ) = explode( '=', $geoPart, 2 );
          $row['GEO']['value'][$key] = $value;
        }
      }
      if( isset( $tablerow['event_location'] )       && !empty( $tablerow['event_location'] ))
        $row['LOCATION']['value'] = $tablerow['event_location'];
      if( isset( $tablerow['event_organizer'] )      && !empty( $tablerow['event_organizer'] ))
        $row['ORGANIZER']['value'] = $tablerow['event_organizer'];
      if( isset( $tablerow['event_class'] )          && !empty( $tablerow['event_class'] ))
        $row['CLASS']['value'] = $tablerow['event_class'];
      if( isset( $tablerow['event_transp'] )         && !empty( $tablerow['event_transp'] ))
        $row['TRANSP']['value'] = $tablerow['event_transp'];
      if( isset( $tablerow['event_status'] )         && !empty( $tablerow['event_status'] ))
        $row['STATUS']['value'] = $tablerow['event_status'];
      if( isset( $tablerow['event_sequence'] )       && ( !empty( $tablerow['event_sequence'] ) || ( '0' == $tablerow['event_sequence'] )))
        $row['SEQUENCE']['value'] = $tablerow['event_sequence'];
      if( isset( $tablerow['event_url'] )            && !empty( $tablerow['event_url'] ))
        $row['URL']['value'] = $tablerow['event_url'];
      if( isset( $tablerow['event_priority'] )       && !empty( $tablerow['event_priority'] ))
        $row['PRIORITY']['value'] = $tablerow['event_priority'];
      if( isset( $tablerow['event_recurrence_id_dt'] ) && !empty( $tablerow['event_recurrence_id_dt'] )) {
        $dt = str_replace( '-', '', $tablerow['event_recurrence_id_dt'] );
        $dt = str_replace( ':', '', $dt );
        $row['RECURRENCE-ID']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['event_recurrence_idutc'] ) && !empty( $tablerow['event_recurrence_idutc'] ))
          $row['RECURRENCE-ID']['value'] .= 'Z';
      }
      elseif( isset( $tablerow['event_recurrence_id'] ) && !empty( $tablerow['event_recurrence_id'] ))
        $row['RECURRENCE-ID']['value'] = str_replace( '-', '', $tablerow['event_recurrence_id'] );
      if( isset( $tablerow['event_created'] )        && !empty( $tablerow['event_created'] )) {
        $dt = str_replace( '-', '', $tablerow['event_created'] );
        $dt = str_replace( ':', '', $dt );
        $row['CREATED']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['event_last_modified'] )  && !empty( $tablerow['event_last_modified'] )) {
        $dt = str_replace( '-', '', $tablerow['event_last_modified'] );
        $dt = str_replace( ':', '', $dt );
        $row['LAST-MODIFIED']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( $this->_log )
        $this->_log->log( var_export( $row, TRUE ), PEAR_LOG_DEBUG );
      if( !empty( $row ))
        $result[$tablerow['event_id']] = $row;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' events', PEAR_LOG_INFO );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $event_id => $propVal ) {
      foreach( $propVal as $prop => $pValue ) {
        if( 'ono' == $prop )
          continue;
        $res = $dbiCal_parameter_DAO->select( $event_id, 'event', $prop );
        if( PEAR::isError( $res ))
          return $res;
        $result[$event_id][$prop]['params'] = array();
        if( !empty( $res )) {
          foreach( $res as $key => $value )
            $result[$event_id][$prop]['params'][$key] = $value;
        }
      }
    }
    $dbiCal_attach_DAO  = dbiCal_attach_DAO::singleton(  $this->_db, $this->_log );
    $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_exdate_DAO  = dbiCal_exdate_DAO::singleton(  $this->_db, $this->_log );
    $dbiCal_rdate_DAO   = dbiCal_rdate_DAO::singleton(   $this->_db, $this->_log );
    foreach( $result as $event_id => $propval ) {
      if( !empty( $event_cnts[$event_id]['event_cnt_attach'] )) {
        $res = $dbiCal_attach_DAO->select( $event_id, 'event' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res ))
          $result[$event_id]['ATTACH'] = $res;
      }
      if( !empty( $event_cnts[$event_id]['event_cnt_exrule'] ) ||
          !empty( $event_cnts[$event_id]['event_cnt_rrule'] )) {
        $res = $dbiCal_rexrule_DAO->select( $event_id, 'event' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res )) {
          foreach( $res as $propValue )
            $result[$event_id][$propValue['value']['rexrule_type']][] = $propValue;
        }
      }
      if( !empty( $event_cnts[$event_id]['event_cnt_exdate'] )) {
        $res = $dbiCal_exdate_DAO->select( $event_id, 'event' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res ))
          $result[$event_id]['EXDATE'] = $res;
      }
      if( empty( $event_cnts[$event_id]['event_cnt_rdate'] ))
        continue;
      $res = $dbiCal_rdate_DAO->select( $event_id, 'event' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res ))
          $result[$event_id]['RDATE'] = $res;
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $event_id => $propval ) {
      if( empty( $event_cnts[$event_id]['event_cnt_mtext'] ))
        continue;
      $res = $dbiCal_mtext_DAO->select( $event_id, 'event' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $mpropname => $mpropval ) {
          $result[$event_id][$mpropname] = $mpropval;
        }
      }
    }
    $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $event_id => $propval ) {
      if( empty( $event_cnts[$event_id]['event_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->select( $event_id, 'event' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $propName => $propValue )
          $result[$event_id][$propName] = $propValue;
      }
    }
    $dbiCal_alarm_DAO = dbiCal_alarm_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $event_id => $propval ) {
      if( empty( $event_cnts[$event_id]['event_cnt_alarm'] ))
        continue;
      $res = $dbiCal_alarm_DAO->select( $event_id, 'event' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res ))
        $result[$event_id]['ALARM'] = $res;
    }
    return $result;
  }
}
/**
 * This class implements the todo table DAO
**/
class dbiCal_todo_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private static $singleProperties;
  /**
   * @access   private
   * @var      object
   */
  private static $mtextProperties;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-11-13
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$singleProperties = array( 'CLASS', 'COMPLETED', 'CREATED', 'DESCRIPTION', 'DTSTAMP', 'DTSTART'
                             , 'DURATION', 'DUE', 'GEO', 'LAST-MODIFIED', 'LOCATION', 'ORGANIZER'
                             , 'PERCENT-COMPLETE', 'PRIORITY', 'RECURRENCE-ID', 'SEQUENCE'
                             , 'STATUS', 'SUMMARY', 'UID', 'URL', );
    self::$mtextProperties  = array( // properties using mtext (DAO)
                               'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'RELATED-TO', 'RESOURCES', 'REQUEST-STATUS' );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_todo_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-20
   */
  function delete( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere = 'FROM todo WHERE todo_owner_id = '.$this->_db->quote( $calendar_id, 'integer' );
    $sql  = "SELECT todo_id, todo_cnt_mtext, todo_cnt_attach, todo_cnt_exdate, todo_cnt_exrule, todo_cnt_rdate, todo_cnt_rrule, todo_cnt_xprop, todo_cnt_alarm $fromWhere";
    $types = array_fill( 0, 9, 'integer' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      $cnt = array();
      $cnt['todo_cnt_mtext']  = ( isset( $tablerow['todo_cnt_mtext'] )  && !empty( $tablerow['todo_cnt_mtext'] ))  ? $tablerow['todo_cnt_mtext']  : 0;
      $cnt['todo_cnt_attach'] = ( isset( $tablerow['todo_cnt_attach'] ) && !empty( $tablerow['todo_cnt_attach'] )) ? $tablerow['todo_cnt_attach'] : 0;
      $cnt['todo_cnt_exdate'] = ( isset( $tablerow['todo_cnt_exdate'] ) && !empty( $tablerow['todo_cnt_exdate'] )) ? $tablerow['todo_cnt_exdate'] : 0;
      $cnt['todo_cnt_exrule'] = ( isset( $tablerow['todo_cnt_exrule'] ) && !empty( $tablerow['todo_cnt_exrule'] )) ? $tablerow['todo_cnt_exrule'] : 0;
      $cnt['todo_cnt_rdate']  = ( isset( $tablerow['todo_cnt_rdate'] )  && !empty( $tablerow['todo_cnt_rdate'] ))  ? $tablerow['todo_cnt_rdate']  : 0;
      $cnt['todo_cnt_rrule']  = ( isset( $tablerow['todo_cnt_rrule'] )  && !empty( $tablerow['todo_cnt_rrule'] ))  ? $tablerow['todo_cnt_rrule']  : 0;
      $cnt['todo_cnt_xprop']  = ( isset( $tablerow['todo_cnt_xprop'] )  && !empty( $tablerow['todo_cnt_xprop'] ))  ? $tablerow['todo_cnt_xprop']  : 0;
      $cnt['todo_cnt_alarm']  = ( isset( $tablerow['todo_cnt_alarm'] )  && !empty( $tablerow['todo_cnt_alarm'] ))  ? $tablerow['todo_cnt_alarm']  : 0;
      $result[$tablerow['todo_id']] = $cnt;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_alarm_DAO     = dbiCal_alarm_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_xprop_DAO     = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $todo_id => $todo_cnts ) {
      $res = $dbiCal_parameter_DAO->delete( $todo_id, 'todo' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $todo_cnts['todo_cnt_alarm'] )) {
        $res = $dbiCal_alarm_DAO->delete( $todo_id, 'todo' );
        if( PEAR::isError( $res ))
          return $res;
      }
      if( empty( $todo_cnts['todo_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->delete( $todo_id, 'todo' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $multiProps = array( 'mtext', 'attach', 'rexrule', 'exdate', 'rdate' );
    foreach( $multiProps as $mProp ) {
      foreach( $result as $todo_id => $todo_cnts ) {
        if(( 'mtext'   == $mProp ) && empty( $todo_cnts['todo_cnt_mtext'] ))
          continue;
        if(( 'attach'  == $mProp ) && empty( $todo_cnts['todo_cnt_attach'] ))
          continue;
        if(( 'rexrule' == $mProp ) && empty( $todo_cnts['todo_cnt_exrule'] ) && empty( $todo_cnts['todo_cnt_rrule'] ))
          continue;
        if(( 'exdate'  == $mProp ) && empty( $todo_cnts['todo_cnt_exdate'] ))
          continue;
        if(( 'rdate'   == $mProp ) && empty( $todo_cnts['todo_cnt_rdate'] ))
          continue;
        $prop_DAO_name = 'dbiCal_'.$mProp.'_DAO';
        $prop_DAO = $prop_DAO_name::singleton( $this->_db, $this->_log );
        $res = $prop_DAO->delete( $todo_id, 'todo' );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $calendar_id
   * @param    array  $todo_values
   * @param    int    $calendar_order
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $calendar_id, & $todo_values= array(), $calendar_order ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $cnts = array();
    foreach( $todo_values as $prop => $propVals ) {
      if( in_array( $prop, self::$singleProperties ) || ( 'ono' == $prop ))
        continue;
      if( in_array( $prop, self::$mtextProperties ))
        $cnts['mtext'] = isset( $cnts['mtext'] ) ? ( $cnts['mtext'] + count( $propVals )) : count( $propVals );
      else
        $cnts[$prop]   = isset( $cnts[$prop] ) ? ( $cnts[$prop] + count( $propVals )) : count( $propVals );
    }
    $sql       = 'INSERT INTO todo (todo_owner_id';
    $values    = ') VALUES ('.$this->_db->quote( $calendar_id, 'integer' );
    if( isset( $calendar_order )) {
      $sql    .= ', todo_ono';
      $values .= ', '.$this->_db->quote( $calendar_order, 'integer' );
    }
    if( isset( $todo_values['UID']['value'] )) {
      $sql    .= ', todo_uid';
      $values .= ', '.$this->_db->quote( $todo_values['UID']['value'], 'text' );
    }
    if( isset( $todo_values['DTSTAMP']['value'] )) {
      $sql    .= ', todo_dtstamp';
      $value   = sprintf("%04d-%02d-%02d", $todo_values['DTSTAMP']['value']['year'], $todo_values['DTSTAMP']['value']['month'], $todo_values['DTSTAMP']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $todo_values['DTSTAMP']['value']['hour'], $todo_values['DTSTAMP']['value']['min'], $todo_values['DTSTAMP']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $todo_values['DTSTART']['value'] )) {
      $value = sprintf("%04d-%02d-%02d", $todo_values['DTSTART']['value']['year'], $todo_values['DTSTART']['value']['month'], $todo_values['DTSTART']['value']['day']);
      if( isset( $todo_values['DTSTART']['value']['hour'] )) {
        $sql    .= ', todo_startdatetime';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $todo_values['DTSTART']['value']['hour'], $todo_values['DTSTART']['value']['min'], $todo_values['DTSTART']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $todo_values['DTSTART']['value']['tz'] ) &&  in_array( $todo_values['DTSTART']['value']['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql    .= ', todo_startdatetimeutc';
          $values .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql    .= ', todo_startdate';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $todo_values['DUE']['value'] )) {
      $value = sprintf("%04d-%02d-%02d", $todo_values['DUE']['value']['year'], $todo_values['DUE']['value']['month'], $todo_values['DUE']['value']['day']);
      if( isset( $todo_values['DUE']['value']['hour'] )) {
        $sql    .= ', todo_duedatetime';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $todo_values['DUE']['value']['hour'], $todo_values['DUE']['value']['min'], $todo_values['DUE']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $todo_values['DUE']['value']['tz'] ) &&  in_array( $todo_values['DUE']['value']['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql    .= ', todo_duedatetimeutc';
          $values .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql    .= ', todo_duedate';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $todo_values['DURATION']['value'] )) {
      $value   = '';
      foreach( $todo_values['DURATION']['value'] as $k => $v )
        $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
      $sql    .= ', todo_duration';
      $values .= ', '.$this->_db->quote( $value, 'text' );
    }
    if( isset( $todo_values['COMPLETED']['value'] )) {
      $sql    .= ', todo_completed';
      $value   = sprintf("%04d-%02d-%02d", $todo_values['COMPLETED']['value']['year'], $todo_values['COMPLETED']['value']['month'], $todo_values['COMPLETED']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $todo_values['COMPLETED']['value']['hour'], $todo_values['COMPLETED']['value']['min'], $todo_values['COMPLETED']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $todo_values['SUMMARY']['value'] )) {
      $sql    .= ', todo_summary';
      $values .= ', '.$this->_db->quote( $todo_values['SUMMARY']['value'], 'text' );
    }
    if( isset( $todo_values['DESCRIPTION']['value'] )) {
      $sql    .= ', todo_description';
      $values .= ', '.$this->_db->quote( $todo_values['DESCRIPTION']['value'], 'text' );
    }
    if( isset( $todo_values['GEO']['value'] )) {
      $value = '';
      foreach( $todo_values['GEO']['value'] as $k => $v )
        $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
      $sql    .= ', todo_geo';
      $values .= ', '.$this->_db->quote( $value, 'text' );
    }
    if( isset( $todo_values['LOCATION']['value'] )) {
      $sql    .= ', todo_location';
      $values .= ', '.$this->_db->quote( $todo_values['LOCATION']['value'], 'text' );
    }
    if( isset( $todo_values['ORGANIZER']['value'] )) {
      $sql    .= ', todo_organizer';
      $values .= ', '.$this->_db->quote( $todo_values['ORGANIZER']['value'], 'text' );
    }
    if( isset( $todo_values['CLASS']['value'] )) {
      $sql    .= ', todo_class';
      $values .= ', '.$this->_db->quote( $todo_values['CLASS']['value'], 'text' );
    }
    if( isset( $todo_values['STATUS']['value'] )) {
      $sql    .= ', todo_status';
      $values .= ', '.$this->_db->quote( $todo_values['STATUS']['value'], 'text' );
    }
    if( isset( $todo_values['URL']['value'] )) {
      $sql    .= ', todo_url';
      $values .= ', '.$this->_db->quote( $todo_values['URL']['value'], 'text' );
    }
    if( isset( $todo_values['PERCENT-COMPLETE']['value'] )) {
      $sql    .= ', todo_percent_complete';
      $values .= ', '.$this->_db->quote( $todo_values['PERCENT-COMPLETE']['value'], 'integer' );
    }
    if( isset( $todo_values['PRIORITY']['value'] )) {
      $sql    .= ', todo_priority';
      $values .= ', '.$this->_db->quote( $todo_values['PRIORITY']['value'], 'integer' );
    }
    if( isset( $todo_values['RECURRENCE-ID']['value'] )) {
      $value = sprintf("%04d-%02d-%02d", $todo_values['RECURRENCE-ID']['value']['year'], $todo_values['RECURRENCE-ID']['value']['month'], $todo_values['RECURRENCE-ID']['value']['day']);
      if( isset( $todo_values['RECURRENCE-ID']['value']['hour'] )) {
        $sql    .= ', todo_recurrence_id_dt';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $todo_values['RECURRENCE-ID']['value']['hour'], $todo_values['RECURRENCE-ID']['value']['min'], $todo_values['RECURRENCE-ID']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $todo_values['RECURRENCE-ID']['value']['tz'] ) &&  in_array( $todo_values['RECURRENCE-ID']['value']['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql    .= ', todo_recurrence_idutc';
          $values .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql    .= ', todo_recurrence_id';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $todo_values['SEQUENCE']['value'] )) {
      $sql    .= ', todo_sequence';
      $values .= ', '.$this->_db->quote( $todo_values['SEQUENCE']['value'], 'integer' );
    }
    if( isset( $todo_values['CREATED']['value'] )) {
      $sql    .= ', todo_created';
      $value   = sprintf("%04d-%02d-%02d", $todo_values['CREATED']['value']['year'], $todo_values['CREATED']['value']['month'], $todo_values['CREATED']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $todo_values['CREATED']['value']['hour'], $todo_values['CREATED']['value']['min'], $todo_values['CREATED']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $todo_values['LAST-MODIFIED']['value'] )) {
      $sql    .= ', todo_last_modified';
      $value   = sprintf("%04d-%02d-%02d", $todo_values['LAST-MODIFIED']['value']['year'], $todo_values['LAST-MODIFIED']['value']['month'], $todo_values['LAST-MODIFIED']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $todo_values['LAST-MODIFIED']['value']['hour'], $todo_values['LAST-MODIFIED']['value']['min'], $todo_values['LAST-MODIFIED']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    foreach( $cnts as $prop => $cnt ) {
      if( !empty( $cnt )) {
        $sql    .= ', todo_cnt_'.strtolower( $prop );
        $values .= ', '.$this->_db->quote( $cnt, 'integer' );
      }
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $todo_id = $this->_db->lastInsertID( 'todo', 'todo_id' );
    if( PEAR::isError( $todo_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$todo_id->getUserInfo().PHP_EOL.$todo_id->getMessage(), PEAR_LOG_ALERT );
      return $todo_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'todo_id='.$todo_id, PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $todo_values as $prop => $pValue ) {
      if( !isset( $pValue['params'] ) || empty( $pValue['params'] ))
        continue;
      if( in_array( $prop, array( 'ATTACH', 'EXDATE', 'EXRULE', 'RDATE', 'RRULE' )))
        continue;
      if( !in_array( $prop, self::$singleProperties ))
        continue;
      if( in_array( $prop, self::$mtextProperties ) || ( 'X-' == substr( $prop, 0, 2 )))
        continue;
      foreach( $pValue['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $todo_id, 'todo', $prop, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $todo_values['ATTACH'] ) && !empty( $todo_values['ATTACH'] )) {
      $dbiCal_attach_DAO = dbiCal_attach_DAO::singleton( $this->_db, $this->_log );
      foreach( $todo_values['ATTACH'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_attach_DAO->insert( $todo_id, 'todo', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $todo_values['EXRULE'] ) && !empty( $todo_values['EXRULE'] )) {
      $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
      foreach( $todo_values['EXRULE'] as $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rexrule_DAO->insert( $todo_id, 'todo', 'EXRULE', $theProp );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $todo_values['RRULE'] ) && !empty( $todo_values['RRULE'] )) {
      $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
      foreach( $todo_values['RRULE'] as $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rexrule_DAO->insert( $todo_id, 'todo', 'RRULE', $theProp );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $todo_values['EXDATE'] ) && !empty( $todo_values['EXDATE'] )) {
      $dbiCal_exdate_DAO = dbiCal_exdate_DAO::singleton( $this->_db, $this->_log );
      foreach( $todo_values['EXDATE'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_exdate_DAO->insert( $todo_id, 'todo', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $todo_values['RDATE'] ) && !empty( $todo_values['RDATE'] )) {
      $dbiCal_rdate_DAO = dbiCal_rdate_DAO::singleton( $this->_db, $this->_log );
      foreach( $todo_values['RDATE'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rdate_DAO->insert( $todo_id, 'todo', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( self::$mtextProperties as $mProp ) {
      if( isset( $todo_values[$mProp] ) && !empty( $todo_values[$mProp] )) {
        foreach( $todo_values[$mProp] as $themProp ) {
          if( isset( $themProp['value'] ) && !empty( $themProp['value'] )) {
            $res = $dbiCal_mtext_DAO->insert( $todo_id, 'todo', $mProp, $themProp );
            if( PEAR::isError( $res ))
              return $res;
          }
        }
      }
    }
    if( isset( $todo_values['XPROP'] ) && !empty( $todo_values['XPROP'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      foreach( $todo_values['XPROP'] as $xprop ) {
        if( isset( $xprop[1]['value'] ) && !empty( $xprop[1]['value'] )) {
          $res = $dbiCal_xprop_DAO->insert( $todo_id, 'todo', $xprop[0], $xprop[1] );
          if( PEAR::isError( $res ))
            return $res;
        }
      }
    }
    if( isset( $todo_values['ALARM'] ) && !empty( $todo_values['ALARM'] )) {
      $dbiCal_alarm_DAO = dbiCal_alarm_DAO::singleton( $this->_db, $this->_log );
      foreach( $todo_values['ALARM'] as $alarmValues ) {
        $res = $dbiCal_alarm_DAO->insert( $todo_id, 'todo', $alarmValues );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $todo_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-02-22
   */
  function select( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM todo WHERE todo_owner_id = '.$this->_db->quote( $calendar_id, 'integer' ).' ORDER BY todo_id';
    $types = array( 'integer', 'integer', 'integer', 'text', 'timestamp', 'timestamp', 'boolean', 'date'
                  , 'timestamp', 'boolean', 'date', 'text','timestamp', 'text', 'text', 'text', 'text', 'text', 'text'
                  , 'text', 'text', 'integer', 'integer', 'timestamp', 'boolean', 'date', 'integer', 'timestamp', 'timestamp' );
    $types = array_merge( $types, array_fill( count( $types ), 8, 'integer' ));
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = $todo_cnts = array();
    while( $tablerow = $res->fetchRow()) {
      $row = $cnt = array();
      $cnt['todo_cnt_mtext']  = ( isset( $tablerow['todo_cnt_mtext'] )  && !empty( $tablerow['todo_cnt_mtext'] ))  ? $tablerow['todo_cnt_mtext']  : 0;
      $cnt['todo_cnt_attach'] = ( isset( $tablerow['todo_cnt_attach'] ) && !empty( $tablerow['todo_cnt_attach'] )) ? $tablerow['todo_cnt_attach'] : 0;
      $cnt['todo_cnt_exdate'] = ( isset( $tablerow['todo_cnt_exdate'] ) && !empty( $tablerow['todo_cnt_exdate'] )) ? $tablerow['todo_cnt_exdate'] : 0;
      $cnt['todo_cnt_exrule'] = ( isset( $tablerow['todo_cnt_exrule'] ) && !empty( $tablerow['todo_cnt_exrule'] )) ? $tablerow['todo_cnt_exrule'] : 0;
      $cnt['todo_cnt_rdate']  = ( isset( $tablerow['todo_cnt_rdate'] )  && !empty( $tablerow['todo_cnt_rdate'] ))  ? $tablerow['todo_cnt_rdate']  : 0;
      $cnt['todo_cnt_rrule']  = ( isset( $tablerow['todo_cnt_rrule'] )  && !empty( $tablerow['todo_cnt_rrule'] ))  ? $tablerow['todo_cnt_rrule']  : 0;
      $cnt['todo_cnt_xprop']  = ( isset( $tablerow['todo_cnt_xprop'] )  && !empty( $tablerow['todo_cnt_xprop'] ))  ? $tablerow['todo_cnt_xprop']  : 0;
      $cnt['todo_cnt_alarm']  = ( isset( $tablerow['todo_cnt_alarm'] )  && !empty( $tablerow['todo_cnt_alarm'] ))  ? $tablerow['todo_cnt_alarm']  : 0;
      $todo_cnts[$tablerow['todo_id']] = $cnt;
      if( isset( $tablerow['todo_ono'] ))
        $row['ono'] = $tablerow['todo_ono'];
      if( isset( $tablerow['todo_uid'] )            && !empty( $tablerow['todo_uid'] ))
        $row['UID']['value'] = $tablerow['todo_uid'];
      if( isset( $tablerow['todo_dtstamp'] )        && !empty( $tablerow['todo_dtstamp'] )) {
        $dt = str_replace( '-', '', $tablerow['todo_dtstamp'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTAMP']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['todo_startdatetime'] )  && !empty( $tablerow['todo_startdatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['todo_startdatetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTART']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['todo_startdatetimeutc'] )  && !empty( $tablerow['todo_startdatetimeutc'] ))
          $row['DTSTART']['value'] .= 'Z';
      }
      elseif( isset( $tablerow['todo_startdate'] ) && !empty( $tablerow['todo_startdate'] )) {
        $row['DTSTART']['value'] = str_replace( '-', '', $tablerow['todo_startdate'] );
      }
      if( isset( $tablerow['todo_duedatetime'] )    && !empty( $tablerow['todo_duedatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['todo_duedatetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['DUE']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['todo_duedatetimeutc'] )  && !empty( $tablerow['todo_duedatetimeutc'] ))
          $row['DUE']['value'] .= 'Z';
      }
      elseif( isset( $tablerow['todo_duedate'] )    && !empty( $tablerow['todo_duedate'] ))
        $row['DUE']['value'] = str_replace( '-', '', $tablerow['todo_duedate'] );
      if( isset( $tablerow['todo_duration'] )       && !empty( $tablerow['todo_duration'] )) {
        $durParts = explode( '|', $tablerow['todo_duration'] );
        $duration = array();
        foreach( $durParts as $durPart ) {
          list( $key, $value ) = explode( '=', $durPart, 2 );
          $duration[$key] = $value;
        }
        $row['DURATION']['value'] = iCalUtilityFunctions::_format_duration( $duration );
      }
      if( isset( $tablerow['todo_completed'] )      && !empty( $tablerow['todo_completed'] )) {
        $dt = str_replace( '-', '', $tablerow['todo_completed'] );
        $dt = str_replace( ':', '', $dt );
        $row['COMPLETED']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['todo_summary'] )        && !empty( $tablerow['todo_summary'] ))
        $row['SUMMARY']['value'] = $tablerow['todo_summary'];
      if( isset( $tablerow['todo_description'] )    && !empty( $tablerow['todo_description'] ))
        $row['DESCRIPTION']['value'] = $tablerow['todo_description'];
      if( isset( $tablerow['todo_geo'] )            && !empty( $tablerow['todo_geo'] )) {
        $geoParts = explode( '|', $tablerow['todo_geo'] );
        foreach( $durParts as $durPart ) {
          list( $key, $value ) = explode( '=', $durpart, 2 );
          $row['GEO']['value'][$key] = $value;
        }
      }
      if( isset( $tablerow['todo_location'] )       && !empty( $tablerow['todo_location'] ))
        $row['LOCATION']['value'] = $tablerow['todo_location'];
      if( isset( $tablerow['todo_organizer'] )      && !empty( $tablerow['todo_organizer'] ))
        $row['ORGANIZER']['value'] = $tablerow['todo_organizer'];
      if( isset( $tablerow['todo_class'] )          && !empty( $tablerow['todo_class'] ))
        $row['CLASS']['value'] = $tablerow['todo_class'];
      if( isset( $tablerow['todo_status'] )         && !empty( $tablerow['todo_status'] ))
        $row['STATUS']['value'] = $tablerow['todo_status'];
      if( isset( $tablerow['todo_url'] )            && !empty( $tablerow['todo_url'] ))
        $row['URL']['value'] = $tablerow['todo_url'];
      if( isset( $tablerow['todo_percent_complete'] ) && !empty( $tablerow['todo_percent_complete'] ))
        $row['PERCENT-COMPLETE']['value'] = $tablerow['todo_percent_complete'];
      if( isset( $tablerow['todo_priority'] )       && !empty( $tablerow['todo_priority'] ))
        $row['PRIORITY']['value'] = $tablerow['todo_priority'];
      if( isset( $tablerow['todo_recurrence_id_dt'] ) && !empty( $tablerow['todo_recurrence_id_dt'] )) {
        $dt = str_replace( '-', '', $tablerow['todo_recurrence_id_dt'] );
        $dt = str_replace( ':', '', $dt );
        $row['RECURRENCE-ID']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['todo_recurrence_idutc'] ) && !empty( $tablerow['todo_recurrence_idutc'] ))
          $row['RECURRENCE-ID']['value'] .= 'Z';
      }
      elseif( isset( $tablerow['todo_recurrence_id'] ) && !empty( $tablerow['todo_recurrence_id'] ))
        $row['RECURRENCE-ID']['value'] = str_replace( '-', '', $tablerow['todo_recurrence_id'] );
      if( isset( $tablerow['todo_sequence'] )       && ( !empty( $tablerow['todo_sequence'] ) || ( '0' == $tablerow['todo_sequence'] )))
        $row['SEQUENCE']['value'] = $tablerow['todo_sequence'];
      if( isset( $tablerow['todo_created'] )        && !empty( $tablerow['todo_created'] )) {
        $dt = str_replace( '-', '', $tablerow['todo_created'] );
        $dt = str_replace( ':', '', $dt );
        $row['CREATED']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['todo_last_modified'] )  && !empty( $tablerow['todo_last_modified'] )) {
        $dt = str_replace( '-', '', $tablerow['last_modified'] );
        $dt = str_replace( ':', '', $dt );
        $row['LAST-MODIFIED']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( $this->_log )
        $this->_log->log( var_export( $row, TRUE ), PEAR_LOG_DEBUG );
      if( !empty( $row ))
        $result[$tablerow['todo_id']] = $row;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' todos', PEAR_LOG_INFO );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $todo_id => $propVal ) {
      foreach( $propVal as $prop => $pValue ) {
        if( 'ono' === $prop )
          continue;
        $res = $dbiCal_parameter_DAO->select( $todo_id, 'todo', $prop );
        if( PEAR::isError( $res ))
          return $res;
        $result[$todo_id][$prop]['params'] = array();
        if( !empty( $res )) {
          foreach( $res as $key => $value )
            $result[$todo_id][$prop]['params'][$key] = $value;
        }
      }
    }
    $dbiCal_attach_DAO  = dbiCal_attach_DAO::singleton(  $this->_db, $this->_log );
    $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_exdate_DAO  = dbiCal_exdate_DAO::singleton(  $this->_db, $this->_log );
    $dbiCal_rdate_DAO   = dbiCal_rdate_DAO::singleton(   $this->_db, $this->_log );
    foreach( $result as $todo_id => $propval ) {
      if( !empty( $todo_cnts[$todo_id]['todo_cnt_attach'] )) {
        $res = $dbiCal_attach_DAO->select( $todo_id, 'todo' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res ))
          $result[$todo_id]['ATTACH'] = $res;
      }
      if( !empty( $todo_cnts[$todo_id]['todo_cnt_exrule'] ) ||
          !empty( $todo_cnts[$todo_id]['todo_cnt_rrule'] )) {
        $res = $dbiCal_rexrule_DAO->select( $todo_id, 'todo' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res )) {
          foreach( $res as $propValue )
            $result[$todo_id][$propValue['value']['rexrule_type']][] = $propValue;
        }
      }
      if( !empty( $todo_cnts[$todo_id]['todo_cnt_exdate'] )) {
        $res = $dbiCal_exdate_DAO->select( $todo_id, 'todo' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res ))
          $result[$todo_id]['EXDATE'] = $res;
      }
      if( empty( $todo_cnts[$todo_id]['todo_cnt_rdate'] ))
        continue;
      $res = $dbiCal_rdate_DAO->select( $todo_id, 'todo' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res ))
          $result[$todo_id]['RDATE'] = $res;
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $todo_id => $propval ) {
      if( empty( $todo_cnts[$todo_id]['todo_cnt_mtext'] ))
        continue;
      $res = $dbiCal_mtext_DAO->select( $todo_id, 'todo' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $mpropname => $mpropval ) {
          $result[$todo_id][$mpropname] = $mpropval;
        }
      }
    }
    $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $todo_id => $propval ) {
      if( empty( $todo_cnts[$todo_id]['todo_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->select( $todo_id, 'todo' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $propName => $propValue )
          $result[$todo_id][$propName] = $propValue;
      }
    }
    $dbiCal_alarm_DAO = dbiCal_alarm_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $todo_id => $propval ) {
      if( empty( $todo_cnts[$todo_id]['todo_cnt_alarm'] ))
        continue;
      $res = $dbiCal_alarm_DAO->select( $todo_id, 'todo' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res ))
        $result[$todo_id]['ALARM'] = $res;
    }
    return $result;
  }
}
/**
 * This class implements the journal table DAO
**/
class dbiCal_journal_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private static $singleProperties;
  /**
   * @access   private
   * @var      object
   */
  private static $mtextProperties;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-11-13
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$singleProperties = array( 'CLASS', 'CREATED', 'DTSTAMP', 'DTSTART', 'LAST-MODIFIED', 'ORGANIZER'
                                   , 'RECURRENCE-ID', 'SEQUENCE', 'STATUS', 'SUMMARY', 'UID', 'URL', );
    self::$mtextProperties  = array( // properties using mtext (DAO)
                               'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'RELATED-TO', 'REQUEST-STATUS' );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-11-20
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_journal_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-13
   */
  function delete( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere = 'FROM journal WHERE journal_owner_id = '.$this->_db->quote( $calendar_id, 'integer' );
    $sql  = "SELECT journal_id, journal_cnt_mtext, journal_cnt_attach, journal_cnt_exdate, journal_cnt_exrule, journal_cnt_rdate, journal_cnt_rrule, journal_cnt_xprop $fromWhere";
    $types = array_fill( 0, 8, 'integer' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      $cnt = array();
      $cnt['journal_cnt_mtext']  = ( isset( $tablerow['journal_cnt_mtext'] )  && !empty( $tablerow['journal_cnt_mtext'] ))  ? $tablerow['journal_cnt_mtext']  : 0;
      $cnt['journal_cnt_attach'] = ( isset( $tablerow['journal_cnt_attach'] ) && !empty( $tablerow['journal_cnt_attach'] )) ? $tablerow['journal_cnt_attach'] : 0;
      $cnt['journal_cnt_exdate'] = ( isset( $tablerow['journal_cnt_exdate'] ) && !empty( $tablerow['journal_cnt_exdate'] )) ? $tablerow['journal_cnt_exdate'] : 0;
      $cnt['journal_cnt_exrule'] = ( isset( $tablerow['journal_cnt_exrule'] ) && !empty( $tablerow['journal_cnt_exrule'] )) ? $tablerow['journal_cnt_exrule'] : 0;
      $cnt['journal_cnt_rdate']  = ( isset( $tablerow['journal_cnt_rdate'] )  && !empty( $tablerow['journal_cnt_rdate'] ))  ? $tablerow['journal_cnt_rdate']  : 0;
      $cnt['journal_cnt_rrule']  = ( isset( $tablerow['journal_cnt_rrule'] )  && !empty( $tablerow['journal_cnt_rrule'] ))  ? $tablerow['journal_cnt_rrule']  : 0;
      $cnt['journal_cnt_xprop']  = ( isset( $tablerow['journal_cnt_xprop'] )  && !empty( $tablerow['journal_cnt_xprop'] ))  ? $tablerow['journal_cnt_xprop']  : 0;
      $result[$tablerow['journal_id']] = $cnt;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $journal_id => $journal_cnts ) {
      $res = $dbiCal_parameter_DAO->delete( $journal_id, 'journal' );
      if( PEAR::isError( $res ))
        return $res;
      if( empty( $journal_cnts['journal_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->delete( $journal_id, 'journal' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $multiProps = array( 'mtext', 'attach', 'rexrule', 'exdate', 'rdate' ); // DAOs
    foreach( $multiProps as $mProp ) {
      foreach( $result as $journal_id => $journal_cnts ) {
        if(( 'mtext'   == $mProp ) && empty( $journal_cnts['journal_cnt_mtext'] ))
          continue;
        if(( 'attach'  == $mProp ) && empty( $journal_cnts['journal_cnt_attach'] ))
          continue;
        if(( 'rexrule' == $mProp ) && empty( $journal_cnts['journal_cnt_exrule'] ) && empty( $journal_cnts['journal_cnt_rrule'] ))
          continue;
        if(( 'exdate'  == $mProp ) && empty( $journal_cnts['journal_cnt_exdate'] ))
          continue;
        if(( 'rdate'   == $mProp ) && empty( $journal_cnts['journal_cnt_rdate'] ))
          continue;
        $prop_DAO_name = 'dbiCal_'.$mProp.'_DAO';
        $prop_DAO = $prop_DAO_name::singleton( $this->_db, $this->_log );
        $res = $prop_DAO->delete( $journal_id, 'journal' );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $calendar_id
   * @param    array  $journal_values
   * @param    int    $calendar_order
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $calendar_id, & $journal_values= array(), $calendar_order=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $cnts = array();
    foreach( $journal_values as $prop => $propVals ) {
      if( in_array( $prop, self::$singleProperties ) || ( 'ono' == $prop ))
        continue;
      if( in_array( $prop, self::$mtextProperties ))
        $cnts['mtext'] = isset( $cnts['mtext'] ) ? ( $cnts['mtext'] + count( $propVals )) : count( $propVals );
      else
        $cnts[$prop]   = isset( $cnts[$prop] ) ? ( $cnts[$prop] + count( $propVals )) : count( $propVals );
    }
    foreach( $journal_values as $prop => $propVals ) {
      if( in_array( $prop, self::$singleProperties ) || ( 'ono' == $prop ))
        continue;
      foreach( $propVals as $propv ) {
        if( in_array( $prop, self::$mtextProperties ))
          $cnts['mtext'] += 1;
        else
          $cnts[$prop]   += 1;
      }
    }
    $sql       = 'INSERT INTO journal (journal_owner_id';
    $values    = ') VALUES ('.$this->_db->quote( $calendar_id, 'integer' );
    if( isset( $calendar_order )) {
      $sql    .= ', journal_ono';
      $values .= ', '.$this->_db->quote( $calendar_order, 'integer' );
    }
    if( isset( $journal_values['UID']['value'] )) {
      $sql    .= ', journal_uid';
      $values .= ', '.$this->_db->quote( $journal_values['UID']['value'], 'text' );
    }
    if( isset( $journal_values['DTSTAMP']['value'] )) {
      $sql    .= ', journal_dtstamp';
      $value   = sprintf("%04d-%02d-%02d", $journal_values['DTSTAMP']['value']['year'], $journal_values['DTSTAMP']['value']['month'], $journal_values['DTSTAMP']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $journal_values['DTSTAMP']['value']['hour'], $journal_values['DTSTAMP']['value']['min'], $journal_values['DTSTAMP']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $journal_values['DTSTART']['value'] )) {
      $value = sprintf("%04d-%02d-%02d", $journal_values['DTSTART']['value']['year'], $journal_values['DTSTART']['value']['month'], $journal_values['DTSTART']['value']['day']);
      if( isset( $journal_values['DTSTART']['value']['hour'] )) {
        $sql    .= ', journal_startdatetime';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $journal_values['DTSTART']['value']['hour'], $journal_values['DTSTART']['value']['min'], $journal_values['DTSTART']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $journal_values['DTSTART']['value']['tz'] ) &&  in_array( $journal_values['DTSTART']['value']['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql    .= ', journal_startdatetimeutc';
          $values .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql    .= ', journal_startdate';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $journal_values['SUMMARY']['value'] )) {
      $sql    .= ', journal_summary';
      $values .= ', '.$this->_db->quote( $journal_values['SUMMARY']['value'], 'text' );
    }
    if( isset( $journal_values['ORGANIZER']['value'] )) {
      $sql    .= ', journal_organizer';
      $values .= ', '.$this->_db->quote( $journal_values['ORGANIZER']['value'], 'text' );
    }
    if( isset( $journal_values['CLASS']['value'] )) {
      $sql    .= ', journal_class';
      $values .= ', '.$this->_db->quote( $journal_values['CLASS']['value'], 'text' );
    }
    if( isset( $journal_values['URL']['value'] )) {
      $sql    .= ', journal_url';
      $values .= ', '.$this->_db->quote( $journal_values['URL']['value'], 'text' );
    }
    if( isset( $journal_values['RECURRENCE-ID']['value'] )) {
      $value = sprintf("%04d-%02d-%02d", $journal_values['RECURRENCE-ID']['value']['year'], $journal_values['RECURRENCE-ID']['value']['month'], $journal_values['RECURRENCE-ID']['value']['day']);
      if( isset( $journal_values['RECURRENCE-ID']['value']['hour'] )) {
        $sql    .= ', journal_recurrence_id_dt';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $journal_values['RECURRENCE-ID']['value']['hour'], $journal_values['RECURRENCE-ID']['value']['min'], $journal_values['RECURRENCE-ID']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $journal_values['RECURRENCE']['value']['tz'] ) &&  in_array( $journal_values['RECURRENCE']['value']['tz'], array( 'Z', 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql    .= ', journal_recurrence_idutc';
          $values .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql    .= ', journal_recurrence_id';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $journal_values['SEQUENCE']['value'] )) {
      $sql    .= ', journal_sequence';
      $values .= ', '.$this->_db->quote( $journal_values['SEQUENCE']['value'], 'integer' );
    }
    if( isset( $journal_values['STATUS']['value'] )) {
      $sql    .= ', journal_status';
      $values .= ', '.$this->_db->quote( $journal_values['STATUS']['value'], 'text' );
    }
    if( isset( $journal_values['CREATED']['value'] )) {
      $sql    .= ', journal_created';
      $value   = sprintf("%04d-%02d-%02d", $journal_values['CREATED']['value']['year'], $journal_values['CREATED']['value']['month'], $journal_values['CREATED']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $journal_values['CREATED']['value']['hour'], $journal_values['CREATED']['value']['min'], $journal_values['CREATED']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $journal_values['LAST-MODIFIED']['value'] )) {
      $sql    .= ', journal_last_modified';
      $value   = sprintf("%04d-%02d-%02d", $journal_values['LAST-MODIFIED']['value']['year'], $journal_values['LAST-MODIFIED']['value']['month'], $journal_values['LAST-MODIFIED']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $journal_values['LAST-MODIFIED']['value']['hour'], $journal_values['LAST-MODIFIED']['value']['min'], $journal_values['LAST-MODIFIED']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    foreach( $cnts as $prop => $cnt ) {
      if( !empty( $cnt )) {
        $sql    .= ', journal_cnt_'.strtolower( $prop );
        $values .= ', '.$this->_db->quote( $cnt, 'integer' );
      }
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $journal_id = $this->_db->lastInsertID( 'journal', 'journal_id' );
    if( PEAR::isError( $journal_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$journal_id->getUserInfo().PHP_EOL.$journal_id->getMessage(), PEAR_LOG_ALERT );
      return $journal_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'journal_id='.$journal_id, PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $journal_values as $prop => $pValue ) {
      if( !isset( $pValue['params'] ) || empty( $pValue['params'] ))
        continue;
      if( in_array( $prop, array( 'ATTACH', 'EXDATE', 'EXRULE', 'RDATE', 'RRULE' )))
        continue;
      if( !in_array( $prop, self::$singleProperties ))
        continue;
      if( in_array( $prop, self::$mtextProperties ) || ( 'X-' == substr( $prop, 0, 2 )))
        continue;
      foreach( $pValue['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $journal_id, 'journal', $prop, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $journal_values['EXRULE'] ) && !empty( $journal_values['EXRULE'] )) {
      $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
      foreach( $journal_values['EXRULE'] as $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rexrule_DAO->insert( $journal_id, 'journal', 'EXRULE', $theProp );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $journal_values['RRULE'] ) && !empty( $journal_values['RRULE'] )) {
      $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
      foreach( $journal_values['RRULE'] as $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rexrule_DAO->insert( $journal_id, 'journal', 'RRULE', $theProp );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $journal_values['EXDATE'] ) && !empty( $journal_values['EXDATE'] )) {
      $dbiCal_exdate_DAO = dbiCal_exdate_DAO::singleton( $this->_db, $this->_log );
      foreach( $journal_values['EXDATE'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_exdate_DAO->insert( $journal_id, 'journal', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $journal_values['RDATE'] ) && !empty( $journal_values['RDATE'] )) {
      $dbiCal_rdate_DAO = dbiCal_rdate_DAO::singleton( $this->_db, $this->_log );
      foreach( $journal_values['RDATE'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_rdate_DAO->insert( $journal_id, 'journal', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $journal_values['ATTACH'] ) && !empty( $journal_values['ATTACH'] )) {
      $dbiCal_attach_DAO = dbiCal_attach_DAO::singleton( $this->_db, $this->_log );
      foreach( $journal_values['ATTACH'] as $pix => $theProp ) {
        if( !isset( $theProp['value'] ))
           continue;
        $res = $dbiCal_attach_DAO->insert( $journal_id, 'journal', $theProp, $pix );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( self::$mtextProperties as $mProp ) {
      if( isset( $journal_values[$mProp] ) && !empty( $journal_values[$mProp] )) {
        foreach( $journal_values[$mProp] as $themProp ) {
          if( isset( $themProp['value'] ) && !empty( $themProp['value'] )) {
            $res = $dbiCal_mtext_DAO->insert( $journal_id, 'journal', $mProp, $themProp );
            if( PEAR::isError( $res ))
              return $res;
          }
        }
      }
    }
    if( isset( $journal_values['XPROP'] ) && !empty( $journal_values['XPROP'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      foreach( $journal_values['XPROP'] as $xprop ) {
        if( isset( $xprop[1]['value'] ) && !empty( $xprop[1]['value'] )) {
          $res = $dbiCal_xprop_DAO->insert( $journal_id, 'journal', $xprop[0], $xprop[1] );
          if( PEAR::isError( $res ))
            return $res;
        }
      }
    }
    return $journal_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM journal WHERE journal_owner_id = '.$this->_db->quote( $calendar_id, 'integer' ).' ORDER BY journal_id';
    $types = array( 'integer', 'integer', 'integer', 'text', 'timestamp', 'timestamp', 'boolean', 'date'
                  , 'text', 'text', 'text', 'text', 'timestamp', 'boolean', 'date', 'integer', 'text', 'timestamp', 'timestamp'  );
    $types = array_merge( $types, array_fill( count( $types ), 7, 'integer' ));
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = $journal_cnts = array();
    while( $tablerow = $res->fetchRow()) {
      $row = $cnt = array();
      $cnt['journal_cnt_mtext']  = ( isset( $tablerow['journal_cnt_mtext'] )  && !empty( $tablerow['journal_cnt_mtext'] ))  ? $tablerow['journal_cnt_mtext']  : 0;
      $cnt['journal_cnt_attach'] = ( isset( $tablerow['journal_cnt_attach'] ) && !empty( $tablerow['journal_cnt_attach'] )) ? $tablerow['journal_cnt_attach'] : 0;
      $cnt['journal_cnt_exdate'] = ( isset( $tablerow['journal_cnt_exdate'] ) && !empty( $tablerow['journal_cnt_exdate'] )) ? $tablerow['journal_cnt_exdate'] : 0;
      $cnt['journal_cnt_exrule'] = ( isset( $tablerow['journal_cnt_exrule'] ) && !empty( $tablerow['journal_cnt_exrule'] )) ? $tablerow['journal_cnt_exrule'] : 0;
      $cnt['journal_cnt_rdate']  = ( isset( $tablerow['journal_cnt_rdate'] )  && !empty( $tablerow['journal_cnt_rdate'] ))  ? $tablerow['journal_cnt_rdate']  : 0;
      $cnt['journal_cnt_rrule']  = ( isset( $tablerow['journal_cnt_rrule'] )  && !empty( $tablerow['journal_cnt_rrule'] ))  ? $tablerow['journal_cnt_rrule']  : 0;
      $cnt['journal_cnt_xprop']  = ( isset( $tablerow['journal_cnt_xprop'] )  && !empty( $tablerow['journal_cnt_xprop'] ))  ? $tablerow['journal_cnt_xprop']  : 0;
      $journal_cnts[$tablerow['journal_id']] = $cnt;
      if( isset( $tablerow['journal_ono'] ))
        $row['ono'] = $tablerow['journal_ono'];
      if( isset( $tablerow['journal_uid'] )            && !empty( $tablerow['journal_uid'] ))
        $row['UID']['value'] = $tablerow['journal_uid'];
      if( isset( $tablerow['journal_dtstamp'] )        && !empty( $tablerow['journal_dtstamp'] )) {
        $dt = str_replace( '-', '', $tablerow['journal_dtstamp'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTAMP']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['journal_startdatetime'] )  && !empty( $tablerow['journal_startdatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['journal_startdatetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTART']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['journal_startdatetimeutc'] ) && !empty( $tablerow['journal_startdatetimeutc'] ))
          $row['DTSTART']['value'] .= 'Z';
      }
      elseif( isset( $tablerow['journal_startdate'] )  && !empty( $tablerow['journal_startdate'] ))
        $row['DTSTART']['value'] = str_replace( '-', '', $tablerow['journal_startdate'] );
     if( isset( $tablerow['journal_summary'] )        && !empty( $tablerow['journal_summary'] ))
        $row['SUMMARY']['value'] = $tablerow['journal_summary'];
      if( isset( $tablerow['journal_description'] )    && !empty( $tablerow['journal_description'] ))
        $row['ORGANIZER']['value'] = $tablerow['journal_organizer'];
      if( isset( $tablerow['journal_class'] )          && !empty( $tablerow['journal_class'] ))
        $row['CLASS']['value'] = $tablerow['journal_class'];
      if( isset( $tablerow['journal_url'] )            && !empty( $tablerow['journal_url'] ))
        $row['URL']['value'] = $tablerow['journal_url'];
      if( isset( $tablerow['journal_recurrence_id_dt'] ) && !empty( $tablerow['journal_recurrence_id_dt'] )) {
        $dt = str_replace( '-', '', $tablerow['journal_recurrence_id_dt'] );
        $dt = str_replace( ':', '', $dt );
        $row['RECURRENCE-ID']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['journal_recurrence_idutc'] )  && !empty( $tablerow['journal_recurrence_idutc'] ))
          $row['RECURRENCE-ID']['value'] .= 'Z';
      }
      elseif( isset( $tablerow['journal_recurrence_id'] ) && !empty( $tablerow['journal_recurrence_id'] ))
        $row['RECURRENCE-ID']['value'] = str_replace( '-', '', $tablerow['journal_recurrence_id'] );
      if( isset( $tablerow['journal_sequence'] )       && ( !empty( $tablerow['journal_sequence'] ) || ( '0' == $tablerow['journal_sequence'])))
        $row['SEQUENCE']['value'] = $tablerow['journal_sequence'];
      if( isset( $tablerow['journal_status'] )         && !empty( $tablerow['journal_status'] ))
        $row['STATUS']['value'] = $tablerow['journal_status'];
      if( isset( $tablerow['journal_created'] )        && !empty( $tablerow['journal_created'] )) {
        $dt = str_replace( '-', '', $tablerow['journal_created'] );
        $dt = str_replace( ':', '', $dt );
        $row['CREATED']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['journal_last_modified'] )  && !empty( $tablerow['journal_last_modified'] )) {
        $dt = str_replace( '-', '', $tablerow['journal_last_modified'] );
        $dt = str_replace( ':', '', $dt );
        $row['LAST-MODIFIED']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( $this->_log )
        $this->_log->log( var_export( $row, TRUE ), PEAR_LOG_DEBUG );
      if( !empty( $row ))
        $result[$tablerow['journal_id']] = $row;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' journals', PEAR_LOG_INFO );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $journal_id => $propVal ) {
      foreach( $propVal as $prop => $pValue ) {
        if( 'ono' == $prop )
          continue;
        $res = $dbiCal_parameter_DAO->select( $journal_id, 'journal', $prop );
        if( PEAR::isError( $res ))
          return $res;
        $result[$journal_id][$prop]['params'] = array();
        if( !empty( $res )) {
          foreach( $res as $key => $value )
            $result[$journal_id][$prop]['params'][$key] = $value;
        }
      }
    }
    $dbiCal_attach_DAO  = dbiCal_attach_DAO::singleton(  $this->_db, $this->_log );
    $dbiCal_rexrule_DAO = dbiCal_rexrule_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_exdate_DAO  = dbiCal_exdate_DAO::singleton(  $this->_db, $this->_log );
    $dbiCal_rdate_DAO   = dbiCal_rdate_DAO::singleton(   $this->_db, $this->_log );
    foreach( $result as $journal_id => $propval ) {
      if( !empty( $journal_cnts[$journal_id]['journal_cnt_attach'] )) {
        $res = $dbiCal_attach_DAO->select( $journal_id, 'journal' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res ))
          $result[$journal_id]['ATTACH'] = $res;
      }
      if( !empty( $journal_cnts[$journal_id]['journal_cnt_exrule'] ) ||
          !empty( $journal_cnts[$journal_id]['journal_cnt_rrule'] )) {
        $res = $dbiCal_rexrule_DAO->select( $journal_id, 'journal' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res )) {
          foreach( $res as $propValue )
            $result[$journal_id][$propValue['rexrule_type']][] = $propValue;
        }
      }
      if( !empty( $journal_cnts[$journal_id]['journal_cnt_exdate'] )) {
        $res = $dbiCal_exdate_DAO->select( $journal_id, 'journal' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res ))
          $result[$journal_id]['EXDATE'] = $res;
      }
      if( empty( $journal_cnts[$journal_id]['journal_cnt_rdate'] ))
        continue;
      $res = $dbiCal_rdate_DAO->select( $journal_id, 'journal' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res ))
        $result[$journal_id]['RDATE'] = $res;
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $journal_id => $propval ) {
      if( empty( $journal_cnts[$journal_id]['journal_cnt_mtext'] ))
        continue;
      $res = $dbiCal_mtext_DAO->select( $journal_id, 'journal' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as  $mpropname => $mprops )
          $result[$journal_id][$mpropname] = $mprops;
      }
    }
    $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $journal_id => $propval ) {
      if( empty( $journal_cnts[$journal_id]['journal_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->select( $journal_id, 'journal' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $propName => $propValue )
          $result[$journal_id][$propName] = $propValue;
      }
    }
    return $result;
  }
}
/**
 * This class implements the freebusy table DAO
**/
class dbiCal_freebusy_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private static $singleProperties;
  /**
   * @access   private
   * @var      object
   */
  private static $mtextProperties;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-30
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$singleProperties = array( 'CONTACT', 'DTSTART', 'DTEND', 'DURATION', 'DTSTAMP', 'DTSTART', 'ORGANIZER', 'UID', 'URL', );
    self::$mtextProperties  = array( // properties using mtext (DAO)
                                     'ATTENDEE', 'COMMENT', 'REQUEST-STATUS' );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_freebusy_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-20
   */
  function delete( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere = 'FROM freebusy WHERE freebusy_owner_id = '.$this->_db->quote( $calendar_id, 'integer' );
    $sql  = "SELECT freebusy_id, freebusy_cnt_mtext, freebusy_cnt_freebusy, freebusy_cnt_xprop $fromWhere";
    $res  = & $this->_db->query( $sql, array_fill( 0, 4, 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      $cnt = array();
      $cnt['freebusy_cnt_mtext']    = ( isset( $tablerow['freebusy_cnt_mtext'] )    && !empty( $tablerow['freebusy_cnt_mtext'] ))    ? $tablerow['freebusy_cnt_mtext']    : 0;
      $cnt['freebusy_cnt_freebusy'] = ( isset( $tablerow['freebusy_cnt_freebusy'] ) && !empty( $tablerow['freebusy_cnt_freebusy'] )) ? $tablerow['freebusy_cnt_freebusy'] : 0;
      $cnt['freebusy_cnt_xprop']    = ( isset( $tablerow['freebusy_cnt_xprop'] )    && !empty( $tablerow['freebusy_cnt_xprop'] ))    ? $tablerow['freebusy_cnt_xprop']    : 0;
      $result[$tablerow['freebusy_id']] = $cnt;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_pfreebusy_DAO = dbiCal_pfreebusy_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_xprop_DAO     = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $freebusy_id => $freebusy_cnts ) {
      $res = $dbiCal_parameter_DAO->delete( $freebusy_id, 'freebusy' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $freebusy_cnts['freebusy_cnt_freebusy'] )) {
        $res = $dbiCal_pfreebusy_DAO->delete( $freebusy_id );
        if( PEAR::isError( $res ))
          return $res;
      }
      if( !empty( $freebusy_cnts['freebusy_cnt_xprop'] )) {
        $res = $dbiCal_xprop_DAO->delete( $freebusy_id, 'freebusy' );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $freebusy_id => $freebusy_cnts ) {
      if( empty( $freebusy_cnts['freebusy_cnt_mtext'] ))
        continue;
      $res = $dbiCal_mtext_DAO->delete( $freebusy_id, 'freebusy' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $calendar_id
   * @param    array  $freebusy_values
   * @param    int    $calendar_order
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $calendar_id, & $freebusy_values= array(), $calendar_order=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $cnts = array();
    foreach( $freebusy_values as $prop => $propVals ) {
      if( in_array( $prop, self::$singleProperties ) || ( 'ono' == $prop ))
        continue;
      if( in_array( $prop, self::$mtextProperties ))
        $cnts['mtext'] = isset( $cnts['mtext'] ) ? ( $cnts['mtext'] + count( $propVals )) : count( $propVals );
      else
        $cnts[$prop]= isset( $cnts[$prop] ) ? ( $cnts[$prop] + count( $propVals )) : count( $propVals );
    }
    $sql       = 'INSERT INTO freebusy (freebusy_owner_id';
    $values    = ') VALUES ('.$this->_db->quote( $calendar_id, 'integer' );
    if( isset( $calendar_order )) {
      $sql    .= ', freebusy_ono';
      $values .= ', '.$this->_db->quote( $calendar_order, 'integer' );
    }
    if( isset( $freebusy_values['UID']['value'] )) {
      $sql    .= ', freebusy_uid';
      $values .= ', '.$this->_db->quote( $freebusy_values['UID']['value'], 'text' );
    }
    if( isset( $freebusy_values['DTSTAMP']['value'] )) {
      $sql    .= ', freebusy_dtstamp';
      $value   = sprintf("%04d-%02d-%02d", $freebusy_values['DTSTAMP']['value']['year'], $freebusy_values['DTSTAMP']['value']['month'], $freebusy_values['DTSTAMP']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $freebusy_values['DTSTAMP']['value']['hour'], $freebusy_values['DTSTAMP']['value']['min'], $freebusy_values['DTSTAMP']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $freebusy_values['DTSTART']['value'] )) {
      $sql    .= ', freebusy_startdatetime';
      $value = sprintf("%04d-%02d-%02d", $freebusy_values['DTSTART']['value']['year'], $freebusy_values['DTSTART']['value']['month'], $freebusy_values['DTSTART']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $freebusy_values['DTSTART']['value']['hour'], $freebusy_values['DTSTART']['value']['min'], $freebusy_values['DTSTART']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $freebusy_values['DTEND']['value'] )) {
      $sql    .= ', freebusy_enddatetime';
      $value = sprintf("%04d-%02d-%02d", $freebusy_values['DTEND']['value']['year'], $freebusy_values['DTEND']['value']['month'], $freebusy_values['DTEND']['value']['day']);
      $value  .= ' '.sprintf("%02d:%02d:%02d", $freebusy_values['DTEND']['value']['hour'], $freebusy_values['DTEND']['value']['min'], $freebusy_values['DTEND']['value']['sec']);
      $values .= ', '.$this->_db->quote( $value, 'timestamp' );
    }
    if( isset( $freebusy_values['DURATION']['value'] )) {
      $sql    .= ', freebusy_duration';
      $value = '';
      foreach( $freebusy_values['DURATION']['value'] as $k => $v )
        $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
      $values .= ', '.$this->_db->quote( $value, 'text' );
    }
    if( isset( $freebusy_values['ORGANIZER']['value'] )) {
      $sql    .= ', freebusy_organizer';
      $values .= ', '.$this->_db->quote( $freebusy_values['ORGANIZER']['value'], 'text' );
    }
    if( isset( $freebusy_values['CONTACT']['value'] )) {
      $sql    .= ', freebusy_contact';
      $values .= ', '.$this->_db->quote( $freebusy_values['CONTACT']['value'], 'text' );
    }
    if( isset( $freebusy_values['URL']['value'] )) {
      $sql    .= ', freebusy_url';
      $values .= ', '.$this->_db->quote( $freebusy_values['URL']['value'], 'text' );
    }
    foreach( $cnts as $prop => $cnt ) {
      if( !empty( $cnt )) {
        $sql    .= ', freebusy_cnt_'.strtolower( str_replace( '-', '_', $prop ));
        $values .= ', '.$this->_db->quote( $cnt, 'integer' );
      }
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $freebusy_id = $this->_db->lastInsertID( 'freebusy', 'freebusy_id' );
    if( PEAR::isError( $freebusy_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$freebusy_id->getUserInfo().PHP_EOL.$freebusy_id->getMessage(), PEAR_LOG_ALERT );
      return $freebusy_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'freebusy_id='.$freebusy_id, PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $freebusy_values as $prop => $pValue ) {
      if( !isset( $pValue['params'] ) || empty( $pValue['params'] ))
        continue;
      if( !in_array( $prop, self::$singleProperties ))
        continue;
      if( in_array( $prop, self::$mtextProperties ) || ( 'X-' == substr( $prop, 0, 2 )))
        continue;
      foreach( $pValue['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $freebusy_id, 'freebusy', $prop, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    if( isset( $freebusy_values['FREEBUSY'] ) && !empty( $freebusy_values['FREEBUSY'] )) {
      $dbiCal_pfreebusy_DAO = dbiCal_pfreebusy_DAO::singleton( $this->_db, $this->_log );
      foreach( $freebusy_values['FREEBUSY'] as $freebusy_ix => $pfreebusy ) {
        $res = $dbiCal_pfreebusy_DAO->insert( $freebusy_id, $freebusy_ix, $pfreebusy );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( self::$mtextProperties as $mProp ) {
      if( isset( $freebusy_values[$mProp] ) && !empty( $freebusy_values[$mProp] )) {
        foreach( $freebusy_values[$mProp] as $themProp ) {
          if( isset( $themProp ) && !empty( $themProp['value'] )) {
            $res = $dbiCal_mtext_DAO->insert( $freebusy_id, 'freebusy', $mProp, $themProp );
            if( PEAR::isError( $res ))
              return $res;
          }
        }
      }
    }
    if( isset( $freebusy_values['XPROP'] ) && !empty( $freebusy_values['XPROP'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      foreach( $freebusy_values['XPROP'] as $xprop ) {
        if( isset( $xprop[1]['value'] ) && !empty( $xprop[1]['value'] )) {
          $res = $dbiCal_xprop_DAO->insert( $freebusy_id, 'freebusy', $xprop[0], $xprop[1] );
          if( PEAR::isError( $res ))
            return $res;
        }
      }
    }
    return $freebusy_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $calendar_id
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $calendar_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM freebusy WHERE freebusy_owner_id = '.$this->_db->quote( $calendar_id, 'integer' ).' ORDER BY freebusy_id';
    $types = array( 'integer', 'integer', 'integer', 'text', 'timestamp'
                  , 'text', 'timestamp', 'timestamp', 'text', 'text', 'text' );
    $types = array_merge( $types, array_fill( count( $types ), 3, 'integer' ));
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = $freebusy_cnts = array();
    while( $tablerow = $res->fetchRow()) {
      $row = $cnt = array();
      $cnt['freebusy_cnt_mtext']    = ( isset( $tablerow['freebusy_cnt_mtext'] )    && !empty( $tablerow['freebusy_cnt_mtext'] ))    ? $tablerow['freebusy_cnt_mtext']    : 0;
      $cnt['freebusy_cnt_freebusy'] = ( isset( $tablerow['freebusy_cnt_freebusy'] ) && !empty( $tablerow['freebusy_cnt_freebusy'] )) ? $tablerow['freebusy_cnt_freebusy'] : 0;
      $cnt['freebusy_cnt_xprop']    = ( isset( $tablerow['freebusy_cnt_xprop'] )    && !empty( $tablerow['freebusy_cnt_xprop'] ))    ? $tablerow['freebusy_id']           : 0;
      $freebusy_cnts[$tablerow['freebusy_id']] = $cnt;
      if( isset( $tablerow['freebusy_ono'] ))
        $row['ono'] = $tablerow['freebusy_ono'];
      if( isset( $tablerow['freebusy_uid'] )            && !empty( $tablerow['freebusy_uid'] ))
        $row['UID']['value'] = $tablerow['freebusy_uid'];
      if( isset( $tablerow['freebusy_dtstamp'] )        && !empty( $tablerow['freebusy_dtstamp'] )) {
        $dt = str_replace( '-', '', $tablerow['freebusy_dtstamp'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTAMP']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['freebusy_contact'] )        && !empty( $tablerow['freebusy_contact'] ))
        $row['CONTACT']['value'] = $tablerow['freebusy_contact'];
      if( isset( $tablerow['freebusy_startdatetime'] )  && !empty( $tablerow['freebusy_startdatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['freebusy_startdatetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTSTART']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['freebusy_enddatetime'] )    && !empty( $tablerow['freebusy_enddatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['freebusy_enddatetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['DTEND']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['freebusy_duration'] )       && !empty( $tablerow['freebusy_duration'] )) {
        $durParts = explode( '|', $tablerow['freebusy_duration'] );
        $duration = array();
        foreach( $durParts as $durPart ) {
          list( $key, $value ) = explode( '=', $durPart, 2 );
          $duration[$key] = $value;
        }
        $row['DURATION']['value'] = iCalUtilityFunctions::_format_duration( $duration );
      }
      if( isset( $tablerow['freebusy_organizer'] )      && !empty( $tablerow['freebusy_organizer'] ))
        $row['ORGANIZER']['value'] = $tablerow['freebusy_organizer'];
      if( isset( $tablerow['freebusy_url'] )            && !empty( $tablerow['freebusy_url'] ))
        $row['URL']['value'] = $tablerow['freebusy_url'];
      if( $this->_log )
        $this->_log->log( var_export( $row, TRUE ), PEAR_LOG_DEBUG );
      if( !empty( $row ))
        $result[$tablerow['freebusy_id']] = $row;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' freebusys', PEAR_LOG_INFO );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $freebusy_id => $propVal ) {
      foreach( $propVal as $prop => $pValue ) {
        if( 'ono' == $prop )
          continue;
        $res = $dbiCal_parameter_DAO->select( $freebusy_id, 'freebusy', $prop );
        if( PEAR::isError( $res ))
          return $res;
        $result[$freebusy_id][$prop]['params'] = array();
        if( !empty( $res )) {
          foreach( $res as $key => $value )
            $result[$freebusy_id][$prop]['params'][$key] = $value;
        }
      }
    }
    $dbiCal_pfreebusy_DAO = dbiCal_pfreebusy_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $freebusy_id => $propval ) {
      if( empty( $freebusy_cnts[$freebusy_id]['freebusy_cnt_freebusy'] ))
        continue;
      $res = $dbiCal_pfreebusy_DAO->select( $freebusy_id );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res ))
        $result[$freebusy_id]['FREEBUSY'] = $res;
    }
    $dbiCal_mtext_DAO = dbiCal_mtext_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $freebusy_id => $propval ) {
      if( empty( $freebusy_cnts[$freebusy_id]['freebusy_cnt_mtext'] ))
        continue;
      $res = $dbiCal_mtext_DAO->select( $freebusy_id, 'freebusy' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $mpropname => $mpropval )
          $result[$freebusy_id][$mpropname] = $mpropval;
      }
    }
    $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $freebusy_id => $propval ) {
      if( empty( $freebusy_cnts[$freebusy_id]['freebusy_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->select( $freebusy_id, 'freebusy' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $propName => $propValue )
          $result[$freebusy_id][$propName] = $propValue;
      }
    }
    return $result;
  }
}
/**
 * This class implements the alarm table DAO
**/
class dbiCal_alarm_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private static $singleProperties;
  /**
   * @access   private
   * @var      object
   */
  private static $multiProperties;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-11-13
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$singleProperties = array( 'ACTION', 'DESCRIPTION', 'DURATION', 'REPEAT', 'SUMMARY', 'TRIGGER' );
    self::$multiProperties  = array( // property name => db table name
                               'ATTACH'         => 'attach'
                             , 'ATTENDEE'       => 'mtext' );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_alarm_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownerType
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-20
   */
  function delete( $owner_id, $ownerType ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM alarm WHERE alarm_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $fromWhere .= ' AND alarm_ownertype = '.$this->_db->quote( $ownerType, 'text' );
    $sql  = "SELECT alarm_id, alarm_cnt_attach, alarm_cnt_mtext, alarm_cnt_xprop $fromWhere";
    $res  = & $this->_db->query( $sql, array( 'integer', 'integer', 'integer', 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      $cnt = array();
      $cnt['alarm_cnt_attach'] = ( isset( $tablerow['alarm_cnt_attach'] ) && !empty( $tablerow['alarm_cnt_attach'] )) ? $tablerow['alarm_cnt_attach'] : 0;
      $cnt['alarm_cnt_mtext']  = ( isset( $tablerow['alarm_cnt_mtext'] )  && !empty( $tablerow['alarm_cnt_mtext'] ))  ? $tablerow['alarm_cnt_mtext']  : 0;
      $cnt['alarm_cnt_xprop']  = ( isset( $tablerow['alarm_cnt_xprop'] )  && !empty( $tablerow['alarm_cnt_xprop'] ))  ? $tablerow['alarm_cnt_xprop']  : 0;
      $result[$tablerow['alarm_id']] = $cnt;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $alarm_id => $alarm_cnts) {
      $res = $dbiCal_parameter_DAO->delete( $alarm_id, 'alarm' );
      if( PEAR::isError( $res ))
        return $res;
      if( empty( $alarm_cnts['alarm_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->delete( $alarm_id, 'alarm' );
      if( PEAR::isError( $res ))
        return $res;
    }
    foreach( self::$multiProperties as $mProp => $mPropdb ) {
      $prop_DAO_name = 'dbiCal_'.$mPropdb.'_DAO';
      $prop_DAO = $prop_DAO_name::singleton( $this->_db, $this->_log );
      foreach( $result as $alarm_id => $alarm_cnts ) {
        if(( 'ATTACH'   == $mProp ) && empty( $alarm_cnts['alarm_cnt_attach'] ))
          continue;
        if(( 'ATTENDEE' == $mProp ) && empty( $alarm_cnts['alarm_cnt_mtext'] ))
          continue;
        $res = $prop_DAO->delete( $alarm_id, 'alarm' );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownerType
   * @param    array  $alarm_values
   * @param    int    $calendar_order
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $ownerType, & $alarm_values= array(), $calendar_order=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $cnts = array();
    foreach( $alarm_values as $prop => $propVals ) {
      if( in_array( $prop, self::$singleProperties ))
        continue;
      if( 'ATTENDEE' == $prop )
        $cnts['mtext'] = isset( $cnts['mtext'] ) ? ( $cnts['mtext'] + count( $propVals )) : count( $propVals );
      else
        $cnts[$prop]= isset( $cnts[$prop] ) ? ( $cnts[$prop] + count( $propVals )) : count( $propVals );
    }
    $sql       = 'INSERT INTO alarm (alarm_owner_id, alarm_ownertype';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $ownerType, 'text' );
    if( isset( $calendar_order )) {
      $sql    .= ', alarm_ono';
      $values .= ', '.$this->_db->quote( $calendar_order, 'integer' );
    }
    if( isset( $alarm_values['ACTION']['value'] )) {
      $sql    .= ', alarm_action';
      $values .= ', '.$this->_db->quote( $alarm_values['ACTION']['value'], 'text' );
    }
    if( isset( $alarm_values['DESCRIPTION']['value'] )) {
      $sql    .= ', alarm_description';
      $values .= ', '.$this->_db->quote( $alarm_values['DESCRIPTION']['value'], 'text' );
    }
    if( isset( $alarm_values['DURATION']['value'] )) {
      $sql    .= ', alarm_duration';
      $value   = '';
      foreach( $alarm_values['DURATION']['value'] as $k => $v )
        $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
      $values  .= ', '.$this->_db->quote( $value, 'text' );
    }
    if( isset( $alarm_values['REPEAT']['value'] )) {
      $sql    .= ', alarm_repeat';
      $values .= ', '.$this->_db->quote( $alarm_values['REPEAT']['value'], 'text' );
    }
    if( isset( $alarm_values['SUMMARY']['value'] )) {
      $sql    .= ', alarm_summary';
      $values .= ', '.$this->_db->quote( $alarm_values['SUMMARY']['value'], 'text' );
    }
    if( isset( $alarm_values['TRIGGER']['value'] )) {
      if( isset( $alarm_values['TRIGGER']['value']['year'] )) {
        $sql    .= ', alarm_trigger_datetime';
        $value   = sprintf("%04d-%02d-%02d", $alarm_values['TRIGGER']['value']['year'], $alarm_values['TRIGGER']['value']['month'], $alarm_values['TRIGGER']['value']['day']);
        $value  .= ' '.sprintf("%02d:%02d:%02d", $alarm_values['TRIGGER']['value']['hour'], $alarm_values['TRIGGER']['value']['min'], $alarm_values['TRIGGER']['value']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
      }
      else {
        $sql    .= ', alarm_trigger';
        $value = '';
        foreach( $alarm_values['TRIGGER']['value'] as $k => $v ) {
          if( in_array( strtolower( $k ), array( 'relatedstart', 'before' )) && is_bool( $v ))
            $v = ( $v ) ? 1 : 0;
          $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
        }
        $values .= ', '.$this->_db->quote( $value, 'text' );
      }
    }
    if( !empty( $cnts['ATTACH'] )) {
      $sql    .= ', alarm_cnt_attach';
      $values .= ', '.$this->_db->quote( $cnts['ATTACH'], 'integer' );
    }
    if( !empty( $cnts['mtext'] )) {
      $sql    .= ', alarm_cnt_mtext';
      $values .= ', '.$this->_db->quote( $cnts['mtext'], 'integer' );
    }
    if( !empty( $cnts['XPROP'] )) {
      $sql    .= ', alarm_cnt_xprop';
      $values .= ', '.$this->_db->quote( $cnts['XPROP'], 'integer' );
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $alarm_id = $this->_db->lastInsertID( 'alarm', 'alarm_id' );
    if( PEAR::isError( $alarm_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$alarm_id->getUserInfo().PHP_EOL.$alarm_id->getMessage(), PEAR_LOG_ALERT );
      return $alarm_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'alarm_id='.$alarm_id, PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $alarm_values as $prop => $pValue ) {
      if( !isset( $pValue['params'] ) || empty( $pValue['params'] ))
        continue;
      if( !in_array( $prop, self::$singleProperties ))
        continue;
      if( in_array( $prop, array_keys( self::$multiProperties )) || ( 'X-' == substr( $prop, 0, 2 )))
        continue;
      foreach( $pValue['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $alarm_id, 'alarm', $prop, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    foreach( self::$multiProperties as $mProp => $mPropdb ) {
      if( isset( $alarm_values[$mProp] ) && !empty( $alarm_values[$mProp] )) {
        $prop_DAO_name = 'dbiCal_'.$mPropdb.'_DAO';
        $prop_DAO = $prop_DAO_name::singleton( $this->_db, $this->_log );
        foreach( $alarm_values[$mProp] as $themProp ) {
          if( isset( $themProp['value'] ) && !empty( $themProp['value'] )) {
            if( 'ATTENDEE' == $mProp )
              $res = $prop_DAO->insert( $alarm_id, 'alarm', $mProp, $themProp );
            else
              $res = $prop_DAO->insert( $alarm_id, 'alarm', $themProp );
            if( PEAR::isError( $res ))
              return $res;
          }
        }
      }
    }
    if( isset( $alarm_values['XPROP'] ) && !empty( $alarm_values['XPROP'] )) {
      $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
      foreach( $alarm_values['XPROP'] as $xprop ) {
        if( isset( $xprop[1]['value'] ) && !empty( $xprop[1]['value'] )) {
          $res = $dbiCal_xprop_DAO->insert( $alarm_id, 'alarm', $xprop[0], $xprop[1] );
          if( PEAR::isError( $res ))
            return $res;
        }
      }
    }
    return $alarm_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownerType
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-29
   */
  function select( $owner_id, $ownerType ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM alarm WHERE alarm_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' AND alarm_ownerType = '.$this->_db->quote( $ownerType, 'text' ).' ORDER BY alarm_id';
    $types = array( 'integer', 'integer', 'text', 'integer', 'text',  'text', 'text', 'integer', 'text', 'timestamp', 'text', 'integer', 'integer', 'integer' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = $alarm_cnts = array();
    while( $tablerow = $res->fetchRow()) {
      $row = $cnt = array();
      $cnt['alarm_cnt_attach'] = ( isset( $tablerow['alarm_cnt_attach'] ) && !empty( $tablerow['alarm_cnt_attach'] )) ? $tablerow['alarm_cnt_attach'] : 0;
      $cnt['alarm_cnt_mtext']  = ( isset( $tablerow['alarm_cnt_mtext'] )  && !empty( $tablerow['alarm_cnt_mtext'] ))  ? $tablerow['alarm_cnt_mtext']  : 0;
      $cnt['alarm_cnt_xprop']  = ( isset( $tablerow['alarm_cnt_xprop'] )  && !empty( $tablerow['alarm_cnt_xprop'] ))  ? $tablerow['alarm_cnt_xprop']  : 0;
      $alarm_cnts[$tablerow['alarm_id']] = $cnt;
      if( isset( $tablerow['alarm_ono'] ))
        $row['ono'] = $tablerow['alarm_ono'];
      if( isset( $tablerow['alarm_action'] )         && !empty( $tablerow['alarm_action'] ))
        $row['ACTION']['value'] = $tablerow['alarm_action'];
      if( isset( $tablerow['alarm_description'] )    && !empty( $tablerow['alarm_description'] ))
        $row['DESCRIPTION']['value'] = $tablerow['alarm_description'];
      if( isset( $tablerow['alarm_duration'] )       && !empty( $tablerow['alarm_duration'] )) {
        $durParts = explode( '|', $tablerow['alarm_duration'] );
        $duration = array();
        foreach( $durParts as $durPart ) {
          list( $key, $value ) = explode( '=', $durPart, 2 );
          $duration[$key] = $value;
        }
        $row['DURATION']['value'] = iCalUtilityFunctions::_format_duration( $duration );
      }
      if( isset( $tablerow['alarm_repeat'] )         && !empty( $tablerow['alarm_repeat'] ))
        $row['REPEAT']['value'] = $tablerow['alarm_repeat'];
      if( isset( $tablerow['alarm_summary'] )        && !empty( $tablerow['alarm_summary'] ))
        $row['SUMMARY']['value'] = $tablerow['alarm_summary'];
      if( isset( $tablerow['alarm_trigger_datetime'] )  && !empty( $tablerow['alarm_trigger_datetime'] )) {
        $dt = str_replace( '-', '', $tablerow['alarm_trigger_datetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['TRIGGER']['value'] = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      elseif( isset( $tablerow['alarm_trigger'] )    &&  !empty( $tablerow['alarm_trigger'] )) {
        $durParts = explode( '|', $tablerow['alarm_trigger'] );
        $duration = array();
        foreach( $durParts as $durPart ) {
          list( $key, $value ) = explode( '=', $durPart, 2 );
          $duration[$key] = $value;
        }
        if( isset( $duration['relatedStart'] ) && ( 1 != $duration['relatedStart'] ))
          $row['TRIGGER']['params']['RELATED'] = 'END';
        $row['TRIGGER']['value']  = ( isset( $duration['before'] ) && ( 1 == $duration['before'] )) ? '-' : '';
        $row['TRIGGER']['value'] .= iCalUtilityFunctions::_format_duration( $duration );
      }
      if( $this->_log )
        $this->_log->log( var_export( $row, TRUE ), PEAR_LOG_DEBUG );
      if( !empty( $row ))
        $result[$tablerow['alarm_id']] = $row;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' alarms', PEAR_LOG_INFO );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $alarm_id => $propVal ) {
      foreach( $propVal as $prop => $pValue ) {
        if( 'ono' == $prop )
          continue;
        $res = $dbiCal_parameter_DAO->select( $alarm_id, 'alarm', $prop );
        if( PEAR::isError( $res ))
          return $res;
        if( !isset( $result[$alarm_id][$prop]['params'] ))
          $result[$alarm_id][$prop]['params'] = array();
        if( !empty( $res )) {
          foreach( $res as $key => $value )
            $result[$alarm_id][$prop]['params'][$key] = $value;
        }
      }
    }
    $dbiCal_attach_DAO = dbiCal_attach_DAO::singleton( $this->_db, $this->_log );
    $dbiCal_mtext_DAO  = dbiCal_mtext_DAO::singleton(  $this->_db, $this->_log );
    foreach( $result as $alarm_id => $propval ) {
      if( !empty( $alarm_cnts[$alarm_id]['alarm_cnt_attach'] )) {
        $res = $dbiCal_attach_DAO->select( $alarm_id, 'alarm' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res ))
          $result[$alarm_id]['ATTACH'] = $res;
      }
      if( !empty( $alarm_cnts[$alarm_id]['alarm_cnt_mtext'] )) {
        $res = $dbiCal_mtext_DAO->select( $alarm_id, 'alarm' );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res )) {
          foreach( $res as $mpropname => $mpropval )
            $result[$alarm_id][$mpropname] = $mpropval;
        }
      }
    }
    $dbiCal_xprop_DAO = dbiCal_xprop_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $alarm_id => $propval ) {
      if( empty( $alarm_cnts[$alarm_id]['alarm_cnt_xprop'] ))
        continue;
      $res = $dbiCal_xprop_DAO->select( $alarm_id, 'alarm' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $propName => $propValue )
            $result[$alarm_id][$propName] = $propValue;
      }
    }
    return $result;
  }
}
/**
 * This class implements the attach table DAO
**/
class dbiCal_attach_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-11-07
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-11-07
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_attach_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-13
   */
  function delete( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM attach WHERE attach_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $fromWhere .= ' AND attach_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $sql  = "SELECT attach_id AS id $fromWhere";
    $res  = & $this->_db->query( $sql, array( 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow())
      if( isset( $row['id'] ) && !empty( $row['id'] ))
        $result[] = $row['id'];
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $attach_id) {
      $res = $dbiCal_parameter_DAO->delete( $attach_id, 'attach' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    array  $attachVal
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $ownertype, $attachVal ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql       = 'INSERT INTO attach (attach_owner_id, attach_ownertype';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $ownertype, 'text' );
    if( isset( $attachVal['value'] ) && !empty( $attachVal['value'] )) {
      if( isset( $attachVal['params']['VALUE'] )    && ( 'BINARY' == strtoupper( $attachVal['params']['VALUE'] )) &&
          isset( $attachVal['params']['ENCODING'] ) && ( 'BASE64' == strtoupper( $attachVal['params']['ENCODING'] ))) {
        $sql    .= ', attach_attach_bin';
        $values .= ', '.$this->_db->quote( $attachVal['value'], 'blob' );
      }
      else {
        $sql    .= ', attach_attach_uri';
        $values .= ', '.$this->_db->quote( $attachVal['value'], 'text' );
      }
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $attach_id = $this->_db->lastInsertID( 'attach', 'attach_id' );
    if( PEAR::isError( $attach_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$attach_id->getUserInfo().PHP_EOL.$attach_id->getMessage(), PEAR_LOG_ALERT );
      return $attach_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'attach_id='.$attach_id, PEAR_LOG_INFO );
    if( isset( $attachVal['params'] ) && !empty( $attachVal['params'] )) {
      $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
      foreach( $attachVal['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $attach_id, 'attach', 'ATTACH', $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $attach_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM attach WHERE attach_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' AND attach_ownertype = '.$this->_db->quote( $ownertype, 'text' ).' ORDER BY attach_id';
    $types = array( 'integer', 'integer', 'text', 'text', 'blob' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      if( isset( $tablerow['attach_attach_bin'] ) && !empty( $tablerow['attach_attach_bin'] ) &&
          !PEAR::isError( $tablerow['attach_attach_bin'] ) && is_resource( $tablerow['attach_attach_bin'] )) {
        $result[$tablerow['attach_id']]['value'] = '';
        while( !feof( $tablerow['attach_attach_bin'] ))
          $result[$tablerow['attach_id']]['value'] .= fread( $tablerow['attach_attach_bin'], 8192 );
        $this->_db->datatype->destroyLOB( $tablerow['attach_attach_bin'] );
        if( $this->_log )
          $this->_log->log( 'blob len='. strlen( $tablerow['attach_attach'] ), PEAR_LOG_DEBUG );
      }
      elseif( isset( $tablerow['attach_attach_uri'] ) && !empty( $tablerow['attach_attach_uri'] ))
        $result[$tablerow['attach_id']]['value'] = $tablerow['attach_attach_uri'];
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' attachs', PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $attach_id => & $attachVal ) {
      $res = $dbiCal_parameter_DAO->select( $attach_id, 'attach', 'ATTACH' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $key => $value )
          $attachVal['params'][$key] = $value;
      }
      else
        $attachVal['params'] = FALSE;
    }
    return $result;
  }
}
/**
 * This class implements the exdate table DAO
**/
class dbiCal_exdate_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_exdate_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-10-31
   */
  function delete( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM exdate WHERE exdate_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $fromWhere .= ' AND exdate_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $sql  = "SELECT exdate_id AS id $fromWhere AND exdate_sequence = ".$this->_db->quote( 1, 'integer' );
    $res  = & $this->_db->query( $sql, array( 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow())
      if( isset( $row['id'] ) && !empty( $row['id'] ))
        $result[] = $row['id'];
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $exdate_id) {
      $res = $dbiCal_parameter_DAO->delete( $exdate_id, 'exdate' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res".' for '.count( $result ).' exdates', PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    array  $exdateVal
   * @param    int    $orderNo
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $ownertype, $exdateVal, $orderNo=1 ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql       = 'INSERT INTO exdate (exdate_owner_id, exdate_ownertype, exdate_order, exdate_sequence';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $ownertype, 'text' ).', '.$this->_db->quote( $orderNo, 'integer' );
    $exdatecnt = 1;
    $exdate_id = array();
    foreach( $exdateVal['value'] as $exdate ) {
      $value  = sprintf("%04d-%02d-%02d", $exdate['year'], $exdate['month'], $exdate['day']);
      if( isset( $exdate['hour'] )) {
        $value  .= ' '.sprintf("%02d:%02d:%02d", $exdate['hour'], $exdate['min'], $exdate['sec']);
        $sql2    = ', exdate_datetime';
        $value2  = ', '.$this->_db->quote( $value, 'timestamp' );
        if( isset( $exdate['tz'] ) &&  in_array( $exdate['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
          $sql2   .= ', exdate_datetimeutc';
          $value2 .= ', '.$this->_db->quote( 1, 'boolean' );
        }
      }
      else {
        $sql2    = ', exdate_date';
        $value2  = ', '.$this->_db->quote( $value, 'date' );
      }
      $sql3    = "$sql $sql2 $values, ".$this->_db->quote( $exdatecnt, 'integer' ).$value2.')';
      $res = & $this->_db->exec( $sql3 );
      if( PEAR::isError( $res )) {
        if( $this->_log )
          $this->_log->log( $sql3.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
        return $res;
      }
      $exdate_id[$exdatecnt] = $this->_db->lastInsertID( 'exdate', 'exdate_id' );
      if( PEAR::isError( $exdate_id[$exdatecnt] )) {
        if( $this->_log )
          $this->_log->log( 'lastInsertID error:'.$exdate_id[$exdatecnt]->getUserInfo().PHP_EOL.$exdate_id[$exdatecnt]->getMessage(), PEAR_LOG_ALERT );
        return $exdate_id[$exdatecnt];
      }
      if( $this->_log )
        $this->_log->log( $sql3.PHP_EOL.'exdate_id[$exdatecnt]='.$exdate_id[$exdatecnt], PEAR_LOG_INFO );
      $exdatecnt += 1;
    } // end foreach( $exdateVal['value'] as $exdate )
    if( isset( $exdateVal['params'] ) && !empty( $exdateVal['params'] )) {
      $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
      foreach( $exdateVal['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $exdate_id[1], 'exdate', 'EXDATE', $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $exdate_id[1];
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM exdate WHERE exdate_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' AND exdate_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $sql .= ' ORDER BY exdate_order, exdate_sequence';
    $types = array( 'integer', 'integer', 'text', 'integer', 'integer', 'timestamp', 'boolean', 'date' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      if( !isset( $result[$tablerow['exdate_order']]['exdate_id'] ) && ( 1 == $tablerow['exdate_sequence'] ))
        $result[$tablerow['exdate_order']]['exdate_id'] = $tablerow['exdate_id'];
      if( isset( $tablerow['exdate_datetime'] )   && !empty( $tablerow['exdate_datetime'] )) {
        $dt = str_replace( '-', '', $tablerow['exdate_datetime'] );
        $dt = str_replace( ':', '', $dt );
        $dt = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['exdate_datetimeutc'] ) && !empty( $tablerow['exdate_datetimeutc'] ))
          $dt .= 'Z';
        $result[$tablerow['exdate_order']]['value'][]  = $dt;
      }
      elseif( isset( $tablerow['exdate_date'] )   &&  !empty( $tablerow['exdate_date'] ))
        $result[$tablerow['exdate_order']]['value'][]  = str_replace( '-', '', $tablerow['exdate_date'] );
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' exdates', PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $exdate_order => & $exdateVal ) {
      $res = $dbiCal_parameter_DAO->select( $exdateVal['exdate_id'], 'exdate', 'EXDATE' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $key => $value )
          $exdateVal['params'][$key] = $value;
      }
      else
        $exdateVal['params'] = FALSE;
    }
    return $result;
  }
}
/**
 * This class implements the mtext table DAO
 * ATTENDEE, CATEGORIES, COMMENT, CONTACT, DESCRIPTION, RELATED-TO, RESOURCES, REQUEST-STATUS
**/
class dbiCal_mtext_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self:$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_mtext_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-13
   */
  function delete( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM mtext WHERE mtext_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $fromWhere .= ' AND mtext_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $sql  = "SELECT mtext_id AS id $fromWhere";
    $res  = & $this->_db->query( $sql, array( 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow())
      if( isset( $row['id'] ) && !empty( $row['id'] ))
        $result[] = $row['id'];
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $mtext_id) {
      $res = $dbiCal_parameter_DAO->delete( $mtext_id, 'mtext' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    string $mtextName
   * @param    array  $mtextVal
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $ownertype, $mtextName, $mtextVal ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql       = 'INSERT INTO mtext (mtext_owner_id, mtext_ownertype, mtext_name';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $ownertype, 'text' ).', '.$this->_db->quote( $mtextName, 'text' );
    if( isset( $mtextVal['value'] ) && !empty( $mtextVal['value'] )) {
      if( 'REQUEST-STATUS' == $mtextName ) {
        $value = '';
        foreach( $mtextVal['value'] as $k => $v )
          $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
        $mtextVal['value'] = $value;
      }
      $sql    .= ', mtext_mtext';
      $values .= ', '.$this->_db->quote( $mtextVal['value'], 'text' );
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $mtext_id = $this->_db->lastInsertID( 'mtext', 'mtext_id' );
    if( PEAR::isError( $mtext_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$mtext_id->getUserInfo().PHP_EOL.$attandee_id->getMessage(), PEAR_LOG_ALERT );
      return $mtext_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'mtext_id='.$mtext_id, PEAR_LOG_INFO );
    if( isset( $mtextVal['params'] ) && !empty( $mtextVal['params'] )) {
      $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
      foreach( $mtextVal['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $mtext_id, 'mtext', $mtextName, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $mtext_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    string $mtextName
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $owner_id, $ownertype, $mtextName=FALSE ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql       = 'SELECT * FROM mtext WHERE mtext_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql      .= ' AND mtext_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $types     =  array( 'integer', 'integer', 'text', 'text', 'text' );
    if( $mtextName )
      $sql    .= ' AND mtext_name = '.$this->_db->quote( $mtextName, 'text' );
    $sql .= ' ORDER BY mtext_id';
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      if( isset( $tablerow['mtext_name'] )  && !empty( $tablerow['mtext_name'] ) &&
          isset( $tablerow['mtext_mtext'] ) && !empty( $tablerow['mtext_mtext'] )) {
        if( 'REQUEST-STATUS' == $tablerow['mtext_name'] ) {
          $rstatusParts = explode( '|', $tablerow['mtext_mtext'] );
          $tablerow['mtext_mtext'] = array();
          foreach( $rstatusParts as $rstatus ) {
            list( $key, $value ) = explode( '=', $rstatus, 2 );
            $tablerow['mtext_mtext'][$key] = $value;
          }
          if( !isset( $tablerow['mtext_mtext']['extdata'] ))
            $tablerow['mtext_mtext']['extdata'] = FALSE;
        }
        $result[$tablerow['mtext_name']][$tablerow['mtext_id']]['value'] = $tablerow['mtext_mtext'];
        if( $this->_log )
          $this->_log->log( $tablerow['mtext_name'].' ('.$tablerow['mtext_id'].') '.$tablerow['mtext_mtext'], PEAR_LOG_DEBUG );
      }
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' mtexts', PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $mtext_name => $mtextVal ) {
      foreach( $mtextVal as $mtext_id => $mtextContent ) {
        $res = $dbiCal_parameter_DAO->select( $mtext_id, 'mtext', $mtext_name );
        if( PEAR::isError( $res ))
          return $res;
        if( !empty( $res )) {
          foreach( $res as $key => $value )
            $result[$mtext_name][$mtext_id]['params'][$key] = $value;
        }
        else
          $result[$mtext_name][$mtext_id]['params'] = FALSE;
      }
    }
    return $result;
  }
}
/**
 * This class implements the pfreebusy table DAO
**/
class dbiCal_pfreebusy_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-11-08
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-11-13
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_pfreebusy_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-13
   */
  function delete( $owner_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM pfreebusy WHERE pfreebusy_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql  = "SELECT pfreebusy_id AS id $fromWhere AND pfreebusy_sequence = ".$this->_db->quote( 1, 'integer' );
    $res  = & $this->_db->query( $sql, array( 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow())
      if( isset( $row['id'] ) && !empty( $row['id'] ))
        $result[] = $row['id'];
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $pfreebusy_id) {
      $res = $dbiCal_parameter_DAO->delete( $pfreebusy_id, 'pfreebusy' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res".' for '.count( $result ).' pfreebusys', PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    int    $orderNo
   * @param    array  $pfreebusyVal
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $orderNo, $pfreebusyVal ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql       = 'INSERT INTO pfreebusy (pfreebusy_owner_id, pfreebusy_order, pfreebusy_pfreebusytype, pfreebusy_sequence, pfreebusy_startdatetime';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $orderNo, 'integer' );
    $values   .= ', '.$this->_db->quote( $pfreebusyVal['value']['fbtype'], 'text' );
    $pfreebusycnt = 1;
    $pfreebusy_id = array();
    foreach( $pfreebusyVal['value'] as $fk => $period ) {
      if( 'fbtype' === strtolower( $fk ))
        continue;
      $value2  = ', '.$this->_db->quote( $pfreebusycnt, 'integer' );
          /* start date[time] */
      $value  = sprintf("%04d-%02d-%02d", $period[0]['year'], $period[0]['month'], $period[0]['day']);
      $value .= ' '.sprintf("%02d:%02d:%02d", $period[0]['hour'], $period[0]['min'], $period[0]['sec']);
      $value3 = $this->_db->quote( $value, 'timestamp' );
      if( array_key_exists( 'year', $period[1] )) { /* period end date[time] */
        $value   = sprintf("%04d-%02d-%02d", $period[1]['year'], $period[1]['month'], $period[1]['day']);
        $value  .= ' '.sprintf("%02d:%02d:%02d", $period[1]['hour'], $period[1]['min'], $period[1]['sec']);
        $sql3    = ', pfreebusy_enddatetime';
        $value3 .= ', '.$this->_db->quote( $value, 'timestamp' );
      }
      else { /* period duration */
        $sql3    = ', pfreebusy_periodduration';
        $value   = '';
        foreach( $period[1] as $k => $v )
          $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
        $value3 .= ', '.$this->_db->quote( $value, 'text' );
      }
      $sql4    = "$sql $sql3 $values $value2, $value3)";
      $res = & $this->_db->exec( $sql4 );
      if( PEAR::isError( $res )) {
        if( $this->_log )
          $this->_log->log( $sql4.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
        return $res;
      }
      $pfreebusy_id[$pfreebusycnt] = $this->_db->lastInsertID( 'pfreebusy', 'pfreebusy_id' );
      if( PEAR::isError( $pfreebusy_id[$pfreebusycnt] )) {
        if( $this->_log )
          $this->_log->log( 'lastInsertID error:'.$pfreebusy_id[$pfreebusycnt]->getUserInfo().PHP_EOL.$pfreebusy_id[$pfreebusycnt]->getMessage(), PEAR_LOG_ALERT );
        return $pfreebusy_id[$pfreebusycnt];
      }
      if( $this->_log )
        $this->_log->log( $sql4.PHP_EOL."pfreebusy_id[$pfreebusycnt]=".$pfreebusy_id[$pfreebusycnt], PEAR_LOG_INFO );
      $pfreebusycnt += 1;
    } // end foreach( $pfreebusy['value'] as $period )
    if( isset( $pfreebusyVal['params'] ) && !empty( $pfreebusyVal['params'] )) {
      $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
      foreach( $pfreebusyVal['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $pfreebusy_id[1], 'pfreebusy', 'FREEBUSY', $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $pfreebusy_id[1];
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @return   mixed  $res (PEAR::Error or result-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $owner_id ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM pfreebusy WHERE pfreebusy_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' ORDER BY pfreebusy_order, pfreebusy_sequence';
    $types = array( 'integer', 'integer', 'integer', 'text', 'integer', 'timestamp', 'timestamp', 'text' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      if( !isset( $result[$tablerow['pfreebusy_order']]['pfreebusy_id'] ) && ( 1 == $tablerow['pfreebusy_sequence'] )) {
        $result[$tablerow['pfreebusy_order']]['pfreebusy_id'] = $tablerow['pfreebusy_id'];
        $result[$tablerow['pfreebusy_order']]['value'] = array( 'fbtype' => $tablerow['pfreebusy_pfreebusytype'] );
      }
      $period = array();
      if( isset( $tablerow['pfreebusy_startdatetime'] ) && !empty( $tablerow['pfreebusy_startdatetime'] )) {
        $dt  = str_replace( '-', '', $tablerow['pfreebusy_startdatetime'] );
        $dt  = str_replace( ':', '', $dt );
        $period[0]  = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      if( isset( $tablerow['pfreebusy_enddatetime'] )   && !empty( $tablerow['pfreebusy_enddatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['pfreebusy_enddatetime'] );
        $dt = str_replace( ':', '', $dt );
        $period[1]  = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      elseif( isset( $tablerow['pfreebusy_periodduration'] ) && !empty( $tablerow['pfreebusy_periodduration'] )) {
        $durParts = explode( '|', $tablerow['pfreebusy_periodduration'] );
        $duration = array();
        foreach( $durParts as $durPart ) {
          list( $key, $value ) = explode( '=', $durPart, 2 );
          $duration[$key] = $value;
        }
        $period[1] = iCalUtilityFunctions::_format_duration( $duration );
      }
      $result[$tablerow['pfreebusy_order']]['value'][] = $period;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' pfreebusys', PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $pfreebusy_order => & $pfreebusyVal ) {
      $res = $dbiCal_parameter_DAO->select( $pfreebusyVal['pfreebusy_id'], 'pfreebusy', 'FREEBUSY' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $key => $value )
          $pfreebusyVal['params'][$key] = $value;
      }
      else
        $pfreebusyVal['params'] = FALSE;
    }
    return $result;
  }
}
/**
 * This class implements the rdate table DAO
**/
class dbiCal_rdate_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-11-06
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-11-06
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_rdate_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-13
   */
  function delete( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM rdate WHERE rdate_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $fromWhere .= ' AND rdate_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $sql        = "SELECT rdate_id AS id $fromWhere AND rdate_sequence = ".$this->_db->quote( 1, 'integer' );
    $res        = & $this->_db->query( $sql, array( 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow())
      if( isset( $row['id'] ) && !empty( $row['id'] ))
        $result[] = $row['id'];
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $rdate_id) {
      $res = $dbiCal_parameter_DAO->delete( $rdate_id, 'rdate' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res".' for '.count( $result ).' rdates', PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    array  $rdateVal
   * @param    int    $orderNo
   * @return   mixed  $res (PEAR::Error eller table-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $ownertype, $rdateVal, $orderNo=1 ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql       = 'INSERT INTO rdate (rdate_owner_id, rdate_ownertype, rdate_order, rdate_sequence';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $ownertype, 'text' ).', '.$this->_db->quote( $orderNo, 'integer' );
    $rdatecnt = 1;
    $rdate_id = array();
    foreach( $rdateVal['value'] as $rdate ) {
      if( $this->_log )
        $this->_log->log( "rdate=".var_export( $rdate, TRUE ), PEAR_LOG_DEBUG );
      if( is_array( $rdate ) && ( 2 == count( $rdate ))) { // PERIOD  datetime - datetime  OR  datetime - duration
          /* period start datetime */
        $value  = sprintf("%04d-%02d-%02d", $rdate[0]['year'], $rdate[0]['month'], $rdate[0]['day']);
        if( isset( $rdate[0]['hour'] )) {
          $value  .= ' '.sprintf("%02d:%02d:%02d", $rdate[0]['hour'], $rdate[0]['min'], $rdate[0]['sec']);
          $sql2    = ', rdate_startdatetime';
          $value2  = ', '.$this->_db->quote( $value, 'timestamp' );
          if( isset( $rdate[0]['tz'] ) &&  in_array( $rdate[0]['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
            $sql2   .= ', rdate_startdatetimeutc';
            $value2 .= ', '.$this->_db->quote( 1, 'boolean' );
          }
        }
        if( array_key_exists( 'year', $rdate[1] )) {
          /* period end datetime */
          $value  = sprintf("%04d-%02d-%02d", $rdate[1]['year'], $rdate[1]['month'], $rdate[1]['day']);
          if( isset( $rdate[1]['hour'] )) {
            $value  .= ' '.sprintf("%02d:%02d:%02d", $rdate[1]['hour'], $rdate[1]['min'], $rdate[1]['sec']);
            $sql2   .= ', rdate_enddatetime';
            $value2 .= ', '.$this->_db->quote( $value, 'timestamp' );
            if( isset( $rdate[1]['tz'] ) &&  in_array( $rdate[1]['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
              $sql2   .= ', rdate_enddatetimeutc';
              $value2 .= ', '.$this->_db->quote( 1, 'boolean' );
            }
          }
        }
        else {
          /* period with duration */
          $value = '';
          foreach( $rdate[1] as $k => $v )
            $value .= ( empty( $value )) ? "$k=$v" : "|$k=$v";
          $sql2    .= ', rdate_periodduration';
          $value2  .= ', '.$this->_db->quote( $value, 'text' );
        }
      } // PERIOD end
      else { // SINGLE date[time]
        $value  = sprintf("%04d-%02d-%02d", $rdate['year'], $rdate['month'], $rdate['day']);
        if( isset( $rdate['hour'] )) {
          $value  .= ' '.sprintf("%02d:%02d:%02d", $rdate['hour'], $rdate['min'], $rdate['sec']);
          $sql2    = ', rdate_startdatetime';
          $value2  = ', '.$this->_db->quote( $value, 'timestamp' );
          if( isset( $rdate['tz'] ) &&  in_array( $rdate['tz'], array( 'utc', 'gmt', 'z', 'UTC', 'GMT', 'Z' ))) {
            $sql2   .= ', rdate_startdatetimeutc';
            $value2 .= ', '.$this->_db->quote( 1, 'boolean' );
          }
        }
        else {
          $sql2   .= ', rdate_startdate';
          $value2 .= ', '.$this->_db->quote( $value, 'date' );
        }
      } // end single date[time]
      $sql3    = "$sql $sql2 $values, ".$this->_db->quote( $rdatecnt, 'integer' ).$value2.')';
      $res = & $this->_db->exec( $sql3 );
      if( PEAR::isError( $res )) {
        if( $this->_log )
          $this->_log->log( $sql3.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
        return $res;
      }
      $rdate_id[$rdatecnt] = $this->_db->lastInsertID( 'rdate', 'rdate_id' );
      if( PEAR::isError( $rdate_id[$rdatecnt] )) {
        if( $this->_log )
          $this->_log->log( 'lastInsertID error:'.$rdate_id[$rdatecnt]->getUserInfo().PHP_EOL.$rdate_id[$rdatecnt]->getMessage(), PEAR_LOG_ALERT );
        return $rdate_id[$rdatecnt];
      }
      if( $this->_log )
        $this->_log->log( $sql3.PHP_EOL."rdate_id[$rdatecnt]=".$rdate_id[$rdatecnt], PEAR_LOG_INFO );
      $rdatecnt += 1;
    } // end foreach( $rdateVal['value'] as $rdate )
    if( isset( $rdateVal['params'] ) && !empty( $rdateVal['params'] )) {
      $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
      foreach( $rdateVal['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $rdate_id[1], 'rdate', 'RDATE', $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $rdate_id[1];
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM rdate WHERE rdate_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' AND rdate_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $sql .= ' ORDER BY rdate_order, rdate_sequence';
    $types = array( 'integer', 'integer', 'text', 'integer', 'integer', 'timestamp', 'boolean', 'date', 'timestamp', 'boolean', 'date', 'text' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      if( !isset( $result[$tablerow['rdate_order']]['rdate_id'] ) && ( 1 == $tablerow['rdate_sequence'] ))
        $result[$tablerow['rdate_order']]['rdate_id'] = $tablerow['rdate_id'];
      $rdate = null;
      if( isset( $tablerow['rdate_startdatetime'] ) && !empty( $tablerow['rdate_startdatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['rdate_startdatetime'] );
        $dt = str_replace( ':', '', $dt );
        $dt = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['rdate_startdatetimeutc'] ) && !empty( $tablerow['rdate_startdatetimeutc'] ))
          $dt .= 'Z';
        if(( isset( $tablerow['rdate_enddatetime'] )    && !empty( $tablerow['rdate_enddatetime'] )) ||
           ( isset( $tablerow['rdate_periodduration'] ) && !empty( $tablerow['rdate_periodduration'] ))) // PERIOD
          $rdate = array( $dt ); // value=PERIOD start datetime
        else
          $rdate = $dt; // value=DATETIME
      }
      elseif( isset( $tablerow['rdate_startdate'] ) &&  !empty( $tablerow['rdate_startdate'] ))
        $rdate = str_replace( '-', '', $tablerow['rdate_startdate'] ); // value=DATE
      if( isset( $tablerow['rdate_enddatetime'] )   && !empty( $tablerow['rdate_enddatetime'] )) {
        $dt = str_replace( '-', '', $tablerow['rdate_enddatetime'] );
        $dt = str_replace( ':', '', $dt );
        $dt = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 );
        if( isset( $tablerow['rdate_enddatetimeutc'] ) && !empty( $tablerow['rdate_enddatetimeutc'] ))
          $dt .= 'Z';
        $rdate[] = $dt; // value=PERIOD end date, always datetime if set
      }
      elseif( isset( $tablerow['rdate_periodduration'] ) && !empty( $tablerow['rdate_periodduration'] )) {
        $durParts = explode( '|', $tablerow['rdate_periodduration'] );
        $duration = array();
        foreach( $durParts as $durPart ) {
          list( $key, $value ) = explode( '=', $durPart, 2 );
          $duration[$key] = $value;
        }
        $rdate[] = iCalUtilityFunctions::_format_duration( $duration ); // value=PERIOD with duration
      }
      $result[$tablerow['rdate_order']]['value'][] = $rdate;
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' rdates', PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $rdate_order => & $rdateVal ) {
      $res = $dbiCal_parameter_DAO->select( $rdateVal['rdate_id'], 'rdate', 'RDATE' );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $key => $value )
          $rdateVal['params'][$key] = $value;
      }
      else
        $rdateVal['params'] = FALSE;
    }
    return $result;
  }
}
/**
 * This class implements the rexrule table DAO
**/
class dbiCal_rexrule_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_rexrule_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2010-11-13
   */
  function delete( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM rexrule WHERE rexrule_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $fromWhere .= ' AND rexrule_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $sql        = "SELECT rexrule_id AS id $fromWhere";
    $res        = & $this->_db->query( $sql, array( 'integer' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow())
      if( isset( $row['id'] ) && !empty( $row['id'] ))
        $result[] = $row['id'];
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $rexrule_id) {
      $res = $dbiCal_parameter_DAO->delete( $rexrule_id, 'rexrule' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    string $rexruletype
   * @param    array  $rexrule
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $ownertype, $rexruletype, $rexrule ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql       = 'INSERT INTO rexrule (rexrule_owner_id, rexrule_ownertype, rexrule_type';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $ownertype, 'text' ).', '.$this->_db->quote( $rexruletype, 'text' );
    if( isset( $rexrule['value']['FREQ'] )       && !empty( $rexrule['value']['FREQ'] )) {
      $sql    .= ', rexrule_freq';
      $values .= ', '.$this->_db->quote( $rexrule['value']['FREQ'], 'text' );
    }
    if( isset( $rexrule['value']['COUNT'] )      && !empty( $rexrule['value']['COUNT'] ))  {
      $sql    .= ', rexrule_count';
      $values .= ', '.$this->_db->quote( $rexrule['value']['COUNT'], 'integer' );
    }
    if( isset( $rexrule['value']['UNTIL'] )      && !empty( $rexrule['value']['UNTIL'] )) {
      $value   = sprintf("%04d-%02d-%02d", $rexrule['value']['UNTIL']['year'], $rexrule['value']['UNTIL']['month'], $rexrule['value']['UNTIL']['day']);
      if( isset( $rexrule['value']['UNTIL']['hour'] )) {
        $sql    .= ', rexrule_until_datetime';
        $value  .= ' '.sprintf("%02d:%02d:%02d", $rexrule['value']['UNTIL']['hour'], $rexrule['value']['UNTIL']['min'], $rexrule['value']['UNTIL']['sec']);
        $values .= ', '.$this->_db->quote( $value, 'timestamp' );
      }
      else {
        $sql    .= ', rexrule_until_date';
        $values .= ', '.$this->_db->quote( $value, 'date' );
      }
    }
    if( isset( $rexrule['value']['INTERVAL'] )   && !empty( $rexrule['value']['INTERVAL'] )) {
      $sql    .= ', rexrule_interval';
      $values .= ', '.$this->_db->quote( $rexrule['value']['INTERVAL'], 'integer' );
    }
    if( isset( $rexrule['value']['BYSECOND'] )   && !empty( $rexrule['value']['BYSECOND'] )) {
      $sql    .= ', rexrule_bysecond';
      if( is_array( $rexrule['value']['BYSECOND'] ) )
        $rexrule['value']['BYSECOND'] = implode( ',', $rexrule['value']['BYSECOND'] );
      $values .= ', '.$this->_db->quote( $rexrule['value']['BYSECOND'], 'text' );
    }
    if( isset( $rexrule['value']['BYMINUTE'] )   && !empty( $rexrule['value']['BYMINUTE'] )) {
      $sql    .= ', rexrule_byminute';
      if( is_array( $rexrule['value']['BYMINUTE'] ) )
        $rexrule['value']['BYMINUTE'] = implode( ',', $rexrule['value']['BYMINUTE'] );
      $values .= ', '.$this->_db->quote( $rexrule['value']['BYMINUTE'], 'text' );
    }
    if( isset( $rexrule['value']['BYHOUR'] )     && !empty( $rexrule['value']['BYHOUR'] )) {
      $sql    .= ', rexrule_byhour';
      if( is_array( $rexrule['value']['BYHOUR'] ) )
        $rexrule['value']['BYHOUR'] = implode( ',', $rexrule['value']['BYHOUR'] );
      $values .= ', '.$this->_db->quote( $rexrule['value']['BYHOUR'], 'text' );
    }
    if( isset( $rexrule['value']['BYDAY'] )      && !empty( $rexrule['value']['BYDAY'] )) {
      $sql    .= ', rexrule_byday';
      if( isset( $rexrule['value']['BYDAY']['DAY'] ))
        $rexrule['value']['BYDAY'] = array( $rexrule['value']['BYDAY'] );
      $value = '';
      foreach( $rexrule['value']['BYDAY'] as $byday ) {
        $byday  = implode( ':', array_values( $byday ));
        $value .= ( empty( $value )) ? $byday : "|$byday";
      }
      $values .= ', '.$this->_db->quote( $value, 'text' );
    }
    if( isset( $rexrule['value']['BYMONTHDAY'] ) && !empty( $rexrule['value']['BYMONTHDAY'] )) {
      $sql    .= ', rexrule_bymonthday';
      if( is_array( $rexrule['value']['BYMONTHDAY'] ) )
        $rexrule['value']['BYMONTHDAY'] = implode( ',', $rexrule['value']['BYMONTHDAY'] );
      $values .= ', '.$this->_db->quote( $rexrule['value']['BYMONTHDAY'], 'text' );
    }
    if( isset( $rexrule['value']['BYYEARDAY'] )  && !empty( $rexrule['value']['BYYEARDAY'] )) {
      $sql    .= ', rexrule_byyearday';
      if( is_array( $rexrule['value']['BYYEARDAY'] ) )
        $rexrule['value']['BYYEARDAY'] = implode( ',', $rexrule['value']['BYYEARDAY'] );
      $values .= ', '.$this->_db->quote( $rexrule['value']['BYYEARDAY'], 'text' );
    }
    if( isset( $rexrule['value']['BYWEEKNO'] )   && !empty( $rexrule['value']['BYWEEKNO'] )) {
      $sql    .= ', rexrule_byweekno';
      if( is_array( $rexrule['value']['BYWEEKNO'] ) )
        $rexrule['value']['BYWEEKNO'] = implode( ',', $rexrule['value']['BYWEEKNO'] );
      $values .= ', '.$this->_db->quote( $rexrule['value']['BYWEEKNO'], 'text' );
    }
    if( isset( $rexrule['value']['BYMONTH'] )    && !empty( $rexrule['value']['BYMONTH'] )) {
      $sql    .= ', rexrule_bymonth';
      if( is_array( $rexrule['value']['BYMONTH'] ) )
        $rexrule['value']['BYMONTH'] = implode( ',', $rexrule['value']['BYMONTH'] );
      $values .= ', '.$this->_db->quote( $rexrule['value']['BYMONTH'], 'text' );
    }
    if( isset( $rexrule['value']['BYSETPOS'] )   && !empty( $rexrule['value']['BYSETPOS'] )) {
      $sql    .= ', rexrule_bysetpos';
      if( is_array( $rexrule['value']['BYSETPOS'] ))
        $rexrule['value']['BYSETPOS'] = implode( ',', $rexrule['value']['BYSETPOS'] );
      $values .= ', '.$this->_db->quote( $rexrule['value']['BYSETPOS'], 'text' );
    }
    if( isset( $rexrule['value']['WKST'] )       && !empty( $rexrule['value']['WKST'] )) {
      $sql    .= ', rexrule_wkst';
      $values .= ', '.$this->_db->quote( $rexrule['value']['WKST'], 'text' );
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $rexrule_id = $this->_db->lastInsertID( 'rexrule', 'rexrule_id' );
    if( PEAR::isError( $rexrule_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$rexrule_id->getUserInfo().PHP_EOL.$rexrule_id->getMessage(), PEAR_LOG_ALERT );
      return $rexrule_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'rexrule_id='.$rexrule_id, PEAR_LOG_INFO );
    if( isset( $rexrule['params'] ) && !empty( $rexrule['params'] )) {
      $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
      foreach( $rexrule['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $rexrule_id, 'rexrule', $rexruletype, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $rexrule_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype ))
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-01-20
   */
  function select( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM rexrule WHERE rexrule_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' AND rexrule_ownertype = '.$this->_db->quote( $ownertype, 'text' ).' ORDER BY rexrule_id';
    $types = array( 'integer', 'integer', 'text', 'text', 'text', 'integer', 'timestamp', 'date', 'integer'
                  , 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      $row = array( 'rexrule_type' => $tablerow['rexrule_type'] );
      if( isset( $tablerow['rexrule_freq'] )           && !empty( $tablerow['rexrule_freq'] ))
        $row['FREQ']        = $tablerow['rexrule_freq'];
      if( isset( $tablerow['rexrule_count'] )          && !empty( $tablerow['rexrule_count'] ))
        $row['COUNT']       = $tablerow['rexrule_count'];
      if( isset( $tablerow['rexrule_until_datetime'] ) && !empty( $tablerow['rexrule_until_datetime'] )) {
        $dt = str_replace( '-', '', $tablerow['rexrule_until_datetime'] );
        $dt = str_replace( ':', '', $dt );
        $row['UNTIL']       = substr( $dt, 0, 8 ).'T'.substr( $dt, 9, 6 ).'Z';
      }
      elseif( isset( $tablerow['rexrule_until_date'] ) && !empty( $tablerow['rexrule_until_date'] ))
        $row['UNTIL']       = str_replace( '-', '', $tablerow['rexrule_until_date'] );
      if( isset( $tablerow['rexrule_interval'] )       && !empty( $tablerow['rexrule_interval'] ))
        $row['INTERVAL']    = $tablerow['rexrule_interval'];
      if( isset( $tablerow['rexrule_bysecond'] )       && !empty( $tablerow['rexrule_bysecond'] )) {
        $row['BYSECOND']    = explode( ',', $tablerow['rexrule_bysecond'] );
        if( 1 == count( $row['BYSECOND'] ))
          $row['BYSECOND']  = $row['BYSECOND'][0];
      }
      if( isset( $tablerow['rexrule_byminute'] )       && !empty( $tablerow['rexrule_byminute'] )) {
        $row['BYMINUTE']    = explode( ',', $tablerow['rexrule_byminute'] );
        if( 1 == count( $row['BYMINUTE'] ))
          $row['BYMINUTE']  = $row['BYMINUTE'][0];
      }
      if( isset( $tablerow['rexrule_byhour'] )         && !empty( $tablerow['rexrule_byhour'] )) {
        $row['BYHOUR']      = explode( ',', $tablerow['rexrule_byhour'] );
        if( 1 == count( $row['BYHOUR'] ))
          $row['BYHOUR']  = $row['BYHOUR'][0];
      }
      if( isset( $tablerow['rexrule_byday'] )          && !empty( $tablerow['rexrule_byday'] )) {
        $bydays = explode( '|', $tablerow['rexrule_byday'] );
        foreach( $bydays as $byday ) {
          $byday = explode( ':', $byday );
          if( 1 == count( $byday ))
            $row['BYDAY'][] = array( 'DAY' => $byday[0] );
          else
            $row['BYDAY'][] = array( $byday[0], 'DAY' => $byday[1] );
        }
        if( 1 == count( $row['BYDAY'] ))
          $row['BYDAY']     = $row['BYDAY'][0];
      }
      if( isset( $tablerow['rexrule_bymonthday'] )     && !empty( $tablerow['rexrule_bymonthday'] )) {
        $row['BYMONTHDAY']  = explode( ',', $tablerow['rexrule_bymonthday'] );
        if( 1 == count( $row['BYMONTHDAY'] ))
          $row['BYMONTHDAY']  = $row['BYMONTHDAY'][0];
      }
      if( isset( $tablerow['rexrule_byyearday'] )      && !empty( $tablerow['rexrule_byyearday'] )) {
        $row['BYYEARDAY']   = explode( ',', $tablerow['rexrule_byyearday'] );
        if( 1 == count( $row['BYYEARDAY'] ))
          $row['BYYEARDAY']  = $row['BYYEARDAY'][0];
      }
      if( isset( $tablerow['rexrule_byweekno'] )       && !empty( $tablerow['rexrule_byweekno'] )) {
        $row['BYWEEKNO']    = explode( ',', $tablerow['rexrule_byweekno'] );
        if( 1 == count( $row['BYWEEKNO'] ))
          $row['BYWEEKNO']  = $row['BYWEEKNO'][0];
      }
      if( isset( $tablerow['rexrule_bymonth'] )        && !empty( $tablerow['rexrule_bymonth'] )) {
        $row['BYMONTH']     = explode( ',', $tablerow['rexrule_bymonth'] );
        if( 1 == count( $row['BYMONTH'] ))
          $row['BYMONTH']  = $row['BYMONTH'][0];
      }
      if( isset( $tablerow['rexrule_bysetpos'] )       && !empty( $tablerow['rexrule_bysetpos'] )) {
        $row['BYSETPOS']    = explode( ',', $tablerow['rexrule_bysetpos'] );
        if( 1 == count( $row['BYSETPOS'] ))
          $row['BYSETPOS']  = $row['BYSETPOS'][0];
      }
      if( isset( $tablerow['rexrule_wkst'] )           && !empty( $tablerow['rexrule_wkst'] ))
        $row['WKST']        = $tablerow['rexrule_wkst'];
      if( !empty( $row )) {
        $result[$tablerow['rexrule_id']]['value'] = $row;
      if( $this->_log )
        $this->_log->log( var_export( $row, TRUE ), PEAR_LOG_DEBUG );
      }
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' rexrules', PEAR_LOG_INFO );
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $rexrule_id => & $rexruleVal ) {
      $res = $dbiCal_parameter_DAO->select( $rexrule_id, 'rexrule', $rexruleVal['value']['rexrule_type'] );
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $key => $value )
          $rexruleVal['params'][$key] = $value;
      }
      else
        $rexruleVal['params'] = FALSE;
    }
    return $result;
  }
}
/**
 * This class implements the xprop table DAO
**/
class dbiCal_xprop_DAO {
  /**
   * @access   private
   * @var      object
   */
  private static $theInstance;
  /**
   * @access   private
   * @var      object
   */
  private $_log;
  /**
   * @access   private
   * @var      object
   */
  private $_db;
  /**
   * __construct
   *
   * @access   private
   * @param    object $_db
   * @param    object $_log
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  private function __construct( & $_db, & $_log ) {
    $this->_db = $_db;
    if( $_log )
      $this->_log = $_log;
    if( $this->_log )
      $this->_log->log( '************ '.get_class( $this ).' initiate ************', PEAR_LOG_DEBUG );
    self::$theInstance = FALSE;
  }
 /**
   * singleton, getter method for creating/returning the single instance of this class
   *
   * @access   private
   * @param    object $_log (reference)
   * @param    object $_DBconnection (reference)
   * @return   void
   * @since    1.0 - 2010-10-31
   */
  public static function singleton( & $_db, & $_log ) {
    if (!self::$theInstance)
      self::$theInstance = new dbiCal_xprop_DAO( $_db, $_log  );
    return self::$theInstance;
  }
  /**
   * getOwners
   *
   * @access   public
   * @param    array  $selectOptions
   * @param    array  $xpropValue
   * @return   mixed  $res (PEAR::Error eller array)
   * @since    1.0 - 2011-02-08
   */
  function getOwners( $ownertype, $selectOptions=array()) {
    if( $this->_log )
      $this->_log->log( __METHOD__." start ownertype=$ownertype selectOptions=".var_export($selectOptions, TRUE ), PEAR_LOG_INFO );
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql      = 'SELECT xprop_owner_id AS calendar_id, xprop_key, xprop_text ';
    $sql     .= 'FROM xprop WHERE xprop_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    if( isset( $selectOptions['calendar_id'] ))
      $sql   .= ' AND xprop_owner_id = '.$this->_db->quote( $selectOptions['calendar_id'], 'integer' );
    elseif( 0 < count( $selectOptions )) {
      $sql   .= ' AND (';
      $orsw   = FALSE;
      foreach( $selectOptions as $xpropName => $xpropValue ) {
        if(( 'X-' != strtoupper( substr( $xpropName, 0, 2 ))) ||
           ( in_array( strtolower( $xpropName ), array( 'date', 'filename' ))))
          continue;
        if( $orsw )
          $sql .= ' OR';
        $sql   .= ' (UPPER(xprop_key) = '.$this->_db->quote( strtoupper( $xpropName ), 'text' );
        if( !empty( $xpropValue ))
          $sql .= ' AND xprop_text = '.$this->_db->quote( $xpropValue, 'text' );
        $sql .= ')';
        $orsw  = TRUE;
      }
      if( !$orsw )
        return array(); // exit if no hits!!
      $sql   .= ')';
    }
    $sql     .= ' ORDER BY 1 DESC';
    $res      = & $this->_db->query( $sql, array( 'integer', 'text', 'text' ));
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow()) {
      if( !isset( $row['calendar_id'] ) || empty( $row['calendar_id'] ))
        continue;
      if( !isset( $result[$row['calendar_id']] ))
        $result[$row['calendar_id']] = array( 'calendar_id' => $row['calendar_id'] );
      $result[$row['calendar_id']][$row['xprop_key']] = $row['xprop_text'];
    }
    $res->free();
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    return $result;
  }
  /**
   * delete
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller TRUE)
   * @since    1.0 - 2011-11-13
   */
  function delete( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $fromWhere  = 'FROM xprop WHERE xprop_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $fromWhere .= ' AND xprop_ownertype = '.$this->_db->quote( $ownertype, 'text' );
    $sql  = "SELECT xprop_id AS id $fromWhere ORDER BY 1";
    $res  = & $this->_db->query( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $result = array();
    while( $row = $res->fetchRow())
      if( isset( $row['id'] ) && !empty( $row['id'] ))
        $result[] = $row['id'];
    $res->free();
    if( $this->_log )
      $this->_log->log( 'result='.var_export( $result, TRUE ), PEAR_LOG_DEBUG );
    if( empty( $result ))
      return null;
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $xprop_id) {
      $res = $dbiCal_parameter_DAO->delete( $xprop_id, 'xprop' );
      if( PEAR::isError( $res ))
        return $res;
    }
    $sql  = "DELETE $fromWhere";
    $res  = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
        return $res;
    }
    if( $this->_log )
      $this->_log->log( "$sql : $res", PEAR_LOG_INFO ); // show number of affected rows
    return TRUE;
  }
  /**
   * insert
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @param    string $xpropName
   * @param    array  $xpropValue
   * @return   mixed  $res (PEAR::Error eller tabell-id)
   * @since    1.0 - 2011-02-20
   */
  public function insert( $owner_id, $ownertype, $xpropName, $xpropValue ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $sql       = 'INSERT INTO xprop (xprop_owner_id, xprop_ownertype';
    $values    = ') VALUES ('.$this->_db->quote( $owner_id, 'integer' ).', '.$this->_db->quote( $ownertype, 'text' );
    if( !empty( $xpropName )) {
      $sql    .= ', xprop_key';
      $values .= ', '.$this->_db->quote( $xpropName, 'text' );
    }
    if( isset( $xpropValue['value'] ) && !empty( $xpropValue['value'] )) {
      $sql    .= ', xprop_text';
      $values .= ', '.$this->_db->quote( $xpropValue['value'], 'text' );
    }
    $sql .= $values.')';
    $res = & $this->_db->exec( $sql );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    $xprop_id = $this->_db->lastInsertID( 'xprop', 'xprop_id' );
    if( PEAR::isError( $xprop_id )) {
      if( $this->_log )
        $this->_log->log( 'lastInsertID error:'.$xprop_id->getUserInfo().PHP_EOL.$xprop_id->getMessage(), PEAR_LOG_ALERT );
      return $xprop_id;
    }
    if( $this->_log )
      $this->_log->log( $sql.PHP_EOL.'xprop_id='.$xprop_id, PEAR_LOG_INFO );
    if( isset( $xpropValue['params'] ) && !empty( $xpropValue['params'] )) {
      $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
      foreach( $xpropValue['params'] as $key => $value ) {
        if( ctype_digit( (string) $key ))
          continue;
        $res = $dbiCal_parameter_DAO->insert( $xprop_id, 'xprop', $xpropName, $key, $value );
        if( PEAR::isError( $res ))
          return $res;
      }
    }
    return $xprop_id;
  }
  /**
   * select
   *
   * @access   public
   * @param    int    $owner_id
   * @param    string $ownertype
   * @return   mixed  $res (PEAR::Error eller resultat-array)
   * @since    1.0 - 2011-01-20
   */
  function select( $owner_id, $ownertype ) {
    if( $this->_log ) {
      $this->_log->log( __METHOD__.' start', PEAR_LOG_INFO );
      $arglist    = func_get_args();
      foreach( $arglist as $aix => $arg )
        $this->_log->log( "argument [$aix]=".var_export( $arg, TRUE ), PEAR_LOG_DEBUG );
    }
    $this->_db->setFetchMode( MDB2_FETCHMODE_ASSOC );
    $sql  = 'SELECT * FROM xprop WHERE xprop_owner_id = '.$this->_db->quote( $owner_id, 'integer' );
    $sql .= ' AND xprop_ownertype = '.$this->_db->quote( $ownertype, 'text' ).' ORDER BY xprop_id';
    $types = array( 'integer', 'integer', 'text', 'text', 'text' );
    $res  = & $this->_db->query( $sql, $types );
    if( PEAR::isError( $res )) {
      if( $this->_log )
        $this->_log->log( $sql.PHP_EOL.$res->getUserInfo().PHP_EOL.$res->getMessage(), PEAR_LOG_ALERT );
      return $res;
    }
    elseif( $this->_db->getOption('result_buffering') && ( 1 > $res->numRows())) {
      $res->free();
      return null;
    }
    $result = array();
    while( $tablerow = $res->fetchRow()) {
      if( isset( $tablerow['xprop_key'] )  && !empty( $tablerow['xprop_key'] ) &&
          isset( $tablerow['xprop_text'] ) && !empty( $tablerow['xprop_text'] )) {
        $result[ $tablerow['xprop_key']]['id']    = $tablerow['xprop_id'];
        $result[ $tablerow['xprop_key']]['value'] = $tablerow['xprop_text'];
        if( $this->_log )
          $this->_log->log( $ownertype.' : '.$tablerow['xprop_key'].' : '.$tablerow['xprop_text'], PEAR_LOG_DEBUG );
      }
    }
    $res->free();
    $dbiCal_parameter_DAO = dbiCal_parameter_DAO::singleton( $this->_db, $this->_log );
    foreach( $result as $xpropName => & $pValue ) {
      $res = $dbiCal_parameter_DAO->select( $pValue['id'], 'xprop', $xpropName);
      if( PEAR::isError( $res ))
        return $res;
      if( !empty( $res )) {
        foreach( $res as $key => $value )
          $pValue['params'][$key] = $value;
      }
      else
        $pValue['params'] = FALSE;
    }
    if( $this->_log )
      $this->_log->log( $sql.' : '.count( $result ).' xprops', PEAR_LOG_INFO );
    return $result;
  }
}
?>
Return current item: dbiCal