097cd632968b8453ba82437a5abf59989c850725
[tine20] / tine20 / Tinebase / Export / Spreadsheet / Ods.php
1 <?php
2 /**
3  * Tinebase Ods generation class
4  *
5  * @package     Tinebase
6  * @subpackage    Export
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  * @copyright   Copyright (c) 2009-2011 Metaways Infosystems GmbH (http://www.metaways.de)
10  * 
11  * @todo        add alternating row styles again?
12  */
13
14 /**
15  * Tinebase Ods generation class
16  * 
17  * @package     Tinebase
18  * @subpackage    Export
19  */
20 class Tinebase_Export_Spreadsheet_Ods extends Tinebase_Export_Spreadsheet_Abstract implements Tinebase_Record_IteratableInterface
21 {
22     /**
23      * user styles
24      *
25      * @var array
26      */
27     protected $_userStyles = array(
28         '<number:date-style style:name="nShortDate" number:automatic-order="true" 
29                 xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 
30                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0">
31             <number:day number:style="long"/>
32             <number:text>.</number:text>
33             <number:month number:style="long"/>
34             <number:text>.</number:text>
35             <number:year number:style="long"/>
36         </number:date-style>',
37         '<number:number-style style:name="N2"
38                 xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 
39                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0">
40             <number:number number:decimal-places="2" number:min-integer-digits="1"/>
41         </number:number-style>',    
42         '<style:style style:name="ceHeader" style:family="table-cell" 
43                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
44                 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">
45             <style:table-cell-properties fo:background-color="#ccffff"/>
46             <style:paragraph-properties fo:text-align="center" fo:margin-left="0cm"/>
47             <style:text-properties fo:font-weight="bold"/>
48         </style:style>',
49         '<style:style style:name="ceBold" style:family="table-cell" style:data-style-name="N2"
50                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
51                 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">
52             <style:text-properties fo:font-weight="bold"/>
53         </style:style>',
54         '<style:style style:name="ceAlternate" style:family="table-cell"
55                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
56                 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">
57             <style:table-cell-properties fo:background-color="#ccccff"/>
58         </style:style>',
59         '<style:style style:name="ceAlternateCentered" style:family="table-cell"
60                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
61                 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">
62             <style:table-cell-properties fo:background-color="#ccccff"/>
63             <style:paragraph-properties fo:text-align="center" fo:margin-left="0cm"/>
64         </style:style>',
65         '<style:style style:name="ceShortDate" style:family="table-cell" style:data-style-name="nShortDate"
66                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
67                 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">
68             <style:paragraph-properties fo:text-align="center" fo:margin-left="0cm"/>
69         </style:style>',
70         '<style:style style:name="numberStyle" style:family="table-cell" style:data-style-name="N2"
71                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
72                 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">
73             <style:paragraph-properties fo:text-align="right"/>
74         </style:style>',
75         '<style:style style:name="numberStyleAlternate" style:family="table-cell" style:data-style-name="N2"
76                 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
77                 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">
78             <style:table-cell-properties fo:background-color="#ccccff"/>
79             <style:paragraph-properties fo:text-align="right"/>
80         </style:style>',
81     );
82     
83     /**
84      * fields with special treatment in addBody
85      *
86      * @var array
87      */
88     protected $_specialFields = array();
89     
90     /**
91      * the opendocument object
92      * 
93      * @var OpenDocument_Document
94      */
95     protected $_openDocumentObject = NULL;
96     
97     /**
98      * spreadsheet table
99      * 
100      * @var OpenDocument_SpreadSheet_Table
101      */
102     protected $_activeTable = NULL;
103     
104     /**
105      * generate export
106      * 
107      * @return string filename
108      */
109     public function generate()
110     {
111         $this->_createDocument();
112         
113         // build export table (use current table if using template)
114         Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating export for ' . $this->_modelName . ' . ' . $this->_getDataTableName());
115         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($this->_config->toArray(), TRUE));
116         
117         $spreadSheet = $this->_openDocumentObject->getBody();
118         
119         // append / use existing table
120         if ($spreadSheet->tableExists($this->_getDataTableName()) === true) {
121             $this->_activeTable = $spreadSheet->getTable($this->_getDataTableName());
122         } else {
123             $this->_activeTable = $spreadSheet->appendTable($this->_getDataTableName());
124         }
125         
126         // add header (disabled at the moment)
127         if (isset($this->_config->header) && $this->_config->header) {
128             $this->_addHead($this->_activeTable);
129         }
130         
131         $this->_exportRecords();
132         
133         // create file
134         $result = $this->_openDocumentObject->getDocument();
135         return $result;
136     }
137     
138     /**
139      * get download content type
140      * 
141      * @return string
142      */
143     public function getDownloadContentType()
144     {
145         return 'application/vnd.oasis.opendocument.spreadsheet';
146     }
147     
148     /**
149      * create new open document document
150      * 
151      * @return void
152      */
153     protected function _createDocument()
154     {
155         // check for template file
156         $templateFile = $this->_getTemplateFilename();
157         
158         $this->_openDocumentObject = new OpenDocument_Document(OpenDocument_Document::SPREADSHEET, $templateFile, Tinebase_Core::getTempDir(), $this->_userStyles);
159     }
160     
161     /**
162      * get open document object
163      * 
164      * @return OpenDocument_Document
165      */
166     public function getDocument()
167     {
168         return $this->_openDocumentObject;
169     }
170     
171     /**
172      * add ods head (headline, column styles)
173      */
174     protected function _addHead()
175     {
176         // add header (replace placeholders)
177         if (isset($this->_config->headers)) {
178             
179             $row = $this->_activeTable->appendRow();
180             
181             $patterns = array(
182                 '/\{date\}/', 
183                 '/\{user\}/',
184             );
185             
186             $replacements = array(
187                 Zend_Date::now()->toString(Zend_Locale_Format::getDateFormat($this->_locale), $this->_locale),
188                 Tinebase_Core::getUser()->accountDisplayName,
189             );
190             
191             foreach($this->_config->headers->header as $headerCell) {
192                 // replace data
193                 $value = preg_replace($patterns, $replacements, $headerCell);
194                 $cell = $row->appendCell($value, OpenDocument_SpreadSheet_Cell::TYPE_STRING);
195             }
196         }
197         
198         // add table headline
199         $row = $this->_activeTable->appendRow();
200         foreach($this->_config->columns->column as $field) {
201             $headerValue = ($field->header) ? $field->header : $field->identifier;
202             $cell = $row->appendCell($headerValue, OpenDocument_SpreadSheet_Cell::TYPE_STRING);
203             $cell->setStyle('ceHeader');
204         }
205     }
206     
207     /**
208      * format strings
209      * 
210      * @var string
211      */
212     protected $_format = 'ods';
213     
214     /**
215      * add body rows
216      *
217      * @param Tinebase_Record_RecordSet $records
218      */
219     public function processIteration($_records)
220     {
221         $this->_resolveRecords($_records);
222         
223         // add record rows
224         $i = 0;
225         foreach ($_records as $record) {
226             
227             $row = $this->_activeTable->appendRow();
228
229             foreach ($this->_config->columns->column as $field) {
230                 
231                 //$altStyle = 'ceAlternate';
232                 
233                 // get type and value for cell
234                 $cellType = $this->_getCellType($field->type);
235                 $cellValue = $this->_getCellValue($field, $record, $cellType);
236                 
237                 // create cell with type and value and add style
238                 $cell = $row->appendCell($cellValue, $cellType);
239                 
240                 // add formula
241                 if ($field->formula) {
242                     $cell->setFormula($field->formula);
243                 }
244
245                 /*
246                 if ($i % 2 == 1) {
247                     $cell->setStyle($altStyle);
248                 }
249                 */
250                 
251                 //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($field->toArray(), true));
252             }
253             $i++;
254         }
255         
256     }
257     
258     /**
259      * add style/width to column
260      *
261      * @param string $_styleName
262      * @param string $_columnWidth (for example: '2,5cm')
263      */
264     protected function _addColumnStyle($_styleName, $_columnWidth) 
265     {
266         $this->_openDocumentObject->addStyle('<style:style style:name="' . $_styleName . '" style:family="table-column" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"><style:table-column-properties style:column-width="' . $_columnWidth . '"/></style:style>');
267     }
268     
269     /**
270      * get name of data table
271      * 
272      * @return string
273      */
274     protected function _getDataTableName()
275     {
276         return $this->_translate->_('Data');
277     }
278     
279     /**
280      * get cell type
281      * 
282      * @param string $_fieldType
283      * @return string
284      */
285     protected function _getCellType($_fieldType)
286     {
287         switch($_fieldType) {
288             case 'date':
289             case 'datetime':
290                 $result = OpenDocument_SpreadSheet_Cell::TYPE_DATE;
291                 break;
292             case 'time':
293                 $result = OpenDocument_SpreadSheet_Cell::TYPE_TIME;
294                 break;
295             case 'currency':
296                 $result = OpenDocument_SpreadSheet_Cell::TYPE_CURRENCY;
297                 break;
298             case 'percentage':
299                 $result = OpenDocument_SpreadSheet_Cell::TYPE_PERCENTAGE;
300                 break;
301             case 'float':
302             case 'number':
303                 $result = OpenDocument_SpreadSheet_Cell::TYPE_FLOAT;
304                 break;
305             default:
306                 $result = OpenDocument_SpreadSheet_Cell::TYPE_STRING;
307         }
308         
309         return $result;
310     }
311 }