1 <?php
   2 
   3 // Nötige Konstanten
   4 define('REX_LIST_OPT_SORT', 0);
   5 define('REX_LIST_OPT_SORT_DIRECTION', 1);
   6 
   7 /*
   8 EXAMPLE:
   9 
  10 $list = rex_list::factory('SELECT id,name FROM rex_article');
  11 $list->setColumnFormat('id', 'date');
  12 $list->setColumnLabel('name', 'Artikel-Name');
  13 $list->setColumnSortable('name');
  14 $list->addColumn('testhead','###id### - ###name###',-1);
  15 $list->addColumn('testhead2','testbody2');
  16 $list->setCaption('thomas macht das css');
  17 $list->show();
  18 
  19 
  20 EXAMPLE USING CUSTOM CALLBACKS WITH setColumnFormat() METHOD:
  21 
  22 function callback_func($params)
  23 {
  24     // $params['subject']  current value
  25     // $params['list']     rex_list object
  26     // $params['params']   custom params
  27 
  28     return $custom_string; // return value showed in list (note: no htmlspechialchars!)
  29 }
  30 
  31 // USING setColumnFormat() BY CALLING A FUNCTION
  32 $list->setColumnFormat('id',                                     // field name
  33                                              'custom',                                 // format type
  34                                              'callback_func',                          // callback function name
  35                                                 array('foo' => 'bar', '123' => '456')    // optional params for callback function
  36 );
  37 
  38 // USING setColumnFormat() BY CALLING CLASS & METHOD
  39 $list->setColumnFormat('id',                                     // field name
  40                                              'custom',                                 // format type
  41                                                 array('CLASS','METHOD'),                 // callback class/method name
  42                                                 array('foo' => 'bar', '123' => '456')    // optional params for callback function
  43                                              );
  44 */
  45 
  46 /**
  47  * Klasse zum erstellen von Listen.
  48  *
  49  * @package redaxo\core
  50  */
  51 class rex_list implements rex_url_provider_interface
  52 {
  53     use rex_factory_trait;
  54 
  55     private $db;
  56     private $query;
  57     private $sql;
  58     private $debug;
  59     private $noRowsMessage;
  60 
  61     // --------- List Attributes
  62     private $name;
  63     private $params;
  64     private $rows;
  65 
  66     // --------- Form Attributes
  67     private $formAttributes;
  68 
  69     // --------- Column Attributes
  70     private $customColumns;
  71     private $columnNames;
  72     private $columnLabels;
  73     private $columnFormates;
  74     private $columnOptions;
  75     private $columnAttributes;
  76     private $columnLayouts;
  77     private $columnParams;
  78     private $columnDisabled;
  79 
  80     // --------- Layout, Default
  81     private $defaultColumnLayout;
  82 
  83     // --------- Table Attributes
  84     private $caption;
  85     private $tableAttributes;
  86     private $tableColumnGroups;
  87 
  88     // --------- Link Attributes
  89     private $linkAttributes;
  90 
  91     // --------- Pagination Attributes
  92     private $pager;
  93 
  94     /**
  95      * Erstellt ein rex_list Objekt.
  96      *
  97      * @param string $query       SELECT Statement
  98      * @param int    $rowsPerPage Anzahl der Elemente pro Zeile
  99      * @param string $listName    Name der Liste
 100      * @param bool   $debug
 101      */
 102     protected function __construct($query, $rowsPerPage = 30, $listName = null, $debug = false, $db = 1)
 103     {
 104         // --------- Validation
 105         if (!$listName) {
 106             // use a hopefully unique (per page) hash
 107             $listName = substr(md5($query), 0, 8);
 108         }
 109 
 110         // --------- List Attributes
 111         $this->db = $db;
 112         $this->query = $query;
 113         $this->sql = rex_sql::factory($db);
 114         $this->debug = $debug;
 115         $this->sql->setDebug($this->debug);
 116         $this->name = $listName;
 117         $this->caption = '';
 118         $this->rows = 0;
 119         $this->params = [];
 120         $this->tableAttributes = [];
 121         $this->noRowsMessage = rex_i18n::msg('list_no_rows');
 122 
 123         // --------- Form Attributes
 124         $this->formAttributes = [];
 125 
 126         // --------- Column Attributes
 127         $this->customColumns = [];
 128         $this->columnLabels = [];
 129         $this->columnFormates = [];
 130         $this->columnParams = [];
 131         $this->columnOptions = [];
 132         $this->columnAttributes = [];
 133         $this->columnLayouts = [];
 134         $this->columnDisabled = [];
 135 
 136         // --------- Default
 137         $this->defaultColumnLayout = ['<th>###VALUE###</th>', '<td data-title="###LABEL###">###VALUE###</td>'];
 138 
 139         // --------- Table Attributes
 140         $this->tableAttributes = [];
 141         $this->tableColumnGroups = [];
 142 
 143         // --------- Link Attributes
 144         $this->linkAttributes = [];
 145 
 146         // --------- Pagination Attributes
 147         $cursorName = $listName .'_start';
 148         if (null === rex_request($cursorName, 'int', null) && rex_request('start', 'int')) {
 149             // BC: Fallback to "start"
 150             $cursorName = 'start';
 151         }
 152         $this->pager = new rex_pager($rowsPerPage, $cursorName);
 153 
 154         // --------- Load Data, Row-Count
 155         $this->sql->setQuery($this->prepareQuery($query));
 156         $sql = rex_sql::factory($db);
 157         $sql->setQuery('SELECT FOUND_ROWS() as '. $sql->escapeIdentifier('rows'));
 158         $this->rows = $sql->getValue('rows');
 159         $this->pager->setRowCount($this->rows);
 160 
 161         foreach ($this->sql->getFieldnames() as $columnName) {
 162             $this->columnNames[] = $columnName;
 163         }
 164 
 165         // --------- Load Env
 166         if (rex::isBackend()) {
 167             $this->loadBackendConfig();
 168         }
 169 
 170         $this->init();
 171     }
 172 
 173     /**
 174      * @param string $query
 175      * @param int    $rowsPerPage
 176      * @param null   $listName
 177      * @param bool   $debug
 178      * @param int    $db          DB connection ID
 179      *
 180      * @return static
 181      */
 182     public static function factory($query, $rowsPerPage = 30, $listName = null, $debug = false, $db = 1)
 183     {
 184         $class = static::getFactoryClass();
 185         return new $class($query, $rowsPerPage, $listName, $debug, $db);
 186     }
 187 
 188     public function init()
 189     {
 190         // nichts tun
 191     }
 192 
 193     // ---------------------- setters/getters
 194 
 195     /**
 196      * Gibt den Namen es Formulars zurück.
 197      *
 198      * @return string
 199      */
 200     public function getName()
 201     {
 202         return $this->name;
 203     }
 204 
 205     /**
 206      * Gibt eine Status Nachricht zurück.
 207      *
 208      * @return string
 209      */
 210     public function getMessage()
 211     {
 212         return rex_escape(rex_request($this->getName() . '_msg', 'string'));
 213     }
 214 
 215     /**
 216      * Gibt eine Warnung zurück.
 217      *
 218      * @return string
 219      */
 220     public function getWarning()
 221     {
 222         return rex_escape(rex_request($this->getName() . '_warning', 'string'));
 223     }
 224 
 225     /**
 226      * Setzt die Caption/den Titel der Tabelle
 227      * Gibt den Namen es Formulars zurück.
 228      *
 229      * @param string $caption Caption/Titel der Tabelle
 230      */
 231     public function setCaption($caption)
 232     {
 233         $this->caption = $caption;
 234     }
 235 
 236     /**
 237      * Gibt die Caption/den Titel der Tabelle zurück.
 238      *
 239      * @return string
 240      */
 241     public function getCaption()
 242     {
 243         return $this->caption;
 244     }
 245 
 246     public function setNoRowsMessage($msg)
 247     {
 248         $this->noRowsMessage = $msg;
 249     }
 250 
 251     public function getNoRowsMessage()
 252     {
 253         return $this->noRowsMessage;
 254     }
 255 
 256     public function addParam($name, $value)
 257     {
 258         $this->params[$name] = $value;
 259     }
 260 
 261     public function getParams()
 262     {
 263         return $this->params;
 264     }
 265 
 266     protected function loadBackendConfig()
 267     {
 268         $this->addParam('page', rex_be_controller::getCurrentPage());
 269     }
 270 
 271     public function addTableAttribute($attrName, $attrValue)
 272     {
 273         $this->tableAttributes[$attrName] = $attrValue;
 274     }
 275 
 276     public function getTableAttributes()
 277     {
 278         return $this->tableAttributes;
 279     }
 280 
 281     public function addFormAttribute($attrName, $attrValue)
 282     {
 283         $this->formAttributes[$attrName] = $attrValue;
 284     }
 285 
 286     public function getFormAttributes()
 287     {
 288         return $this->formAttributes;
 289     }
 290 
 291     public function addLinkAttribute($columnName, $attrName, $attrValue)
 292     {
 293         $this->linkAttributes[$columnName][$attrName] = $attrValue;
 294     }
 295 
 296     public function getLinkAttributes($column, $default = null)
 297     {
 298         return isset($this->linkAttributes[$column]) ? $this->linkAttributes[$column] : $default;
 299     }
 300 
 301     // ---------------------- Column setters/getters/etc
 302 
 303     /**
 304      * Methode, um eine Spalte einzufügen.
 305      *
 306      * @param string $columnHead   Titel der Spalte
 307      * @param string $columnBody   Text/Format der Spalte
 308      * @param int    $columnIndex  Stelle, an der die neue Spalte erscheinen soll
 309      * @param array  $columnLayout Layout der Spalte
 310      */
 311     public function addColumn($columnHead, $columnBody, $columnIndex = -1, $columnLayout = null)
 312     {
 313         // Bei negativem columnIndex, das Element am Ende anfügen
 314         if ($columnIndex < 0) {
 315             $columnIndex = count($this->columnNames);
 316         }
 317 
 318         array_splice($this->columnNames, $columnIndex, 0, [$columnHead]);
 319         $this->customColumns[$columnHead] = $columnBody;
 320         $this->setColumnLayout($columnHead, $columnLayout);
 321     }
 322 
 323     /**
 324      * Entfernt eine Spalte aus der Anzeige.
 325      *
 326      * @param string $columnName Name der Spalte
 327      */
 328     public function removeColumn($columnName)
 329     {
 330         $this->columnDisabled[] = $columnName;
 331     }
 332 
 333     /**
 334      * Methode, um das Layout einer Spalte zu setzen.
 335      *
 336      * @param string $columnHead   Titel der Spalte
 337      * @param array  $columnLayout Layout der Spalte
 338      */
 339     public function setColumnLayout($columnHead, $columnLayout)
 340     {
 341         $this->columnLayouts[$columnHead] = $columnLayout;
 342     }
 343 
 344     /**
 345      * Gibt das Layout einer Spalte zurück.
 346      *
 347      * @param string $columnName Name der Spalte
 348      *
 349      * @return array
 350      */
 351     public function getColumnLayout($columnName)
 352     {
 353         if (isset($this->columnLayouts[$columnName]) && is_array($this->columnLayouts[$columnName])) {
 354             return $this->columnLayouts[$columnName];
 355         }
 356 
 357         return $this->defaultColumnLayout;
 358     }
 359 
 360     /**
 361      * Gibt die Layouts aller Spalten zurück.
 362      */
 363     public function getColumnLayouts()
 364     {
 365         return $this->columnLayouts;
 366     }
 367 
 368     /**
 369      * Gibt den Namen einer Spalte zurück.
 370      *
 371      * @param int   $columnIndex Nummer der Spalte
 372      * @param mixed $default     Defaultrückgabewert, falls keine Spalte mit der angegebenen Nummer vorhanden ist
 373      *
 374      * @return string|null
 375      */
 376     public function getColumnName($columnIndex, $default = null)
 377     {
 378         if (isset($this->columnNames[$columnIndex])) {
 379             return $this->columnNames[$columnIndex];
 380         }
 381 
 382         return $default;
 383     }
 384 
 385     /**
 386      * Gibt alle Namen der Spalten als Array zurück.
 387      *
 388      * @return array
 389      */
 390     public function getColumnNames()
 391     {
 392         return $this->columnNames;
 393     }
 394 
 395     /**
 396      * Setzt ein Label für eine Spalte.
 397      *
 398      * @param string $columnName Name der Spalte
 399      * @param string $label      Label für die Spalte
 400      */
 401     public function setColumnLabel($columnName, $label)
 402     {
 403         $this->columnLabels[$columnName] = $label;
 404     }
 405 
 406     /**
 407      * Gibt das Label der Spalte zurück, falls gesetzt.
 408      *
 409      * Falls nicht vorhanden und der Parameter $default auf null steht,
 410      * wird der Spaltenname zurückgegeben
 411      *
 412      * @param string $columnName Name der Spalte
 413      * @param mixed  $default    Defaultrückgabewert, falls kein Label gesetzt ist
 414      *
 415      * @return string|null
 416      */
 417     public function getColumnLabel($columnName, $default = null)
 418     {
 419         if (isset($this->columnLabels[$columnName])) {
 420             return $this->columnLabels[$columnName];
 421         }
 422 
 423         return $default === null ? $columnName : $default;
 424     }
 425 
 426     /**
 427      * Setzt ein Format für die Spalte.
 428      *
 429      * @param string $columnName  Name der Spalte
 430      * @param string $format_type Formatierungstyp
 431      * @param mixed  $format      Zu verwendentes Format
 432      * @param array  $params      Custom params für callback func bei format_type 'custom'
 433      */
 434     public function setColumnFormat($columnName, $format_type, $format = '', array $params = [])
 435     {
 436         $this->columnFormates[$columnName] = [$format_type, $format, $params];
 437     }
 438 
 439     /**
 440      * Gibt das Format für eine Spalte zurück.
 441      *
 442      * @param string $columnName Name der Spalte
 443      * @param mixed  $default    Defaultrückgabewert, falls keine Formatierung gesetzt ist
 444      *
 445      * @return string|null
 446      */
 447     public function getColumnFormat($columnName, $default = null)
 448     {
 449         if (isset($this->columnFormates[$columnName])) {
 450             return $this->columnFormates[$columnName];
 451         }
 452 
 453         return $default;
 454     }
 455 
 456     /**
 457      * Markiert eine Spalte als sortierbar.
 458      *
 459      * @param string $columnName Name der Spalte
 460      * @param string $direction  Startsortierrichtung der Spalte [ASC|DESC]
 461      */
 462     public function setColumnSortable($columnName, $direction = 'asc')
 463     {
 464         $this->setColumnOption($columnName, REX_LIST_OPT_SORT, true);
 465         $this->setColumnOption($columnName, REX_LIST_OPT_SORT_DIRECTION, strtolower($direction));
 466     }
 467 
 468     /**
 469      * Setzt eine Option für eine Spalte
 470      * (z.b. Sortable,..).
 471      *
 472      * @param string $columnName Name der Spalte
 473      * @param string $option     Name/Id der Option
 474      * @param mixed  $value      Wert der Option
 475      */
 476     public function setColumnOption($columnName, $option, $value)
 477     {
 478         $this->columnOptions[$columnName][$option] = $value;
 479     }
 480 
 481     /**
 482      * Gibt den Wert einer Option für eine Spalte zurück.
 483      *
 484      * @param string $columnName Name der Spalte
 485      * @param string $option     Name/Id der Option
 486      * @param mixed  $default    Defaultrückgabewert, falls die Option nicht gesetzt ist
 487      *
 488      * @return mixed|null
 489      */
 490     public function getColumnOption($columnName, $option, $default = null)
 491     {
 492         if ($this->hasColumnOption($columnName, $option)) {
 493             return $this->columnOptions[$columnName][$option];
 494         }
 495         return $default;
 496     }
 497 
 498     /**
 499      * Gibt zurück, ob für eine Spalte eine Option gesetzt wurde.
 500      *
 501      * @param string $columnName Name der Spalte
 502      * @param string $option     Name/Id der Option
 503      *
 504      * @return bool
 505      */
 506     public function hasColumnOption($columnName, $option)
 507     {
 508         return isset($this->columnOptions[$columnName][$option]);
 509     }
 510 
 511     /**
 512      * Verlinkt eine Spalte mit den übergebenen Parametern.
 513      *
 514      * @param string $columnName Name der Spalte
 515      * @param array  $params     Array von Parametern
 516      */
 517     public function setColumnParams($columnName, array $params = [])
 518     {
 519         $this->columnParams[$columnName] = $params;
 520     }
 521 
 522     /**
 523      * Gibt die Parameter für eine Spalte zurück.
 524      *
 525      * @param string $columnName Name der Spalte
 526      *
 527      * @return array
 528      */
 529     public function getColumnParams($columnName)
 530     {
 531         if (isset($this->columnParams[$columnName]) && is_array($this->columnParams[$columnName])) {
 532             return $this->columnParams[$columnName];
 533         }
 534         return [];
 535     }
 536 
 537     /**
 538      * Gibt zurück, ob Parameter für eine Spalte existieren.
 539      *
 540      * @param string $columnName Name der Spalte
 541      *
 542      * @return bool
 543      */
 544     public function hasColumnParams($columnName)
 545     {
 546         return isset($this->columnParams[$columnName]) && is_array($this->columnParams[$columnName]) && count($this->columnParams[$columnName]) > 0;
 547     }
 548 
 549     // ---------------------- TableColumnGroup setters/getters/etc
 550 
 551     /**
 552      * Methode um eine Colgroup einzufügen.
 553      *
 554      * Beispiel 1:
 555      *
 556      * $list->addTableColumnGroup([40, '*', 240, 140]);
 557      *
 558      * Beispiel 2:
 559      *
 560      * $list->addTableColumnGroup([
 561      *     ['width' => 40],
 562      *     ['width' => 140, 'span' => 2],
 563      *     ['width' => 240]
 564      * ]);
 565      *
 566      * Beispiel 3:
 567      *
 568      * $list->addTableColumnGroup([
 569      *     ['class' => 'classname-a'],
 570      *     ['class' => 'classname-b'],
 571      *     ['class' => 'classname-c']
 572      * ]);
 573      *
 574      * @param array $columns         Array von Spalten
 575      * @param int   $columnGroupSpan Span der Columngroup
 576      */
 577     public function addTableColumnGroup(array $columns, $columnGroupSpan = null)
 578     {
 579         $tableColumnGroup = ['columns' => []];
 580         if ($columnGroupSpan) {
 581             $tableColumnGroup['span'] = $columnGroupSpan;
 582         }
 583         $this->tableColumnGroups[] = $tableColumnGroup;
 584 
 585         foreach ($columns as $column) {
 586             if (is_array($column)) {
 587                 $this->addTableColumn(isset($column['width']) ? $column['width'] : null, isset($column['span']) ? $column['span'] : null, isset($column['class']) ? $column['class'] : null);
 588             } else {
 589                 $this->addTableColumn($column);
 590             }
 591         }
 592     }
 593 
 594     /**
 595      * @return array
 596      */
 597     public function getTableColumnGroups()
 598     {
 599         return $this->tableColumnGroups;
 600     }
 601 
 602     /**
 603      * Fügt der zuletzte eingefügten TableColumnGroup eine weitere Spalte hinzu.
 604      *
 605      * @param int $width Breite der Spalte
 606      * @param int $span  Span der Spalte
 607      */
 608     public function addTableColumn($width, $span = null, $class = null)
 609     {
 610         $tableColumn = [];
 611         if (is_numeric($width)) {
 612             $width = $width . 'px';
 613         }
 614         if ($width && '*' != $width) {
 615             $tableColumn['style'] = 'width:' . $width;
 616         }
 617         if ($span) {
 618             $tableColumn['span'] = $span;
 619         }
 620         if ($class) {
 621             $tableColumn['class'] = $class;
 622         }
 623 
 624         $lastIndex = count($this->tableColumnGroups) - 1;
 625 
 626         if ($lastIndex < 0) {
 627             // Falls noch keine TableColumnGroup vorhanden, eine leere anlegen!
 628             $this->addTableColumnGroup([]);
 629             ++$lastIndex;
 630         }
 631 
 632         $groupColumns = $this->tableColumnGroups[$lastIndex]['columns'];
 633         $groupColumns[] = $tableColumn;
 634         $this->tableColumnGroups[$lastIndex]['columns'] = $groupColumns;
 635     }
 636 
 637     // ---------------------- Url generation
 638 
 639     /**
 640      * {@inheritdoc}
 641      */
 642     public function getUrl(array $params = [], $escape = true)
 643     {
 644         $params = array_merge($this->getParams(), $params);
 645 
 646         $params['list'] = $this->getName();
 647 
 648         if (!isset($params['sort'])) {
 649             $sortColumn = $this->getSortColumn();
 650             if ($sortColumn != null) {
 651                 $params['sort'] = $sortColumn;
 652                 $params['sorttype'] = $this->getSortType();
 653             }
 654         }
 655 
 656         $_params = [];
 657         foreach ($params as $name => $value) {
 658             if (is_array($value)) {
 659                 foreach ($value as $v) {
 660                     $_params[$name] = $v;
 661                 }
 662             } else {
 663                 $_params[$name] = $value;
 664             }
 665         }
 666 
 667         return rex::isBackend() ? rex_url::backendController($_params, $escape) : rex_url::frontendController($_params, $escape);
 668     }
 669 
 670     /**
 671      * Gibt eine Url zurück, die die Parameter $params enthält
 672      * Dieser Url werden die Standard rexList Variablen zugefügt.
 673      *
 674      * Innerhalb dieser Url werden variablen ersetzt
 675      *
 676      * @see #replaceVariable, #replaceVariables
 677      *
 678      * @param array $params
 679      * @param bool  $escape Flag whether the argument separator "&" should be escaped (&amp;)
 680      *
 681      * @return string
 682      */
 683     public function getParsedUrl($params = [], $escape = true)
 684     {
 685         $params = array_merge($this->getParams(), $params);
 686 
 687         $params['list'] = $this->getName();
 688 
 689         if (!isset($params['sort'])) {
 690             $sortColumn = $this->getSortColumn();
 691             if ($sortColumn != null) {
 692                 $params['sort'] = $sortColumn;
 693                 $params['sorttype'] = $this->getSortType();
 694             }
 695         }
 696 
 697         $_params = [];
 698         foreach ($params as $name => $value) {
 699             if (is_array($value)) {
 700                 foreach ($value as $v) {
 701                     $_params[$name] = $this->replaceVariables($v);
 702                 }
 703             } else {
 704                 $_params[$name] = $this->replaceVariables($value);
 705             }
 706         }
 707         return rex::isBackend() ? rex_url::backendController($_params, $escape) : rex_url::frontendController($_params, $escape);
 708     }
 709 
 710     // ---------------------- Pagination
 711 
 712     /**
 713      * Prepariert das SQL Statement vorm anzeigen der Liste.
 714      *
 715      * @param string $query SQL Statement
 716      *
 717      * @return string
 718      */
 719     protected function prepareQuery($query)
 720     {
 721         $rowsPerPage = $this->pager->getRowsPerPage();
 722         $startRow = $this->pager->getCursor();
 723 
 724         // prepare query for fast rowcount calculation
 725         $query = preg_replace('/^\s*SELECT/i', 'SELECT SQL_CALC_FOUND_ROWS', $query, 1);
 726 
 727         $sortColumn = $this->getSortColumn();
 728         if ($sortColumn != '') {
 729             $sortType = $this->getSortType();
 730 
 731             $sql = rex_sql::factory($this->db);
 732             $sortColumn = $sql->escapeIdentifier($sortColumn);
 733 
 734             if (stripos($query, ' ORDER BY ') === false) {
 735                 $query .= ' ORDER BY ' . $sortColumn . ' ' . $sortType;
 736             } else {
 737                 $query = preg_replace('/ORDER\sBY\s[^ ]*(\sasc|\sdesc)?/i', 'ORDER BY ' . $sortColumn . ' ' . $sortType, $query);
 738             }
 739         }
 740 
 741         if (stripos($query, ' LIMIT ') === false) {
 742             $query .= ' LIMIT ' . $startRow . ',' . $rowsPerPage;
 743         }
 744 
 745         return $query;
 746     }
 747 
 748     /**
 749      * Gibt die Anzahl der Zeilen zurück, welche vom ursprüngliche SQL Statement betroffen werden.
 750      *
 751      * @return int
 752      */
 753     public function getRows()
 754     {
 755         return $this->rows;
 756     }
 757 
 758     /**
 759      * Returns the pager for this list.
 760      *
 761      * @return rex_pager
 762      */
 763     public function getPager()
 764     {
 765         return $this->pager;
 766     }
 767 
 768     /**
 769      * Gibt zurück, nach welcher Spalte sortiert werden soll.
 770      *
 771      * @param mixed $default
 772      *
 773      * @return string
 774      */
 775     public function getSortColumn($default = null)
 776     {
 777         if (rex_request('list', 'string') == $this->getName()) {
 778             return rex_request('sort', 'string', $default);
 779         }
 780         return $default;
 781     }
 782 
 783     /**
 784      * Gibt zurück, in welcher Art und Weise sortiert werden soll (ASC/DESC).
 785      *
 786      * @param mixed $default
 787      *
 788      * @return string
 789      */
 790     public function getSortType($default = null)
 791     {
 792         if (rex_request('list', 'string') == $this->getName()) {
 793             $sortType = strtolower(rex_request('sorttype', 'string'));
 794 
 795             if (in_array($sortType, ['asc', 'desc'])) {
 796                 return $sortType;
 797             }
 798         }
 799         return $default;
 800     }
 801 
 802     /**
 803      * Gibt die Navigation der Liste zurück.
 804      *
 805      * @return string
 806      */
 807     protected function getPagination()
 808     {
 809         $fragment = new rex_fragment();
 810         $fragment->setVar('urlprovider', $this);
 811         $fragment->setVar('pager', $this->pager);
 812         return $fragment->parse('core/navigations/pagination.php');
 813     }
 814 
 815     /**
 816      * Gibt den Footer der Liste zurück.
 817      *
 818      * @return string
 819      */
 820     public function getFooter()
 821     {
 822         $s = '';
 823         /*
 824         $s .= '            <tr>'. "\n";
 825         $s .= '                <td colspan="'. count($this->getColumnNames()) .'"><input type="text" name="items" value="'. $this->getRowsPerPage() .'" maxlength="2" /><input type="submit" value="Anzeigen" /></td>'. "\n";
 826         $s .= '            </tr>'. "\n";
 827         */
 828         return $s;
 829     }
 830 
 831     /**
 832      * Gibt den Header der Liste zurück.
 833      *
 834      * @return string
 835      */
 836     public function getHeader()
 837     {
 838         $s = '';
 839         $s .= $this->getPagination();
 840 
 841         return $s;
 842     }
 843 
 844     // ---------------------- Generate Output
 845 
 846     public function replaceVariable($string, $varname)
 847     {
 848         return str_replace('###' . $varname . '###', rex_escape($this->getValue($varname)), $string);
 849     }
 850 
 851     /**
 852      * Ersetzt alle Variablen im Format ###&lt;Spaltenname&gt;###.
 853      *
 854      * @param string $value Zu durchsuchender String
 855      *
 856      * @return string
 857      */
 858     public function replaceVariables($value)
 859     {
 860         if (strpos($value, '###') === false) {
 861             return $value;
 862         }
 863 
 864         $columnNames = $this->getColumnNames();
 865 
 866         if (is_array($columnNames)) {
 867             foreach ($columnNames as $columnName) {
 868                 // Spalten, die mit addColumn eingefügt wurden
 869                 if (isset($this->customColumns[$columnName])) {
 870                     continue;
 871                 }
 872 
 873                 $value = $this->replaceVariable($value, $columnName);
 874             }
 875         }
 876         return $value;
 877     }
 878 
 879     public function isCustomFormat($format)
 880     {
 881         return is_array($format) && isset($format[0]) && $format[0] == 'custom';
 882     }
 883 
 884     /**
 885      * Formatiert einen übergebenen String anhand der rexFormatter Klasse.
 886      *
 887      * @param string $value  Zu formatierender String
 888      * @param array  $format mit den Formatierungsinformationen
 889      * @param bool   $escape Flag, Ob escapen von $value erlaubt ist
 890      * @param string $field
 891      *
 892      * @return string
 893      */
 894     public function formatValue($value, $format, $escape, $field = null)
 895     {
 896         if (is_array($format)) {
 897             // Callbackfunktion -> Parameterliste aufbauen
 898             if ($this->isCustomFormat($format)) {
 899                 $format[2] = isset($format[2]) ? $format[2] : [];
 900                 $format[1] = [$format[1], ['list' => $this, 'field' => $field, 'value' => $value, 'format' => $format[0], 'escape' => $escape, 'params' => $format[2]]];
 901             }
 902 
 903             $value = rex_formatter::format($value, $format[0], $format[1]);
 904         }
 905 
 906         // Nur escapen, wenn formatter aufgerufen wird, der kein html zurückgeben können soll
 907         if ($escape &&
 908             !$this->isCustomFormat($format) &&
 909             $format[0] != 'email' &&
 910             $format[0] != 'url'
 911         ) {
 912             $value = rex_escape($value);
 913         }
 914 
 915         return $value;
 916     }
 917 
 918     protected function _getAttributeString($array)
 919     {
 920         $s = '';
 921 
 922         foreach ($array as $name => $value) {
 923             $s .= ' ' . rex_escape($name, 'html_attr') . '="' . rex_escape($value) . '"';
 924         }
 925 
 926         return $s;
 927     }
 928 
 929     public function getColumnLink($columnName, $columnValue, $params = [])
 930     {
 931         return '<a href="' . $this->getParsedUrl(array_merge($this->getColumnParams($columnName), $params)) . '"' . $this->_getAttributeString($this->getLinkAttributes($columnName, [])) . '>' . $columnValue . '</a>';
 932     }
 933 
 934     public function getValue($colname)
 935     {
 936         return isset($this->customColumns[$colname]) ? $this->customColumns[$colname] : $this->sql->getValue($colname);
 937     }
 938 
 939     public function getArrayValue($colname)
 940     {
 941         return json_decode($this->getValue($colname), true);
 942     }
 943 
 944     /**
 945      * Erstellt den Tabellen Quellcode.
 946      *
 947      * @return string
 948      */
 949     public function get()
 950     {
 951         rex_extension::registerPoint(new rex_extension_point('REX_LIST_GET', $this, [], true));
 952 
 953         $s = "\n";
 954 
 955         // Form vars
 956         $this->addFormAttribute('action', $this->getUrl([], false));
 957         $this->addFormAttribute('method', 'post');
 958 
 959         // Table vars
 960         $caption = $this->getCaption();
 961         $tableColumnGroups = $this->getTableColumnGroups();
 962         $class = 'table';
 963         if (isset($this->tableAttributes['class'])) {
 964             $class .= ' ' . $this->tableAttributes['class'];
 965         }
 966         $this->addTableAttribute('class', $class);
 967 
 968         // Columns vars
 969         $columnFormates = [];
 970         $columnNames = [];
 971         foreach ($this->getColumnNames() as $columnName) {
 972             if (!in_array($columnName, $this->columnDisabled)) {
 973                 $columnNames[] = $columnName;
 974             }
 975         }
 976 
 977         // List vars
 978         $sortColumn = $this->getSortColumn();
 979         $sortType = $this->getSortType();
 980         $warning = $this->getWarning();
 981         $message = $this->getMessage();
 982         $nbRows = $this->getRows();
 983 
 984         $header = $this->getHeader();
 985         $footer = $this->getFooter();
 986 
 987         if ($warning != '') {
 988             $s .= rex_view::warning($warning) . "\n";
 989         } elseif ($message != '') {
 990             $s .= rex_view::info($message) . "\n";
 991         }
 992 
 993         if ($header != '') {
 994             $s .= $header . "\n";
 995         }
 996 
 997         $s .= '<form' . $this->_getAttributeString($this->getFormAttributes()) . '>' . "\n";
 998         $s .= '    <table' . $this->_getAttributeString($this->getTableAttributes()) . '>' . "\n";
 999 
1000         if ($caption != '') {
1001             $s .= '        <caption>' . rex_escape($caption) . '</caption>' . "\n";
1002         }
1003 
1004         if (count($tableColumnGroups) > 0) {
1005             foreach ($tableColumnGroups as $tableColumnGroup) {
1006                 $tableColumns = $tableColumnGroup['columns'];
1007                 unset($tableColumnGroup['columns']);
1008 
1009                 $s .= '        <colgroup' . $this->_getAttributeString($tableColumnGroup) . '>' . "\n";
1010 
1011                 foreach ($tableColumns as $tableColumn) {
1012                     $s .= '            <col' . $this->_getAttributeString($tableColumn) . ' />' . "\n";
1013                 }
1014 
1015                 $s .= '        </colgroup>' . "\n";
1016             }
1017         }
1018 
1019         $s .= '        <thead>' . "\n";
1020         $s .= '            <tr>' . "\n";
1021         foreach ($columnNames as $columnName) {
1022             $columnHead = $this->getColumnLabel($columnName);
1023             if ($this->hasColumnOption($columnName, REX_LIST_OPT_SORT)) {
1024                 if ($columnName == $sortColumn) {
1025                     $columnSortType = $sortType == 'desc' ? 'asc' : 'desc';
1026                 } else {
1027                     $columnSortType = $this->getColumnOption($columnName, REX_LIST_OPT_SORT_DIRECTION, 'asc');
1028                 }
1029                 $columnHead = '<a href="' . $this->getUrl([$this->pager->getCursorName() => $this->pager->getCursor(), 'sort' => $columnName, 'sorttype' => $columnSortType]) . '">' . $columnHead . '</a>';
1030             }
1031 
1032             $layout = $this->getColumnLayout($columnName);
1033             $s .= '        ' . str_replace('###VALUE###', $columnHead, $layout[0]) . "\n";
1034 
1035             // Formatierungen hier holen, da diese Schleife jede Spalte nur einmal durchläuft
1036             $columnFormates[$columnName] = $this->getColumnFormat($columnName);
1037         }
1038         $s .= '            </tr>' . "\n";
1039         $s .= '        </thead>' . "\n";
1040 
1041         if ($footer != '') {
1042             $s .= '        <tfoot>' . "\n";
1043             $s .= $footer;
1044             $s .= '        </tfoot>' . "\n";
1045         }
1046 
1047         if ($nbRows > 0) {
1048             $maxRows = $nbRows - $this->pager->getCursor();
1049 
1050             $s .= '        <tbody>' . "\n";
1051             for ($i = 0; $i < $this->pager->getRowsPerPage() && $i < $maxRows; ++$i) {
1052                 $s .= '            <tr>' . "\n";
1053                 foreach ($columnNames as $columnName) {
1054                     $columnValue = $this->formatValue($this->getValue($columnName), $columnFormates[$columnName], !isset($this->customColumns[$columnName]), $columnName);
1055 
1056                     if (!$this->isCustomFormat($columnFormates[$columnName]) && $this->hasColumnParams($columnName)) {
1057                         $columnValue = $this->getColumnLink($columnName, $columnValue);
1058                     }
1059 
1060                     $columnHead = $this->getColumnLabel($columnName);
1061                     $layout = $this->getColumnLayout($columnName);
1062                     $columnValue = str_replace(['###VALUE###', '###LABEL###'], [$columnValue, $columnHead], $layout[1]);
1063                     $columnValue = $this->replaceVariables($columnValue);
1064                     $s .= '        ' . $columnValue . "\n";
1065                 }
1066                 $s .= '            </tr>' . "\n";
1067 
1068                 $this->sql->next();
1069             }
1070             $s .= '        </tbody>' . "\n";
1071         } else {
1072             $s .= '<tr class="table-no-results"><td colspan="' . count($columnNames) . '">' . $this->getNoRowsMessage() . '</td></tr>';
1073         }
1074 
1075         $s .= '    </table>' . "\n";
1076         $s .= '</form>' . "\n";
1077 
1078         return $s;
1079     }
1080 
1081     public function show()
1082     {
1083         echo $this->get();
1084     }
1085 }
1086