PageRenderTime 27ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/tine20/Tinebase/Export/Spreadsheet/Xls.php

https://gitlab.com/rsilveira1987/Expresso
PHP | 421 lines | 236 code | 65 blank | 120 comment | 45 complexity | 5465a9765b78dddd452524282281eeab MD5 | raw file
  1. <?php
  2. /**
  3. * Tinebase xls 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. */
  12. // set include path for phpexcel
  13. set_include_path(dirname(dirname(dirname(dirname(__FILE__)))) . '/library/PHPExcel' . PATH_SEPARATOR . get_include_path() );
  14. /**
  15. * Tinebase xls generation class
  16. *
  17. * @package Tinebase
  18. * @subpackage Export
  19. *
  20. */
  21. class Tinebase_Export_Spreadsheet_Xls extends Tinebase_Export_Spreadsheet_Abstract implements Tinebase_Record_IteratableInterface
  22. {
  23. /**
  24. * current row number
  25. *
  26. * @var integer
  27. */
  28. protected $_currentRowIndex = 0;
  29. /**
  30. * the phpexcel object
  31. *
  32. * @var PHPExcel
  33. */
  34. protected $_excelObject = NULL;
  35. /**
  36. * format strings
  37. *
  38. * @var string
  39. */
  40. protected $_format = 'xls';
  41. /**
  42. * generate export
  43. *
  44. * @return PHPExcel
  45. */
  46. public function generate()
  47. {
  48. $this->_createDocument();
  49. $this->_setDocumentProperties();
  50. $this->_addHeader();
  51. $this->_exportRecords();
  52. $this->_setColumnWidths();
  53. return $this->getDocument();
  54. }
  55. /**
  56. * sets the colunm widths by config column->width
  57. */
  58. protected function _setColumnWidths()
  59. {
  60. $index = 0;
  61. foreach($this->_config->columns->column as $field) {
  62. if ($this->_groupBy !== NULL && $this->_groupBy == $field->identifier) {
  63. continue;
  64. }
  65. if (isset($field->width)) {
  66. $this->_excelObject->getActiveSheet()->getColumnDimensionByColumn($index)->setWidth((string) $field->width);
  67. }
  68. $index++;
  69. }
  70. }
  71. /**
  72. * add header
  73. */
  74. protected function _addHeader()
  75. {
  76. $patterns = array(
  77. '/\{date\}/',
  78. '/\{user\}/',
  79. );
  80. $replacements = array(
  81. Zend_Date::now()->toString(Zend_Locale_Format::getDateFormat($this->_locale), $this->_locale),
  82. Tinebase_Core::getUser()->accountDisplayName,
  83. );
  84. $this->_currentRowIndex = 1;
  85. $columnId = 0;
  86. if ($this->_config->headers) {
  87. foreach($this->_config->headers->header as $headerCell) {
  88. // replace data
  89. $value = preg_replace($patterns, $replacements, $headerCell);
  90. $this->_excelObject->getActiveSheet()->setCellValueByColumnAndRow(0, $this->_currentRowIndex, $value);
  91. $this->_currentRowIndex++;
  92. }
  93. $this->_currentRowIndex++;
  94. }
  95. if (isset($this->_config->header) && $this->_config->header) {
  96. $this->_addHead();
  97. }
  98. }
  99. /**
  100. * get excel object
  101. *
  102. * @return PHPExcel
  103. */
  104. public function getDocument()
  105. {
  106. return $this->_excelObject;
  107. }
  108. /**
  109. * get export content type
  110. *
  111. * @return string
  112. */
  113. public function getDownloadContentType()
  114. {
  115. $contentType = ($this->_config->writer == 'Excel2007')
  116. // Excel 2007 content type
  117. ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  118. // Excel 5 content type or other
  119. : 'application/vnd.ms-excel';
  120. return $contentType;
  121. }
  122. /**
  123. * return download filename
  124. * @param string $_appName
  125. * @param string $_format
  126. */
  127. public function getDownloadFilename($_appName, $_format)
  128. {
  129. $result = parent::getDownloadFilename($_appName, $_format);
  130. if ($this->_config->writer == 'Excel2007') {
  131. // excel2007 extension is .xlsx
  132. $result .= 'x';
  133. }
  134. return $result;
  135. }
  136. /**
  137. * output result
  138. */
  139. public function write()
  140. {
  141. $xlsFormat = ($this->_config->writer) ? $this->_config->writer : 'Excel5';
  142. Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating and sending xls to client (Format: ' . $xlsFormat . ').');
  143. $xlswriter = PHPExcel_IOFactory::createWriter($this->_excelObject, $xlsFormat);
  144. // precalcualting formula values costs tons of time, because sum formulas are like SUM C1:C65000
  145. $xlswriter->setPreCalculateFormulas(FALSE);
  146. $xlswriter->save('php://output');
  147. }
  148. /**
  149. * create new excel document
  150. *
  151. * @return void
  152. */
  153. protected function _createDocument()
  154. {
  155. $templateFile = $this->_getTemplateFilename();
  156. if ($templateFile !== NULL) {
  157. if (! $this->_config->reader || $this->_config->reader == 'autodetection') {
  158. $this->_excelObject = PHPExcel_IOFactory::load($templateFile);
  159. } else {
  160. $reader = PHPExcel_IOFactory::createReader($this->_config->reader);
  161. $this->_excelObject = $reader->load($templateFile);
  162. }
  163. // need to unregister the zip stream wrapper because it is overwritten by PHPExcel!
  164. // TODO file a bugreport to PHPExcel
  165. @stream_wrapper_restore("zip");
  166. $activeSheet = isset($this->_config->sheet) ? $this->_config->sheet : 1;
  167. $this->_excelObject->setActiveSheetIndex($activeSheet);
  168. } else {
  169. Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating new PHPExcel object.');
  170. $this->_excelObject = new PHPExcel();
  171. }
  172. }
  173. /**
  174. * get cell value
  175. *
  176. * @param Zend_Config $_field
  177. * @param Tinebase_Record_Interface $_record
  178. * @param string $_cellType
  179. * @return string
  180. */
  181. protected function _getCellValue(Zend_Config $_field, Tinebase_Record_Interface $_record, &$_cellType)
  182. {
  183. switch($_field->type) {
  184. case 'datetime':
  185. case 'date':
  186. if ($_record->{$_field->identifier} instanceof DateTime) {
  187. if (! isset($_field->timestamp) || $_field->timestamp == 1) {
  188. $result = PHPExcel_Shared_Date::PHPToExcel($_record->{$_field->identifier}->getTimestamp());
  189. } else {
  190. $result = parent::_getCellValue($_field, $_record, $_cellType);
  191. }
  192. } else {
  193. $result = $_record->{$_field->identifier};
  194. }
  195. // empty date cells, get displayed as 30.12.1899
  196. if(empty($result)) {
  197. $result = NULL;
  198. }
  199. break;
  200. default:
  201. $result = parent::_getCellValue($_field, $_record, $_cellType);
  202. break;
  203. }
  204. return $result;
  205. }
  206. /**
  207. * set properties
  208. *
  209. * @return void
  210. */
  211. protected function _setDocumentProperties()
  212. {
  213. // set metadata/properties
  214. if ($this->_config->writer == 'Excel2007') {
  215. $this->_excelObject->getProperties()
  216. ->setCreator(Tinebase_Core::getUser()->accountDisplayName)
  217. ->setLastModifiedBy(Tinebase_Core::getUser()->accountDisplayName)
  218. ->setTitle('Tine 2.0 ' . $this->_applicationName . ' Export')
  219. ->setSubject('Office 2007 XLSX Test Document')
  220. ->setDescription('Export for ' . $this->_applicationName . ', generated using PHP classes.')
  221. ->setKeywords("tine20 openxml php")
  222. ->setCreated(Zend_Date::now()->get());
  223. //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($this->_excelObject->getProperties(), true));
  224. }
  225. }
  226. /**
  227. * add xls head (headline, column styles)
  228. */
  229. protected function _addHead()
  230. {
  231. $columnId = 0;
  232. foreach($this->_config->columns->column as $field) {
  233. if ($this->_groupBy !== NULL && $this->_groupBy == $field->identifier) {
  234. continue;
  235. }
  236. $headerValue = ($field->header) ? $this->_translate->translate($field->header) : $field->identifier;
  237. $this->_excelObject->getActiveSheet()->setCellValueByColumnAndRow($columnId++, $this->_currentRowIndex, $headerValue);
  238. }
  239. $this->_currentRowIndex++;
  240. }
  241. /**
  242. * adds a header for each group
  243. *
  244. * @param Tinebase_Record_Interface $record
  245. */
  246. protected function _addGroupHeader($group)
  247. {
  248. // find out fieldconfig, if not found already
  249. if (! $this->_groupByFieldConfig) {
  250. $this->_columnCount = 0;
  251. foreach ($this->_config->columns->column as $field) {
  252. if ($field->identifier == $this->_groupBy) {
  253. $this->_groupByFieldConfig = $field;
  254. $this->_groupByFieldType = (isset($field->type)) ? $field->type : 'string';
  255. }
  256. $this->_columnCount++;
  257. }
  258. } else {
  259. $this->_currentRowIndex++;
  260. $this->_currentRowIndex++;
  261. }
  262. $fontColor = 'b79511';
  263. $backgroundColor = '008bcf';
  264. $fontSize = 16;
  265. if ($this->_config->grouping->groupheader) {#
  266. $gh = $this->_config->grouping->groupheader;
  267. $fontColor = $gh->fontcolor ? (string) $gh->fontcolor : $fontColor;
  268. $backgroundColor = $gh->backgroundcolor ? (string) $gh->backgroundcolor : $backgroundColor;
  269. $fontSize = $gh->fontsize ? (int) $gh->fontsize : $fontSize;
  270. }
  271. $cell = $this->_excelObject->getActiveSheet()->setCellValueByColumnAndRow(0, $this->_currentRowIndex, $group, TRUE);
  272. $styleArray = array(
  273. 'font' => array(
  274. 'bold' => true,
  275. 'color' => array('rgb' => $fontColor),
  276. 'size' => $fontSize,
  277. ),
  278. 'fill' => array(
  279. 'type' => PHPExcel_Style_Fill::FILL_SOLID,
  280. 'color' => array('rgb' => $backgroundColor)
  281. )
  282. );
  283. $this->_excelObject->getActiveSheet()->getStyle($cell->getCoordinate())->applyFromArray($styleArray);
  284. $this->_excelObject->getActiveSheet()->mergeCellsByColumnAndRow(0, $this->_currentRowIndex, ($this->_columnCount - 2), $this->_currentRowIndex);
  285. $this->_currentRowIndex++;
  286. if ($this->_config->grouping->header) {
  287. $this->_addHead();
  288. }
  289. $this->_currentRowIndex++;
  290. }
  291. /**
  292. * (non-PHPdoc)
  293. * @see Tinebase_Export_Abstract::_onAfterExportRecords()
  294. */
  295. protected function _onAfterExportRecords($result)
  296. {
  297. // save number of records (only if we have more than 1 sheets / records are on the second sheet by default)
  298. if ($this->_excelObject->getSheetCount() > 1) {
  299. $this->_excelObject->setActiveSheetIndex(0);
  300. $this->_excelObject->getActiveSheet()->setCellValueByColumnAndRow(5, 2, $result['totalcount']);
  301. }
  302. }
  303. /**
  304. * add body rows
  305. *
  306. * @param Tinebase_Record_RecordSet $records
  307. *
  308. * @todo add formulas
  309. */
  310. public function processIteration($_records)
  311. {
  312. $this->_resolveRecords($_records);
  313. $lastGroup = NULL;
  314. $woString = $this->_translate->_('Without company assigned');
  315. // add record rows
  316. $i = 0;
  317. foreach ($_records as $record) {
  318. if ($this->_groupBy !== NULL && $lastGroup !== $record->{$this->_groupBy}
  319. && (! (empty($record->{$this->_groupBy}) && $record->{$this->_groupBy} == $woString)))
  320. {
  321. $lastGroup = empty($record->{$this->_groupBy}) ? $woString : $record->{$this->_groupBy};
  322. $this->_addGroupHeader($lastGroup);
  323. }
  324. $columnId = 0;
  325. foreach ($this->_config->columns->column as $field) {
  326. // don't show group by field
  327. if ($this->_groupBy !== NULL && $field->identifier == $this->_groupBy) {
  328. continue;
  329. }
  330. // get type and value for cell
  331. $cellType = (isset($field->type)) ? $field->type : 'string';
  332. $cellValue = $this->_getCellValue($field, $record, $cellType);
  333. // add formula
  334. if ($field->formula) {
  335. //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Adding formula: ' . $field->formula);
  336. $cellValue = $field->formula;
  337. }
  338. $this->_excelObject->getActiveSheet()->setCellValueByColumnAndRow($columnId++, $this->_currentRowIndex, $cellValue);
  339. }
  340. $i++;
  341. $this->_currentRowIndex++;
  342. }
  343. }
  344. /**
  345. * (non-PHPdoc)
  346. * @see Tinebase_Export_Abstract::_exportRecords()
  347. */
  348. protected function _exportRecords()
  349. {
  350. parent::_exportRecords();
  351. $sheet = $this->_excelObject->getActiveSheet();
  352. for ($i = 0; $i < $this->_columnCount; $i++) {
  353. $sheet->getColumnDimension($i)->setAutoSize(TRUE);
  354. }
  355. }
  356. }