Overview

Packages

  • Kolab
    • Storage

Classes

  • Horde_Kolab_Storage_Base
  • Horde_Kolab_Storage_Cache
  • Horde_Kolab_Storage_Cache_Data
  • Horde_Kolab_Storage_Cache_List
  • Horde_Kolab_Storage_Cached
  • Horde_Kolab_Storage_Data_Base
  • Horde_Kolab_Storage_Data_Cached
  • Horde_Kolab_Storage_Data_Decorator_Log
  • Horde_Kolab_Storage_Data_Format_Mime
  • Horde_Kolab_Storage_Data_Modifiable
  • Horde_Kolab_Storage_Data_Old
  • Horde_Kolab_Storage_Data_Parser_Structure
  • Horde_Kolab_Storage_Data_Query_History_Base
  • Horde_Kolab_Storage_Data_Query_History_Cache
  • Horde_Kolab_Storage_Data_Query_Preferences_Base
  • Horde_Kolab_Storage_Data_Query_Preferences_Cache
  • Horde_Kolab_Storage_Decorator_Synchronization
  • Horde_Kolab_Storage_Driver_Base
  • Horde_Kolab_Storage_Driver_Cclient
  • Horde_Kolab_Storage_Driver_Decorator_Base
  • Horde_Kolab_Storage_Driver_Decorator_Log
  • Horde_Kolab_Storage_Driver_Decorator_Timer
  • Horde_Kolab_Storage_Driver_Imap
  • Horde_Kolab_Storage_Driver_Mock
  • Horde_Kolab_Storage_Driver_Mock_Data
  • Horde_Kolab_Storage_Driver_Pear
  • Horde_Kolab_Storage_Driver_Rcube
  • Horde_Kolab_Storage_Exception
  • Horde_Kolab_Storage_Exception_Pear
  • Horde_Kolab_Storage_Factory
  • Horde_Kolab_Storage_Folder_Base
  • Horde_Kolab_Storage_Folder_Decorator_Base
  • Horde_Kolab_Storage_Folder_Decorator_Trigger
  • Horde_Kolab_Storage_Folder_Namespace
  • Horde_Kolab_Storage_Folder_Namespace_Config
  • Horde_Kolab_Storage_Folder_Namespace_Element
  • Horde_Kolab_Storage_Folder_Namespace_Element_Other
  • Horde_Kolab_Storage_Folder_Namespace_Element_Personal
  • Horde_Kolab_Storage_Folder_Namespace_Element_Shared
  • Horde_Kolab_Storage_Folder_Namespace_Element_SharedWithPrefix
  • Horde_Kolab_Storage_Folder_Namespace_Fixed
  • Horde_Kolab_Storage_Folder_Namespace_Imap
  • Horde_Kolab_Storage_Folder_Stamp_Uids
  • Horde_Kolab_Storage_Folder_Type
  • Horde_Kolab_Storage_List_Base
  • Horde_Kolab_Storage_List_Decorator_Cache
  • Horde_Kolab_Storage_List_Decorator_Log
  • Horde_Kolab_Storage_List_Query_Acl_Base
  • Horde_Kolab_Storage_List_Query_Acl_Cache
  • Horde_Kolab_Storage_List_Query_ActiveSync_Base
  • Horde_Kolab_Storage_List_Query_ActiveSync_Cache
  • Horde_Kolab_Storage_List_Query_List_Base
  • Horde_Kolab_Storage_List_Query_List_Cache
  • Horde_Kolab_Storage_List_Query_Share_Base
  • Horde_Kolab_Storage_List_Query_Share_Cache
  • Horde_Kolab_Storage_QuerySet_Base
  • Horde_Kolab_Storage_QuerySet_Cached
  • Horde_Kolab_Storage_QuerySet_Uncached
  • Horde_Kolab_Storage_Synchronization
  • Horde_Kolab_Storage_Translation
  • Horde_Kolab_Storage_Uncached

Interfaces

  • Horde_Kolab_Storage
  • Horde_Kolab_Storage_Data
  • Horde_Kolab_Storage_Data_Format
  • Horde_Kolab_Storage_Data_Parser
  • Horde_Kolab_Storage_Data_Query
  • Horde_Kolab_Storage_Data_Query_History
  • Horde_Kolab_Storage_Data_Query_Preferences
  • Horde_Kolab_Storage_Driver
  • Horde_Kolab_Storage_Folder
  • Horde_Kolab_Storage_Folder_Stamp
  • Horde_Kolab_Storage_List
  • Horde_Kolab_Storage_List_Query
  • Horde_Kolab_Storage_List_Query_Acl
  • Horde_Kolab_Storage_List_Query_ActiveSync
  • Horde_Kolab_Storage_List_Query_List
  • Horde_Kolab_Storage_List_Query_Share
  • Horde_Kolab_Storage_Queriable
  • Horde_Kolab_Storage_Query
  • Horde_Kolab_Storage_QuerySet
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * The Kolab_Folder class represents an single folder in the Kolab
   4:  * backend.
   5:  *
   6:  * PHP version 5
   7:  *
   8:  * @category Kolab
   9:  * @package  Kolab_Storage
  10:  * @author   Gunnar Wrobel <wrobel@pardus.de>
  11:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
  12:  * @link     http://pear.horde.org/index.php?package=Kolab_Storage
  13:  */
  14: 
  15: /**
  16:  * The Kolab_Folder class represents an single folder in the Kolab
  17:  * backend.
  18:  *
  19:  * Copyright 2004-2012 Horde LLC (http://www.horde.org/)
  20:  *
  21:  * See the enclosed file COPYING for license information (LGPL). If you
  22:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  23:  *
  24:  * @author  Stuart Binge <omicron@mighty.co.za>
  25:  * @author  Gunnar Wrobel <wrobel@pardus.de>
  26:  * @author  Thomas Jarosch <thomas.jarosch@intra2net.com>
  27:  * @package Kolab_Storage
  28:  */
  29: class Horde_Kolab_Storage_Folder_Base
  30: implements Horde_Kolab_Storage_Folder
  31: {
  32:     /**
  33:      * The handler for the list of folders.
  34:      *
  35:      * @var Horde_Kolab_Storage_List
  36:      */
  37:     private $_list;
  38: 
  39:     /**
  40:      * The folder path.
  41:      *
  42:      * @var string
  43:      */
  44:     private $_path;
  45: 
  46:     /**
  47:      * Additional folder information.
  48:      *
  49:      * @var array
  50:      */
  51:     private $_data;
  52: 
  53:     /**
  54:      * Constructor.
  55:      *
  56:      * @param Horde_Kolab_Storage_List $list The handler for the list of
  57:      *                                       folders.
  58:      * @param string                   $path Path of the folder.
  59:      */
  60:     public function __construct(Horde_Kolab_Storage_List $list, $path)
  61:     {
  62:         $this->_list = $list;
  63:         $this->_path = $path;
  64:     }
  65: 
  66:     /**
  67:      * Fetch the data array.
  68:      *
  69:      * @return NULL
  70:      */
  71:     private function _init()
  72:     {
  73:         if ($this->_data === null) {
  74:             $this->_data = $this->_list->getQuery()->folderData($this->_path);
  75:         }
  76:     }
  77: 
  78:     /**
  79:      * Fetch a data value.
  80:      *
  81:      * @param string $key The name of the data value to fetch.
  82:      *
  83:      * @return mixed The data value
  84:      */
  85:     public function get($key)
  86:     {
  87:         $this->_init();
  88:         if (isset($this->_data[$key])) {
  89:             return $this->_data[$key];
  90:         }
  91:         throw new Horde_Kolab_Storage_Exception(
  92:             sprintf('No "%s" information available!', $key)
  93:         );
  94:     }
  95: 
  96:     /**
  97:      * Fetch a data value and accept a missing value (represented by the return value NULL).
  98:      *
  99:      * @param string $key The name of the data value to fetch.
 100:      *
 101:      * @return mixed The data value
 102:      */
 103:     public function getWithNull($key)
 104:     {
 105:         $this->_init();
 106:         if (isset($this->_data[$key])) {
 107:             return $this->_data[$key];
 108:         }
 109:     }
 110: 
 111:     /**
 112:      * Return the storage path of the folder.
 113:      *
 114:      * @return string The storage path of the folder.
 115:      */
 116:     public function getPath()
 117:     {
 118:         return $this->_path;
 119:     }
 120: 
 121:     /**
 122:      * Return the namespace type of the folder.
 123:      *
 124:      * @return string The namespace type of the folder.
 125:      */
 126:     public function getNamespace()
 127:     {
 128:         return $this->get('namespace');
 129:     }
 130: 
 131:     /**
 132:      * Return the namespace prefix of the folder.
 133:      *
 134:      * @since Horde_Kolab_Storage 1.1.0
 135:      *
 136:      * @return string The namespace prefix of the folder.
 137:      */
 138:     public function getPrefix()
 139:     {
 140:         return $this->get('prefix');
 141:     }
 142: 
 143:     /**
 144:      * Returns a readable title for this folder.
 145:      *
 146:      * @return string  The folder title.
 147:      */
 148:     public function getTitle()
 149:     {
 150:         return $this->get('name');
 151:     }
 152: 
 153:     /**
 154:      * Returns the owner of the folder.
 155:      *
 156:      * @return string The owner of this folder.
 157:      */
 158:     public function getOwner()
 159:     {
 160:         return $this->getWithNull('owner');
 161:     }
 162: 
 163:     /**
 164:      * Returns the folder path without namespace components.
 165:      *
 166:      * @return string The subpath of this folder.
 167:      */
 168:     public function getSubpath()
 169:     {
 170:         return $this->get('subpath');
 171:     }
 172: 
 173:     /**
 174:      * Returns the folder parent.
 175:      *
 176:      * @return string The parent of this folder.
 177:      */
 178:     public function getParent()
 179:     {
 180:         return $this->get('parent');
 181:     }
 182: 
 183:     /**
 184:      * Is this a default folder?
 185:      *
 186:      * @return boolean Boolean that indicates the default status.
 187:      */
 188:     public function isDefault()
 189:     {
 190:         return $this->get('default');
 191:     }
 192: 
 193:     /**
 194:      * The type of this folder.
 195:      *
 196:      * @return string The folder type.
 197:      */
 198:     public function getType()
 199:     {
 200:         return $this->get('type');
 201:     }
 202: 
 203: 
 204: 
 205: 
 206: 
 207: 
 208: 
 209: 
 210: 
 211: 
 212: 
 213: 
 214: 
 215:     /**
 216:      * The root of the Kolab annotation hierarchy, used on the various IMAP
 217:      * folder that are used by Kolab clients.
 218:      */
 219:     const ANNOT_ROOT = '/shared/vendor/kolab/';
 220: 
 221:     /**
 222:      * The annotation, as defined by the Kolab format spec, that is used to store
 223:      * information about what groupware format the folder contains.
 224:      */
 225:     const ANNOT_FOLDER_TYPE = '/shared/vendor/kolab/folder-type';
 226: 
 227:     /**
 228:      * Horde-specific annotations on the imap folder have this prefix.
 229:      */
 230:     const ANNOT_SHARE_ATTR = '/shared/vendor/horde/share-';
 231: 
 232:     /**
 233:      * Kolab specific free/busy relevance
 234:      */
 235:     const FBRELEVANCE_ADMINS  = 0;
 236:     const FBRELEVANCE_READERS = 1;
 237:     const FBRELEVANCE_NOBODY  = 2;
 238: 
 239:     /**
 240:      * Additional Horde folder attributes.
 241:      *
 242:      * @var array
 243:      */
 244:     private $_attributes;
 245: 
 246:     /**
 247:      * Additional Kolab folder attributes.
 248:      *
 249:      * @var array
 250:      */
 251:     private $_kolab_attributes;
 252: 
 253:     /**
 254:      * The permission handler for the folder.
 255:      *
 256:      * @var Horde_Permission_Kolab
 257:      */
 258:     var $_perms;
 259: 
 260:     /**
 261:      * Links to the data handlers for this folder.
 262:      *
 263:      * @var array
 264:      */
 265:     //    var $_data;
 266: 
 267:     /**
 268:      * Links to the annotation data handlers for this folder.
 269:      *
 270:      * @var array
 271:      */
 272:     var $_annotation_data;
 273: 
 274:     /**
 275:      * Indicate that the folder data has been modified from the
 276:      * outside and all Data handlers need to synchronize.
 277:      *
 278:      * @var boolean
 279:      */
 280:     var $tainted = false;
 281: 
 282: 
 283: 
 284: 
 285: 
 286: 
 287: 
 288: 
 289: 
 290: 
 291:     /**
 292:      * Saves the folder.
 293:      *
 294:      * @param array $attributes An array of folder attributes. You can
 295:      *                          set any attribute but there are a few
 296:      *                          special ones like 'type', 'default',
 297:      *                          'owner' and 'desc'.
 298:      *
 299:      * @return NULL
 300:      */
 301:     public function save($attributes = null)
 302:     {
 303:         if (!isset($this->_path)) {
 304:             /* A new folder needs to be created */
 305:             if (!isset($this->_new_path)) {
 306:                 throw new Horde_Kolab_Storage_Exception('Cannot create this folder! The name has not yet been set.',
 307:                                                         Horde_Kolab_Storage_Exception::FOLDER_NAME_UNSET);
 308:             }
 309: 
 310:             if (isset($attributes['type'])) {
 311:                 $this->_type = $attributes['type'];
 312:                 unset($attributes['type']);
 313:             } else {
 314:                 $this->_type = 'mail';
 315:             }
 316: 
 317:             if (isset($attributes['default'])) {
 318:                 $this->_default = $attributes['default'];
 319:                 unset($attributes['default']);
 320:             } else {
 321:                 $this->_default = false;
 322:             }
 323: 
 324:             $result = $this->_driver->exists($this->_new_path);
 325:             if ($result) {
 326:                 throw new Horde_Kolab_Storage_Exception(sprintf("Unable to add %s: destination folder already exists",
 327:                                                                 $this->_new_path),
 328:                                                         Horde_Kolab_Storage_Exception::FOLDER_EXISTS);
 329:             }
 330: 
 331:             $this->_driver->create($this->_new_path);
 332: 
 333:             $this->_path = $this->_new_path;
 334:             $this->_new_path = null;
 335: 
 336:             /* Initialize the new folder to default permissions */
 337:             if (empty($this->_perms)) {
 338:                 $this->getPermission();
 339:             }
 340:         } else {
 341: 
 342:             $type = $this->getType();
 343: 
 344:             if (isset($attributes['type'])) {
 345:                 if ($attributes['type'] != $type) {
 346:                     Horde::logMessage(sprintf('Cannot modify the type of a folder from %s to %s!',
 347:                                               $type, $attributes['type']), 'ERR');
 348:                 }
 349:                 unset($attributes['type']);
 350:             }
 351: 
 352:             if (isset($attributes['default'])) {
 353:                 $this->_default = $attributes['default'];
 354:                 unset($attributes['default']);
 355:             } else {
 356:                 $this->_default = $this->isDefault();
 357:             }
 358: 
 359:             if (isset($this->_new_path)
 360:                 && $this->_new_path != $this->_path) {
 361:                 /** The folder needs to be renamed */
 362:                 $result = $this->_driver->exists($this->_new_path);
 363:                 if ($result) {
 364:                     throw new Horde_Kolab_Storage_Exception(sprintf(Horde_Kolab_Storage_Translation::t("Unable to rename %s to %s: destination folder already exists"),
 365:                                                                     $name, $new_name));
 366:                 }
 367: 
 368:                 $result = $this->_driver->rename($this->_path, $this->_new_path);
 369:                 $this->_storage->removeFromCache($this);
 370: 
 371:                 $this->_path     = $this->_new_path;
 372:                 $this->_new_path = null;
 373:                 $this->_title   = null;
 374:                 $this->_owner   = null;
 375:             }
 376:         }
 377: 
 378:         if (isset($attributes['owner'])) {
 379:             if ($attributes['owner'] != $this->getOwner()) {
 380:                 Horde::logMessage(sprintf('Cannot modify the owner of a folder from %s to %s!',
 381:                                           $this->getOwner(), $attributes['owner']), 'ERR');
 382:             }
 383:             unset($attributes['owner']);
 384:         }
 385: 
 386:         /** Handle the folder type */
 387:         $folder_type = $this->_type . ($this->_default ? '.default' : '');
 388:         if ($this->_type_annotation != $folder_type) {
 389:             try {
 390:                 $result = $this->_setAnnotation(self::ANNOT_FOLDER_TYPE, $folder_type);
 391:             } catch (Exception $e) {
 392:                 $this->_type = null;
 393:                 $this->_default = false;
 394:                 $this->_type_annotation = null;
 395:                 throw $e;
 396:             }
 397:         }
 398: 
 399:         if (!empty($attributes)) {
 400:             if (!is_array($attributes)) {
 401:                 $attributes = array($attributes);
 402:             }
 403:             foreach ($attributes as $key => $value) {
 404:                 if ($key == 'params') {
 405:                     $params = unserialize($value);
 406:                     if (isset($params['xfbaccess'])) {
 407:                         $result = $this->setXfbAccess($params['xfbaccess']);
 408:                         if (is_a($result, 'PEAR_Error')) {
 409:                             return $result;
 410:                         }
 411:                     }
 412:                     if (isset($params['fbrelevance'])) {
 413:                         $result = $this->setFbrelevance($params['fbrelevance']);
 414:                         if (is_a($result, 'PEAR_Error')) {
 415:                             return $result;
 416:                         }
 417:                     }
 418:                 }
 419: 
 420:                 // setAnnotation apparently does not suppoort UTF-8 nor any special characters
 421:                 $store = base64_encode($value);
 422:                 if ($key == 'desc') {
 423:                     $entry = '/shared/comment';
 424:                 } else {
 425:                     $entry = self::ANNOT_SHARE_ATTR . $key;
 426:                 }
 427:                 $result = $this->_setAnnotation($entry, $store);
 428:                 if (is_a($result, 'PEAR_Error')) {
 429:                     return $result;
 430:                 }
 431:             }
 432:             $this->_attributes = $attributes;
 433:         }
 434: 
 435:         /** Now save the folder permissions */
 436:         if (isset($this->_perms)) {
 437:             $this->_perms->save();
 438:             $this->_perms = null;
 439:         }
 440: 
 441:         $this->_storage->addToCache($this);
 442: 
 443:         return true;
 444:     }
 445: 
 446:     /**
 447:      * Delete this folder.
 448:      *
 449:      * @return boolean|PEAR_Error True if the operation succeeded.
 450:      */
 451:     function delete()
 452:     {
 453:         $this->_driver->delete($this->_path);
 454:         $this->_storage->removeFromCache($this);
 455:         return true;
 456:     }
 457: 
 458:     /**
 459:      * Returns one of the attributes of the folder, or an empty string
 460:      * if it isn't defined.
 461:      *
 462:      * @param string $attribute  The attribute to retrieve.
 463:      *
 464:      * @return mixed The value of the attribute, an empty string or an
 465:      *               error.
 466:      */
 467:     function getAttribute($attribute)
 468:     {
 469:         if (!isset($this->_attributes[$attribute])) {
 470:             if ($attribute == 'desc') {
 471:                 $entry = '/comment';
 472:             } else {
 473:                 $entry = self::ANNOT_SHARE_ATTR . $attribute;
 474:             }
 475:             $annotation = $this->_getAnnotation($entry, $this->_path);
 476:             if (is_a($annotation, 'PEAR_Error')) {
 477:                 return $annotation;
 478:             }
 479:             if (empty($annotation)) {
 480:                 $this->_attributes[$attribute] = '';
 481:             } else {
 482:                 $this->_attributes[$attribute] = base64_decode($annotation);
 483:             }
 484:         }
 485:         return $this->_attributes[$attribute];
 486:     }
 487: 
 488:     /**
 489:      * Returns one of the Kolab attributes of the folder, or an empty
 490:      * string if it isn't defined.
 491:      *
 492:      * @param string $attribute  The attribute to retrieve.
 493:      *
 494:      * @return mixed The value of the attribute, an empty string or an
 495:      *               error.
 496:      */
 497:     function getKolabAttribute($attribute)
 498:     {
 499:         if (!isset($this->_kolab_attributes[$attribute])) {
 500:             $entry = KOLAB_ANNOT_ROOT . $attribute;
 501:             $annotation = $this->_getAnnotation($entry, $this->_path);
 502:             if (is_a($annotation, 'PEAR_Error')) {
 503:                 return $annotation;
 504:             }
 505:             if (empty($annotation)) {
 506:                 $this->_kolab_attributes[$attribute] = '';
 507:             } else {
 508:                 $this->_kolab_attributes[$attribute] = $annotation;
 509:             }
 510:         }
 511:         return $this->_kolab_attributes[$attribute];
 512:     }
 513: 
 514: 
 515:     /**
 516:      * Returns whether the folder exists.
 517:      *
 518:      * @return boolean|PEAR_Error  True if the folder exists.
 519:      */
 520:     function exists()
 521:     {
 522:         if ($this->_path === null) {
 523:             return false;
 524:         }
 525:         try {
 526:             return $this->_driver->exists($this->_path);
 527:         } catch (Horde_Imap_Client_Exception $e) {
 528:             return false;
 529:         }
 530:     }
 531: 
 532:     /**
 533:      * Retrieve a handler for the data in this folder.
 534:      *
 535:      * @param Kolab_List $list  The handler for the list of folders.
 536:      *
 537:      * @return Horde_Kolab_Storage_Data The data handler.
 538:      */
 539:     public function getData($object_type = null, $data_version = 1)
 540:     {
 541:         if (empty($object_type)) {
 542:             $object_type = $this->getType();
 543:             if (is_a($object_type, 'PEAR_Error')) {
 544:                 return $object_type;
 545:             }
 546:         }
 547: 
 548:         if ($this->tainted) {
 549:             foreach ($this->_data as $data) {
 550:                 $data->synchronize();
 551:             }
 552:             $this->tainted = false;
 553:         }
 554: 
 555:         $key = $object_type . '|' . $data_version;
 556:         if (!isset($this->_data[$key])) {
 557:             if ($object_type != 'annotation') {
 558:                 $type = $this->getType();
 559:             } else {
 560:                 $type = 'annotation';
 561:             }
 562:             $data = new Horde_Kolab_Storage_Data($type, $object_type, $data_version);
 563:             $data->setFolder($this);
 564:             $data->setCache($this->_storage->getDataCache());
 565:             $data->synchronize();
 566:             $this->_data[$key] = &$data;
 567:         }
 568:         return $this->_data[$key];
 569:     }
 570: 
 571:     /**
 572:      * Delete the specified message from this folder.
 573:      *
 574:      * @param  string  $id      IMAP id of the message to be deleted.
 575:      * @param  boolean $trigger Should the folder be triggered?
 576:      *
 577:      * @return NULL
 578:      */
 579:     public function deleteMessage($id, $trigger = true)
 580:     {
 581:         // Select folder
 582:         $this->_driver->deleteMessages($this->_path, $id);
 583:         $this->_driver->expunge($this->_path);
 584:     }
 585: 
 586:     /**
 587:      * Move the specified message to the specified folder.
 588:      *
 589:      * @param string $id     IMAP id of the message to be moved.
 590:      * @param string $folder Name of the receiving folder.
 591:      *
 592:      * @return boolean True if successful.
 593:      */
 594:     public function moveMessage($id, $folder)
 595:     {
 596:         $this->_driver->select($this->_path);
 597:         $this->_driver->moveMessage($this->_path, $id, $folder);
 598:         $this->_driver->expunge($this->_path);
 599:     }
 600: 
 601:     /**
 602:      * Move the specified message to the specified share.
 603:      *
 604:      * @param string $id    IMAP id of the message to be moved.
 605:      * @param string $share Name of the receiving share.
 606:      *
 607:      * @return NULL
 608:      */
 609:     public function moveMessageToShare($id, $share)
 610:     {
 611:         $folder = $this->_storage->getByShare($share, $this->getType());
 612:         $folder->tainted = true;
 613: 
 614:         $success = $this->moveMessage($id, $folder->name);
 615:     }
 616: 
 617:     /**
 618:      * Retrieve the supported formats.
 619:      *
 620:      * @return array The names of the supported formats.
 621:      */
 622:     function getFormats()
 623:     {
 624:         global $conf;
 625: 
 626:         if (empty($conf['kolab']['misc']['formats'])) {
 627:             $formats = array('XML');
 628:         } else {
 629:             $formats = $conf['kolab']['misc']['formats'];
 630:         }
 631:         if (!is_array($formats)) {
 632:             $formats = array($formats);
 633:         }
 634:         if (!in_array('XML', $formats)) {
 635:             $formats[] = 'XML';
 636:         }
 637:         return $formats;
 638:     }
 639: 
 640:     /**
 641:      * Save an object in this folder.
 642:      *
 643:      * @param array  $object       The array that holds the data of the object.
 644:      * @param int    $data_version The format handler version.
 645:      * @param string $object_type  The type of the kolab object.
 646:      * @param string $id           The IMAP id of the old object if it
 647:      *                             existed before
 648:      * @param array  $old_object   The array that holds the current data of the
 649:      *                             object.
 650:      *
 651:      * @return boolean True on success.
 652:      */
 653:     public function saveObject(&$object, $data_version, $object_type, $id = null,
 654:                         &$old_object = null)
 655:     {
 656:         // Select folder
 657:         $this->_driver->select($this->_path);
 658: 
 659:         $new_headers = new Horde_Mime_Headers();
 660:         $new_headers->setEOL("\r\n");
 661: 
 662:         $formats = $this->getFormats();
 663: 
 664:         $handlers = array();
 665:         foreach ($formats as $type) {
 666:             $handlers[$type] = &Horde_Kolab_Format::factory($type, $object_type,
 667:                                                             $data_version);
 668:             if (is_a($handlers[$type], 'PEAR_Error')) {
 669:                 if ($type == 'XML') {
 670:                     return $handlers[$type];
 671:                 }
 672:                 Horde::logMessage(sprintf('Loading format handler "%s" failed: %s',
 673:                                           $type, $handlers[$type]->getMessage()), 'ERR');
 674:                 continue;
 675:             }
 676:         }
 677: 
 678:         if ($id != null) {
 679:             /** Update an existing kolab object */
 680:             if (!in_array($id, $this->_driver->getUids($this->_path))) {
 681:                 return PEAR::raiseError(sprintf(Horde_Kolab_Storage_Translation::t("The message with ID %s does not exist. This probably means that the Kolab object has been modified by somebody else while you were editing it. Your edits have been lost."),
 682:                                                 $id));
 683:             }
 684: 
 685:             /** Parse email and load Kolab format structure */
 686:             $result = $this->parseMessage($id, $handlers['XML']->getMimeType(),
 687:                                           true, $formats);
 688:             if (is_a($result, 'PEAR_Error')) {
 689:                 return $result;
 690:             }
 691:             list($old_message, $part_ids, $mime_message, $mime_headers) = $result;
 692:             if (is_a($old_message, 'PEAR_Error')) {
 693:                 return $old_message;
 694:             }
 695: 
 696:             if (isset($object['_attachments']) && isset($old_object['_attachments'])) {
 697:                 $attachments = array_keys($object['_attachments']);
 698:                 foreach (array_keys($old_object['_attachments']) as $attachment) {
 699:                     if (!in_array($attachment, $attachments)) {
 700:                         foreach ($mime_message->getParts() as $part) {
 701:                             if ($part->getName() === $attachment) {
 702:                                 foreach (array_keys($mime_message->_parts) as $key) {
 703:                                     if ($mime_message->_parts[$key]->getMimeId() == $part->getMimeId()) {
 704:                                         unset($mime_message->_parts[$key]);
 705:                                         break;
 706:                                     }
 707:                                 }
 708:                                 $mime_message->_generateIdMap($mime_message->_parts);
 709:                             }
 710:                         }
 711:                     }
 712:                 }
 713:             }
 714:             $object = array_merge($old_object, $object);
 715: 
 716:             if (isset($attachments)) {
 717:                 foreach ($mime_message->getParts() as $part) {
 718:                     $name = $part->getName();
 719:                     foreach ($attachments as $attachment) {
 720:                         if ($name === $attachment) {
 721:                             $object['_attachments'][$attachment]['id'] = $part->getMimeId();
 722:                         }
 723:                     }
 724:                 }
 725:             }
 726: 
 727:             /** Copy email header */
 728:             if (!empty($mime_headers) && !$mime_headers === false) {
 729:                 foreach ($mime_headers as $header => $value) {
 730:                     $new_headers->addheader($header, $value);
 731:                 }
 732:             }
 733:         } else {
 734:             $mime_message = $this->_prepareNewMessage($new_headers);
 735:             $mime_part_id = false;
 736:         }
 737: 
 738:         if (isset($object['_attachments'])) {
 739:             $attachments = array_keys($object['_attachments']);
 740:             foreach ($attachments as $attachment) {
 741:                 $data = $object['_attachments'][$attachment];
 742: 
 743:                 if (!isset($data['content']) && !isset($data['path'])) {
 744:                     /**
 745:                      * There no new content and no new path. Do not rewrite the
 746:                      * attachment.
 747:                      */
 748:                     continue;
 749:                 }
 750: 
 751:                 $part = new Horde_Mime_Part();
 752:                 $part->setType(isset($data['type']) ? $data['type'] : null);
 753:                 $part->setContents(isset($data['content']) ? $data['content'] : file_get_contents($data['path']));
 754:                 $part->setCharset('UTF-8');
 755:                 $part->setTransferEncoding('quoted-printable');
 756:                 $part->setDisposition('attachment');
 757:                 $part->setName($attachment);
 758: 
 759:                 if (!isset($data['id'])) {
 760:                     $mime_message->addPart($part);
 761:                 } else {
 762:                     $mime_message->alterPart($data['id'], $part);
 763:                 }
 764:             }
 765:         }
 766: 
 767:         foreach ($formats as $type) {
 768:             $new_content = $handlers[$type]->save($object);
 769:             if (is_a($new_content, 'PEAR_Error')) {
 770:                 return $new_content;
 771:             }
 772: 
 773:             /** Update mime part */
 774:             $part = new Horde_Mime_Part();
 775:             $part->setType($handlers[$type]->getMimeType());
 776:             $part->setContents($new_content);
 777:             $part->setCharset('UTF-8');
 778:             $part->setTransferEncoding('quoted-printable');
 779:             $part->setDisposition($handlers[$type]->getDisposition());
 780:             $part->setDispositionParameter('x-kolab-type', $type);
 781:             $part->setName($handlers[$type]->getName());
 782: 
 783:             if (!isset($part_ids) || $part_ids[$type] === false) {
 784:                 $mime_message->addPart($part);
 785:             } else {
 786:                 $mime_message->alterPart($part_ids[$type], $part);
 787:             }
 788:         }
 789: 
 790:         // Update email headers
 791:         $new_headers->addHeader('From', $this->_driver->getAuth());
 792:         $new_headers->addHeader('To', $this->_driver->getAuth());
 793:         $new_headers->addHeader('Date', date('r'));
 794:         $new_headers->addHeader('X-Kolab-Type', $handlers['XML']->getMimeType());
 795:         $new_headers->addHeader('Subject', $object['uid']);
 796:         $new_headers->addHeader('User-Agent', 'Horde::Kolab::Storage v0.2');
 797:         $new_headers->addHeader('MIME-Version', '1.0');
 798:         $mime_message->addMimeHeaders(array('headers' => $new_headers));
 799: 
 800:         $msg = $new_headers->toString() . $mime_message->toString(array('canonical' => true,
 801:                                                                         'headers' => false));
 802: 
 803:         // delete old email?
 804:         if ($id != null) {
 805:             $this->_driver->deleteMessages($this->_path, $id);
 806:         }
 807: 
 808:         // store new email
 809:         try {
 810:             $result = $this->_driver->appendMessage($this->_path, $msg);
 811:         } catch (Horde_Kolab_Storage_Exception $e) {
 812:             if ($id != null) {
 813:                 $this->_driver->undeleteMessages($id);
 814:             }
 815:         }
 816: 
 817:         // remove deleted object
 818:         if ($id != null) {
 819:             $this->_driver->expunge($this->_path);
 820:         }
 821:     }
 822: 
 823:     /**
 824:      * Get an IMAP message and retrieve the Kolab Format object.
 825:      *
 826:      * @param int     $id             The message to retrieve.
 827:      * @param string  $mime_type      The mime type of the part to retrieve.
 828:      * @param boolean $parse_headers  Should the heades be Mime parsed?
 829:      * @param array   $formats        The list of possible format parts.
 830:      *
 831:      * @return array|PEAR_Error An array that list the Kolab XML
 832:      *                          object text, the mime ID of the part
 833:      *                          with the XML object, the Mime parsed
 834:      *                          message and the Mime parsed headers if
 835:      *                          requested.
 836:      */
 837:     function parseMessage($id, $mime_type, $parse_headers = true,
 838:                           $formats = array('XML'))
 839:     {
 840:         $raw_headers = $this->_driver->getMessageHeader($this->_path, $id);
 841:         if (is_a($raw_headers, 'PEAR_Error')) {
 842:             return PEAR::raiseError(sprintf(Horde_Kolab_Storage_Translation::t("Failed retrieving the message with ID %s. Original error: %s."),
 843:                                             $id, $raw_headers->getMessage()));
 844:         }
 845: 
 846:         $body = $this->_driver->getMessageBody($this->_path, $id);
 847:         if (is_a($body, 'PEAR_Error')) {
 848:             return PEAR::raiseError(sprintf(Horde_Kolab_Storage_Translation::t("Failed retrieving the message with ID %s. Original error: %s."),
 849:                                             $id, $body->getMessage()));
 850:         }
 851: 
 852:         //@todo: not setting "forcemime" means the subparts get checked too. Seems incorrect.
 853:         $mime_message = Horde_Mime_Part::parseMessage($raw_headers . "\r" . $body, array('forcemime' => true));
 854:         $parts = $mime_message->contentTypeMap();
 855: 
 856:         $mime_headers = false;
 857:         $xml = false;
 858: 
 859:         // Read in a Kolab event object, if one exists
 860:         $part_ids['XML'] = array_search($mime_type, $parts);
 861:         if ($part_ids['XML'] !== false) {
 862:             if ($parse_headers) {
 863:                 $mime_headers = Horde_Mime_Headers::parseHeaders($raw_headers);
 864:                 $mime_headers->setEOL("\r\n");
 865:             }
 866: 
 867:             $part = $mime_message->getPart($part_ids['XML']);
 868:             //@todo: Check what happened to this call
 869:             //$part->transferDecodeContents();
 870:             $xml = $part->getContents();
 871:         }
 872: 
 873:         $alternate_formats = array_diff(array('XML'), $formats);
 874:         if (!empty($alternate_formats)) {
 875:             foreach ($alternate_formats as $type) {
 876:                 $part_ids[$type] = false;
 877:             }
 878:             foreach ($mime_message->getParts() as $part) {
 879:                 $params = $part->getDispositionParameters();
 880:                 foreach ($alternate_formats as $type) {
 881:                     if (isset($params['x-kolab-format'])
 882:                         && $params['x-kolab-format'] == $type) {
 883:                         $part_ids[$type] = $part->getMimeId();
 884:                     }
 885:                 }
 886:             }
 887:         }
 888: 
 889:         $result = array($xml, $part_ids, $mime_message, $mime_headers);
 890:         return $result;
 891:     }
 892: 
 893:     /**
 894:      * Get annotation values on IMAP servers that do not support
 895:      * METADATA.
 896:      *
 897:      * @return array|PEAR_Error  The anotations of this folder.
 898:      */
 899:     function _getAnnotationData()
 900:     {
 901:         $this->_annotation_data = $this->getData('annotation');
 902:     }
 903: 
 904: 
 905:     /**
 906:      * Get an annotation value of this folder.
 907:      *
 908:      * @param $key The key of the annotation to retrieve.
 909:      *
 910:      * @return string|PEAR_Error  The anotation value.
 911:      */
 912:     function _getAnnotation($key)
 913:     {
 914:         global $conf;
 915: 
 916:         if (empty($conf['kolab']['imap']['no_annotations'])) {
 917:             return $this->_driver->getAnnotation($key, $this->_path);
 918:         }
 919: 
 920:         if (!isset($this->_annotation_data)) {
 921:             $this->_getAnnotationData();
 922:         }
 923:         $data = $this->_annotation_data->getObject('KOLAB_FOLDER_CONFIGURATION');
 924:         if (is_a($data, 'PEAR_Error')) {
 925:             Horde::logMessage(sprintf('Error retrieving annotation data on folder %s: %s',
 926:                                       $this->_path, $data->getMessage()), 'ERR');
 927:             return '';
 928:         }
 929:         if (isset($data[$key])) {
 930:             return $data[$key];
 931:         } else {
 932:             return '';
 933:         }
 934:     }
 935: 
 936:     /**
 937:      * Set an annotation value of this folder.
 938:      *
 939:      * @param $key   The key of the annotation to change.
 940:      * @param $value The new value.
 941:      *
 942:      * @return boolean|PEAR_Error  True on success.
 943:      */
 944:     function _setAnnotation($key, $value)
 945:     {
 946:         if (empty($conf['kolab']['imap']['no_annotations'])) {
 947:             return $this->_driver->setAnnotation($key, $value, $this->_path);
 948:         }
 949: 
 950:         if (!isset($this->_annotation_data)) {
 951:             $this->_getAnnotationData();
 952:         }
 953:         $data = $this->_annotation_data->getObject('KOLAB_FOLDER_CONFIGURATION');
 954:         if (is_a($data, 'PEAR_Error')) {
 955:             Horde::logMessage(sprintf('Error retrieving annotation data on folder %s: %s',
 956:                                       $this->_path, $data->getMessage()), 'ERR');
 957:             $data = array();
 958:             $uid = null;
 959:         } else {
 960:             $uid = 'KOLAB_FOLDER_CONFIGURATION';
 961:         }
 962:         $data[$key] = $value;
 963:         $data['uid'] = 'KOLAB_FOLDER_CONFIGURATION';
 964:         return $this->_annotation_data->save($data, $uid);
 965:     }
 966: 
 967: 
 968: 
 969:     /**
 970:      * Get the free/busy relevance for this folder
 971:      *
 972:      * @return int  Value containing the FB_RELEVANCE.
 973:      */
 974:     function getFbrelevance()
 975:     {
 976:         $result = $this->getKolabAttribute('incidences-for');
 977:         if (is_a($result, 'PEAR_Error') || empty($result)) {
 978:             return KOLAB_FBRELEVANCE_ADMINS;
 979:         }
 980:         switch ($result) {
 981:         case 'admins':
 982:             return KOLAB_FBRELEVANCE_ADMINS;
 983:         case 'readers':
 984:             return KOLAB_FBRELEVANCE_READERS;
 985:         case 'nobody':
 986:             return KOLAB_FBRELEVANCE_NOBODY;
 987:         default:
 988:             return KOLAB_FBRELEVANCE_ADMINS;
 989:         }
 990:     }
 991: 
 992:     /**
 993:      * Set the free/busy relevance for this folder
 994:      *
 995:      * @param int $relevance Value containing the FB_RELEVANCE
 996:      *
 997:      * @return mixed  True on success or a PEAR_Error.
 998:      */
 999:     function setFbrelevance($relevance)
1000:     {
1001:         switch ($relevance) {
1002:         case KOLAB_FBRELEVANCE_ADMINS:
1003:             $value = 'admins';
1004:             break;
1005:         case KOLAB_FBRELEVANCE_READERS:
1006:             $value = 'readers';
1007:             break;
1008:         case KOLAB_FBRELEVANCE_NOBODY:
1009:             $value = 'nobody';
1010:             break;
1011:         default:
1012:             $value = 'admins';
1013:         }
1014: 
1015:         return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'incidences-for',
1016:                                      $value);
1017:     }
1018: 
1019:     /**
1020:      * Get the extended free/busy access settings for this folder
1021:      *
1022:      * @return array  Array containing the users with access to the
1023:      *                extended information.
1024:      */
1025:     function getXfbaccess()
1026:     {
1027:         $result = $this->getKolabAttribute('pxfb-readable-for');
1028:         if (is_a($result, 'PEAR_Error') || empty($result)) {
1029:             return array();
1030:         }
1031:         return explode(' ', $result);
1032:     }
1033: 
1034:     /**
1035:      * Set the extended free/busy access settings for this folder
1036:      *
1037:      * @param array $access  Array containing the users with access to the
1038:      *                      extended information.
1039:      *
1040:      * @return mixed  True on success or a PEAR_Error.
1041:      */
1042:     function setXfbaccess($access)
1043:     {
1044:         $value = join(' ', $access);
1045:         return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'pxfb-readable-for',
1046:                                      $value);
1047:     }
1048: }
1049: 
API documentation generated by ApiGen