1: <?php
2: /**
3: * Implementation of the Kolab XML format.
4: *
5: * PHP version 5
6: *
7: * @category Kolab
8: * @package Kolab_Format
9: * @author Thomas Jarosch <thomas.jarosch@intra2net.com>
10: * @author Gunnar Wrobel <wrobel@pardus.de>
11: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
12: * @link http://www.horde.org/libraries/Horde_Kolab_Format
13: */
14:
15: /**
16: * Kolab XML to array hash converter.
17: *
18: * For implementing a new format type you will have to inherit this
19: * class and provide a _load/_save function.
20: *
21: * Copyright 2007-2009 Klarälvdalens Datakonsult AB
22: * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
23: *
24: * See the enclosed file COPYING for license information (LGPL). If you did not
25: * receive this file, see
26: * http://www.horde.org/licenses/lgpl21.
27: *
28: * @category Kolab
29: * @package Kolab_Format
30: * @author Thomas Jarosch <thomas.jarosch@intra2net.com>
31: * @author Gunnar Wrobel <wrobel@pardus.de>
32: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
33: * @link http://www.horde.org/libraries/Horde_Kolab_Format
34: */
35: class Horde_Kolab_Format_Xml implements Horde_Kolab_Format
36: {
37: /**
38: * Defines a XML value that should get a default value if missing
39: */
40: const PRODUCT_ID = __CLASS__;
41:
42: /**
43: * Defines a XML value that should get a default value if missing
44: */
45: const VALUE_DEFAULT = 0;
46:
47: /**
48: * Defines a XML value that may be missing
49: */
50: const VALUE_MAYBE_MISSING = 1;
51:
52: /**
53: * Defines a XML value that may not be missing
54: */
55: const VALUE_NOT_EMPTY = 2;
56:
57: /**
58: * Defines a XML value that will be calculated by its own function
59: */
60: const VALUE_CALCULATED = 3;
61:
62: /**
63: * Defines a XML value as string type
64: */
65: const TYPE_STRING = 'Horde_Kolab_Format_Xml_Type_String';
66:
67: /**
68: * Defines a XML value as integer type
69: */
70: const TYPE_INTEGER = 'Horde_Kolab_Format_Xml_Type_Integer';
71:
72: /**
73: * Defines a XML value as boolean type
74: */
75: const TYPE_BOOLEAN = 'Horde_Kolab_Format_Xml_Type_Boolean';
76:
77: /**
78: * Defines a XML value as date type
79: */
80: const TYPE_DATE = 'Horde_Kolab_Format_Xml_Type_Date';
81:
82: /**
83: * Defines a XML value as datetime type
84: */
85: const TYPE_DATETIME = 'Horde_Kolab_Format_Xml_Type_DateTime';
86:
87: /**
88: * Defines a XML value as date or datetime type
89: */
90: const TYPE_DATE_OR_DATETIME = 'Horde_Kolab_Format_Xml_Type_DateTime';
91:
92: /**
93: * Defines a XML value as color type
94: */
95: const TYPE_COLOR = 'Horde_Kolab_Format_Xml_Type_Color';
96:
97: /**
98: * Defines a XML value as composite value type
99: */
100: const TYPE_COMPOSITE = 'Horde_Kolab_Format_Xml_Type_Composite';
101:
102: /**
103: * Defines a XML value as array type
104: */
105: const TYPE_MULTIPLE = 'Horde_Kolab_Format_Xml_Type_Multiple';
106:
107: /**
108: * Defines a XML value as raw XML
109: */
110: const TYPE_XML = 'Horde_Kolab_Format_Xml_Type_XmlAppend';
111:
112: /**
113: * Represents the Kolab format root node
114: */
115: const TYPE_ROOT = 'Horde_Kolab_Format_Xml_Type_Root';
116:
117: /**
118: * The parser dealing with the input.
119: *
120: * @var Horde_Kolab_Format_Xml_Parser
121: */
122: protected $_parser;
123:
124: /**
125: * The factory for additional objects.
126: *
127: * @var Horde_Kolab_Format_Factory
128: */
129: protected $_factory;
130:
131: /**
132: * Requested version of the data array to return
133: *
134: * @var int
135: */
136: protected $_version = 2;
137:
138: /**
139: * The XML document this driver works with.
140: *
141: * @var DOMDocument
142: */
143: protected $_xmldoc = null;
144:
145: /**
146: * The name of the root element.
147: *
148: * @var string
149: */
150: protected $_root_name = 'kolab';
151:
152: /**
153: * Kolab format version of the root element.
154: *
155: * @var string
156: */
157: protected $_root_version = '1.0';
158:
159: /**
160: * Specific data fields for the contact object
161: *
162: * @var array
163: */
164: protected $_fields_specific;
165:
166: /**
167: * Constructor
168: *
169: * @param Horde_Kolab_Format_Xml_Parser $parser The XML parser.
170: * @param Horde_Kolab_Format_Factory $factory The factory for helper
171: * objects.
172: * @param array $params Any additional options.
173: */
174: public function __construct(
175: Horde_Kolab_Format_Xml_Parser $parser,
176: Horde_Kolab_Format_Factory $factory,
177: $params = null
178: )
179: {
180: $this->_parser = $parser;
181: $this->_factory = $factory;
182:
183: if (is_array($params) && isset($params['version'])) {
184: $this->_version = $params['version'];
185: } else {
186: $this->_version = 2;
187: }
188:
189: }
190:
191: /**
192: * Throw the parser instance away.
193: *
194: * @return NULL
195: */
196: private function _refreshParser()
197: {
198: $this->_parser = null;
199: }
200:
201: /**
202: * Fetch the XML parser.
203: *
204: * @return Horde_Kolab_Format_Xml_Parser The parser.
205: */
206: private function _getParser()
207: {
208: if ($this->_parser === null) {
209: $this->_parser = $this->_factory->createXmlParser();
210: }
211: return $this->_parser;
212: }
213:
214: /**
215: * Load an object based on the given XML stream. The stream may only contain
216: * UTF-8 data.
217: *
218: * @param resource $xml The XML stream of the message.
219: * @param array $options Additional options when parsing the XML.
220: * <pre>
221: * - relaxed: Relaxed error checking (default: false)
222: * </pre>
223: *
224: * @return array The data array representing the object.
225: *
226: * @throws Horde_Kolab_Format_Exception If parsing the XML data failed.
227: *
228: * @todo Check encoding of the returned array. It seems to be ISO-8859-1 at
229: * the moment and UTF-8 would seem more appropriate.
230: */
231: public function load($xml, $options = array())
232: {
233: $this->_xmldoc = $this->_getParser()->parse($xml, $options);
234: $this->_refreshParser();
235:
236: $params = $this->_getParameters($options);
237: $this->_getRoot($params)->load(
238: $this->_root_name,
239: $object,
240: $this->_xmldoc,
241: $this->_factory->createXmlHelper($this->_xmldoc),
242: $params
243: );
244: return $object;
245: }
246:
247: /**
248: * Convert the data to a XML stream. Strings contained in the data array may
249: * only be provided as UTF-8 data.
250: *
251: * @param array $object The data array representing the object.
252: * @param array $options Additional options when writing the XML.
253: * <pre>
254: * - previos: The previous XML text (default: empty string)
255: * - relaxed: Relaxed error checking (default: false)
256: * </pre>
257: *
258: * @return resource The data as XML stream.
259: *
260: * @throws Horde_Kolab_Format_Exception If converting the data to XML failed.
261: */
262: public function save($object, $options = array())
263: {
264: if (!isset($options['previous'])) {
265: $this->_xmldoc = $this->_getParser()->getDocument();
266: } else {
267: $parse_options = $options;
268: unset($parse_options['previous']);
269: $this->_xmldoc = $this->_getParser()->parse(
270: $options['previous'], $parse_options
271: );
272: }
273: $this->_refreshParser();
274:
275: $params = $this->_getParameters($options);
276: $this->_getRoot($params)->save(
277: $this->_root_name,
278: $object,
279: $this->_xmldoc,
280: $this->_factory->createXmlHelper($this->_xmldoc),
281: $params
282: );
283: return $this->_xmldoc->saveXML();
284: }
285:
286: /**
287: * Return the API version of the data structures that are being used for in-
288: * and output.
289: *
290: * @since Horde_Kolab_Format 1.1.0
291: *
292: * @return int The version number;
293: */
294: public function getVersion()
295: {
296: return $this->_version;
297: }
298:
299: /**
300: * Generate the internal parameter list for this operation.
301: *
302: * @param array $options The options for this operation.
303: *
304: * @return array
305: */
306: private function _getParameters($options)
307: {
308: $params = array_merge(
309: $options,
310: array(
311: 'expected-version' => $this->_root_version,
312: 'api-version' => $this->_version
313: )
314: );
315: if (!empty($this->_fields_specific)) {
316: $params['attributes-specific'] = $this->_fields_specific;
317: }
318: return $params;
319: }
320:
321: /**
322: * Return the root handler.
323: *
324: * @param array $params Additional parameters.
325: *
326: * @return Horde_Kolab_Xml_Type_Root The root handler.
327: */
328: private function _getRoot($params = array())
329: {
330: return $this->_factory->createXmlType(self::TYPE_ROOT, $params);
331: }
332: }
333: