1<?php 2/** 3 * 4 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 5 * @author Andreas Gohr <andi@splitbrain.org> 6 */ 7// must be run within Dokuwiki 8if(!defined('DOKU_INC')) die(); 9 10/** 11 * Class syntax_plugin_dataau_table 12 */ 13class syntax_plugin_dataau_table extends DokuWiki_Syntax_Plugin { 14 15 /** 16 * will hold the dataau helper plugin 17 * 18 * @var $dthlp helper_plugin_data 19 */ 20 var $dthlp = null; 21 22 var $sums = array(); 23 24 /** 25 * Constructor. Load helper plugin 26 */ 27 function __construct() { 28 $this->dthlp = plugin_load('helper', 'dataau'); 29 } 30 31 /** 32 * What kind of syntax are we? 33 */ 34 function getType() { 35 return 'substition'; 36 } 37 38 /** 39 * What about paragraphs? 40 */ 41 function getPType() { 42 return 'block'; 43 } 44 45 /** 46 * Where to sort in? 47 */ 48 function getSort() { 49 return 155; 50 } 51 52 /** 53 * Connect pattern to lexer 54 */ 55 function connectTo($mode) { 56 $this->Lexer->addSpecialPattern('----+ *datatable(?: [ a-zA-Z0-9_]*)?-+\n.*?\n----+', $mode, 'plugin_dataau_table'); 57 } 58 59 /** 60 * Handle the match - parse the data 61 * 62 * This parsing is shared between the multiple different output/control 63 * syntaxes 64 * 65 * @param string $match The text matched by the patterns 66 * @param int $state The lexer state for the match 67 * @param int $pos The character position of the matched text 68 * @param Doku_Handler $handler The Doku_Handler object 69 * @return bool|array Return an array with all data you want to use in render, false don't add an instruction 70 */ 71 function handle($match, $state, $pos, Doku_Handler $handler) { 72 if(!$this->dthlp->ready()) return null; 73 74 // get lines and additional class 75 $lines = explode("\n", $match); 76 array_pop($lines); 77 $class = array_shift($lines); 78 $class = preg_replace('/^----+ *dataau[a-z]+/', '', $class); 79 $class = trim($class, '- '); 80 81 $dataau = array( 82 'classes' => $class, 83 'limit' => 0, 84 'dynfilters' => false, 85 'summarize' => false, 86 'rownumbers' => (bool) $this->getConf('rownumbers'), 87 'sepbyheaders' => false, 88 'headers' => array(), 89 'widths' => array(), 90 'filter' => array() 91 ); 92 93 // parse info 94 foreach($lines as $line) { 95 // ignore comments 96 $line = preg_replace('/(?<![&\\\\])#.*$/', '', $line); 97 $line = str_replace('\\#', '#', $line); 98 $line = trim($line); 99 if(empty($line)) continue; 100 $line = preg_split('/\s*:\s*/', $line, 2); 101 $line[0] = strtolower($line[0]); 102 103 $logic = 'OR'; 104 // handle line commands (we allow various aliases here) 105 switch($line[0]) { 106 case 'select': 107 case 'cols': 108 case 'field': 109 case 'col': 110 $cols = explode(',', $line[1]); 111 foreach($cols as $col) { 112 $col = trim($col); 113 if(!$col) continue; 114 $column = $this->dthlp->_column($col); 115 $dataau['cols'][$column['key']] = $column; 116 } 117 break; 118 case 'title': 119 $dataau['title'] = $line[1]; 120 break; 121 case 'head': 122 case 'header': 123 case 'headers': 124 $cols = $this->parseValues($line[1]); 125 $dataau['headers'] = array_merge($dataau['headers'], $cols); 126 break; 127 case 'align': 128 $cols = explode(',', $line[1]); 129 foreach($cols as $col) { 130 $col = trim(strtolower($col)); 131 if($col[0] == 'c') { 132 $col = 'center'; 133 } elseif($col[0] == 'r') { 134 $col = 'right'; 135 } else { 136 $col = 'left'; 137 } 138 $dataau['align'][] = $col; 139 } 140 break; 141 case 'widths': 142 $cols = explode(',', $line[1]); 143 foreach($cols as $col) { 144 $col = trim($col); 145 $dataau['widths'][] = $col; 146 } 147 break; 148 case 'min': 149 $dataau['min'] = abs((int) $line[1]); 150 break; 151 case 'limit': 152 case 'max': 153 $dataau['limit'] = abs((int) $line[1]); 154 break; 155 case 'order': 156 case 'sort': 157 $column = $this->dthlp->_column($line[1]); 158 $sort = $column['key']; 159 if(substr($sort, 0, 1) == '^') { 160 $dataau['sort'] = array(substr($sort, 1), 'DESC'); 161 } else { 162 $dataau['sort'] = array($sort, 'ASC'); 163 } 164 break; 165 case 'where': 166 case 'filter': 167 case 'filterand': 168 /** @noinspection PhpMissingBreakStatementInspection */ 169 case 'and': 170 $logic = 'AND'; 171 case 'filteror': 172 case 'or': 173 if(!$logic) { 174 $logic = 'OR'; 175 } 176 $flt = $this->dthlp->_parse_filter($line[1]); 177 if(is_array($flt)) { 178 $flt['logic'] = $logic; 179 $dataau['filter'][] = $flt; 180 } 181 break; 182 case 'page': 183 case 'target': 184 $dataau['page'] = cleanID($line[1]); 185 break; 186 case 'dynfilters': 187 $dataau['dynfilters'] = (bool) $line[1]; 188 break; 189 case 'rownumbers': 190 $dataau['rownumbers'] = (bool) $line[1]; 191 break; 192 case 'summarize': 193 $dataau['summarize'] = (bool) $line[1]; 194 break; 195 case 'sepbyheaders': 196 $dataau['sepbyheaders'] = (bool) $line[1]; 197 break; 198 default: 199 msg("dataau plugin: unknown option '" . hsc($line[0]) . "'", -1); 200 } 201 } 202 203 // we need at least one column to display 204 if(!is_array($dataau['cols']) || !count($dataau['cols'])) { 205 msg('dataau plugin: no columns selected', -1); 206 return null; 207 } 208 209 // fill up headers with field names if necessary 210 $dataau['headers'] = (array) $dataau['headers']; 211 $cnth = count($dataau['headers']); 212 $cntf = count($dataau['cols']); 213 for($i = $cnth; $i < $cntf; $i++) { 214 $column = array_slice($dataau['cols'], $i, 1); 215 $columnprops = array_pop($column); 216 $dataau['headers'][] = $columnprops['title']; 217 } 218 219 $dataau['sql'] = $this->_buildSQL($dataau); 220 221 // Save current request params for comparison in updateSQL 222 $dataau['cur_param'] = $this->dthlp->_get_current_param(false); 223 return $dataau; 224 } 225 226 protected $before_item = '<tr>'; 227 protected $after_item = '</tr>'; 228 protected $before_val = '<td %s>'; 229 protected $after_val = '</td>'; 230 231 /** 232 * Handles the actual output creation. 233 * 234 * @param string $format output format being rendered 235 * @param Doku_Renderer $R the current renderer object 236 * @param array $dataau data created by handler() 237 * @return boolean rendered correctly? (however, returned value is not used at the moment) 238 */ 239 function render($format, Doku_Renderer $R, $dataau) { 240 if($format != 'xhtml') return false; 241 /** @var Doku_Renderer_xhtml $R */ 242 243 if(is_null($dataau)) return false; 244 if(!$this->dthlp->ready()) return false; 245 $sqlite = $this->dthlp->_getDB(); 246 if(!$sqlite) return false; 247 248 $R->info['cache'] = false; 249 250 //reset counters 251 $this->sums = array(); 252 253 if($this->hasRequestFilter() OR isset($_REQUEST['dataauofs'])) { 254 $this->updateSQLwithQuery($dataau); // handles request params 255 } 256 $this->dthlp->_replacePlaceholdersInSQL($dataau); 257 258 // run query 259 $clist = array_keys($dataau['cols']); 260 $res = $sqlite->query($dataau['sql']); 261 262 $rows = $sqlite->res2arr($res); 263 $cnt = count($rows); 264 265 if($cnt === 0) { 266 $this->nullList($dataau, $clist, $R); 267 return true; 268 } 269 270 if($dataau['limit'] && $cnt > $dataau['limit']) { 271 $rows = array_slice($rows, 0, $dataau['limit']); 272 } 273 274 //build classnames per column 275 $classes = array(); 276 $class_names_cache = array(); 277 $offset = 0; 278 if($dataau['rownumbers']) { 279 $offset = 1; //rownumbers are in first column 280 $classes[] = $dataau['align'][0] . 'align rownumbers'; 281 } 282 foreach($clist as $index => $col) { 283 $class = $dataau['align'][$index + $offset] . 'align'; 284 $class .= ' ' . hsc(sectionID($col, $class_names_cache)); 285 $classes[] = $class; 286 } 287 288 //start table/list 289 $R->doc .= $this->preList($clist, $dataau); 290 291 foreach($rows as $rownum => $row) { 292 // build data rows 293 $R->doc .= $this->before_item; 294 295 if($dataau['rownumbers']) { 296 $R->doc .= sprintf($this->before_val, 'class="' . $classes[0] . '"'); 297 $R->doc .= $rownum + 1; 298 $R->doc .= $this->after_val; 299 } 300 301 foreach(array_values($row) as $num => $cval) { 302 $num_rn = $num + $offset; 303 304 $R->doc .= sprintf($this->beforeVal($dataau, $num_rn), 'class="' . $classes[$num_rn] . '"'); 305 $R->doc .= $this->dthlp->_formatData( 306 $dataau['cols'][$clist[$num]], 307 $cval, $R 308 ); 309 $R->doc .= $this->afterVal($dataau, $num_rn); 310 311 // clean currency symbols 312 $nval = str_replace('$€₤', '', $cval); 313 $nval = str_replace('/ [A-Z]{0,3}$/', '', $nval); 314 $nval = str_replace(',', '.', $nval); 315 $nval = trim($nval); 316 317 // summarize 318 if($dataau['summarize'] && is_numeric($nval)) { 319 if(!isset($this->sums[$num])) { 320 $this->sums[$num] = 0; 321 } 322 $this->sums[$num] += $nval; 323 } 324 325 } 326 $R->doc .= $this->after_item; 327 } 328 $R->doc .= $this->postList($dataau, $cnt); 329 330 return true; 331 } 332 333 /** 334 * Before value in table cell 335 * 336 * @param array $dataau instructions by handler 337 * @param int $colno column number 338 * @return string 339 */ 340 protected function beforeVal(&$dataau, $colno) { 341 return $this->before_val; 342 } 343 344 /** 345 * After value in table cell 346 * 347 * @param array $data 348 * @param int $colno 349 * @return string 350 */ 351 protected function afterVal(&$dataau, $colno) { 352 return $this->after_val; 353 } 354 355 /** 356 * Create table header 357 * 358 * @param array $clist keys of the columns 359 * @param array $dataau instruction by handler 360 * @return string html of table header 361 */ 362 function preList($clist, $dataau) { 363 global $ID; 364 global $conf; 365 366 // Save current request params to not loose them 367 $cur_params = $this->dthlp->_get_current_param(); 368 369 //show active filters 370 $text = '<div class="table dataaggregation">'; 371 if(isset($_REQUEST['dataflt'])) { 372 $filters = $this->dthlp->_get_filters(); 373 $fltrs = array(); 374 foreach($filters as $filter) { 375 if(strpos($filter['compare'], 'LIKE') !== false) { 376 if(strpos($filter['compare'], 'NOT') !== false) { 377 $comparator_value = '!~' . str_replace('%', '*', $filter['value']); 378 } else { 379 $comparator_value = '*~' . str_replace('%', '', $filter['value']); 380 } 381 $fltrs[] = $filter['key'] . $comparator_value; 382 } else { 383 $fltrs[] = $filter['key'] . $filter['compare'] . $filter['value']; 384 } 385 } 386 387 $text .= '<div class="filter">'; 388 $text .= '<h4>' . sprintf($this->getLang('tablefilteredby'), hsc(implode(' & ', $fltrs))) . '</h4>'; 389 $text .= '<div class="resetfilter">' . 390 '<a href="' . wl($ID) . '">' . $this->getLang('tableresetfilter') . '</a>' . 391 '</div>'; 392 $text .= '</div>'; 393 } 394 // build table 395 $text .= '<table class="inline dataauplugin_table ' . $dataau['classes'] . '">'; 396 // build column headers 397 $text .= '<tr>'; 398 399 if($dataau['rownumbers']) { 400 $text .= '<th>#</th>'; 401 } 402 403 foreach($dataau['headers'] as $num => $head) { 404 $ckey = $clist[$num]; 405 406 $width = ''; 407 if(isset($dataau['widths'][$num]) AND $dataau['widths'][$num] != '-') { 408 $width = ' style="width: ' . $dataau['widths'][$num] . ';"'; 409 } 410 $text .= '<th' . $width . '>'; 411 412 // add sort arrow 413 if(isset($dataau['sort']) && $ckey == $dataau['sort'][0]) { 414 if($dataau['sort'][1] == 'ASC') { 415 $text .= '<span>↓</span> '; 416 $ckey = '^' . $ckey; 417 } else { 418 $text .= '<span>↑</span> '; 419 } 420 } 421 422 // Clickable header for dynamic sorting 423 $text .= '<a href="' . wl($ID, array('dataausrt' => $ckey) + $cur_params) . 424 '" title="' . $this->getLang('sort') . '">' . hsc($head) . '</a>'; 425 $text .= '</th>'; 426 } 427 $text .= '</tr>'; 428 429 // Dynamic filters 430 if($dataau['dynfilters']) { 431 $text .= '<tr class="dataflt">'; 432 433 if($dataau['rownumbers']) { 434 $text .= '<th></th>'; 435 } 436 437 foreach($dataau['headers'] as $num => $head) { 438 $text .= '<th>'; 439 $form = new Doku_Form(array('method' => 'GET')); 440 $form->_hidden = array(); 441 if(!$conf['userewrite']) { 442 $form->addHidden('id', $ID); 443 } 444 445 $key = 'dataflt[' . $dataau['cols'][$clist[$num]]['colname'] . '*~' . ']'; 446 $val = isset($cur_params[$key]) ? $cur_params[$key] : ''; 447 448 // Add current request params 449 foreach($cur_params as $c_key => $c_val) { 450 if($c_val !== '' && $c_key !== $key) { 451 $form->addHidden($c_key, $c_val); 452 } 453 } 454 455 $form->addElement(form_makeField('text', $key, $val, '')); 456 $text .= $form->getForm(); 457 $text .= '</th>'; 458 } 459 $text .= '</tr>'; 460 } 461 462 return $text; 463 } 464 465 /** 466 * Create an empty table 467 * 468 * @param array $dataau instruction by handler() 469 * @param array $clist keys of the columns 470 * @param Doku_Renderer $R 471 */ 472 function nullList($dataau, $clist, $R) { 473 $R->doc .= $this->preList($clist, $dataau); 474 $R->tablerow_open(); 475 $R->tablecell_open(count($clist), 'center'); 476 $R->cdata($this->getLang('none')); 477 $R->tablecell_close(); 478 $R->tablerow_close(); 479 $R->doc .= '</table></div>'; 480 } 481 482 /** 483 * Create table footer 484 * 485 * @param array $dataau instruction by handler() 486 * @param int $rowcnt number of rows 487 * @return string html of table footer 488 */ 489 function postList($dataau, $rowcnt) { 490 global $ID; 491 $text = ''; 492 // if summarize was set, add sums 493 if($dataau['summarize']) { 494 $text .= '<tr>'; 495 $len = count($dataau['cols']); 496 497 if($dataau['rownumbers']) $text .= '<td></td>'; 498 499 for($i = 0; $i < $len; $i++) { 500 $text .= '<td class="' . $dataau['align'][$i] . 'align">'; 501 if(!empty($this->sums[$i])) { 502 $text .= '∑ ' . $this->sums[$i]; 503 } else { 504 $text .= ' '; 505 } 506 $text .= '</td>'; 507 } 508 $text .= '<tr>'; 509 } 510 511 // if limit was set, add control 512 if($dataau['limit']) { 513 $text .= '<tr><th colspan="' . (count($dataau['cols']) + ($dataau['rownumbers'] ? 1 : 0)) . '">'; 514 $offset = (int) $_REQUEST['dataauofs']; 515 if($offset) { 516 $prev = $offset - $dataau['limit']; 517 if($prev < 0) { 518 $prev = 0; 519 } 520 521 // keep url params 522 $params = $this->dthlp->_a2ua('dataflt', $_REQUEST['dataflt']); 523 if(isset($_REQUEST['dataausrt'])) { 524 $params['dataausrt'] = $_REQUEST['dataausrt']; 525 } 526 $params['dataauofs'] = $prev; 527 528 $text .= '<a href="' . wl($ID, $params) . 529 '" title="' . $this->getLang('prev') . 530 '" class="prev">' . $this->getLang('prev') . '</a>'; 531 } 532 533 $text .= ' '; 534 535 if($rowcnt > $dataau['limit']) { 536 $next = $offset + $dataau['limit']; 537 538 // keep url params 539 $params = $this->dthlp->_a2ua('dataflt', $_REQUEST['dataflt']); 540 if(isset($_REQUEST['dataausrt'])) { 541 $params['dataausrt'] = $_REQUEST['dataausrt']; 542 } 543 $params['dataauofs'] = $next; 544 545 $text .= '<a href="' . wl($ID, $params) . 546 '" title="' . $this->getLang('next') . 547 '" class="next">' . $this->getLang('next') . '</a>'; 548 } 549 $text .= '</th></tr>'; 550 } 551 552 $text .= '</table></div>'; 553 return $text; 554 } 555 556 /** 557 * Builds the SQL query from the given data 558 * 559 * @param array &$dataau instruction by handler 560 * @return bool|string SQL query or false 561 */ 562 function _buildSQL(&$dataau) { 563 $cnt = 0; 564 $tables = array(); 565 $select = array(); 566 $from = ''; 567 568 $from2 = ''; 569 $where2 = '1 = 1'; 570 571 $sqlite = $this->dthlp->_getDB(); 572 if(!$sqlite) return false; 573 574 // prepare the columns to show 575 foreach($dataau['cols'] as &$col) { 576 $key = $col['key']; 577 if($key == '%pageid%') { 578 // Prevent stripping of trailing zeros by forcing a CAST 579 $select[] = '" " || pages.page'; 580 } elseif($key == '%class%') { 581 // Prevent stripping of trailing zeros by forcing a CAST 582 $select[] = '" " || pages.class'; 583 } elseif($key == '%lastmod%') { 584 $select[] = 'pages.lastmod'; 585 } elseif($key == '%title%') { 586 $select[] = "pages.page || '|' || pages.title"; 587 } else { 588 if(!isset($tables[$key])) { 589 $tables[$key] = 'T' . (++$cnt); 590 $from .= ' LEFT JOIN dataau AS ' . $tables[$key] . ' ON ' . $tables[$key] . '.pid = W1.pid'; 591 $from .= ' AND ' . $tables[$key] . ".key = " . $sqlite->quote_string($key); 592 } 593 $type = $col['type']; 594 if(is_array($type)) { 595 $type = $type['type']; 596 } 597 switch($type) { 598 case 'pageid': 599 case 'wiki': 600 //note in multivalued case: adds pageid only to first value 601 $select[] = "pages.page || '|' || group_concat(" . $tables[$key] . ".value,'\n')"; 602 break; 603 default: 604 // Prevent stripping of trailing zeros by forcing a CAST 605 $select[] = 'group_concat(" " || ' . $tables[$key] . ".value,'\n')"; 606 } 607 } 608 } 609 unset($col); 610 611 // prepare sorting 612 if(isset($dataau['sort'])) { 613 $col = $dataau['sort'][0]; 614 615 if($col == '%pageid%') { 616 $order = 'ORDER BY pages.page ' . $dataau['sort'][1]; 617 } elseif($col == '%class%') { 618 $order = 'ORDER BY pages.class ' . $dataau['sort'][1]; 619 } elseif($col == '%title%') { 620 $order = 'ORDER BY pages.title ' . $dataau['sort'][1]; 621 } elseif($col == '%lastmod%') { 622 $order = 'ORDER BY pages.lastmod ' . $dataau['sort'][1]; 623 } else { 624 // sort by hidden column? 625 if(!$tables[$col]) { 626 $tables[$col] = 'T' . (++$cnt); 627 $from .= ' LEFT JOIN dataau AS ' . $tables[$col] . ' ON ' . $tables[$col] . '.pid = W1.pid'; 628 $from .= ' AND ' . $tables[$col] . ".key = " . $sqlite->quote_string($col); 629 } 630 631 $order = 'ORDER BY ' . $tables[$col] . '.value ' . $dataau['sort'][1]; 632 } 633 } else { 634 $order = 'ORDER BY 1 ASC'; 635 } 636 637 // may be disabled from config. as it decreases performance a lot 638 $use_dataresolve = $this->getConf('use_dataresolve'); 639 640 // prepare filters 641 $cnt = 0; 642 if(is_array($dataau['filter']) && count($dataau['filter'])) { 643 644 foreach($dataau['filter'] as $filter) { 645 $col = $filter['key']; 646 $closecompare = ($filter['compare'] == 'IN(' ? ')' : ''); 647 648 if($col == '%pageid%') { 649 $where2 .= " " . $filter['logic'] . " pages.page " . $filter['compare'] . " '" . $filter['value'] . "'" . $closecompare; 650 } elseif($col == '%class%') { 651 $where2 .= " " . $filter['logic'] . " pages.class " . $filter['compare'] . " '" . $filter['value'] . "'" . $closecompare; 652 } elseif($col == '%title%') { 653 $where2 .= " " . $filter['logic'] . " pages.title " . $filter['compare'] . " '" . $filter['value'] . "'" . $closecompare; 654 } elseif($col == '%lastmod%') { 655 # parse value to int? 656 $filter['value'] = (int) strtotime($filter['value']); 657 $where2 .= " " . $filter['logic'] . " pages.lastmod " . $filter['compare'] . " " . $filter['value'] . $closecompare; 658 } else { 659 // filter by hidden column? 660 $table = 'T' . (++$cnt); 661 $from2 .= ' LEFT JOIN dataau AS ' . $table . ' ON ' . $table . '.pid = pages.pid'; 662 $from2 .= ' AND ' . $table . ".key = " . $sqlite->quote_string($col); 663 664 // apply dataau resolving? 665 if($use_dataresolve && $filter['colname'] && (substr($filter['compare'], -4) == 'LIKE')) { 666 $where2 .= ' ' . $filter['logic'] . ' DATARESOLVE(' . $table . '.value,\'' . $sqlite->escape_string($filter['colname']) . '\') ' . $filter['compare'] . 667 " '" . $filter['value'] . "'"; //value is already escaped 668 } else { 669 $where2 .= ' ' . $filter['logic'] . ' ' . $table . '.value ' . $filter['compare'] . 670 " '" . $filter['value'] . "'" . $closecompare; //value is already escaped 671 } 672 } 673 } 674 } 675 676 // build the query 677 $sql = "SELECT " . join(', ', $select) . " 678 FROM ( 679 SELECT DISTINCT pages.pid AS pid 680 FROM pages $from2 681 WHERE $where2 682 ) AS W1 683 $from 684 LEFT JOIN pages ON W1.pid=pages.pid 685 GROUP BY W1.pid 686 $order"; 687 688 // offset and limit 689 if($dataau['limit']) { 690 $sql .= ' LIMIT ' . ($dataau['limit'] + 1); 691 // offset is added from REQUEST params in updateSQLwithQuery 692 } 693 694 return $sql; 695 } 696 697 /** 698 * Handle request paramaters, rebuild sql when needed 699 * 700 * @param array $dataau instruction by handler() 701 */ 702 function updateSQLwithQuery(&$dataau) { 703 if($this->hasRequestFilter()) { 704 if(isset($_REQUEST['dataausrt'])) { 705 if($_REQUEST['dataausrt']{0} == '^') { 706 $dataau['sort'] = array(substr($_REQUEST['dataausrt'], 1), 'DESC'); 707 } else { 708 $dataau['sort'] = array($_REQUEST['dataausrt'], 'ASC'); 709 } 710 } 711 712 // add request filters 713 $dataau['filter'] = array_merge($dataau['filter'], $this->dthlp->_get_filters()); 714 715 // Rebuild SQL FIXME do this smarter & faster 716 $dataau['sql'] = $this->_buildSQL($dataau); 717 } 718 719 if($dataau['limit'] && (int) $_REQUEST['dataauofs']) { 720 $dataau['sql'] .= ' OFFSET ' . ((int) $_REQUEST['dataauofs']); 721 } 722 } 723 724 /** 725 * Check whether a sort or filter request parameters are available 726 * 727 * @return bool 728 */ 729 function hasRequestFilter() { 730 return isset($_REQUEST['dataausrt']) || isset($_REQUEST['dataflt']); 731 } 732 733 /** 734 * Split values at the commas, 735 * - Wrap with quotes to escape comma, quotes escaped by two quotes 736 * - Within quotes spaces are stored. 737 * 738 * @param string $line 739 * @return array 740 */ 741 protected function parseValues($line) { 742 $values = array(); 743 $inQuote = false; 744 $escapedQuote = false; 745 $value = ''; 746 747 $len = strlen($line); 748 for($i = 0; $i < $len; $i++) { 749 if($line{$i} == '"') { 750 if($inQuote) { 751 if($escapedQuote) { 752 $value .= '"'; 753 $escapedQuote = false; 754 continue; 755 } 756 if($line{$i + 1} == '"') { 757 $escapedQuote = true; 758 continue; 759 } 760 array_push($values, $value); 761 $inQuote = false; 762 $value = ''; 763 continue; 764 765 } else { 766 $inQuote = true; 767 $value = ''; //don't store stuff before the opening quote 768 continue; 769 } 770 } else if($line{$i} == ',') { 771 if($inQuote) { 772 $value .= ','; 773 continue; 774 } else { 775 if(strlen($value) < 1) { 776 continue; 777 } 778 array_push($values, trim($value)); 779 $value = ''; 780 continue; 781 } 782 } 783 784 $value .= $line{$i}; 785 } 786 if(strlen($value) > 0) { 787 array_push($values, trim($value)); 788 } 789 return $values; 790 } 791} 792 793