From: Gunnar Wrobel Date: Sun, 30 Aug 2009 21:17:37 +0000 (+0200) Subject: Further cleanup for Horde 4. Renamed XML classes to Xml. Some tests still failing... X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=fc5a4a8abd16bcafeefec01bade6d115cd51d861;p=horde.git Further cleanup for Horde 4. Renamed XML classes to Xml. Some tests still failing though. --- diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format.php b/framework/Kolab_Format/lib/Horde/Kolab/Format.php index 37dc3f47b..06e422213 100644 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format.php +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format.php @@ -54,7 +54,7 @@ abstract class Horde_Kolab_Format static public function &factory($format_type = '', $object_type = '', $params = null) { - $class = 'Horde_Kolab_Format_' . $format_type; + $class = 'Horde_Kolab_Format_' . ucfirst(strtolower($format_type)); if (class_exists($class)) { $driver = call_user_func(array($class, 'factory'), $object_type, $params); diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/XML.php deleted file mode 100644 index 269cb9b0d..000000000 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML.php +++ /dev/null @@ -1,1420 +0,0 @@ - - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - */ - -/** - * Kolab XML to array hash converter. - * - * For implementing a new format type you will have to inherit this - * class and provide a _load/_save function. - * - * Copyright 2007-2009 Klarälvdalens Datakonsult AB - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @category Kolab - * @package Kolab_Format - * @author Thomas Jarosch - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - * @since Horde 3.2 - */ -class Horde_Kolab_Format_XML -{ - - /** - * Defines a XML value that should get a default value if missing - */ - const PRODUCT_ID = 'Horde::Kolab'; - - /** - * Defines a XML value that should get a default value if missing - */ - const VALUE_DEFAULT = 0; - - /** - * Defines a XML value that may be missing - */ - const VALUE_MAYBE_MISSING = 1; - - /** - * Defines a XML value that may not be missing - */ - const VALUE_NOT_EMPTY = 2; - - /** - * Defines a XML value that will be calculated by its own function - */ - const VALUE_CALCULATED = 3; - - /** - * Defines a XML value as string type - */ - const TYPE_STRING = 0; - - /** - * Defines a XML value as integer type - */ - const TYPE_INTEGER = 1; - - /** - * Defines a XML value as boolean type - */ - const TYPE_BOOLEAN = 2; - - /** - * Defines a XML value as date type - */ - const TYPE_DATE = 3; - - /** - * Defines a XML value as datetime type - */ - const TYPE_DATETIME = 4; - - /** - * Defines a XML value as date or datetime type - */ - const TYPE_DATE_OR_DATETIME = 5; - - /** - * Defines a XML value as color type - */ - const TYPE_COLOR = 6; - - /** - * Defines a XML value as composite value type - */ - const TYPE_COMPOSITE = 7; - - /** - * Defines a XML value as array type - */ - const TYPE_MULTIPLE = 8; - - /** - * Requested version of the data array to return - * - * @var int - */ - protected $_version = 1; - - /** - * The name of the resulting document. - * - * @var string - */ - protected $_name = 'kolab.xml'; - - /** - * The XML document this driver works with. - * - * @var Horde_DOM_Document - * - * @todo Make protected (fix the XmlTest for that) - */ - public $_xmldoc = null; - - /** - * The name of the root element. - * - * @var string - */ - protected $_root_name = 'kolab'; - - /** - * Kolab format version of the root element. - * - * @var string - */ - protected $_root_version = '1.0'; - - /** - * Basic fields in any Kolab object - * - * @var array - */ - protected $_fields_basic; - - /** - * Automatically create categories if they are missing? - * - * @var boolean - */ - protected $_create_categories = true; - - /** - * Fields for a simple person - * - * @var array - * - * @todo Make protected (fix the XmlTest for that) - */ - public $_fields_simple_person = array( - 'type' => self::TYPE_COMPOSITE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'display-name' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'smtp-address' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'uid' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - ), - ); - - /** - * Fields for an attendee - * - * @var array - * - * @todo Make protected (fix the XmlTest for that) - */ - public $_fields_attendee = array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_DEFAULT, - 'default' => array(), - 'array' => array( - 'type' => self::TYPE_COMPOSITE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'display-name' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'smtp-address' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'status' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => 'none', - ), - 'request-response' => array( - 'type' => self::TYPE_BOOLEAN, - 'value' => self::VALUE_DEFAULT, - 'default' => true, - ), - 'role' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => 'required', - ), - ), - ), - ); - - /** - * Fields for a recurrence - * - * @var array - */ - protected $_fields_recurrence = array( - // Attribute on root node: cycle - // Attribute on root node: type - 'interval' => array( - 'type' => self::TYPE_INTEGER, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'day' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ), - 'daynumber' => array( - 'type' => self::TYPE_INTEGER, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'month' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - // Attribute on range: type - 'range' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'exclusion' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ), - 'complete' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ), - ); - - /** - * Constructor - * - * @param array $params Any additional options - */ - public function __construct($params = null) - { - if (is_array($params) && isset($params['version'])) { - $this->_version = $params['version']; - } else { - $this->_version = 1; - } - - /* Generic fields, in kolab format specification order */ - $this->_fields_basic = array( - 'uid' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_NOT_EMPTY, - ), - 'body' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'categories' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'creation-date' => array( - 'type' => self::TYPE_DATETIME, - 'value' => self::VALUE_CALCULATED, - 'load' => 'CreationDate', - 'save' => 'CreationDate', - ), - 'last-modification-date' => array( - 'type' => self::TYPE_DATETIME, - 'value' => self::VALUE_CALCULATED, - 'load' => 'ModificationDate', - 'save' => 'ModificationDate', - ), - 'sensitivity' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => 'public', - ), - 'inline-attachment' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ), - 'link-attachment' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ), - 'product-id' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_CALCULATED, - 'load' => 'ProductId', - 'save' => 'ProductId', - ), - ); - } - - /** - * Attempts to return a concrete Horde_Kolab_Format_XML instance. - * based on $object_type. - * - * @param string $object_type The object type that should be handled. - * @param array $params Any additional parameters. - * - * @return Horde_Kolab_Format_XML The newly created concrete - * Horde_Kolab_Format_XML instance. - * - * @throws Horde_Exception If the class for the object type could - * not be loaded. - */ - public function &factory($object_type = '', $params = null) - { - $object_type = ucfirst(str_replace('-', '', $object_type)); - $class = 'Horde_Kolab_Format_XML_' . $object_type; - - if (class_exists($class)) { - $driver = &new $class($params); - } else { - throw new Horde_Exception(sprintf(_("Failed to load Kolab XML driver %s"), - $object_type)); - } - - return $driver; - } - - /** - * Return the name of the resulting document. - * - * @return string The name that may be used as filename. - */ - public function getName() - { - return $this->_name; - } - - /** - * Return the mime type of the resulting document. - * - * @return string The mime type of the result. - */ - public function getMimeType() - { - return 'application/x-vnd.kolab.' . $this->_root_name; - } - - /** - * Return the disposition of the resulting document. - * - * @return string The disportion of this document. - */ - public function getDisposition() - { - return 'attachment'; - } - - /** - * Load an object based on the given XML string. - * - * @todo Check encoding of the returned array. It seems to be ISO-8859-1 at - * the moment and UTF-8 would seem more appropriate. - * - * @param string $xmltext The XML of the message as string. - * - * @return array The data array representing the object. - * - * @throws Horde_Exception If parsing the XML data failed. - */ - public function load(&$xmltext) - { - try { - $this->_parseXml($xmltext); - } catch (DOMException $e) { - /** - * If the first call does not return successfully this might mean we - * got an attachment with broken encoding. There are some Kolab - * client versions in the wild that might have done that. So the - * next section starts a second attempt by guessing the encoding and - * trying again. - */ - if (strcasecmp(mb_detect_encoding($xmltext, - 'UTF-8, ISO-8859-1'), 'UTF-8') !== 0) { - $xmltext = mb_convert_encoding($xmltext, 'UTF-8', 'ISO-8859-1'); - } - $this->_parseXml($xmltext); - } - if (empty($this->_xmldoc)) { - return false; - } - - if (!$this->_xmldoc->hasChildNodes()) { - throw new Horde_Exception(_("No or unreadable content in Kolab XML object")); - } - - // fresh object data - $object = array(); - - $result = $this->_loadArray($this->_xmldoc->childNodes, $this->_fields_basic); - $object = array_merge($object, $result); - $this->_loadMultipleCategories($object); - - $result = $this->_load($this->_xmldoc->childNodes); - $object = array_merge($object, $result); - - // uid is vital - if (!isset($object['uid'])) { - throw new Horde_Exception(_("UID not found in Kolab XML object")); - } - - return $object; - } - - /** - * Load the groupware object based on the specifc XML values. - * - * @param array $children An array of XML nodes. - * - * @return array The data array representing the object. - * - * @throws Horde_Exception If parsing the XML data failed. - */ - protected function _load(&$children) - { - if (!empty($this->_fields_specific)) { - return $this->_loadArray($children, $this->_fields_specific); - } else { - return array(); - } - } - - /** - * Load an array with data from the XML nodes. - * - * @param array $object The resulting data array. - * @param array $children An array of XML nodes. - * @param array $fields The fields to populate in the object array. - * - * @return boolean True on success. - * - * @throws Horde_Exception If parsing the XML data failed. - */ - protected function _loadArray(&$children, $fields) - { - $object = array(); - - // basic fields below the root node - foreach($fields as $field => $params) { - $result = $this->_getXmlData($children, $field, $params); - if (isset($result)) { - $object[$field] = $result; - } - } - return $object; - } - - /** - * Get the text content of the named data node among the specified - * children. - * - * @param array &$children The children to search. - * @param string $name The name of the node to return. - * @param array $params Parameters for the data conversion. - * - * @return string The content of the specified node or an empty - * string. - * - * @throws Horde_Exception If parsing the XML data failed. - * - * @todo Make protected (fix the XmlTest for that) - */ - public function _getXmlData(&$children, $name, $params) - { - if ($params['type'] == self::TYPE_MULTIPLE) { - $result = array(); - foreach($children as $child) { - if ($child->nodeType == XML_ELEMENT_NODE && $child->tagName == $name) { - $value = $this->_getXmlData(array($child), $name, - $params['array']); - $result[] = $value; - } - } - return $result; - } - - $value = null; - $missing = false; - - // identify the child node - $child = $this->_findNode($children, $name); - - // Handle empty values - if (!$child) { - if ($params['value'] == self::VALUE_MAYBE_MISSING) { - // 'MAYBE_MISSING' means we should return null - return null; - } elseif ($params['value'] == self::VALUE_NOT_EMPTY) { - // May not be empty. Return an error - throw new Horde_Exception(sprintf(_("Data value for %s is empty in Kolab XML object!"), - $name)); - } elseif ($params['value'] == self::VALUE_DEFAULT) { - // Return the default - return $params['default']; - } elseif ($params['value'] == self::VALUE_CALCULATED) { - $missing = true; - } - } - - // Do we need to calculate the value? - if ($params['value'] == self::VALUE_CALCULATED && isset($params['load'])) { - if (method_exists($this, '_load' . $params['load'])) { - $value = call_user_func(array($this, '_load' . $params['load']), - $child, $missing); - } else { - throw new Horde_Exception(sprintf("Kolab XML: Missing function %s!", - $params['load'])); - } - } elseif ($params['type'] == self::TYPE_COMPOSITE) { - return $this->_loadArray($child->childNodes, $params['array']); - } else { - return $this->_loadDefault($child, $params); - } - - // Nothing specified. Return the value as it is. - return $value; - } - - /** - * Parse the XML string. The root node is returned on success. - * - * @param string $xmltext The XML of the message as string. - * - * @return NULL - * - * @throws Horde_Exception If parsing the XML data failed. - * - * @todo Make protected (fix the XmlTest for that) - */ - public function _parseXml(&$xmltext) - { - $this->_xmldoc = new DOMDocument(); - $this->_xmldoc->preserveWhiteSpace = false; - $this->_xmldoc->formatOutput = true; - $this->_xmldoc->loadXML($xmltext); - } - - /** - * Convert the data to a XML string. - * - * @param array $attributes The data array representing the note. - * - * @return string The data as XML string. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - public function save($object) - { - $root = $this->_prepareSave(); - - $this->_saveMultipleCategories($object); - $this->_saveArray($root, $object, $this->_fields_basic); - $this->_save($root, $object); - - return $this->_xmldoc->saveXML(); - } - - /** - * Save the specific XML values. - * - * @param array &$root The XML document root. - * @param array $object The resulting data array. - * - * @return boolean True on success. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _save(&$root, $object) - { - if (!empty($this->_fields_specific)) { - $this->_saveArray($root, $object, $this->_fields_specific); - } - return true; - } - - /** - * Creates a new XML document if necessary. - * - * @param string $xmltext The XML of the message as string. - * - * @return Horde_DOM_Node The root node of the document. - * - * @todo Make protected (fix the XmlTest for that) - */ - public function &_prepareSave() - { - if (empty($this->_xmldoc)) { - // create new XML - $this->_xmldoc = new DOMDocument(); - $this->_xmldoc->preserveWhiteSpace = false; - $this->_xmldoc->formatOutput = true; - $root = $this->_xmldoc->createElement($this->_root_name); - $root = $this->_xmldoc->appendChild($root); - $root->setAttribute('version', $this->_root_version); - } - return $this->_xmldoc; - } - - /** - * Save a data array to XML nodes. - * - * @param array $root The XML document root. - * @param array $object The data array. - * @param array $fields The fields to write into the XML object. - * @param boolean $append Should the nodes be appended? - * - * @return boolean True on success. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _saveArray($root, $object, $fields, $append = false) - { - // basic fields below the root node - foreach($fields as $field => $params) { - $this->_updateNode($root, $object, $field, $params, $append); - } - return true; - } - - /** - * Update the specified node. - * - * @param Horde_DOM_Node $parent_node The parent node of the node that - * should be updated. - * @param array $attributes The data array that holds all - * attribute values. - * @param string $name The name of the the attribute - * to be updated. - * @param array $params Parameters for saving the node - * @param boolean $append Should the node be appended? - * - * @return Horde_DOM_Node The new/updated child node. - * - * @throws Horde_Exception If converting the data to XML failed. - * - * @todo Make protected (fix the XmlTest for that) - */ - public function _updateNode($parent_node, $attributes, $name, $params, - $append = false) - { - $value = null; - $missing = false; - - // Handle empty values - if (!isset($attributes[$name])) { - // Do we have information if this may be empty? - if ($params['value'] == self::VALUE_DEFAULT) { - // Use the default - $value = $params['default']; - } elseif ($params['value'] == self::VALUE_NOT_EMPTY) { - // May not be empty. Return an error - throw new Horde_Exception(sprintf(_("Data value for %s is empty in Kolab XML object!"), - $name)); - } elseif ($params['value'] == self::VALUE_MAYBE_MISSING) { - /** - * 'MAYBE_MISSING' means we should not create an XML - * node here - */ - $this->_removeNodes($parent_node, $name); - return false; - } elseif ($params['value'] == self::VALUE_CALCULATED) { - $missing = true; - } - } else { - $value = $attributes[$name]; - } - - if ($params['value'] == self::VALUE_CALCULATED) { - // Calculate the value - if (method_exists($this, '_save' . $params['save'])) { - return call_user_func(array($this, '_save' . $params['save']), - $parent_node, $name, $value, $missing); - } else { - throw new Horde_Exception(sprintf("Kolab XML: Missing function %s!", - $params['save'])); - } - } elseif ($params['type'] == self::TYPE_COMPOSITE) { - // Possibly remove the old node first - if (!$append) { - $this->_removeNodes($parent_node, $name); - } - - // Create a new complex node - $composite_node = $this->_xmldoc->createElement($name); - $composite_node = $parent_node->appendChild($composite_node); - return $this->_saveArray($composite_node, $value, $params['array']); - } elseif ($params['type'] == self::TYPE_MULTIPLE) { - // Remove the old nodes first - $this->_removeNodes($parent_node, $name); - - // Add the new nodes - foreach($value as $add_node) { - $this->_saveArray($parent_node, - array($name => $add_node), - array($name => $params['array']), - true); - } - return true; - } else { - return $this->_saveDefault($parent_node, $name, $value, $params, - $append); - } - } - - /** - * Create a text node. - * - * @param Horde_DOM_Node $parent The parent of the new node. - * @param string $name The name of the child node to create. - * @param string $value The value of the child node to create. - * - * @return Horde_DOM_Node The new node. - */ - protected function _createTextNode($parent, $name, $value) - { - $value = Horde_String::convertCharset($value, Horde_Nls::getCharset(), 'utf-8'); - - $node = $this->_xmldoc->createElement($name); - - $node = $parent->appendChild($node); - - // content - $text = $this->_xmldoc->createTextNode($value); - $text = $node->appendChild($text); - - return $node; - } - - /** - * Return the named node among a list of nodes. - * - * @param array %$nodes The list of nodes. - * @param string $name The name of the node to return. - * - * @return mixed The named Horde_DOM_Node or false if no node was found. - */ - protected function _findNode(&$nodes, $name) - { - foreach($nodes as $node) { - if ($node->nodeType == XML_ELEMENT_NODE && $node->tagName == $name) { - return $node; - } - } - return false; - } - - /** - * Retrieve a named child from a named parent if it has the given - * value. - * - * @param array $nodes The list of nodes. - * @param string $parent_name The name of the parent node. - * @param string $child_name The name of the child node. - * @param string $value The value of the child node - * - * @return mixed The specified Horde_DOM_Node or false if no node was found. - */ - protected function _findNodeByChildData($nodes, $parent_name, $child_name, - $value) - { - foreach($nodes as $node) - { - if ($node->nodeType == XML_ELEMENT_NODE && $node->tagName == $parent_name) { - $children = $node->childNodes; - foreach ($children as $child) - if ($child->nodeType == XML_ELEMENT_NODE - && $child->tagName == $child_name - && $child->textContent == $value) { - return $node; - } - } - } - - return false; - } - - /** - * Retrieve the content of a Horde_DOM_Node. - * - * @param Horde_DOM_Node $nodes The node that should be read. - * - * @return string The content of the node. - */ - protected function _getNodeContent($node) - { - return Horde_String::convertCharset($node->textContent, 'utf-8'); - } - - - /** - * Create a new named node on a parent node. - * - * @param Horde_DOM_Node $parent The parent node. - * @param string $name The name of the new child node. - * - * @return Horde_DOM_Node The new child node. - */ - protected function _createChildNode($parent, $name) - { - $node = $this->_xmldoc->createElement($name); - $node = $parent->appendChild($node); - - return $node; - } - - /** - * Remove named nodes from a parent node. - * - * @param Horde_DOM_Node $parent The parent node. - * @param string $name The name of the children to be removed. - */ - protected function _removeNodes($parent_node, $name) - { - while ($old_node = $this->_findNode($parent_node->childNodes, $name)) { - $parent_node->removeChild($old_node); - } - } - - /** - * Create a new named node on a parent node if it is not already - * present in the given children. - * - * @param Horde_DOM_Node $parent The parent node. - * @param array $children The children that might already - * contain the node. - * @param string $name The name of the new child node. - * - * @return Horde_DOM_Node The new or already existing child node. - */ - protected function _createOrFindChildNode($parent, $children, $name) - { - // look for existing node - $old_node = $this->_findNode($children, $name); - if ($old_node !== false) { - return $old_node; - } - - // create new parent node - return $this->_createChildNode($parent, $name); - } - - /** - * Load the different XML types. - * - * @param string $node The node to load the data from - * @param array $params Parameters for loading the value - * - * @return string The loaded value. - * - * @throws Horde_Exception If converting the data from XML failed. - */ - protected function _loadDefault($node, $params) - { - $content = $this->_getNodeContent($node); - - switch($params['type']) { - case self::TYPE_DATE: - return Horde_Kolab_Format_Date::decodeDate($content); - - case self::TYPE_DATETIME: - return Horde_Kolab_Format_Date::decodeDateTime($content); - - case self::TYPE_DATE_OR_DATETIME: - return Horde_Kolab_Format_Date::decodeDateOrDateTime($content); - - case self::TYPE_INTEGER: - return (int) $content; - - case self::TYPE_BOOLEAN: - return (bool) $content; - - default: - // Strings and colors are returned as they are - return $content; - } - } - - /** - * Save a data array as a XML node attached to the given parent node. - * - * @param Horde_DOM_Node $parent_node The parent node to attach - * the child to - * @param string $name The name of the node - * @param mixed $value The value to store - * @param boolean $missing Has the value been missing? - * @param boolean $append Should the node be appended? - * - * @return Horde_DOM_Node The new child node. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _saveDefault($parent_node, $name, $value, $params, - $append = false) - { - if (!$append) { - $this->_removeNodes($parent_node, $name); - } - - switch ($params['type']) { - case self::TYPE_DATE: - $value = Horde_Kolab_Format_Date::encodeDate($value); - break; - - case self::TYPE_DATETIME: - case self::TYPE_DATE_OR_DATETIME: - $value = Horde_Kolab_Format_Date::encodeDateTime($value); - break; - - case self::TYPE_INTEGER: - $value = (string) $value; - break; - - case self::TYPE_BOOLEAN: - if ($value) { - $value = 'true'; - } else { - $value = 'false'; - } - - break; - } - - // create the node - return $this->_createTextNode($parent_node, $name, $value); - } - - /** - * Handle loading of categories. Preserve multiple categories in a hidden - * object field. Optionally creates categories unknown to the Horde user. - * - * @param array $object Array of strings, containing the 'categories' field. - * - * @return NULL - */ - protected function _loadMultipleCategories(&$object) - { - global $prefs; - - if (empty($object['categories'])) { - return; - } - - // Create horde category if needed - @include_once 'Horde/Prefs/CategoryManager.php'; - if ($this->_create_categories - && class_exists('Prefs_CategoryManager') - && isset($prefs) && is_a($prefs, 'Prefs')) { - $cManager = new Prefs_CategoryManager(); - $horde_categories = $cManager->get(); - } else { - $cManager = null; - $horde_categories = null; - } - - $kolab_categories = explode (',', $object['categories']); - - $primary_category = ''; - foreach ($kolab_categories as $kolab_category) { - $kolab_category = trim($kolab_category); - - $valid_category = true; - if ($cManager && - array_search($kolab_category, $horde_categories) === false) { - // Unknown category -> Add - if ($cManager->add($kolab_category) === false) { - // categories might be locked - $valid_category = false; - } - } - - // First valid category becomes primary category - if ($valid_category && empty($primary_category)) { - $primary_category = $kolab_category; - } - } - - // Backup multiple categories - if (count($kolab_categories) > 1) { - $object['_categories_all'] = $object['categories']; - $object['_categories_primary'] = $primary_category; - } - // Make default category visible to Horde - $object['categories'] = $primary_category; - } - - /** - * Preserve multiple categories on save if "categories" didn't change. - * The name "categories" currently refers to one primary category. - * - * @param array $object Array of strings, containing the 'categories' field. - * - * @return NULL - */ - protected function _saveMultipleCategories(&$object) - { - // Check for multiple categories. - if (!isset($object['_categories_all']) - || !isset($object['_categories_primary']) - || !isset($object['categories'])) - { - return; - } - - // Preserve multiple categories if "categories" didn't change - if ($object['_categories_primary'] == $object['categories']) { - $object['categories'] = $object['_categories_all']; - } - } - - /** - * Load the object creation date. - * - * @param Horde_DOM_Node $node The original node if set. - * @param boolean $missing Has the node been missing? - * - * @return string The creation date. - * - * @throws Horde_Exception If converting the data from XML failed. - */ - protected function _loadCreationDate($node, $missing) - { - if ($missing) { - // Be gentle and accept a missing creation date. - return time(); - } - return $this->_loadDefault($node, - array('type' => self::TYPE_DATETIME)); - } - - /** - * Save the object creation date. - * - * @param Horde_DOM_Node $parent_node The parent node to attach the child - * to. - * @param string $name The name of the node. - * @param mixed $value The value to store. - * @param boolean $missing Has the value been missing? - * - * @return Horde_DOM_Node The new child node. - */ - protected function _saveCreationDate($parent_node, $name, $value, $missing) - { - // Only create the creation date if it has not been set before - if ($missing) { - $value = time(); - } - return $this->_saveDefault($parent_node, - $name, - $value, - array('type' => self::TYPE_DATETIME)); - } - - /** - * Load the object modification date. - * - * @param Horde_DOM_Node $node The original node if set. - * @param boolean $missing Has the node been missing? - * - * @return string The last modification date. - */ - protected function _loadModificationDate($node, $missing) - { - if ($missing) { - // Be gentle and accept a missing modification date. - return time(); - } - return $this->_loadDefault($node, - array('type' => self::TYPE_DATETIME)); - } - - /** - * Save the object modification date. - * - * @param Horde_DOM_Node $parent_node The parent node to attach - * the child to. - * @param string $name The name of the node. - * @param mixed $value The value to store. - * @param boolean $missing Has the value been missing? - * - * @return Horde_DOM_Node The new child node. - */ - protected function _saveModificationDate($parent_node, $name, $value, $missing) - { - // Always store now as modification date - return $this->_saveDefault($parent_node, - $name, - time(), - array('type' => self::TYPE_DATETIME)); - } - - /** - * Load the name of the last client that modified this object - * - * @param Horde_DOM_Node $node The original node if set. - * @param boolean $missing Has the node been missing? - * - * @return string The last modification date. - */ - protected function _loadProductId($node, $missing) - { - if ($missing) { - // Be gentle and accept a missing product id - return ''; - } - return $this->_getNodeContent($node); - } - - /** - * Save the name of the last client that modified this object. - * - * @param Horde_DOM_Node $parent_node The parent node to attach - * the child to. - * @param string $name The name of the node. - * @param mixed $value The value to store. - * @param boolean $missing Has the value been missing? - * - * @return Horde_DOM_Node The new child node. - */ - protected function _saveProductId($parent_node, $name, $value, $missing) - { - // Always store now as modification date - return $this->_saveDefault($parent_node, - $name, - self::PRODUCT_ID, - array('type' => self::TYPE_STRING)); - } - - /** - * Load recurrence information. - * - * @param Horde_DOM_Node $node The original node if set. - * @param boolean $missing Has the node been missing? - * - * @return array The recurrence information. - * - * @throws Horde_Exception If converting the data from XML failed. - */ - protected function _loadRecurrence($node, $missing) - { - if ($missing) { - return null; - } - - // Collect all child nodes - $children = $node->child_nodes(); - - $recurrence = $this->_loadArray($children, $this->_fields_recurrence); - - // Get the cycle type (must be present) - $recurrence['cycle'] = $node->get_attribute('cycle'); - // Get the sub type (may be present) - $recurrence['type'] = $node->get_attribute('type'); - - // Exclusions. - if (isset($recurrence['exclusion'])) { - $exceptions = array(); - foreach($recurrence['exclusion'] as $exclusion) { - if (!empty($exclusion)) { - list($year, $month, $mday) = sscanf($exclusion, '%04d-%02d-%02d'); - - $exceptions[] = sprintf('%04d%02d%02d', $year, $month, $mday); - } - } - $recurrence['exceptions'] = $exceptions; - } - - // Completed dates. - if (isset($recurrence['complete'])) { - $completions = array(); - foreach($recurrence['complete'] as $complete) { - if (!empty($complete)) { - list($year, $month, $mday) = sscanf($complete, '%04d-%02d-%02d'); - - $completions[] = sprintf('%04d%02d%02d', $year, $month, $mday); - } - } - $recurrence['completions'] = $completions; - } - - // Range is special - foreach($children as $child) { - if ($child->tagname == 'range') { - $recurrence['range-type'] = $child->get_attribute('type'); - } - } - - if (isset($recurrence['range']) && isset($recurrence['range-type']) - && $recurrence['range-type'] == 'date') { - $recurrence['range'] = Horde_Kolab_Format_Date::decodeDate($recurrence['range']); - } - - // Sanity check - $valid = $this->_validateRecurrence($recurrence); - - return $recurrence; - } - - /** - * Validate recurrence hash information. - * - * @param array $recurrence Recurrence hash loaded from XML. - * - * @return boolean True on success. - * - * @throws Horde_Exception If the recurrence data is invalid. - */ - protected function _validateRecurrence(&$recurrence) - { - if (!isset($recurrence['cycle'])) { - throw new Horde_Exception('recurrence tag error: cycle attribute missing'); - } - - if (!isset($recurrence['interval'])) { - throw new Horde_Exception('recurrence tag error: interval tag missing'); - } - $interval = $recurrence['interval']; - if ($interval < 0) { - throw new Horde_Exception('recurrence tag error: interval cannot be below zero: ' - . $interval); - } - - if ($recurrence['cycle'] == 'weekly') { - // Check for - if (!isset($recurrence['day']) || count($recurrence['day']) == 0) { - throw new Horde_Exception('recurrence tag error: day tag missing for weekly recurrence'); - } - } - - // The code below is only for monthly or yearly recurrences - if ($recurrence['cycle'] != 'monthly' - && $recurrence['cycle'] != 'yearly') - return true; - - if (!isset($recurrence['type'])) { - throw new Horde_Exception('recurrence tag error: type attribute missing'); - } - - if (!isset($recurrence['daynumber'])) { - throw new Horde_Exception('recurrence tag error: daynumber tag missing'); - } - $daynumber = $recurrence['daynumber']; - if ($daynumber < 0) { - throw new Horde_Exception('recurrence tag error: daynumber cannot be below zero: ' - . $daynumber); - } - - if ($recurrence['type'] == 'daynumber') { - if ($recurrence['cycle'] == 'yearly' && $daynumber > 366) { - throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 366 for yearly recurrences: ' . $daynumber); - } else if ($recurrence['cycle'] == 'monthly' && $daynumber > 31) { - throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 31 for monthly recurrences: ' . $daynumber); - } - } else if ($recurrence['type'] == 'weekday') { - // daynumber is the week of the month - if ($daynumber > 5) { - throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 5 for type weekday: ' . $daynumber); - } - - // Check for - if (!isset($recurrence['day']) || count($recurrence['day']) == 0) { - throw new Horde_Exception('recurrence tag error: day tag missing for type weekday'); - } - } - - if (($recurrence['type'] == 'monthday' || $recurrence['type'] == 'yearday') - && $recurrence['cycle'] == 'monthly') - { - throw new Horde_Exception('recurrence tag error: type monthday/yearday is only allowed for yearly recurrences'); - } - - if ($recurrence['cycle'] == 'yearly') { - if ($recurrence['type'] == 'monthday') { - // daynumber and month - if (!isset($recurrence['month'])) { - throw new Horde_Exception('recurrence tag error: month tag missing for type monthday'); - } - if ($daynumber > 31) { - throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 31 for type monthday: ' . $daynumber); - } - } else if ($recurrence['type'] == 'yearday') { - if ($daynumber > 366) { - throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 366 for type yearday: ' . $daynumber); - } - } - } - - return true; - } - - /** - * Save recurrence information. - * - * @param Horde_DOM_Node $parent_node The parent node to attach - * the child to. - * @param string $name The name of the node. - * @param mixed $value The value to store. - * @param boolean $missing Has the value been missing? - * - * @return Horde_DOM_Node The new child node. - */ - protected function _saveRecurrence($parent_node, $name, $value, $missing) - { - $this->_removeNodes($parent_node, $name); - - if (empty($value)) { - return false; - } - - // Exclusions. - if (isset($value['exceptions'])) { - $exclusions = array(); - foreach($value['exceptions'] as $exclusion) { - if (!empty($exclusion)) { - list($year, $month, $mday) = sscanf($exclusion, '%04d%02d%02d'); - $exclusions[] = "$year-$month-$mday"; - } - } - $value['exclusion'] = $exclusions; - } - - // Completed dates. - if (isset($value['completions'])) { - $completions = array(); - foreach($value['completions'] as $complete) { - if (!empty($complete)) { - list($year, $month, $mday) = sscanf($complete, '%04d%02d%02d'); - $completions[] = "$year-$month-$mday"; - } - } - $value['complete'] = $completions; - } - - if (isset($value['range']) - && isset($value['range-type']) && $value['range-type'] == 'date') { - $value['range'] = Horde_Kolab_Format_Date::encodeDate($value['range']); - } - - $r_node = $this->_xmldoc->createElement($name); - $r_node = $parent_node->appendChild($r_node); - - // Save normal fields - $this->_saveArray($r_node, $value, $this->_fields_recurrence); - - // Add attributes - $r_node->setAttribute('cycle', $value['cycle']); - if (isset($value['type'])) { - $r_node->setAttribute('type', $value['type']); - } - - $child = $this->_findNode($r_node->childNodes, 'range'); - if ($child) { - $child->setAttribute('type', $value['range-type']); - } - - return $r_node; - } -} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Annotation.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Annotation.php deleted file mode 100644 index 61a14b522..000000000 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Annotation.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - */ - -/** - * Kolab XML handler for IMAP folder annotations. - * - * Copyright 2008-2009 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @category Kolab - * @package Kolab_Format - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - * @since Horde 3.2 - */ -class Horde_Kolab_Format_XML_Annotation extends Horde_Kolab_Format_XML -{ - /** - * Specific data fields for the prefs object - * - * @var Kolab - */ - protected $_fields_specific; - - /** - * Constructor - */ - public function __construct() - { - $this->_root_name = 'annotations'; - - /** - * Specific preferences fields, in kolab format specification order - */ - $this->_fields_specific = array( - 'annotation' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ), - ); - - parent::__construct(); - } - - /** - * Load the groupware object based on the specifc XML values. - * - * @param array &$children An array of XML nodes. - * - * @return array Array with the object data - * - * @throws Horde_Exception If parsing the XML data failed. - */ - protected function _load(&$children) - { - $object = $this->_loadArray($children, $this->_fields_specific); - - $result = array(); - foreach ($object['annotation'] as $annotation) { - list($key, $value) = split('#', $annotation, 2); - $result[base64_decode($key)] = base64_decode($value); - } - - return $result; - } - - /** - * Save the specific XML values. - * - * @param array $root The XML document root. - * @param array $object The resulting data array. - * - * @return boolean True on success. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _save($root, $object) - { - $annotations = array(); - foreach ($object as $key => $value) { - if ($key != 'uid') { - $annotations['annotation'][] = base64_encode($key) . - '#' . base64_encode($value); - } - } - - return $this->_saveArray($root, $annotations, $this->_fields_specific); - } -} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Contact.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Contact.php deleted file mode 100644 index 44f4783a7..000000000 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Contact.php +++ /dev/null @@ -1,525 +0,0 @@ - - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - */ - -/** - * Kolab XML handler for contact groupware objects - * - * Copyright 2007-2009 Klarälvdalens Datakonsult AB - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @category Kolab - * @package Kolab_Format - * @author Thomas Jarosch - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - * @since Horde 3.2 - */ -class Horde_Kolab_Format_XML_Contact extends Horde_Kolab_Format_XML -{ - /** - * Specific data fields for the contact object - * - * @var array - */ - protected $_fields_specific; - - /** - * Structure of the name field - * - * @var array - */ - protected $_fields_name = array( - 'given-name' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'middle-names' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'last-name' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'full-name' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'initials' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'prefix' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'suffix' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ) - ); - - /** - * Structure of an address field - * - * @var array - */ - protected $_fields_address = array( - 'type' => self::TYPE_COMPOSITE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => 'home', - ), - 'street' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'locality' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'region' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'postal-code' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'country' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ) - ); - - /** - * Structure of a phone field - * - * @var array - */ - protected $_fields_phone = array( - 'type' => self::TYPE_COMPOSITE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'number' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ), - ); - - /** - * Address types - * - * @var array - */ - protected $_address_types = array( - 'business', - 'home', - 'other', - ); - - /** - * Phone types - * - * @var array - */ - protected $_phone_types = array( - 'business1', - 'business2', - 'businessfax', - 'callback', - 'car', - 'company', - 'home1', - 'home2', - 'homefax', - 'isdn', - 'mobile', - 'pager', - 'primary', - 'radio', - 'telex', - 'ttytdd', - 'assistant', - 'other', - ); - - /** - * Constructor - */ - public function __construct() - { - $this->_root_name = "contact"; - - /** Specific task fields, in kolab format specification order - */ - $this->_fields_specific = array( - 'name' => array ( - 'type' => self::TYPE_COMPOSITE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => $this->_fields_name, - ), - 'free-busy-url' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'organization' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'web-page' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'im-address' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'department' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'office-location' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'profession' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'job-title' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'manager-name' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'assistant' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'nick-name' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'spouse-name' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'birthday' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'anniversary' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'picture' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'children' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'gender' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'language' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'address' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => $this->_fields_address, - ), - 'email' => array ( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => $this->_fields_simple_person, - ), - 'phone' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => $this->_fields_phone, - ), - 'preferred-address' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'latitude' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'longitude' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - // Horde specific fields - 'pgp-publickey' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - // Support for broken clients - 'website' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'im-adress' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ); - - parent::__construct(); - } - - /** - * Load the groupware object based on the specifc XML values. - * - * @param array &$children An array of XML nodes. - * - * @return array Array with the object data. - * - * @throws Horde_Exception If parsing the XML data failed. - */ - protected function _load(&$children) - { - $object = $this->_loadArray($children, $this->_fields_specific); - - // Handle name fields - if (isset($object['name'])) { - $object = array_merge($object['name'], $object); - unset($object['name']); - } - - // Handle email fields - $emails = array(); - if (isset($object['email'])) { - foreach ($object['email'] as $email) { - $smtp_address = $email['smtp-address']; - if (!empty($smtp_address)) { - $emails[] = $smtp_address; - } - } - } - $object['emails'] = implode(', ', $emails); - - // Handle phone fields - if (isset($object['phone'])) { - foreach ($object['phone'] as $phone) { - if (isset($phone['number']) && - in_array($phone['type'], $this->_phone_types)) { - $object["phone-" . $phone['type']] = $phone['number']; - } - } - } - - // Handle address fields - if (isset($object['address'])) { - foreach ($object['address'] as $address) { - if (in_array($address['type'], $this->_address_types)) { - foreach ($address as $name => $value) { - $object["addr-" . $address['type'] . "-" . $name] = $value; - } - } - } - } - - // Handle gender field - if (isset($object['gender'])) { - $gender = $object['gender']; - - if ($gender == "female") { - $object['gender'] = 1; - } else if ($gender == "male") { - $object['gender'] = 0; - } else { - // unspecified gender - unset($object['gender']); - } - } - - // Compatibility with broken clients - $broken_fields = array("website" => "web-page", - "im-adress" => "im-address"); - foreach ($broken_fields as $broken_field => $real_field) { - if (!empty($object[$broken_field]) && empty($object[$real_field])) { - $object[$real_field] = $object[$broken_field]; - } - unset($object[$broken_field]); - } - - $object['__type'] = 'Object'; - - return $object; - } - - /** - * Save the specifc XML values. - * - * @param array $root The XML document root. - * @param array $object The resulting data array. - * - * @return boolean True on success. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _save($root, $object) - { - // Handle name fields - $name = array(); - foreach (array_keys($this->_fields_name) as $key) { - if (isset($object[$key])) { - $name[$key] = $object[$key]; - unset($object[$key]); - } - } - $object['name'] = $name; - - // Handle email fields - if (!isset($object['emails'])) { - $emails = array(); - } else { - $emails = explode(',', $object['emails']); - } - - if (isset($object['email']) && - !in_array($object['email'], $emails)) { - $emails[] = $object['email']; - } - - $object['email'] = array(); - - foreach ($emails as $email) { - $email = trim($email); - if (!empty($email)) { - $new_email = array('display-name' => $object['name']['full-name'], - 'smtp-address' => $email); - - $object['email'][] = $new_email; - } - } - - // Handle phone fields - if (!isset($object['phone'])) { - $object['phone'] = array(); - } - foreach ($this->_phone_types as $type) { - $key = 'phone-' . $type; - if (array_key_exists($key, $object)) { - $new_phone = array('type' => $type, - 'number' => $object[$key]); - - // Update existing phone entry of this type - $updated = false; - foreach ($object['phone'] as $index => $phone) { - if ($phone['type'] == $type) { - $object['phone'][$index] = $new_phone; - - $updated = true; - break; - } - } - if (!$updated) { - $object['phone'][] = $new_phone; - } - } - } - - // Phone cleanup: remove empty numbers - foreach ($object['phone'] as $index => $phone) { - if (empty($phone['number'])) { - unset($object['phone'][$index]); - } - } - - // Handle address fields - if (!isset($object['address'])) { - $object['address'] = array(); - } - - foreach ($this->_address_types as $type) { - $basekey = 'addr-' . $type . '-'; - $new_address = array('type' => $type); - foreach (array_keys($this->_fields_address['array']) as $subkey) { - $key = $basekey . $subkey; - if (array_key_exists($key, $object)) { - $new_address[$subkey] = $object[$key]; - } - } - - // Update existing address entry of this type - $updated = false; - foreach ($object['address'] as $index => $address) { - if ($address['type'] == $type) { - $object['address'][$index] = $new_address; - - $updated = true; - } - } - if (!$updated) { - $object['address'][] = $new_address; - } - } - - // Address cleanup: remove empty addresses - foreach ($object['address'] as $index => $address) { - $all_empty = true; - foreach ($address as $name => $value) { - if (!empty($value) && $name != "type") { - $all_empty = false; - break; - } - } - - if ($all_empty) { - unset($object['address'][$index]); - } - } - - // Handle gender field - if (isset($object['gender'])) { - $gender = $object['gender']; - - if ($gender == "0") { - $object['gender'] = "male"; - } else if ($gender == "1") { - $object['gender'] = "female"; - } else { - // unspecified gender - unset($object['gender']); - } - } - - // Do the actual saving - return $this->_saveArray($root, $object, $this->_fields_specific); - } -} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Distributionlist.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Distributionlist.php deleted file mode 100644 index 0e784687e..000000000 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Distributionlist.php +++ /dev/null @@ -1,113 +0,0 @@ - - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - */ - -/** - * Kolab XML handler for distributionlist groupware objects - * - * Copyright 2007-2009 Klarälvdalens Datakonsult AB - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @category Kolab - * @package Kolab_Format - * @author Thomas Jarosch - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - * @since Horde 3.2 - */ -class Horde_Kolab_Format_XML_Distributionlist extends Horde_Kolab_Format_XML -{ - /** - * Specific data fields for the contact object - * - * @var array - */ - protected $_fields_specific; - - /** - * Constructor - */ - public function __construct() - { - $this->_root_name = "distribution-list"; - - /** Specific task fields, in kolab format specification order - */ - $this->_fields_specific = array( - 'display-name' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_NOT_EMPTY - ), - 'member' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => $this->_fields_simple_person, - ) - ); - - parent::__construct(); - } - - /** - * Load the groupware object based on the specifc XML values. - * - * @param array &$children An array of XML nodes. - * - * @return array Array with data. - * - * @throws Horde_Exception If parsing the XML data failed. - */ - protected function _load(&$children) - { - $object = $this->_loadArray($children, $this->_fields_specific); - - // Map the display-name of a kolab dist list to horde's lastname attribute - if (isset($object['display-name'])) { - $object['last-name'] = $object['display-name']; - unset($object['display-name']); - } - - /** - * The mapping from $object['member'] as stored in XML back to - * Turba_Objects (contacts) must be performed in the - * Kolab_IMAP storage driver as we need access to the search - * facilities of the kolab storage driver. - */ - $object['__type'] = 'Group'; - return $object; - } - - /** - * Save the specifc XML values. - * - * @param array $root The XML document root. - * @param array $object The resulting data array. - * - * @return boolean True on success. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _save($root, $object) - { - // Map the display-name of a kolab dist list to horde's lastname attribute - if (isset($object['last-name'])) { - $object['display-name'] = $object['last-name']; - unset($object['last-name']); - } - - return $this->_saveArray($root, $object, $this->_fields_specific); - } -} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Event.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Event.php deleted file mode 100644 index 63b5df70c..000000000 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Event.php +++ /dev/null @@ -1,139 +0,0 @@ - - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - */ - -/** - * Kolab XML handler for event groupware objects. - * - * Copyright 2007-2009 Klarälvdalens Datakonsult AB - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @category Kolab - * @package Kolab_Format - * @author Thomas Jarosch - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - * @since Horde 3.2 - */ -class Horde_Kolab_Format_XML_Event extends Horde_Kolab_Format_XML -{ - /** - * Specific data fields for the contact object - * - * @var array - */ - protected $_fields_specific; - - /** - * Constructor - */ - public function __construct() - { - $this->_root_name = 'event'; - - /** Specific event fields, in kolab format specification order - */ - $this->_fields_specific = array( - 'summary' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'location' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'organizer' => $this->_fields_simple_person, - 'start-date' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_NOT_EMPTY, - ), - 'alarm' => array( - 'type' => self::TYPE_INTEGER, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'recurrence' => array( - 'type' => self::TYPE_COMPOSITE, - 'value' => self::VALUE_CALCULATED, - 'load' => 'Recurrence', - 'save' => 'Recurrence', - ), - 'attendee' => $this->_fields_attendee, - 'show-time-as' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'color-label' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'end-date' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_NOT_EMPTY, - ), - ); - - parent::__construct(); - } - - /** - * Load event XML values and translate start/end date. - * - * @param array &$children An array of XML nodes. - * - * @return array Array with the object data. - * - * @throws Horde_Exception If parsing the XML data failed. - */ - protected function _load(&$children) - { - $object = parent::_load($children); - - // Translate start/end date including full day events - if (strlen($object['start-date']) == 10) { - $object['start-date'] = Horde_Kolab_Format_Date::decodeDate($object['start-date']); - $object['end-date'] = Horde_Kolab_Format_Date::decodeDate($object['end-date']) + 24*60*60; - } else { - $object['start-date'] = Horde_Kolab_Format_Date::decodeDateTime($object['start-date']); - $object['end-date'] = Horde_Kolab_Format_Date::decodeDateTime($object['end-date']); - } - - return $object; - } - - /** - * Save event XML values and translate start/end date. - * - * @param array $root The XML document root. - * @param array $object The resulting data array. - * - * @return boolean True on success. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _save($root, $object) - { - // Translate start/end date including full day events - if (!empty($object['_is_all_day'])) { - $object['start-date'] = Horde_Kolab_Format_Date::encodeDate($object['start-date']); - $object['end-date'] = Horde_Kolab_Format_Date::encodeDate($object['end-date'] - 24*60*60); - } else { - $object['start-date'] = Horde_Kolab_Format_Date::encodeDateTime($object['start-date']); - $object['end-date'] = Horde_Kolab_Format_Date::encodeDateTime($object['end-date']); - } - - return parent::_save($root, $object); - } -} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Hprefs.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Hprefs.php deleted file mode 100644 index 8f63c715f..000000000 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Hprefs.php +++ /dev/null @@ -1,119 +0,0 @@ - - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - */ - -/** - * Kolab XML handler for client preferences. - * - * Copyright 2007-2009 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @category Kolab - * @package Kolab_Format - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - * @since Horde 3.2 - */ -class Horde_Kolab_Format_XML_Hprefs extends Horde_Kolab_Format_XML -{ - /** - * Specific data fields for the prefs object - * - * @var Kolab - */ - protected $_fields_specific; - - /** - * Automatically create categories if they are missing? - * - * @var boolean - */ - protected $_create_categories = false; - - /** - * Constructor - */ - public function __construct() - { - $this->_root_name = 'h-prefs'; - - /** Specific preferences fields, in kolab format specification order - */ - $this->_fields_specific = array( - 'application' => array ( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'pref' => array( - 'type' => self::TYPE_MULTIPLE, - 'value' => self::VALUE_MAYBE_MISSING, - 'array' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ), - ); - - parent::__construct(); - } - - /** - * Load an object based on the given XML string. - * - * @param string &$xmltext The XML of the message as string. - * - * @return array The data array representing the object. - * - * @throws Horde_Exception If parsing the XML data failed. - */ - public function load(&$xmltext) - { - $object = parent::load($xmltext); - - if (empty($object['application'])) { - if (!empty($object['categories'])) { - $object['application'] = $object['categories']; - unset($object['categories']); - } else { - throw new Horde_Exception('Preferences XML object is missing an application setting.'); - } - } - - return $object; - } - - /** - * Convert the data to a XML string. - * - * @param array $object The data array representing the note. - * - * @return string The data as XML string. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - public function save($object) - { - if (empty($object['application'])) { - if (!empty($object['categories'])) { - $object['application'] = $object['categories']; - unset($object['categories']); - } else { - throw new Horde_Exception('Preferences XML object is missing an application setting.'); - } - } - - return parent::save($object); - } -} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Note.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Note.php deleted file mode 100644 index c15bb6420..000000000 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Note.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - */ - -/** - * Kolab XML handler for note groupware objects. - * - * Copyright 2007-2009 Klarälvdalens Datakonsult AB - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @category Kolab - * @package Kolab_Format - * @author Thomas Jarosch - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - * @since Horde 3.2 - */ -class Horde_Kolab_Format_XML_Note extends Horde_Kolab_Format_XML -{ - /** - * Specific data fields for the note object - * - * @var Kolab - */ - protected $_fields_specific; - - /** - * Constructor - */ - public function __construct() - { - $this->_root_name = 'note'; - - /** Specific note fields, in kolab format specification order - */ - $this->_fields_specific = array( - 'summary' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'background-color' => array( - 'type' => self::TYPE_COLOR, - 'value' => self::VALUE_DEFAULT, - 'default' => '#000000', - ), - 'foreground-color' => array( - 'type' => self::TYPE_COLOR, - 'value' => self::VALUE_DEFAULT, - 'default' => '#ffff00', - ), - ); - - parent::__construct(); - } - - /** - * Load the groupware object based on the specifc XML values. - * - * @param array &$children An array of XML nodes. - * - * @return array Array with the object data - * - * @throws Horde_Exception If parsing the XML data failed. - */ - protected function _load(&$children) - { - $object = $this->_loadArray($children, $this->_fields_specific); - - $object['desc'] = $object['summary']; - unset($object['summary']); - - return $object; - } - - /** - * Save the specific XML values. - * - * @param array $root The XML document root. - * @param array $object The resulting data array. - * - * @return boolean True on success. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _save($root, $object) - { - $object['summary'] = $object['desc']; - unset($object['desc']); - - return $this->_saveArray($root, $object, $this->_fields_specific); - } -} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Task.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Task.php deleted file mode 100644 index 48bb5bf95..000000000 --- a/framework/Kolab_Format/lib/Horde/Kolab/Format/XML/Task.php +++ /dev/null @@ -1,199 +0,0 @@ - - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - */ - -/** - * Kolab XML handler for task groupware objects. - * - * Copyright 2007-2009 Klarälvdalens Datakonsult AB - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @category Kolab - * @package Kolab_Format - * @author Thomas Jarosch - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Server - * @since Horde 3.2 - */ -class Horde_Kolab_Format_XML_Task extends Horde_Kolab_Format_XML -{ - /** - * Specific data fields for the note object - * - * @var array - */ - protected $_fields_specific; - - /** - * Constructor - */ - public function __construct() - { - $this->_root_name = 'task'; - - /** Specific task fields, in kolab format specification order - */ - $this->_fields_specific = array( - 'summary' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'location' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => '', - ), - 'creator' => $this->_fields_simple_person, - 'organizer' => $this->_fields_simple_person, - 'start-date' => array( - 'type' => self::TYPE_DATE_OR_DATETIME, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'alarm' => array( - 'type' => self::TYPE_INTEGER, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'recurrence' => array( - 'type' => self::TYPE_COMPOSITE, - 'value' => self::VALUE_CALCULATED, - 'load' => 'Recurrence', - 'save' => 'Recurrence', - ), - 'attendee' => $this->_fields_attendee, - 'priority' => array( - 'type' => self::TYPE_INTEGER, - 'value' => self::VALUE_DEFAULT, - 'default' => 3, - ), - 'completed' => array( - 'type' => self::TYPE_INTEGER, - 'value' => self::VALUE_DEFAULT, - 'default' => 0, - ), - 'status' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_DEFAULT, - 'default' => 'not-started', - ), - 'due-date' => array( - 'type' => self::TYPE_DATE_OR_DATETIME, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'parent' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - // These are not part of the Kolab specification but it is - // ok if the client supports additional entries - 'estimate' => array( - 'type' => self::TYPE_STRING, - 'value' => self::VALUE_MAYBE_MISSING, - ), - 'completed_date' => array( - 'type' => self::TYPE_DATE_OR_DATETIME, - 'value' => self::VALUE_MAYBE_MISSING, - ), - ); - - parent::__construct(); - } - - /** - * Load the groupware object based on the specifc XML values. - * - * @param array &$children An array of XML nodes. - * - * @return array Array with data. - * - * @throws Horde_Exception If parsing the XML data failed. - */ - protected function _load(&$children) - { - $object = $this->_loadArray($children, $this->_fields_specific); - - $object['name'] = $object['summary']; - unset($object['summary']); - - if (empty($object['completed-date'])) { - $object['completed-date'] = null; - } - - if (empty($object['alarm'])) { - $object['alarm'] = null; - } - - if (isset($object['due-date'])) { - $object['due'] = $object['due-date']; - unset($object['due-date']); - } else { - $object['due'] = null; - } - - if (isset($object['start-date'])) { - $object['start'] = $object['start-date']; - unset($object['start-date']); - } else { - $object['start'] = null; - } - - if (!isset($object['estimate'])) { - $object['estimate'] = null; - } else { - $object['estimate'] = (float) $object['estimate']; - } - - if (!isset($object['parent'])) { - $object['parent'] = null; - } - - $object['completed'] = (bool) Kolab::percentageToBoolean($object['completed']); - - if (isset($object['organizer']) && isset($object['organizer']['smtp-address'])) { - $object['assignee'] = $object['organizer']['smtp-address']; - } - - return $object; - } - - /** - * Save the specific XML values. - * - * @param array $root The XML document root. - * @param array $object The resulting data array. - * - * @return boolean True on success. - * - * @throws Horde_Exception If converting the data to XML failed. - */ - protected function _save($root, $object) - { - $object['summary'] = $object['name']; - unset($object['name']); - - $object['due-date'] = $object['due']; - unset($object['due']); - - $object['start-date'] = $object['start']; - unset($object['start']); - - $object['estimate'] = number_format($object['estimate'], 2); - - $object['completed'] = Kolab::BooleanToPercentage($object['completed']); - - return $this->_saveArray($root, $object, $this->_fields_specific); - } -} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml.php new file mode 100644 index 000000000..9650af82e --- /dev/null +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml.php @@ -0,0 +1,1421 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + */ + +/** + * Kolab XML to array hash converter. + * + * For implementing a new format type you will have to inherit this + * class and provide a _load/_save function. + * + * Copyright 2007-2009 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_Format + * @author Thomas Jarosch + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + * @since Horde 3.2 + */ +class Horde_Kolab_Format_Xml +{ + + /** + * Defines a XML value that should get a default value if missing + */ + const PRODUCT_ID = 'Horde::Kolab'; + + /** + * Defines a XML value that should get a default value if missing + */ + const VALUE_DEFAULT = 0; + + /** + * Defines a XML value that may be missing + */ + const VALUE_MAYBE_MISSING = 1; + + /** + * Defines a XML value that may not be missing + */ + const VALUE_NOT_EMPTY = 2; + + /** + * Defines a XML value that will be calculated by its own function + */ + const VALUE_CALCULATED = 3; + + /** + * Defines a XML value as string type + */ + const TYPE_STRING = 0; + + /** + * Defines a XML value as integer type + */ + const TYPE_INTEGER = 1; + + /** + * Defines a XML value as boolean type + */ + const TYPE_BOOLEAN = 2; + + /** + * Defines a XML value as date type + */ + const TYPE_DATE = 3; + + /** + * Defines a XML value as datetime type + */ + const TYPE_DATETIME = 4; + + /** + * Defines a XML value as date or datetime type + */ + const TYPE_DATE_OR_DATETIME = 5; + + /** + * Defines a XML value as color type + */ + const TYPE_COLOR = 6; + + /** + * Defines a XML value as composite value type + */ + const TYPE_COMPOSITE = 7; + + /** + * Defines a XML value as array type + */ + const TYPE_MULTIPLE = 8; + + /** + * Requested version of the data array to return + * + * @var int + */ + protected $_version = 1; + + /** + * The name of the resulting document. + * + * @var string + */ + protected $_name = 'kolab.xml'; + + /** + * The XML document this driver works with. + * + * @var Horde_DOM_Document + * + * @todo Make protected (fix the XmlTest for that) + */ + public $_xmldoc = null; + + /** + * The name of the root element. + * + * @var string + */ + protected $_root_name = 'kolab'; + + /** + * Kolab format version of the root element. + * + * @var string + */ + protected $_root_version = '1.0'; + + /** + * Basic fields in any Kolab object + * + * @var array + */ + protected $_fields_basic; + + /** + * Automatically create categories if they are missing? + * + * @var boolean + */ + protected $_create_categories = true; + + /** + * Fields for a simple person + * + * @var array + * + * @todo Make protected (fix the XmlTest for that) + */ + public $_fields_simple_person = array( + 'type' => self::TYPE_COMPOSITE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'display-name' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'smtp-address' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'uid' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + ), + ); + + /** + * Fields for an attendee + * + * @var array + * + * @todo Make protected (fix the XmlTest for that) + */ + public $_fields_attendee = array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_DEFAULT, + 'default' => array(), + 'array' => array( + 'type' => self::TYPE_COMPOSITE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'display-name' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'smtp-address' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'status' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => 'none', + ), + 'request-response' => array( + 'type' => self::TYPE_BOOLEAN, + 'value' => self::VALUE_DEFAULT, + 'default' => true, + ), + 'role' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => 'required', + ), + ), + ), + ); + + /** + * Fields for a recurrence + * + * @var array + */ + protected $_fields_recurrence = array( + // Attribute on root node: cycle + // Attribute on root node: type + 'interval' => array( + 'type' => self::TYPE_INTEGER, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'day' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ), + 'daynumber' => array( + 'type' => self::TYPE_INTEGER, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'month' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + // Attribute on range: type + 'range' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'exclusion' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ), + 'complete' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ), + ); + + /** + * Constructor + * + * @param array $params Any additional options + */ + public function __construct($params = null) + { + if (is_array($params) && isset($params['version'])) { + $this->_version = $params['version']; + } else { + $this->_version = 1; + } + + /* Generic fields, in kolab format specification order */ + $this->_fields_basic = array( + 'uid' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_NOT_EMPTY, + ), + 'body' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'categories' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'creation-date' => array( + 'type' => self::TYPE_DATETIME, + 'value' => self::VALUE_CALCULATED, + 'load' => 'CreationDate', + 'save' => 'CreationDate', + ), + 'last-modification-date' => array( + 'type' => self::TYPE_DATETIME, + 'value' => self::VALUE_CALCULATED, + 'load' => 'ModificationDate', + 'save' => 'ModificationDate', + ), + 'sensitivity' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => 'public', + ), + 'inline-attachment' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ), + 'link-attachment' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ), + 'product-id' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_CALCULATED, + 'load' => 'ProductId', + 'save' => 'ProductId', + ), + ); + } + + /** + * Attempts to return a concrete Horde_Kolab_Format_Xml instance. + * based on $object_type. + * + * @param string $object_type The object type that should be handled. + * @param array $params Any additional parameters. + * + * @return Horde_Kolab_Format_Xml The newly created concrete + * Horde_Kolab_Format_Xml instance. + * + * @throws Horde_Exception If the class for the object type could + * not be loaded. + */ + public function &factory($object_type = '', $params = null) + { + $object_type = ucfirst(str_replace('-', '', $object_type)); + $class = 'Horde_Kolab_Format_Xml_' . $object_type; + + if (class_exists($class)) { + $driver = &new $class($params); + } else { + throw new Horde_Exception(sprintf(_("Failed to load Kolab XML driver %s"), + $object_type)); + } + + return $driver; + } + + /** + * Return the name of the resulting document. + * + * @return string The name that may be used as filename. + */ + public function getName() + { + return $this->_name; + } + + /** + * Return the mime type of the resulting document. + * + * @return string The mime type of the result. + */ + public function getMimeType() + { + return 'application/x-vnd.kolab.' . $this->_root_name; + } + + /** + * Return the disposition of the resulting document. + * + * @return string The disportion of this document. + */ + public function getDisposition() + { + return 'attachment'; + } + + /** + * Load an object based on the given XML string. + * + * @todo Check encoding of the returned array. It seems to be ISO-8859-1 at + * the moment and UTF-8 would seem more appropriate. + * + * @param string $xmltext The XML of the message as string. + * + * @return array The data array representing the object. + * + * @throws Horde_Exception If parsing the XML data failed. + */ + public function load(&$xmltext) + { + try { + $this->_parseXml($xmltext); + } catch (DOMException $e) { + /** + * If the first call does not return successfully this might mean we + * got an attachment with broken encoding. There are some Kolab + * client versions in the wild that might have done that. So the + * next section starts a second attempt by guessing the encoding and + * trying again. + */ + if (strcasecmp(mb_detect_encoding($xmltext, + 'UTF-8, ISO-8859-1'), 'UTF-8') !== 0) { + $xmltext = mb_convert_encoding($xmltext, 'UTF-8', 'ISO-8859-1'); + } + $this->_parseXml($xmltext); + } + if (empty($this->_xmldoc)) { + return false; + } + + if (!$this->_xmldoc->documentElement->hasChildNodes()) { + throw new Horde_Exception(_("No or unreadable content in Kolab XML object")); + } + + // fresh object data + $object = array(); + + $result = $this->_loadArray($this->_xmldoc->documentElement->childNodes, $this->_fields_basic); + $object = array_merge($object, $result); + $this->_loadMultipleCategories($object); + + $result = $this->_load($this->_xmldoc->documentElement->childNodes); + $object = array_merge($object, $result); + + // uid is vital + if (!isset($object['uid'])) { + throw new Horde_Exception(_("UID not found in Kolab XML object")); + } + + return $object; + } + + /** + * Load the groupware object based on the specifc XML values. + * + * @param array $children An array of XML nodes. + * + * @return array The data array representing the object. + * + * @throws Horde_Exception If parsing the XML data failed. + */ + protected function _load(&$children) + { + if (!empty($this->_fields_specific)) { + return $this->_loadArray($children, $this->_fields_specific); + } else { + return array(); + } + } + + /** + * Load an array with data from the XML nodes. + * + * @param array $object The resulting data array. + * @param array $children An array of XML nodes. + * @param array $fields The fields to populate in the object array. + * + * @return boolean True on success. + * + * @throws Horde_Exception If parsing the XML data failed. + */ + protected function _loadArray(&$children, $fields) + { + $object = array(); + + // basic fields below the root node + foreach($fields as $field => $params) { + $result = $this->_getXmlData($children, $field, $params); + if (isset($result)) { + $object[$field] = $result; + } + } + return $object; + } + + /** + * Get the text content of the named data node among the specified + * children. + * + * @param array &$children The children to search. + * @param string $name The name of the node to return. + * @param array $params Parameters for the data conversion. + * + * @return string The content of the specified node or an empty + * string. + * + * @throws Horde_Exception If parsing the XML data failed. + * + * @todo Make protected (fix the XmlTest for that) + */ + public function _getXmlData(&$children, $name, $params) + { + if ($params['type'] == self::TYPE_MULTIPLE) { + $result = array(); + foreach($children as $child) { + if ($child->nodeType == XML_ELEMENT_NODE && $child->tagName == $name) { + $child_a = array($child); + $value = $this->_getXmlData($child_a, $name, + $params['array']); + $result[] = $value; + } + } + return $result; + } + + $value = null; + $missing = false; + + // identify the child node + $child = $this->_findNode($children, $name); + + // Handle empty values + if (!$child) { + if ($params['value'] == self::VALUE_MAYBE_MISSING) { + // 'MAYBE_MISSING' means we should return null + return null; + } elseif ($params['value'] == self::VALUE_NOT_EMPTY) { + // May not be empty. Return an error + throw new Horde_Exception(sprintf(_("Data value for %s is empty in Kolab XML object!"), + $name)); + } elseif ($params['value'] == self::VALUE_DEFAULT) { + // Return the default + return $params['default']; + } elseif ($params['value'] == self::VALUE_CALCULATED) { + $missing = true; + } + } + + // Do we need to calculate the value? + if ($params['value'] == self::VALUE_CALCULATED && isset($params['load'])) { + if (method_exists($this, '_load' . $params['load'])) { + $value = call_user_func(array($this, '_load' . $params['load']), + $child, $missing); + } else { + throw new Horde_Exception(sprintf("Kolab XML: Missing function %s!", + $params['load'])); + } + } elseif ($params['type'] == self::TYPE_COMPOSITE) { + return $this->_loadArray($child->childNodes, $params['array']); + } else { + return $this->_loadDefault($child, $params); + } + + // Nothing specified. Return the value as it is. + return $value; + } + + /** + * Parse the XML string. The root node is returned on success. + * + * @param string $xmltext The XML of the message as string. + * + * @return NULL + * + * @throws Horde_Exception If parsing the XML data failed. + * + * @todo Make protected (fix the XmlTest for that) + */ + public function _parseXml(&$xmltext) + { + $this->_xmldoc = new DOMDocument(); + $this->_xmldoc->preserveWhiteSpace = false; + $this->_xmldoc->formatOutput = true; + $this->_xmldoc->loadXML($xmltext); + } + + /** + * Convert the data to a XML string. + * + * @param array $attributes The data array representing the note. + * + * @return string The data as XML string. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + public function save($object) + { + $root = $this->_prepareSave(); + + $this->_saveMultipleCategories($object); + $this->_saveArray($root, $object, $this->_fields_basic); + $this->_save($root, $object); + + return $this->_xmldoc->saveXML(); + } + + /** + * Save the specific XML values. + * + * @param array &$root The XML document root. + * @param array $object The resulting data array. + * + * @return boolean True on success. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _save(&$root, $object) + { + if (!empty($this->_fields_specific)) { + $this->_saveArray($root, $object, $this->_fields_specific); + } + return true; + } + + /** + * Creates a new XML document if necessary. + * + * @param string $xmltext The XML of the message as string. + * + * @return Horde_DOM_Node The root node of the document. + * + * @todo Make protected (fix the XmlTest for that) + */ + public function &_prepareSave() + { + if (empty($this->_xmldoc)) { + // create new XML + $this->_xmldoc = new DOMDocument(); + $this->_xmldoc->preserveWhiteSpace = false; + $this->_xmldoc->formatOutput = true; + $root = $this->_xmldoc->createElement($this->_root_name); + $this->_xmldoc->appendChild($root); + $root->setAttribute('version', $this->_root_version); + } + return $root; + } + + /** + * Save a data array to XML nodes. + * + * @param array $root The XML document root. + * @param array $object The data array. + * @param array $fields The fields to write into the XML object. + * @param boolean $append Should the nodes be appended? + * + * @return boolean True on success. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _saveArray($root, $object, $fields, $append = false) + { + // basic fields below the root node + foreach($fields as $field => $params) { + $this->_updateNode($root, $object, $field, $params, $append); + } + return true; + } + + /** + * Update the specified node. + * + * @param Horde_DOM_Node $parent_node The parent node of the node that + * should be updated. + * @param array $attributes The data array that holds all + * attribute values. + * @param string $name The name of the the attribute + * to be updated. + * @param array $params Parameters for saving the node + * @param boolean $append Should the node be appended? + * + * @return Horde_DOM_Node The new/updated child node. + * + * @throws Horde_Exception If converting the data to XML failed. + * + * @todo Make protected (fix the XmlTest for that) + */ + public function _updateNode($parent_node, $attributes, $name, $params, + $append = false) + { + $value = null; + $missing = false; + + // Handle empty values + if (!isset($attributes[$name])) { + // Do we have information if this may be empty? + if ($params['value'] == self::VALUE_DEFAULT) { + // Use the default + $value = $params['default']; + } elseif ($params['value'] == self::VALUE_NOT_EMPTY) { + // May not be empty. Return an error + throw new Horde_Exception(sprintf(_("Data value for %s is empty in Kolab XML object!"), + $name)); + } elseif ($params['value'] == self::VALUE_MAYBE_MISSING) { + /** + * 'MAYBE_MISSING' means we should not create an XML + * node here + */ + $this->_removeNodes($parent_node, $name); + return false; + } elseif ($params['value'] == self::VALUE_CALCULATED) { + $missing = true; + } + } else { + $value = $attributes[$name]; + } + + if ($params['value'] == self::VALUE_CALCULATED) { + // Calculate the value + if (method_exists($this, '_save' . $params['save'])) { + return call_user_func(array($this, '_save' . $params['save']), + $parent_node, $name, $value, $missing); + } else { + throw new Horde_Exception(sprintf("Kolab XML: Missing function %s!", + $params['save'])); + } + } elseif ($params['type'] == self::TYPE_COMPOSITE) { + // Possibly remove the old node first + if (!$append) { + $this->_removeNodes($parent_node, $name); + } + + // Create a new complex node + $composite_node = $this->_xmldoc->createElement($name); + $composite_node = $parent_node->appendChild($composite_node); + return $this->_saveArray($composite_node, $value, $params['array']); + } elseif ($params['type'] == self::TYPE_MULTIPLE) { + // Remove the old nodes first + $this->_removeNodes($parent_node, $name); + + // Add the new nodes + foreach($value as $add_node) { + $this->_saveArray($parent_node, + array($name => $add_node), + array($name => $params['array']), + true); + } + return true; + } else { + return $this->_saveDefault($parent_node, $name, $value, $params, + $append); + } + } + + /** + * Create a text node. + * + * @param Horde_DOM_Node $parent The parent of the new node. + * @param string $name The name of the child node to create. + * @param string $value The value of the child node to create. + * + * @return Horde_DOM_Node The new node. + */ + protected function _createTextNode($parent, $name, $value) + { + $value = Horde_String::convertCharset($value, Horde_Nls::getCharset(), 'utf-8'); + + $node = $this->_xmldoc->createElement($name); + + $node = $parent->appendChild($node); + + // content + $text = $this->_xmldoc->createTextNode($value); + $text = $node->appendChild($text); + + return $node; + } + + /** + * Return the named node among a list of nodes. + * + * @param array %$nodes The list of nodes. + * @param string $name The name of the node to return. + * + * @return mixed The named Horde_DOM_Node or false if no node was found. + */ + protected function _findNode(&$nodes, $name) + { + foreach($nodes as $node) { + if ($node->nodeType == XML_ELEMENT_NODE && $node->tagName == $name) { + return $node; + } + } + return false; + } + + /** + * Retrieve a named child from a named parent if it has the given + * value. + * + * @param array $nodes The list of nodes. + * @param string $parent_name The name of the parent node. + * @param string $child_name The name of the child node. + * @param string $value The value of the child node + * + * @return mixed The specified Horde_DOM_Node or false if no node was found. + */ + protected function _findNodeByChildData($nodes, $parent_name, $child_name, + $value) + { + foreach($nodes as $node) + { + if ($node->nodeType == XML_ELEMENT_NODE && $node->tagName == $parent_name) { + $children = $node->childNodes; + foreach ($children as $child) + if ($child->nodeType == XML_ELEMENT_NODE + && $child->tagName == $child_name + && $child->textContent == $value) { + return $node; + } + } + } + + return false; + } + + /** + * Retrieve the content of a Horde_DOM_Node. + * + * @param Horde_DOM_Node $nodes The node that should be read. + * + * @return string The content of the node. + */ + protected function _getNodeContent($node) + { + return Horde_String::convertCharset($node->textContent, 'utf-8'); + } + + + /** + * Create a new named node on a parent node. + * + * @param Horde_DOM_Node $parent The parent node. + * @param string $name The name of the new child node. + * + * @return Horde_DOM_Node The new child node. + */ + protected function _createChildNode($parent, $name) + { + $node = $this->_xmldoc->createElement($name); + $node = $parent->appendChild($node); + + return $node; + } + + /** + * Remove named nodes from a parent node. + * + * @param Horde_DOM_Node $parent The parent node. + * @param string $name The name of the children to be removed. + */ + protected function _removeNodes($parent_node, $name) + { + while ($old_node = $this->_findNode($parent_node->childNodes, $name)) { + $parent_node->removeChild($old_node); + } + } + + /** + * Create a new named node on a parent node if it is not already + * present in the given children. + * + * @param Horde_DOM_Node $parent The parent node. + * @param array $children The children that might already + * contain the node. + * @param string $name The name of the new child node. + * + * @return Horde_DOM_Node The new or already existing child node. + */ + protected function _createOrFindChildNode($parent, $children, $name) + { + // look for existing node + $old_node = $this->_findNode($children, $name); + if ($old_node !== false) { + return $old_node; + } + + // create new parent node + return $this->_createChildNode($parent, $name); + } + + /** + * Load the different XML types. + * + * @param string $node The node to load the data from + * @param array $params Parameters for loading the value + * + * @return string The loaded value. + * + * @throws Horde_Exception If converting the data from XML failed. + */ + protected function _loadDefault($node, $params) + { + $content = $this->_getNodeContent($node); + + switch($params['type']) { + case self::TYPE_DATE: + return Horde_Kolab_Format_Date::decodeDate($content); + + case self::TYPE_DATETIME: + return Horde_Kolab_Format_Date::decodeDateTime($content); + + case self::TYPE_DATE_OR_DATETIME: + return Horde_Kolab_Format_Date::decodeDateOrDateTime($content); + + case self::TYPE_INTEGER: + return (int) $content; + + case self::TYPE_BOOLEAN: + return (bool) $content; + + default: + // Strings and colors are returned as they are + return $content; + } + } + + /** + * Save a data array as a XML node attached to the given parent node. + * + * @param Horde_DOM_Node $parent_node The parent node to attach + * the child to + * @param string $name The name of the node + * @param mixed $value The value to store + * @param boolean $missing Has the value been missing? + * @param boolean $append Should the node be appended? + * + * @return Horde_DOM_Node The new child node. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _saveDefault($parent_node, $name, $value, $params, + $append = false) + { + if (!$append) { + $this->_removeNodes($parent_node, $name); + } + + switch ($params['type']) { + case self::TYPE_DATE: + $value = Horde_Kolab_Format_Date::encodeDate($value); + break; + + case self::TYPE_DATETIME: + case self::TYPE_DATE_OR_DATETIME: + $value = Horde_Kolab_Format_Date::encodeDateTime($value); + break; + + case self::TYPE_INTEGER: + $value = (string) $value; + break; + + case self::TYPE_BOOLEAN: + if ($value) { + $value = 'true'; + } else { + $value = 'false'; + } + + break; + } + + // create the node + return $this->_createTextNode($parent_node, $name, $value); + } + + /** + * Handle loading of categories. Preserve multiple categories in a hidden + * object field. Optionally creates categories unknown to the Horde user. + * + * @param array $object Array of strings, containing the 'categories' field. + * + * @return NULL + */ + protected function _loadMultipleCategories(&$object) + { + global $prefs; + + if (empty($object['categories'])) { + return; + } + + // Create horde category if needed + @include_once 'Horde/Prefs/CategoryManager.php'; + if ($this->_create_categories + && class_exists('Prefs_CategoryManager') + && isset($prefs) && is_a($prefs, 'Prefs')) { + $cManager = new Prefs_CategoryManager(); + $horde_categories = $cManager->get(); + } else { + $cManager = null; + $horde_categories = null; + } + + $kolab_categories = explode (',', $object['categories']); + + $primary_category = ''; + foreach ($kolab_categories as $kolab_category) { + $kolab_category = trim($kolab_category); + + $valid_category = true; + if ($cManager && + array_search($kolab_category, $horde_categories) === false) { + // Unknown category -> Add + if ($cManager->add($kolab_category) === false) { + // categories might be locked + $valid_category = false; + } + } + + // First valid category becomes primary category + if ($valid_category && empty($primary_category)) { + $primary_category = $kolab_category; + } + } + + // Backup multiple categories + if (count($kolab_categories) > 1) { + $object['_categories_all'] = $object['categories']; + $object['_categories_primary'] = $primary_category; + } + // Make default category visible to Horde + $object['categories'] = $primary_category; + } + + /** + * Preserve multiple categories on save if "categories" didn't change. + * The name "categories" currently refers to one primary category. + * + * @param array $object Array of strings, containing the 'categories' field. + * + * @return NULL + */ + protected function _saveMultipleCategories(&$object) + { + // Check for multiple categories. + if (!isset($object['_categories_all']) + || !isset($object['_categories_primary']) + || !isset($object['categories'])) + { + return; + } + + // Preserve multiple categories if "categories" didn't change + if ($object['_categories_primary'] == $object['categories']) { + $object['categories'] = $object['_categories_all']; + } + } + + /** + * Load the object creation date. + * + * @param Horde_DOM_Node $node The original node if set. + * @param boolean $missing Has the node been missing? + * + * @return string The creation date. + * + * @throws Horde_Exception If converting the data from XML failed. + */ + protected function _loadCreationDate($node, $missing) + { + if ($missing) { + // Be gentle and accept a missing creation date. + return time(); + } + return $this->_loadDefault($node, + array('type' => self::TYPE_DATETIME)); + } + + /** + * Save the object creation date. + * + * @param Horde_DOM_Node $parent_node The parent node to attach the child + * to. + * @param string $name The name of the node. + * @param mixed $value The value to store. + * @param boolean $missing Has the value been missing? + * + * @return Horde_DOM_Node The new child node. + */ + protected function _saveCreationDate($parent_node, $name, $value, $missing) + { + // Only create the creation date if it has not been set before + if ($missing) { + $value = time(); + } + return $this->_saveDefault($parent_node, + $name, + $value, + array('type' => self::TYPE_DATETIME)); + } + + /** + * Load the object modification date. + * + * @param Horde_DOM_Node $node The original node if set. + * @param boolean $missing Has the node been missing? + * + * @return string The last modification date. + */ + protected function _loadModificationDate($node, $missing) + { + if ($missing) { + // Be gentle and accept a missing modification date. + return time(); + } + return $this->_loadDefault($node, + array('type' => self::TYPE_DATETIME)); + } + + /** + * Save the object modification date. + * + * @param Horde_DOM_Node $parent_node The parent node to attach + * the child to. + * @param string $name The name of the node. + * @param mixed $value The value to store. + * @param boolean $missing Has the value been missing? + * + * @return Horde_DOM_Node The new child node. + */ + protected function _saveModificationDate($parent_node, $name, $value, $missing) + { + // Always store now as modification date + return $this->_saveDefault($parent_node, + $name, + time(), + array('type' => self::TYPE_DATETIME)); + } + + /** + * Load the name of the last client that modified this object + * + * @param Horde_DOM_Node $node The original node if set. + * @param boolean $missing Has the node been missing? + * + * @return string The last modification date. + */ + protected function _loadProductId($node, $missing) + { + if ($missing) { + // Be gentle and accept a missing product id + return ''; + } + return $this->_getNodeContent($node); + } + + /** + * Save the name of the last client that modified this object. + * + * @param Horde_DOM_Node $parent_node The parent node to attach + * the child to. + * @param string $name The name of the node. + * @param mixed $value The value to store. + * @param boolean $missing Has the value been missing? + * + * @return Horde_DOM_Node The new child node. + */ + protected function _saveProductId($parent_node, $name, $value, $missing) + { + // Always store now as modification date + return $this->_saveDefault($parent_node, + $name, + self::PRODUCT_ID, + array('type' => self::TYPE_STRING)); + } + + /** + * Load recurrence information. + * + * @param Horde_DOM_Node $node The original node if set. + * @param boolean $missing Has the node been missing? + * + * @return array The recurrence information. + * + * @throws Horde_Exception If converting the data from XML failed. + */ + protected function _loadRecurrence($node, $missing) + { + if ($missing) { + return null; + } + + // Collect all child nodes + $children = $node->childNodes; + + $recurrence = $this->_loadArray($children, $this->_fields_recurrence); + + // Get the cycle type (must be present) + $recurrence['cycle'] = $node->getAttribute('cycle'); + // Get the sub type (may be present) + $recurrence['type'] = $node->getAttribute('type'); + + // Exclusions. + if (isset($recurrence['exclusion'])) { + $exceptions = array(); + foreach($recurrence['exclusion'] as $exclusion) { + if (!empty($exclusion)) { + list($year, $month, $mday) = sscanf($exclusion, '%04d-%02d-%02d'); + + $exceptions[] = sprintf('%04d%02d%02d', $year, $month, $mday); + } + } + $recurrence['exceptions'] = $exceptions; + } + + // Completed dates. + if (isset($recurrence['complete'])) { + $completions = array(); + foreach($recurrence['complete'] as $complete) { + if (!empty($complete)) { + list($year, $month, $mday) = sscanf($complete, '%04d-%02d-%02d'); + + $completions[] = sprintf('%04d%02d%02d', $year, $month, $mday); + } + } + $recurrence['completions'] = $completions; + } + + // Range is special + foreach($children as $child) { + if ($child->tagname == 'range') { + $recurrence['range-type'] = $child->get_attribute('type'); + } + } + + if (isset($recurrence['range']) && isset($recurrence['range-type']) + && $recurrence['range-type'] == 'date') { + $recurrence['range'] = Horde_Kolab_Format_Date::decodeDate($recurrence['range']); + } + + // Sanity check + $valid = $this->_validateRecurrence($recurrence); + + return $recurrence; + } + + /** + * Validate recurrence hash information. + * + * @param array $recurrence Recurrence hash loaded from XML. + * + * @return boolean True on success. + * + * @throws Horde_Exception If the recurrence data is invalid. + */ + protected function _validateRecurrence(&$recurrence) + { + if (!isset($recurrence['cycle'])) { + throw new Horde_Exception('recurrence tag error: cycle attribute missing'); + } + + if (!isset($recurrence['interval'])) { + throw new Horde_Exception('recurrence tag error: interval tag missing'); + } + $interval = $recurrence['interval']; + if ($interval < 0) { + throw new Horde_Exception('recurrence tag error: interval cannot be below zero: ' + . $interval); + } + + if ($recurrence['cycle'] == 'weekly') { + // Check for + if (!isset($recurrence['day']) || count($recurrence['day']) == 0) { + throw new Horde_Exception('recurrence tag error: day tag missing for weekly recurrence'); + } + } + + // The code below is only for monthly or yearly recurrences + if ($recurrence['cycle'] != 'monthly' + && $recurrence['cycle'] != 'yearly') + return true; + + if (!isset($recurrence['type'])) { + throw new Horde_Exception('recurrence tag error: type attribute missing'); + } + + if (!isset($recurrence['daynumber'])) { + throw new Horde_Exception('recurrence tag error: daynumber tag missing'); + } + $daynumber = $recurrence['daynumber']; + if ($daynumber < 0) { + throw new Horde_Exception('recurrence tag error: daynumber cannot be below zero: ' + . $daynumber); + } + + if ($recurrence['type'] == 'daynumber') { + if ($recurrence['cycle'] == 'yearly' && $daynumber > 366) { + throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 366 for yearly recurrences: ' . $daynumber); + } else if ($recurrence['cycle'] == 'monthly' && $daynumber > 31) { + throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 31 for monthly recurrences: ' . $daynumber); + } + } else if ($recurrence['type'] == 'weekday') { + // daynumber is the week of the month + if ($daynumber > 5) { + throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 5 for type weekday: ' . $daynumber); + } + + // Check for + if (!isset($recurrence['day']) || count($recurrence['day']) == 0) { + throw new Horde_Exception('recurrence tag error: day tag missing for type weekday'); + } + } + + if (($recurrence['type'] == 'monthday' || $recurrence['type'] == 'yearday') + && $recurrence['cycle'] == 'monthly') + { + throw new Horde_Exception('recurrence tag error: type monthday/yearday is only allowed for yearly recurrences'); + } + + if ($recurrence['cycle'] == 'yearly') { + if ($recurrence['type'] == 'monthday') { + // daynumber and month + if (!isset($recurrence['month'])) { + throw new Horde_Exception('recurrence tag error: month tag missing for type monthday'); + } + if ($daynumber > 31) { + throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 31 for type monthday: ' . $daynumber); + } + } else if ($recurrence['type'] == 'yearday') { + if ($daynumber > 366) { + throw new Horde_Exception('recurrence tag error: daynumber cannot be larger than 366 for type yearday: ' . $daynumber); + } + } + } + + return true; + } + + /** + * Save recurrence information. + * + * @param Horde_DOM_Node $parent_node The parent node to attach + * the child to. + * @param string $name The name of the node. + * @param mixed $value The value to store. + * @param boolean $missing Has the value been missing? + * + * @return Horde_DOM_Node The new child node. + */ + protected function _saveRecurrence($parent_node, $name, $value, $missing) + { + $this->_removeNodes($parent_node, $name); + + if (empty($value)) { + return false; + } + + // Exclusions. + if (isset($value['exceptions'])) { + $exclusions = array(); + foreach($value['exceptions'] as $exclusion) { + if (!empty($exclusion)) { + list($year, $month, $mday) = sscanf($exclusion, '%04d%02d%02d'); + $exclusions[] = "$year-$month-$mday"; + } + } + $value['exclusion'] = $exclusions; + } + + // Completed dates. + if (isset($value['completions'])) { + $completions = array(); + foreach($value['completions'] as $complete) { + if (!empty($complete)) { + list($year, $month, $mday) = sscanf($complete, '%04d%02d%02d'); + $completions[] = "$year-$month-$mday"; + } + } + $value['complete'] = $completions; + } + + if (isset($value['range']) + && isset($value['range-type']) && $value['range-type'] == 'date') { + $value['range'] = Horde_Kolab_Format_Date::encodeDate($value['range']); + } + + $r_node = $this->_xmldoc->createElement($name); + $r_node = $parent_node->appendChild($r_node); + + // Save normal fields + $this->_saveArray($r_node, $value, $this->_fields_recurrence); + + // Add attributes + $r_node->setAttribute('cycle', $value['cycle']); + if (isset($value['type'])) { + $r_node->setAttribute('type', $value['type']); + } + + $child = $this->_findNode($r_node->childNodes, 'range'); + if ($child) { + $child->setAttribute('type', $value['range-type']); + } + + return $r_node; + } +} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Annotation.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Annotation.php new file mode 100644 index 000000000..7604cffcf --- /dev/null +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Annotation.php @@ -0,0 +1,106 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + */ + +/** + * Kolab XML handler for IMAP folder annotations. + * + * Copyright 2008-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_Format + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + * @since Horde 3.2 + */ +class Horde_Kolab_Format_Xml_Annotation extends Horde_Kolab_Format_Xml +{ + /** + * Specific data fields for the prefs object + * + * @var Kolab + */ + protected $_fields_specific; + + /** + * Constructor + */ + public function __construct() + { + $this->_root_name = 'annotations'; + + /** + * Specific preferences fields, in kolab format specification order + */ + $this->_fields_specific = array( + 'annotation' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ), + ); + + parent::__construct(); + } + + /** + * Load the groupware object based on the specifc XML values. + * + * @param array &$children An array of XML nodes. + * + * @return array Array with the object data + * + * @throws Horde_Exception If parsing the XML data failed. + */ + protected function _load(&$children) + { + $object = $this->_loadArray($children, $this->_fields_specific); + + $result = array(); + foreach ($object['annotation'] as $annotation) { + list($key, $value) = split('#', $annotation, 2); + $result[base64_decode($key)] = base64_decode($value); + } + + return $result; + } + + /** + * Save the specific XML values. + * + * @param array $root The XML document root. + * @param array $object The resulting data array. + * + * @return boolean True on success. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _save($root, $object) + { + $annotations = array(); + foreach ($object as $key => $value) { + if ($key != 'uid') { + $annotations['annotation'][] = base64_encode($key) . + '#' . base64_encode($value); + } + } + + return $this->_saveArray($root, $annotations, $this->_fields_specific); + } +} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Contact.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Contact.php new file mode 100644 index 000000000..ef66f2f58 --- /dev/null +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Contact.php @@ -0,0 +1,525 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + */ + +/** + * Kolab XML handler for contact groupware objects + * + * Copyright 2007-2009 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_Format + * @author Thomas Jarosch + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + * @since Horde 3.2 + */ +class Horde_Kolab_Format_Xml_Contact extends Horde_Kolab_Format_Xml +{ + /** + * Specific data fields for the contact object + * + * @var array + */ + protected $_fields_specific; + + /** + * Structure of the name field + * + * @var array + */ + protected $_fields_name = array( + 'given-name' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'middle-names' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'last-name' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'full-name' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'initials' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'prefix' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'suffix' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ) + ); + + /** + * Structure of an address field + * + * @var array + */ + protected $_fields_address = array( + 'type' => self::TYPE_COMPOSITE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => 'home', + ), + 'street' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'locality' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'region' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'postal-code' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'country' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ) + ); + + /** + * Structure of a phone field + * + * @var array + */ + protected $_fields_phone = array( + 'type' => self::TYPE_COMPOSITE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'number' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ), + ); + + /** + * Address types + * + * @var array + */ + protected $_address_types = array( + 'business', + 'home', + 'other', + ); + + /** + * Phone types + * + * @var array + */ + protected $_phone_types = array( + 'business1', + 'business2', + 'businessfax', + 'callback', + 'car', + 'company', + 'home1', + 'home2', + 'homefax', + 'isdn', + 'mobile', + 'pager', + 'primary', + 'radio', + 'telex', + 'ttytdd', + 'assistant', + 'other', + ); + + /** + * Constructor + */ + public function __construct() + { + $this->_root_name = "contact"; + + /** Specific task fields, in kolab format specification order + */ + $this->_fields_specific = array( + 'name' => array ( + 'type' => self::TYPE_COMPOSITE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => $this->_fields_name, + ), + 'free-busy-url' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'organization' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'web-page' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'im-address' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'department' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'office-location' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'profession' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'job-title' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'manager-name' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'assistant' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'nick-name' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'spouse-name' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'birthday' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'anniversary' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'picture' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'children' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'gender' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'language' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'address' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => $this->_fields_address, + ), + 'email' => array ( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => $this->_fields_simple_person, + ), + 'phone' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => $this->_fields_phone, + ), + 'preferred-address' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'latitude' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'longitude' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + // Horde specific fields + 'pgp-publickey' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + // Support for broken clients + 'website' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'im-adress' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ); + + parent::__construct(); + } + + /** + * Load the groupware object based on the specifc XML values. + * + * @param array &$children An array of XML nodes. + * + * @return array Array with the object data. + * + * @throws Horde_Exception If parsing the XML data failed. + */ + protected function _load(&$children) + { + $object = $this->_loadArray($children, $this->_fields_specific); + + // Handle name fields + if (isset($object['name'])) { + $object = array_merge($object['name'], $object); + unset($object['name']); + } + + // Handle email fields + $emails = array(); + if (isset($object['email'])) { + foreach ($object['email'] as $email) { + $smtp_address = $email['smtp-address']; + if (!empty($smtp_address)) { + $emails[] = $smtp_address; + } + } + } + $object['emails'] = implode(', ', $emails); + + // Handle phone fields + if (isset($object['phone'])) { + foreach ($object['phone'] as $phone) { + if (isset($phone['number']) && + in_array($phone['type'], $this->_phone_types)) { + $object["phone-" . $phone['type']] = $phone['number']; + } + } + } + + // Handle address fields + if (isset($object['address'])) { + foreach ($object['address'] as $address) { + if (in_array($address['type'], $this->_address_types)) { + foreach ($address as $name => $value) { + $object["addr-" . $address['type'] . "-" . $name] = $value; + } + } + } + } + + // Handle gender field + if (isset($object['gender'])) { + $gender = $object['gender']; + + if ($gender == "female") { + $object['gender'] = 1; + } else if ($gender == "male") { + $object['gender'] = 0; + } else { + // unspecified gender + unset($object['gender']); + } + } + + // Compatibility with broken clients + $broken_fields = array("website" => "web-page", + "im-adress" => "im-address"); + foreach ($broken_fields as $broken_field => $real_field) { + if (!empty($object[$broken_field]) && empty($object[$real_field])) { + $object[$real_field] = $object[$broken_field]; + } + unset($object[$broken_field]); + } + + $object['__type'] = 'Object'; + + return $object; + } + + /** + * Save the specifc XML values. + * + * @param array $root The XML document root. + * @param array $object The resulting data array. + * + * @return boolean True on success. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _save($root, $object) + { + // Handle name fields + $name = array(); + foreach (array_keys($this->_fields_name) as $key) { + if (isset($object[$key])) { + $name[$key] = $object[$key]; + unset($object[$key]); + } + } + $object['name'] = $name; + + // Handle email fields + if (!isset($object['emails'])) { + $emails = array(); + } else { + $emails = explode(',', $object['emails']); + } + + if (isset($object['email']) && + !in_array($object['email'], $emails)) { + $emails[] = $object['email']; + } + + $object['email'] = array(); + + foreach ($emails as $email) { + $email = trim($email); + if (!empty($email)) { + $new_email = array('display-name' => $object['name']['full-name'], + 'smtp-address' => $email); + + $object['email'][] = $new_email; + } + } + + // Handle phone fields + if (!isset($object['phone'])) { + $object['phone'] = array(); + } + foreach ($this->_phone_types as $type) { + $key = 'phone-' . $type; + if (array_key_exists($key, $object)) { + $new_phone = array('type' => $type, + 'number' => $object[$key]); + + // Update existing phone entry of this type + $updated = false; + foreach ($object['phone'] as $index => $phone) { + if ($phone['type'] == $type) { + $object['phone'][$index] = $new_phone; + + $updated = true; + break; + } + } + if (!$updated) { + $object['phone'][] = $new_phone; + } + } + } + + // Phone cleanup: remove empty numbers + foreach ($object['phone'] as $index => $phone) { + if (empty($phone['number'])) { + unset($object['phone'][$index]); + } + } + + // Handle address fields + if (!isset($object['address'])) { + $object['address'] = array(); + } + + foreach ($this->_address_types as $type) { + $basekey = 'addr-' . $type . '-'; + $new_address = array('type' => $type); + foreach (array_keys($this->_fields_address['array']) as $subkey) { + $key = $basekey . $subkey; + if (array_key_exists($key, $object)) { + $new_address[$subkey] = $object[$key]; + } + } + + // Update existing address entry of this type + $updated = false; + foreach ($object['address'] as $index => $address) { + if ($address['type'] == $type) { + $object['address'][$index] = $new_address; + + $updated = true; + } + } + if (!$updated) { + $object['address'][] = $new_address; + } + } + + // Address cleanup: remove empty addresses + foreach ($object['address'] as $index => $address) { + $all_empty = true; + foreach ($address as $name => $value) { + if (!empty($value) && $name != "type") { + $all_empty = false; + break; + } + } + + if ($all_empty) { + unset($object['address'][$index]); + } + } + + // Handle gender field + if (isset($object['gender'])) { + $gender = $object['gender']; + + if ($gender == "0") { + $object['gender'] = "male"; + } else if ($gender == "1") { + $object['gender'] = "female"; + } else { + // unspecified gender + unset($object['gender']); + } + } + + // Do the actual saving + return $this->_saveArray($root, $object, $this->_fields_specific); + } +} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Distributionlist.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Distributionlist.php new file mode 100644 index 000000000..e471953e1 --- /dev/null +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Distributionlist.php @@ -0,0 +1,113 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + */ + +/** + * Kolab XML handler for distributionlist groupware objects + * + * Copyright 2007-2009 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_Format + * @author Thomas Jarosch + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + * @since Horde 3.2 + */ +class Horde_Kolab_Format_Xml_Distributionlist extends Horde_Kolab_Format_Xml +{ + /** + * Specific data fields for the contact object + * + * @var array + */ + protected $_fields_specific; + + /** + * Constructor + */ + public function __construct() + { + $this->_root_name = "distribution-list"; + + /** Specific task fields, in kolab format specification order + */ + $this->_fields_specific = array( + 'display-name' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_NOT_EMPTY + ), + 'member' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => $this->_fields_simple_person, + ) + ); + + parent::__construct(); + } + + /** + * Load the groupware object based on the specifc XML values. + * + * @param array &$children An array of XML nodes. + * + * @return array Array with data. + * + * @throws Horde_Exception If parsing the XML data failed. + */ + protected function _load(&$children) + { + $object = $this->_loadArray($children, $this->_fields_specific); + + // Map the display-name of a kolab dist list to horde's lastname attribute + if (isset($object['display-name'])) { + $object['last-name'] = $object['display-name']; + unset($object['display-name']); + } + + /** + * The mapping from $object['member'] as stored in XML back to + * Turba_Objects (contacts) must be performed in the + * Kolab_IMAP storage driver as we need access to the search + * facilities of the kolab storage driver. + */ + $object['__type'] = 'Group'; + return $object; + } + + /** + * Save the specifc XML values. + * + * @param array $root The XML document root. + * @param array $object The resulting data array. + * + * @return boolean True on success. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _save($root, $object) + { + // Map the display-name of a kolab dist list to horde's lastname attribute + if (isset($object['last-name'])) { + $object['display-name'] = $object['last-name']; + unset($object['last-name']); + } + + return $this->_saveArray($root, $object, $this->_fields_specific); + } +} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Event.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Event.php new file mode 100644 index 000000000..9544ae041 --- /dev/null +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Event.php @@ -0,0 +1,139 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + */ + +/** + * Kolab XML handler for event groupware objects. + * + * Copyright 2007-2009 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_Format + * @author Thomas Jarosch + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + * @since Horde 3.2 + */ +class Horde_Kolab_Format_Xml_Event extends Horde_Kolab_Format_Xml +{ + /** + * Specific data fields for the contact object + * + * @var array + */ + protected $_fields_specific; + + /** + * Constructor + */ + public function __construct() + { + $this->_root_name = 'event'; + + /** Specific event fields, in kolab format specification order + */ + $this->_fields_specific = array( + 'summary' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'location' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'organizer' => $this->_fields_simple_person, + 'start-date' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_NOT_EMPTY, + ), + 'alarm' => array( + 'type' => self::TYPE_INTEGER, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'recurrence' => array( + 'type' => self::TYPE_COMPOSITE, + 'value' => self::VALUE_CALCULATED, + 'load' => 'Recurrence', + 'save' => 'Recurrence', + ), + 'attendee' => $this->_fields_attendee, + 'show-time-as' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'color-label' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'end-date' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_NOT_EMPTY, + ), + ); + + parent::__construct(); + } + + /** + * Load event XML values and translate start/end date. + * + * @param array &$children An array of XML nodes. + * + * @return array Array with the object data. + * + * @throws Horde_Exception If parsing the XML data failed. + */ + protected function _load(&$children) + { + $object = parent::_load($children); + + // Translate start/end date including full day events + if (strlen($object['start-date']) == 10) { + $object['start-date'] = Horde_Kolab_Format_Date::decodeDate($object['start-date']); + $object['end-date'] = Horde_Kolab_Format_Date::decodeDate($object['end-date']) + 24*60*60; + } else { + $object['start-date'] = Horde_Kolab_Format_Date::decodeDateTime($object['start-date']); + $object['end-date'] = Horde_Kolab_Format_Date::decodeDateTime($object['end-date']); + } + + return $object; + } + + /** + * Save event XML values and translate start/end date. + * + * @param array $root The XML document root. + * @param array $object The resulting data array. + * + * @return boolean True on success. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _save($root, $object) + { + // Translate start/end date including full day events + if (!empty($object['_is_all_day'])) { + $object['start-date'] = Horde_Kolab_Format_Date::encodeDate($object['start-date']); + $object['end-date'] = Horde_Kolab_Format_Date::encodeDate($object['end-date'] - 24*60*60); + } else { + $object['start-date'] = Horde_Kolab_Format_Date::encodeDateTime($object['start-date']); + $object['end-date'] = Horde_Kolab_Format_Date::encodeDateTime($object['end-date']); + } + + return parent::_save($root, $object); + } +} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Hprefs.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Hprefs.php new file mode 100644 index 000000000..b7a8eebc9 --- /dev/null +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Hprefs.php @@ -0,0 +1,119 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + */ + +/** + * Kolab XML handler for client preferences. + * + * Copyright 2007-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_Format + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + * @since Horde 3.2 + */ +class Horde_Kolab_Format_Xml_Hprefs extends Horde_Kolab_Format_Xml +{ + /** + * Specific data fields for the prefs object + * + * @var Kolab + */ + protected $_fields_specific; + + /** + * Automatically create categories if they are missing? + * + * @var boolean + */ + protected $_create_categories = false; + + /** + * Constructor + */ + public function __construct() + { + $this->_root_name = 'h-prefs'; + + /** Specific preferences fields, in kolab format specification order + */ + $this->_fields_specific = array( + 'application' => array ( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'pref' => array( + 'type' => self::TYPE_MULTIPLE, + 'value' => self::VALUE_MAYBE_MISSING, + 'array' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ), + ); + + parent::__construct(); + } + + /** + * Load an object based on the given XML string. + * + * @param string &$xmltext The XML of the message as string. + * + * @return array The data array representing the object. + * + * @throws Horde_Exception If parsing the XML data failed. + */ + public function load(&$xmltext) + { + $object = parent::load($xmltext); + + if (empty($object['application'])) { + if (!empty($object['categories'])) { + $object['application'] = $object['categories']; + unset($object['categories']); + } else { + throw new Horde_Exception('Preferences XML object is missing an application setting.'); + } + } + + return $object; + } + + /** + * Convert the data to a XML string. + * + * @param array $object The data array representing the note. + * + * @return string The data as XML string. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + public function save($object) + { + if (empty($object['application'])) { + if (!empty($object['categories'])) { + $object['application'] = $object['categories']; + unset($object['categories']); + } else { + throw new Horde_Exception('Preferences XML object is missing an application setting.'); + } + } + + return parent::save($object); + } +} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Note.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Note.php new file mode 100644 index 000000000..bd12d2116 --- /dev/null +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Note.php @@ -0,0 +1,106 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + */ + +/** + * Kolab XML handler for note groupware objects. + * + * Copyright 2007-2009 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_Format + * @author Thomas Jarosch + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + * @since Horde 3.2 + */ +class Horde_Kolab_Format_Xml_Note extends Horde_Kolab_Format_Xml +{ + /** + * Specific data fields for the note object + * + * @var Kolab + */ + protected $_fields_specific; + + /** + * Constructor + */ + public function __construct() + { + $this->_root_name = 'note'; + + /** Specific note fields, in kolab format specification order + */ + $this->_fields_specific = array( + 'summary' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'background-color' => array( + 'type' => self::TYPE_COLOR, + 'value' => self::VALUE_DEFAULT, + 'default' => '#000000', + ), + 'foreground-color' => array( + 'type' => self::TYPE_COLOR, + 'value' => self::VALUE_DEFAULT, + 'default' => '#ffff00', + ), + ); + + parent::__construct(); + } + + /** + * Load the groupware object based on the specifc XML values. + * + * @param array &$children An array of XML nodes. + * + * @return array Array with the object data + * + * @throws Horde_Exception If parsing the XML data failed. + */ + protected function _load(&$children) + { + $object = $this->_loadArray($children, $this->_fields_specific); + + $object['desc'] = $object['summary']; + unset($object['summary']); + + return $object; + } + + /** + * Save the specific XML values. + * + * @param array $root The XML document root. + * @param array $object The resulting data array. + * + * @return boolean True on success. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _save($root, $object) + { + $object['summary'] = $object['desc']; + unset($object['desc']); + + return $this->_saveArray($root, $object, $this->_fields_specific); + } +} diff --git a/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Task.php b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Task.php new file mode 100644 index 000000000..5d37389f2 --- /dev/null +++ b/framework/Kolab_Format/lib/Horde/Kolab/Format/Xml/Task.php @@ -0,0 +1,199 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + */ + +/** + * Kolab XML handler for task groupware objects. + * + * Copyright 2007-2009 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_Format + * @author Thomas Jarosch + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Server + * @since Horde 3.2 + */ +class Horde_Kolab_Format_Xml_Task extends Horde_Kolab_Format_Xml +{ + /** + * Specific data fields for the note object + * + * @var array + */ + protected $_fields_specific; + + /** + * Constructor + */ + public function __construct() + { + $this->_root_name = 'task'; + + /** Specific task fields, in kolab format specification order + */ + $this->_fields_specific = array( + 'summary' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'location' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => '', + ), + 'creator' => $this->_fields_simple_person, + 'organizer' => $this->_fields_simple_person, + 'start-date' => array( + 'type' => self::TYPE_DATE_OR_DATETIME, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'alarm' => array( + 'type' => self::TYPE_INTEGER, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'recurrence' => array( + 'type' => self::TYPE_COMPOSITE, + 'value' => self::VALUE_CALCULATED, + 'load' => 'Recurrence', + 'save' => 'Recurrence', + ), + 'attendee' => $this->_fields_attendee, + 'priority' => array( + 'type' => self::TYPE_INTEGER, + 'value' => self::VALUE_DEFAULT, + 'default' => 3, + ), + 'completed' => array( + 'type' => self::TYPE_INTEGER, + 'value' => self::VALUE_DEFAULT, + 'default' => 0, + ), + 'status' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_DEFAULT, + 'default' => 'not-started', + ), + 'due-date' => array( + 'type' => self::TYPE_DATE_OR_DATETIME, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'parent' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + // These are not part of the Kolab specification but it is + // ok if the client supports additional entries + 'estimate' => array( + 'type' => self::TYPE_STRING, + 'value' => self::VALUE_MAYBE_MISSING, + ), + 'completed_date' => array( + 'type' => self::TYPE_DATE_OR_DATETIME, + 'value' => self::VALUE_MAYBE_MISSING, + ), + ); + + parent::__construct(); + } + + /** + * Load the groupware object based on the specifc XML values. + * + * @param array &$children An array of XML nodes. + * + * @return array Array with data. + * + * @throws Horde_Exception If parsing the XML data failed. + */ + protected function _load(&$children) + { + $object = $this->_loadArray($children, $this->_fields_specific); + + $object['name'] = $object['summary']; + unset($object['summary']); + + if (empty($object['completed-date'])) { + $object['completed-date'] = null; + } + + if (empty($object['alarm'])) { + $object['alarm'] = null; + } + + if (isset($object['due-date'])) { + $object['due'] = $object['due-date']; + unset($object['due-date']); + } else { + $object['due'] = null; + } + + if (isset($object['start-date'])) { + $object['start'] = $object['start-date']; + unset($object['start-date']); + } else { + $object['start'] = null; + } + + if (!isset($object['estimate'])) { + $object['estimate'] = null; + } else { + $object['estimate'] = (float) $object['estimate']; + } + + if (!isset($object['parent'])) { + $object['parent'] = null; + } + + $object['completed'] = (bool) Kolab::percentageToBoolean($object['completed']); + + if (isset($object['organizer']) && isset($object['organizer']['smtp-address'])) { + $object['assignee'] = $object['organizer']['smtp-address']; + } + + return $object; + } + + /** + * Save the specific XML values. + * + * @param array $root The XML document root. + * @param array $object The resulting data array. + * + * @return boolean True on success. + * + * @throws Horde_Exception If converting the data to XML failed. + */ + protected function _save($root, $object) + { + $object['summary'] = $object['name']; + unset($object['name']); + + $object['due-date'] = $object['due']; + unset($object['due']); + + $object['start-date'] = $object['start']; + unset($object['start']); + + $object['estimate'] = number_format($object['estimate'], 2); + + $object['completed'] = Kolab::BooleanToPercentage($object['completed']); + + return $this->_saveArray($root, $object, $this->_fields_specific); + } +} diff --git a/framework/Kolab_Format/package.xml b/framework/Kolab_Format/package.xml index bd98fce45..df33af729 100644 --- a/framework/Kolab_Format/package.xml +++ b/framework/Kolab_Format/package.xml @@ -34,7 +34,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> 2009-04-02 - 1.0.1 + 1.0.2 1.0.0 @@ -43,14 +43,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> LGPL - * Handle parsing errors within the DOM XML extension correctly - kolab/issue3520 (calendar with certain entries does not display in web client) - https://www.intevation.de/roundup/kolab/issue3520 - kolab/issue3525 (free/busy regeneration aborts for unparsable events) - https://www.intevation.de/roundup/kolab/issue3525 - * Accept ISO-8859-1 encoding even if advertised as UTF-8 - kolab/issue3528 (Events with broken encoding should work) - https://www.intevation.de/roundup/kolab/issue3528 + * Converted to Horde4/PHP5 + * Removed Horde_DOM dependency. @@ -80,8 +74,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> - - + + @@ -89,7 +83,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> - + @@ -127,12 +121,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> 1.4.0b1 - Horde_DOM - pear.horde.org - 0.1.0 - - - Horde_NLS + Nls pear.horde.org @@ -146,7 +135,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> pear.horde.org - Horde_Date + Date pear.horde.org @@ -158,14 +147,14 @@ http://pear.php.net/dtd/package-2.0.xsd"> - - - - - - - - + + + + + + + + @@ -182,6 +171,28 @@ http://pear.php.net/dtd/package-2.0.xsd"> + 2009-04-02 + + 1.0.1 + 1.0.0 + + + stable + stable + + LGPL + + * Handle parsing errors within the DOM XML extension correctly + kolab/issue3520 (calendar with certain entries does not display in web client) + https://www.intevation.de/roundup/kolab/issue3520 + kolab/issue3525 (free/busy regeneration aborts for unparsable events) + https://www.intevation.de/roundup/kolab/issue3525 + * Accept ISO-8859-1 encoding even if advertised as UTF-8 + kolab/issue3528 (Events with broken encoding should work) + https://www.intevation.de/roundup/kolab/issue3528 + + + 2008-12-12 1.0.0 diff --git a/framework/Kolab_Format/test/Horde/Kolab/Format/ContactTest.php b/framework/Kolab_Format/test/Horde/Kolab/Format/ContactTest.php index 3648013bb..5cd856786 100644 --- a/framework/Kolab_Format/test/Horde/Kolab/Format/ContactTest.php +++ b/framework/Kolab_Format/test/Horde/Kolab/Format/ContactTest.php @@ -8,14 +8,10 @@ */ /** - * We need the unit test framework + * The Autoloader allows us to omit "require/include" statements. */ -require_once 'PHPUnit/Framework.php'; +require_once 'Horde/Autoloader.php'; -require_once 'Horde/Nls.php'; -require_once 'Horde/Kolab/Format.php'; -require_once 'Horde/Kolab/Format/XML.php'; -require_once 'Horde/Kolab/Format/XML/Contact.php'; class DummyRegistry { function get() @@ -24,7 +20,7 @@ class DummyRegistry { } } -class Horde_Kolab_Format_XML_contact_dummy extends Horde_Kolab_Format_XML_contact +class Horde_Kolab_Format_Xml_Contact_dummy extends Horde_Kolab_Format_Xml_Contact { function _saveCreationDate($parent_node, $name, $value, $missing) { @@ -77,7 +73,7 @@ class Horde_Kolab_Format_ContactTest extends PHPUnit_Framework_TestCase */ public function testSingleEmail() { - $contact = &new Horde_Kolab_Format_XML_contact_dummy(); + $contact = &new Horde_Kolab_Format_Xml_contact_dummy(); $object = array('uid' => '1', 'full-name' => 'User Name', 'email' => 'user@example.org'); @@ -91,7 +87,7 @@ class Horde_Kolab_Format_ContactTest extends PHPUnit_Framework_TestCase */ public function testPGP() { - $contact = &new Horde_Kolab_Format_XML_contact_dummy(); + $contact = &new Horde_Kolab_Format_Xml_contact_dummy(); $object = array('uid' => '1', 'full-name' => 'User Name', 'pgp-publickey' => 'PGP Test Key', @@ -108,7 +104,7 @@ class Horde_Kolab_Format_ContactTest extends PHPUnit_Framework_TestCase { global $prefs; - $contact = &new Horde_Kolab_Format_XML_contact(); + $contact = &new Horde_Kolab_Format_Xml_contact(); $xml = file_get_contents(dirname(__FILE__) . '/fixtures/contact_category.xml'); $object = $contact->load($xml); $this->assertContains('Test', $object['categories']); @@ -134,7 +130,7 @@ class Horde_Kolab_Format_ContactTest extends PHPUnit_Framework_TestCase /* Monkey patch to allw the value to be set. */ $prefs->_prefs['categories'] = array('v' => ''); - $contact = &new Horde_Kolab_Format_XML_contact(); + $contact = &new Horde_Kolab_Format_Xml_contact(); $xml = file_get_contents(dirname(__FILE__) . '/fixtures/contact_category.xml'); $object = $contact->load($xml); diff --git a/framework/Kolab_Format/test/Horde/Kolab/Format/EventTest.php b/framework/Kolab_Format/test/Horde/Kolab/Format/EventTest.php index a8fc5c4be..bec51f6b9 100644 --- a/framework/Kolab_Format/test/Horde/Kolab/Format/EventTest.php +++ b/framework/Kolab_Format/test/Horde/Kolab/Format/EventTest.php @@ -8,12 +8,9 @@ */ /** - * We need the unit test framework + * The Autoloader allows us to omit "require/include" statements. */ -require_once 'PHPUnit/Framework.php'; - -require_once 'Horde/Nls.php'; -require_once 'Horde/Kolab/Format.php'; +require_once 'Horde/Autoloader.php'; /** * Test event handling. diff --git a/framework/Kolab_Format/test/Horde/Kolab/Format/MimeAttrTest.php b/framework/Kolab_Format/test/Horde/Kolab/Format/MimeAttrTest.php index f2d5535b9..99733e396 100644 --- a/framework/Kolab_Format/test/Horde/Kolab/Format/MimeAttrTest.php +++ b/framework/Kolab_Format/test/Horde/Kolab/Format/MimeAttrTest.php @@ -15,12 +15,9 @@ */ /** - * We need the unit test framework + * The Autoloader allows us to omit "require/include" statements. */ -require_once 'PHPUnit/Framework.php'; - -require_once 'Horde/Nls.php'; -require_once 'Horde/Kolab/Format.php'; +require_once 'Horde/Autoloader.php'; /** * Test Kolab Format MIME attributes diff --git a/framework/Kolab_Format/test/Horde/Kolab/Format/PreferencesTest.php b/framework/Kolab_Format/test/Horde/Kolab/Format/PreferencesTest.php index fc50a53e3..b752810ec 100644 --- a/framework/Kolab_Format/test/Horde/Kolab/Format/PreferencesTest.php +++ b/framework/Kolab_Format/test/Horde/Kolab/Format/PreferencesTest.php @@ -8,17 +8,13 @@ */ /** - * We need the unit test framework + * The Autoloader allows us to omit "require/include" statements. */ -require_once 'PHPUnit/Framework.php'; +require_once 'Horde/Autoloader.php'; -require_once 'Horde/Nls.php'; -require_once 'Horde/Kolab/Format.php'; -require_once 'Horde/Kolab/Format/XML.php'; -require_once 'Horde/Kolab/Format/XML/Hprefs.php'; -class Horde_Kolab_Format_XML_hprefs_dummy extends Horde_Kolab_Format_XML_hprefs +class Horde_Kolab_Format_Xml_Hprefs_dummy extends Horde_Kolab_Format_Xml_Hprefs { function _saveCreationDate($parent_node, $name, $value, $missing) { @@ -71,7 +67,7 @@ class Horde_Kolab_Format_PreferencesTest extends PHPUnit_Framework_TestCase */ public function testConversionFromOld() { - $preferences = &new Horde_Kolab_Format_XML_hprefs_dummy(); + $preferences = &new Horde_Kolab_Format_Xml_hprefs_dummy(); $xml = file_get_contents(dirname(__FILE__) . '/fixtures/preferences_read_old.xml'); $object = $preferences->load($xml); diff --git a/framework/Kolab_Format/test/Horde/Kolab/Format/RecurrenceTest.php b/framework/Kolab_Format/test/Horde/Kolab/Format/RecurrenceTest.php index 0f81ba05a..cefd966b5 100644 --- a/framework/Kolab_Format/test/Horde/Kolab/Format/RecurrenceTest.php +++ b/framework/Kolab_Format/test/Horde/Kolab/Format/RecurrenceTest.php @@ -8,12 +8,9 @@ */ /** - * We need the unit test framework + * The Autoloader allows us to omit "require/include" statements. */ -require_once 'PHPUnit/Framework.php'; - -require_once 'Horde/Nls.php'; -require_once 'Horde/Kolab/Format.php'; +require_once 'Horde/Autoloader.php'; /** * Test recurrence handling diff --git a/framework/Kolab_Format/test/Horde/Kolab/Format/XmlTest.php b/framework/Kolab_Format/test/Horde/Kolab/Format/XmlTest.php index b5f23e74f..380c8aca2 100644 --- a/framework/Kolab_Format/test/Horde/Kolab/Format/XmlTest.php +++ b/framework/Kolab_Format/test/Horde/Kolab/Format/XmlTest.php @@ -8,13 +8,9 @@ */ /** - * We need the unit test framework + * The Autoloader allows us to omit "require/include" statements. */ -require_once 'PHPUnit/Framework.php'; - -require_once 'Horde/Nls.php'; -require_once 'Horde/Kolab/Format.php'; -require_once 'Horde/Kolab/Format/XML.php'; +require_once 'Horde/Autoloader.php'; /** * Test the XML format. @@ -71,8 +67,7 @@ class Horde_Kolab_Format_XmlTest extends PHPUnit_Framework_TestCase public function testAdd() { $xml = &new Horde_Kolab_Format_XML(); - $xml->_prepareSave(); - $root = $xml->_xmldoc; + $root = $xml->_prepareSave(); $base = $xml->_xmldoc->saveXML(); // A missing attribute should cause no change if it @@ -120,9 +115,8 @@ class Horde_Kolab_Format_XmlTest extends PHPUnit_Framework_TestCase */ public function testNodeOps() { - $dxml = new Horde_Kolab_Format_XML_dummy(); - $dxml->_prepareSave(); - $droot = $dxml->_xmldoc; + $dxml = new Horde_Kolab_Format_Xml_dummy(); + $droot = $dxml->_prepareSave(); // Test calculated nodes $dxml->_updateNode($droot, @@ -137,9 +131,8 @@ class Horde_Kolab_Format_XmlTest extends PHPUnit_Framework_TestCase 'save' => 'Value', 'type' => 0)); $this->assertEquals("\n\n empty2: , missing\n present1: present1\n\n", $dxml->_xmldoc->saveXML()); - $xml = &new Horde_Kolab_Format_XML(); - $xml->_prepareSave(); - $root = $xml->_xmldoc; + $xml = &new Horde_Kolab_Format_Xml(); + $root = $xml->_prepareSave(); $xml->_updateNode($root, array(), 'empty1', @@ -229,7 +222,7 @@ class Horde_Kolab_Format_XmlTest extends PHPUnit_Framework_TestCase public function testReleod() { // Save an object and reload it - $xml = new Horde_Kolab_Format_XML(); + $xml = new Horde_Kolab_Format_Xml(); $result = $xml->save(array('uid'=>'test', 'body' => 'body', 'dummy' => 'hello', @@ -251,9 +244,8 @@ class Horde_Kolab_Format_XmlTest extends PHPUnit_Framework_TestCase public function testComplex() { // Continue with complex values - $xml = new Horde_Kolab_Format_XML(); - $xml->_prepareSave(); - $root = $xml->_xmldoc; + $xml = new Horde_Kolab_Format_Xml(); + $root = $xml->_prepareSave(); // Test saving a composite value $xml->_updateNode($root, @@ -267,7 +259,7 @@ class Horde_Kolab_Format_XmlTest extends PHPUnit_Framework_TestCase 'attendee1', $xml->_fields_attendee); $this->assertEquals("\n\n \n test\n test@example.com\n \n \n \n test\n \n none\n true\n required\n \n \n \n test@example.com\n none\n true\n required\n \n\n", $xml->_xmldoc->saveXML()); - $children = $root->child_nodes(); + $children = $root->childNodes; // Load a composite value $data = $xml->_getXmlData($children, @@ -301,7 +293,7 @@ class Horde_Kolab_Format_XmlTest extends PHPUnit_Framework_TestCase * @author Gunnar Wrobel * @package Kolab_Format */ -class Horde_Kolab_Format_XML_dummy extends Horde_Kolab_Format_XML +class Horde_Kolab_Format_Xml_dummy extends Horde_Kolab_Format_Xml { function _saveValue($node, $name, $value, $missing) {