ExportTableAjax.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. Lib::loadClass('Core_AclHelper');
  4. Lib::loadClass('Response');
  5. class Route_ExportTableAjax extends RouteBase {
  6. /**
  7. * @param $_REQUEST['namespace'] = AclNamespace
  8. * @param $_REQUEST['format'] = 'csv' | 'html'
  9. * @param $_REQUEST['flds'] = csv - coma separated field names
  10. * @param $_REQUEST['sortCol'] = FieldName
  11. * @param $_REQUEST['sortDir'] = SortDir ('desc' | 'asc')
  12. * @param $_REQUEST['f_{$fieldName}'] = filter
  13. * @param $_REQUEST['sf_{$fieldName}'] = force filter
  14. * @param $_POST['backRefNS'] - back ref filter
  15. * @param $_POST['backRefPK']
  16. * @param $_POST['backRefField']
  17. * @param $_POST['childRefNS'] - child ref filter
  18. * @param $_POST['childRefPK']
  19. */
  20. function defaultAction() {
  21. if (!empty($_POST)) {
  22. $backRefFilter = [
  23. 'namespace' => V::get('backRefNS', '', $_POST),
  24. 'primaryKey' => V::get('backRefPK', '', $_POST),
  25. 'fieldName' => V::get('backRefField', '', $_POST),
  26. ];
  27. $childRefFilter = [
  28. 'namespace' => V::get('childRefNS', '', $_POST),
  29. 'primaryKey' => V::get('childRefPK', '', $_POST),
  30. ];
  31. return $this->executeExport(
  32. $_REQUEST // `f_%` may be sent by GET
  33. , (!empty($backRefFilter['namespace']) && !empty($backRefFilter['primaryKey'])) ? $backRefFilter : null
  34. , (!empty($childRefFilter['namespace']) && !empty($childRefFilter['primaryKey'])) ? $childRefFilter : null
  35. );
  36. }
  37. $this->executeExport($_GET);
  38. }
  39. function executeExport($args, $backRefFilter = null, $childRefFilter = null) {
  40. $namespace = V::get('namespace', '', $args, 'word');
  41. if (!$namespace) throw new HttpException("Bad Request - missing namespace", 400);
  42. $acl = Core_AclHelper::getAclByNamespace($namespace);
  43. $exportLimit = 10000;
  44. $params = array();
  45. if ($backRefFilter) {
  46. $params['__backRef'] = $backRefFilter;
  47. DBG::log($params, 'array', '$params __backRef');
  48. }
  49. if ($childRefFilter) {
  50. $params['__childRef'] = $childRefFilter;
  51. DBG::log($params, 'array', '$params __childRef');
  52. }
  53. $params['limit'] = $exportLimit;
  54. // $params['limitstart'] = 0;
  55. $params['order_by'] = V::get('sortCol', '', $args);
  56. $params['order_dir'] = V::get('sortDir', '', $args);
  57. $params['cols'] = array($acl->getPrimaryKeyField());
  58. $toExportFields = explode(',', V::get('flds', '', $args));
  59. $toExportFields = array_filter($toExportFields, [ 'V', 'filterNotEmpty' ]);
  60. if (empty($toExportFields)) throw new Exception("Nie wybrano żandych pól do exportu.");
  61. $allowedExportFieldList = Core_AclHelper::getExportFieldList($acl);
  62. foreach ($toExportFields as $fieldName) {
  63. if ($fieldName == $acl->getPrimaryKeyField()) continue;
  64. if (!in_array($fieldName, $allowedExportFieldList)) throw new Exception("Brak uprawnień do exportu pola '{$fieldName}'");
  65. $params['cols'][] = $fieldName;
  66. }
  67. foreach ($args as $k => $v) {
  68. if (strlen($k) > 3 && substr($k, 0, 2) == 'f_' && strlen($v) > 0) {// filter prefix
  69. $params[$k] = $v;
  70. }
  71. else if (strlen($k) > 4 && substr($k, 0, 3) == 'sf_' && strlen($v) > 0) {// special filter prefix
  72. $params[$k] = $v;
  73. }
  74. }
  75. try {
  76. $queryFeatures = $acl->buildQuery($params);
  77. $total = $queryFeatures->getTotal();
  78. $listItems = $queryFeatures->getItems();
  79. $primaryKeyField = $acl->getPrimaryKeyField();
  80. $items = []; foreach ($listItems as $item) $items[ $item[$primaryKeyField] ] = $item;
  81. } catch (Exception $e) {
  82. DBG::log($e);
  83. throw $e;
  84. }
  85. $format = V::get('format', 'html', $args);
  86. switch ($format) {
  87. case 'html': return $this->_exportToHTML($acl, $items, $toExportFields, $format);
  88. case 'xls': return $this->_exportToXLS($acl, $items, $toExportFields, $format);
  89. case 'xlsx': return $this->_exportToXLSX($acl, $items, $toExportFields, $format);
  90. case 'csv_cp1250': return $this->_exportToCSV($acl, $items, $toExportFields, $format);
  91. case 'csv': return $this->_exportToCSV($acl, $items, $toExportFields, $format);
  92. }
  93. die("Nieobsługiwany format danych.");
  94. }
  95. function _exportToHTML($acl, $items, $toExportFields, $format = 'html') {
  96. $labels = array();
  97. foreach ($toExportFields as $fieldName) {
  98. $labels[ $fieldName ] = $acl->getFieldLabel($fieldName);
  99. }
  100. UI::gora();
  101. echo UI::h('table', ['class'=>'table table-bordered table-hover'], [
  102. UI::h('thead', [], [
  103. UI::h('tr', [], array_map(function ($label) {
  104. return UI::h('th', [], $label);
  105. }, $labels))
  106. ]),
  107. UI::h('tbody', [], array_map(function ($item) use($labels) {
  108. return UI::h('tr', [], array_map(function ($fieldName) use ($item) {
  109. return UI::h('td', [], V::get($fieldName, '', $item));
  110. }, array_keys($labels)));
  111. }, $items)),
  112. ]);
  113. UI::dol();
  114. }
  115. function _exportToCSV($acl, $items, $toExportFields, $format = 'csv') {
  116. $csvFileName = "Tabela-" . $acl->getName() . "-" . date("Y-m-d_H_s");
  117. $csvSeparator = ';';
  118. $labels = array();
  119. foreach ($toExportFields as $fieldName) {
  120. $labels[ $fieldName ] = $acl->getFieldLabel($fieldName);
  121. }
  122. $csvHeader = implode($csvSeparator, array_map(function ($label) use ($item) {
  123. return '"' . addslashes($label) . '"';
  124. }, array_values($labels)));
  125. $csvRows = implode("\r\n", array_map(function ($item) use ($labels, $csvSeparator) {
  126. return implode($csvSeparator, array_map(function ($fieldName) use ($item) {
  127. return '"' . addslashes(V::get($fieldName, '', $item)) . '"';
  128. }, array_keys($labels)));
  129. }, $items));
  130. header('Content-Type: text/csv; charset=utf-8');
  131. header("Content-Disposition: attachment; filename={$csvFileName}.csv");
  132. switch ($format) {
  133. case 'csv': echo $csvHeader . "\n" . $csvRows; exit;
  134. case 'csv_cp1250': echo iconv('utf-8', 'Windows-1250//IGNORE', $csvHeader) . "\r\n" . iconv('utf-8', 'Windows-1250//IGNORE', $csvRows); exit;
  135. die("Nieobsługiwane kodowanie danych csv.");
  136. }
  137. }
  138. function _exportToXLS($acl, $items, $toExportFields, $format = 'xls') {
  139. // https://en.wikipedia.org/wiki/Microsoft_Excel#XML_Spreadsheet
  140. $DBG_OUTPUT = 0;
  141. $csvFileName = "Tabela-" . $acl->getName() . "-" . date("Y-m-d_H_s");
  142. $labels = array();
  143. foreach ($toExportFields as $fieldName) {
  144. $label = $acl->getFieldLabel($fieldName);
  145. $labels[ $fieldName ] = ($label) ? $label : $fieldName;
  146. }
  147. if ($DBG_OUTPUT) {
  148. $items = array_slice($items, 0, 10);
  149. DBG::nicePrint($acl, '$acl');
  150. DBG::nicePrint($labels, '$labels');
  151. DBG::nicePrint($toExportFields, '$toExportFields');
  152. }
  153. $xw = new XMLWriter();
  154. $xw->openMemory();
  155. if ($DBG_OUTPUT) $xw->setIndent(true); //set the indentation to true (if false all the xml will be written on one line)
  156. $xw->startDocument("1.0");
  157. $xw->startElement("Workbook");
  158. $xw->writeAttribute("xmlns", "urn:schemas-microsoft-com:office:spreadsheet");
  159. $xw->writeAttribute("xmlns:o", "urn:schemas-microsoft-com:office:office");
  160. $xw->writeAttribute("xmlns:x", "urn:schemas-microsoft-com:office:excel");
  161. $xw->writeAttribute("xmlns:ss", "urn:schemas-microsoft-com:office:spreadsheet");
  162. $xw->writeAttribute("xmlns:html", "http://www.w3.org/TR/REC-html40");
  163. {
  164. // TODO: format date 'Y-m-d' - not working in LibreOffice
  165. // <Styles>
  166. // <Style ss:ID="p5FieldType_Date">
  167. // <NumberFormat ss:Format="yyyy\-mm\-dd"/>
  168. // </Style>
  169. // <Style ss:ID="p5FieldType_Date2">
  170. // <!-- <NumberFormat ss:Format="YYYY\-MM\-DD"/> -->
  171. // <NumberFormat ss:Format="YYYY\-MM\-DD"/>
  172. // </Style>
  173. // <Style ss:ID="p5FieldType_Date3">
  174. // <!-- <NumberFormat ss:Format="YYYY\-MM\-DD"/> -->
  175. // <DateFormat ss:Format="YYYY\-MM\-DD"/>
  176. // </Style>
  177. // </Styles>
  178. $xw->startElement("Worksheet");
  179. $xw->writeAttribute("ss:Name", "Sheet1");
  180. {
  181. $xw->startElement("Table");
  182. $xw->writeAttribute("ss:ExpandedColumnCount", "2");
  183. $xw->writeAttribute("ss:ExpandedRowCount", "2");
  184. $xw->writeAttribute("x:FullColumns", "1");
  185. $xw->writeAttribute("x:FullRows", "1");
  186. {
  187. $xw->startElement("Row");
  188. foreach ($labels as $fieldName => $label) {
  189. $xw->startElement('Cell');
  190. {
  191. $xw->startElement('Data');
  192. $xw->writeAttribute("ss:Type", "String");
  193. $xw->text(str_replace([ '<br>', '<br/>' ], "\n", $label));
  194. $xw->endElement();
  195. }
  196. $xw->endElement();
  197. }
  198. $xw->endElement();
  199. foreach ($items as $item) {
  200. $xw->startElement("Row");
  201. foreach ($labels as $fieldName => $label) {
  202. $xw->startElement('Cell');
  203. {
  204. $xw->startElement('Data');
  205. // ss:Type:
  206. // - <xsd:enumeration value="Number" />
  207. // - <xsd:enumeration value="DateTime" />
  208. // - <xsd:enumeration value="Boolean" />
  209. // - <xsd:enumeration value="String" />
  210. // - <xsd:enumeration value="Error" />
  211. $xsdType = $acl->getXsdFieldType($fieldName);
  212. switch ($xsdType) {
  213. case 'xsd:double':
  214. {
  215. if (!empty($item[$fieldName])) {
  216. $xw->writeAttribute("ss:Type", "Number");
  217. } else {
  218. $xw->writeAttribute("ss:Type", "String");
  219. }
  220. break;
  221. }
  222. // case 'xsd:date':
  223. // {
  224. // if (!empty($item[$fieldName])) {
  225. // $xw->writeAttribute("ss:Type", "Date");
  226. // } else {
  227. // $xw->writeAttribute("ss:Type", "String");
  228. // }
  229. // break;
  230. // }
  231. default: $xw->writeAttribute("ss:Type", "String");
  232. }
  233. $xw->text($item[$fieldName]);
  234. $xw->endElement();
  235. }
  236. $xw->endElement();
  237. }
  238. $xw->endElement();
  239. }
  240. }
  241. $xw->endElement();
  242. }
  243. $xw->endElement();
  244. }
  245. $xw->endElement();
  246. $xw->endDocument();
  247. if (!$DBG_OUTPUT) {
  248. header("Content-Type: text/xml; charset=utf-8");
  249. header("Content-Disposition: attachment; filename={$csvFileName}.xls");
  250. }
  251. echo $xw->outputMemory();
  252. }
  253. function _exportToXLSX($acl, $items, $toExportFields, $format = 'xlsx') {
  254. // https://github.com/mk-j/PHP_XLSXWriter
  255. $DBG_OUTPUT = 0;
  256. if ($DBG_OUTPUT) {
  257. $items = array_slice($items, 0, 10);
  258. DBG::nicePrint($acl, '$acl');
  259. }
  260. Lib::loadClass('Vendor_XLSXWriter');
  261. $csvFileName = "Tabela-" . $acl->getName() . "-" . date("Y-m-d_H_s");
  262. $labels = array();
  263. $columnTypes = array();
  264. // string @
  265. // integer 0
  266. // date YYYY-MM-DD
  267. // datetime YYYY-MM-DD HH:MM:SS
  268. // time HH:MM:SS
  269. // price #,##0.00
  270. // dollar [$$-1009]#,##0.00;[RED]-[$$-1009]#,##0.00
  271. // euro #,##0.00 [$€-407];[RED]-#,##0.00 [$€-407]
  272. foreach ($toExportFields as $fieldName) {
  273. $label = $acl->getFieldLabel($fieldName);
  274. $labels[ $fieldName ] = ($label) ? $label : $fieldName;
  275. $typeXLSL = 'string';
  276. $xsdType = $acl->getXsdFieldType($fieldName);
  277. switch ($xsdType) {
  278. case 'xsd:double': $typeXLSL = 'price'; break;
  279. case 'xsd:date': $typeXLSL = 'date'; break;
  280. case 'xsd:dateTime': $typeXLSL = 'datetime'; break;
  281. }
  282. $columnTypes[ $fieldName ] = $typeXLSL;
  283. }
  284. $xlsx = new Vendor_XLSXWriter();
  285. $xlsx->writeSheetHeader('Sheet1', array_combine(
  286. array_values($labels),
  287. // array_map(function ($label) { return 'string'; }, $labels)
  288. array_values($columnTypes)
  289. ));
  290. foreach ($items as $item) {
  291. $row = array_map(function ($fieldName) use ($item) {
  292. return V::get($fieldName, '', $item);
  293. }, array_keys($labels));
  294. $xlsx->writeSheetRow('Sheet1', $row);
  295. }
  296. if (!$DBG_OUTPUT) {
  297. header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8");
  298. header("Content-Disposition: attachment; filename={$csvFileName}.xlsx");
  299. }
  300. $xlsx->writeToStdOut();
  301. }
  302. }