PluginProbe
TablePress – Tables in WordPress made easy / 3.0.4
TablePress – Tables in WordPress made easy v3.0.4
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
← All changes | classes/class-render.php +107 -61 2.43.0.4 View file →
@@ -26,9 +26,9 @@
26 26 *
27 27 * @since 1.0.0
28 28 * @var array<string, mixed>
29 29 */
30 - protected $table = array();
30 + protected array $table = array();
31 31
32 32 /**
33 33 * Table options that influence the output result.
34 34 *
@@ -34,9 +34,9 @@
34 34 *
35 35 * @since 1.0.0
36 36 * @var array<string, mixed>
37 37 */
38 - protected $render_options = array();
38 + protected array $render_options = array();
39 39
40 40 /**
41 41 * Rendered HTML code of the table or PHP array.
42 42 *
@@ -50,9 +50,9 @@
50 50 *
51 51 * @since 1.0.0
52 52 * @var array<string, string>
53 53 */
54 - protected $span_trigger = array(
54 + protected array $span_trigger = array(
55 55 'colspan' => '#colspan#',
56 56 'rowspan' => '#rowspan#',
57 57 'span' => '#span#',
58 58 );
@@ -62,9 +62,9 @@
62 62 *
63 63 * @since 1.0.0
64 64 * @var int[]
65 65 */
66 - protected $rowspan = array();
66 + protected array $rowspan = array();
67 67
68 68 /**
69 69 * Buffer to store the counts of colspan per row, initialized in _render_table().
70 70 *
@@ -70,25 +70,30 @@
70 70 *
71 71 * @since 1.0.0
72 72 * @var int[]
73 73 */
74 - protected $colspan = array();
74 + protected array $colspan = array();
75 75
76 76 /**
77 + * Whether the table has connected cells (colspan or rowspan), set in _render_table().
78 + *
79 + * @since 3.0.0
80 + */
81 + protected bool $tbody_has_connected_cells = false;
82 +
83 + /**
77 84 * Index of the last row of the visible data in the table, set in _render_table().
78 85 *
79 86 * @since 1.0.0
80 - * @var int
81 87 */
82 - protected $last_row_idx;
88 + protected int $last_row_idx;
83 89
84 90 /**
85 91 * Index of the last column of the visible data in the table, set in _render_table().
86 92 *
87 93 * @since 1.0.0
88 - * @var int
89 94 */
90 - protected $last_column_idx;
95 + protected int $last_column_idx;
91 96
92 97 /**
93 98 * Class constructor.
94 99 *
@@ -461,30 +466,46 @@
461 466 if ( $this->render_options['print_description'] && 'above' === $this->render_options['print_description_position'] ) {
462 467 $output .= $print_description_html;
463 468 }
464 469
465 - $thead = '';
466 - $tfoot = '';
470 + $thead = array();
471 + $tfoot = array();
467 472 $tbody = array();
468 473
469 474 $this->last_row_idx = $num_rows - 1;
470 475 $this->last_column_idx = $num_columns - 1;
476 +
471 477 // Loop through rows in reversed order, to search for rowspan trigger keyword.
472 - for ( $row_idx = $this->last_row_idx; $row_idx >= 0; $row_idx-- ) {
473 - // Last row, need to check for footer (but only if at least two rows).
474 - if ( $this->last_row_idx === $row_idx && $this->render_options['table_foot'] && $num_rows > 1 ) {
475 - $tfoot = $this->_render_row( $row_idx, 'th' );
476 - continue;
478 + $row_idx = $this->last_row_idx;
479 +
480 + // Render the table footer rows, if there is at least one extra row.
481 + if ( $this->render_options['table_foot'] > 0 && $num_rows >= $this->render_options['table_head'] + $this->render_options['table_foot'] ) { // @phpstan-ignore greaterOrEqual.invalid (`table_head` and `table_foot` are integers.)
482 + $last_tbody_idx = $this->last_row_idx - $this->render_options['table_foot'];
483 + while ( $row_idx > $last_tbody_idx ) {
484 + $tfoot[] = $this->_render_row( $row_idx, 'th' );
485 + --$row_idx;
477 486 }
478 - // First row, need to check for head (but only if at least two rows).
479 - if ( 0 === $row_idx && $this->render_options['table_head'] && $num_rows > 1 ) {
480 - $thead = $this->_render_row( $row_idx, 'th' );
481 - continue;
482 - }
483 - // Neither first nor last row (with respective head/foot enabled), so render as body row.
487 + // Reverse rows because we looped through the rows in reverse order.
488 + $tfoot = array_reverse( $tfoot );
489 + }
490 +
491 + // Render the table body rows.
492 + $last_thead_idx = $this->render_options['table_head'] - 1;
493 + while ( $row_idx > $last_thead_idx ) {
484 494 $tbody[] = $this->_render_row( $row_idx, 'td' );
495 + --$row_idx;
485 496 }
497 + // Reverse rows because we looped through the rows in reverse order.
498 + $tbody = array_reverse( $tbody );
486 499
500 + // Render the table header rows, if rows are left.
501 + while ( $row_idx > -1 ) {
502 + $thead[] = $this->_render_row( $row_idx, 'th' );
503 + --$row_idx;
504 + }
505 + // Reverse rows because we looped through the rows in reverse order.
506 + $thead = array_reverse( $thead );
507 +
487 508 // <caption> tag.
488 509 /**
489 510 * Filters the content for the HTML caption element of the table.
490 511 *
@@ -551,18 +572,36 @@
551 572 if ( ! empty( $colgroup ) ) {
552 573 $colgroup = "<colgroup>\n{$colgroup}</colgroup>\n";
553 574 }
554 575
555 - // <thead>, <tfoot>, and <tbody> tags.
576 + /*
577 + * <thead>, <tfoot>, and <tbody> tags.
578 + */
579 +
556 580 if ( ! empty( $thead ) ) {
557 - $thead = "<thead>\n{$thead}</thead>\n";
581 + $thead = "<thead>\n" . implode( '', $thead ) . "</thead>\n";
582 + } else {
583 + $thead = '';
558 584 }
585 +
559 586 if ( ! empty( $tfoot ) ) {
560 - $tfoot = "<tfoot>\n{$tfoot}</tfoot>\n";
587 + $tfoot = "<tfoot>\n" . implode( '', $tfoot ) . "</tfoot>\n";
588 + } else {
589 + $tfoot = '';
561 590 }
562 - $tbody_class = ( $this->render_options['row_hover'] ) ? ' class="row-hover"' : '';
563 - // Reverse rows because we looped through the rows in reverse order.
564 - $tbody = array_reverse( $tbody );
591 +
592 + $tbody_classes = array();
593 + if ( $this->render_options['alternating_row_colors'] ) {
594 + $tbody_classes[] = 'row-striping';
595 + }
596 + if ( $this->render_options['row_hover'] ) {
597 + $tbody_classes[] = 'row-hover';
598 + }
599 + $tbody_class = implode( ' ', $tbody_classes );
600 + if ( '' !== $tbody_class ) {
601 + $tbody_class = ' class="' . esc_attr( $tbody_class ) . '"';
602 + }
603 +
565 604 $tbody = "<tbody{$tbody_class}>\n" . implode( '', $tbody ) . "</tbody>\n";
566 605
567 606 // Attributes for the table (HTML table element).
568 607 $table_attributes = array();
@@ -572,9 +611,16 @@
572 611 $table_attributes['id'] = $this->render_options['html_id'];
573 612 }
574 613
575 614 // "class" attribute.
576 - $css_classes = array( 'tablepress', "tablepress-id-{$this->table['id']}", $this->render_options['extra_css_classes'] );
615 + $css_classes = array(
616 + 'tablepress',
617 + "tablepress-id-{$this->table['id']}",
618 + $this->render_options['extra_css_classes'],
619 + );
620 + if ( $this->tbody_has_connected_cells ) {
621 + $css_classes[] = 'tbody-has-connected-cells';
622 + }
577 623 /**
578 624 * Filters the CSS classes that are given to the HTML table element.
579 625 *
580 626 * @since 1.0.0
@@ -586,10 +632,11 @@
586 632 // $css_classes might contain several classes in one array entry.
587 633 $css_classes = explode( ' ', implode( ' ', $css_classes ) );
588 634 $css_classes = array_map( array( 'TablePress', 'sanitize_css_class' ), $css_classes );
589 635 $css_classes = array_unique( $css_classes );
590 - $css_classes = trim( implode( ' ', $css_classes ) );
591 - if ( ! empty( $css_classes ) ) {
636 + $css_classes = array_filter( $css_classes ); // Remove empty entries.
637 + $css_classes = implode( ' ', $css_classes );
638 + if ( '' !== $css_classes ) {
592 639 $table_attributes['class'] = $css_classes;
593 640 }
594 641
595 642 // ARIA label attributes.
@@ -655,12 +702,12 @@
655 702 unset( $table_html ); // Unset the potentially large variable to free up memory.
656 703
657 704 // name/description below table (HTML already generated above).
658 705 if ( $this->render_options['print_name'] && 'below' === $this->render_options['print_name_position'] ) {
659 - $output .= $print_name_html;
706 + $output .= $print_name_html; // @phpstan-ignore variable.undefined (The variable is set above.)
660 707 }
661 708 if ( $this->render_options['print_description'] && 'below' === $this->render_options['print_description_position'] ) {
662 - $output .= $print_description_html;
709 + $output .= $print_description_html; // @phpstan-ignore variable.undefined (The variable is set above.)
663 710 }
664 711
665 712 /**
666 713 * Filters the generated HTML code for the table and HTML elements around it.
@@ -691,10 +738,10 @@
691 738
692 739 if ( $this->span_trigger['rowspan'] === $cell_content ) { // There will be a rowspan.
693 740 if ( ! (
694 741 ( 0 === $row_idx ) // No rowspan inside first row.
695 - || ( 1 === $row_idx && $this->render_options['table_head'] ) // No rowspan into table head.
696 - || ( $this->last_row_idx === $row_idx && $this->render_options['table_foot'] ) // No rowspan out of table foot.
742 + || ( $this->render_options['table_head'] === $row_idx ) // No rowspan into table head.
743 + || ( $this->last_row_idx - $this->render_options['table_foot'] + 1 === $row_idx ) // No rowspan out of table foot.
697 744 ) ) {
698 745 // Increase counter for rowspan in this column.
699 746 ++$this->rowspan[ $col_idx ];
700 747 // Reset counter for colspan in this row, combined col- and rowspan might be happening.
@@ -704,9 +751,10 @@
704 751 // Invalid rowspan, so we set cell content from #rowspan# to empty.
705 752 $cell_content = '';
706 753 } elseif ( $this->span_trigger['colspan'] === $cell_content ) { // There will be a colspan.
707 754 if ( ! (
708 - ( 0 === $col_idx ) // No colspan inside first column.
755 + ( ( 0 === $row_idx ) && 1 === $this->render_options['table_head'] && $this->render_options['use_datatables'] ) // Don't allow colspan inside a single row table head, as DataTables seems to have a bug here.
756 + || ( 0 === $col_idx ) // No colspan inside first column.
709 757 || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
710 758 ) ) {
711 759 // Increase counter for colspan in this row.
712 760 ++$this->colspan[ $row_idx ];
@@ -718,10 +766,10 @@
718 766 $cell_content = '';
719 767 } elseif ( $this->span_trigger['span'] === $cell_content ) { // There will be a combined col- and rowspan.
720 768 if ( ! (
721 769 ( 0 === $row_idx ) // No rowspan inside first row.
722 - || ( 1 === $row_idx && $this->render_options['table_head'] ) // No rowspan into table head.
723 - || ( $this->last_row_idx === $row_idx && $this->render_options['table_foot'] ) // No rowspan out of table foot.
770 + || ( $this->render_options['table_head'] === $row_idx ) // No rowspan into table head.
771 + || ( $this->last_row_idx - $this->render_options['table_foot'] + 1 === $row_idx ) // No rowspan out of table foot.
724 772 ) && ! (
725 773 ( 0 === $col_idx ) // No colspan inside first column.
726 774 || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
727 775 ) ) {
@@ -736,11 +784,19 @@
736 784
737 785 // "colspan" and "rowspan" attributes.
738 786 if ( $this->colspan[ $row_idx ] > 1 ) { // We have colspaned cells.
739 787 $tag_attributes['colspan'] = (string) $this->colspan[ $row_idx ];
788 + if ( ! $this->tbody_has_connected_cells && $row_idx > $this->render_options['table_head'] - 1 && $row_idx < $this->last_row_idx - $this->render_options['table_foot'] + 1 ) {
789 + // Set flag that there are connected cells in the tbody.
790 + $this->tbody_has_connected_cells = true;
791 + }
740 792 }
741 793 if ( $this->rowspan[ $col_idx ] > 1 ) { // We have rowspaned cells.
742 794 $tag_attributes['rowspan'] = (string) $this->rowspan[ $col_idx ];
795 + if ( ! $this->tbody_has_connected_cells && $row_idx > $this->render_options['table_head'] - 1 && $row_idx < $this->last_row_idx - $this->render_options['table_foot'] + 1 ) {
796 + // Set flag that there are connected cells in the tbody.
797 + $this->tbody_has_connected_cells = true;
798 + }
743 799 }
744 800
745 801 // "class" attribute.
746 802 $cell_class = 'column-' . ( $col_idx + 1 );
@@ -796,11 +852,8 @@
796 852 $tr_attributes = array();
797 853
798 854 // "class" attribute.
799 855 $row_classes = 'row-' . ( $row_idx + 1 );
800 - if ( $this->render_options['alternating_row_colors'] ) {
801 - $row_classes .= ( 1 === ( $row_idx % 2 ) ) ? ' even' : ' odd';
802 - }
803 856 /**
804 857 * Filters the CSS classes that are given to a row (HTML tr element) of a table.
805 858 *
806 859 * @since 1.0.0
@@ -852,10 +905,8 @@
852 905
853 906 /**
854 907 * Possibly replace certain HTML entities and replace line breaks with HTML.
855 908 *
856 - * @todo Find a better solution than this function, e.g. something like wpautop().
857 - *
858 909 * @since 1.0.0
859 910 *
860 911 * @param string $text The string to process.
861 912 * @return string Processed string for output.
@@ -898,8 +949,9 @@
898 949 'cellspacing' => false,
899 950 'column_widths' => '',
900 951 'convert_line_breaks' => true,
901 952 'datatables_custom_commands' => null,
953 + 'datatables_datetime' => '',
902 954 'datatables_filter' => null,
903 955 'datatables_info' => null,
904 956 'datatables_lengthchange' => null,
905 957 'datatables_locale' => get_locale(),
@@ -940,30 +992,24 @@
940 992 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
941 993 $default_css_minified = $tablepress_css->load_default_css_from_file( $is_rtl );
942 994 if ( false === $default_css_minified ) {
943 995 $default_css_minified = '';
944 - } else {
945 - // Change relative URLs to web font files to absolute URLs, as combining the CSS files and saving to another directory breaks the relative URLs.
946 - $absolute_path = plugins_url( 'css/build/tablepress.', TABLEPRESS__FILE__ );
947 - // Make the absolute URL protocol-relative to prevent mixed content warnings.
948 - $absolute_path = str_replace( array( 'http:', 'https:' ), '', $absolute_path );
949 - $default_css_minified = str_replace( 'url(tablepress.', 'url(' . $absolute_path, $default_css_minified );
950 996 }
951 997
952 998 $rtl_direction = $is_rtl ? "\ndirection: rtl;" : '';
953 999
954 1000 return <<<CSS
955 -<style>
956 -/* iframe */
957 -body {
958 - margin: 10px;
959 - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;{$rtl_direction}
960 -}
961 -p {
962 - font-size: 13px;
963 -}
964 -{$default_css_minified}
965 -</style>
966 -CSS;
1001 + <style>
1002 + /* iframe */
1003 + body {
1004 + margin: 10px;
1005 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;{$rtl_direction}
1006 + }
1007 + p {
1008 + font-size: 13px;
1009 + }
1010 + {$default_css_minified}
1011 + </style>
1012 + CSS;
967 1013 }
968 1014
969 1015 } // class TablePress_Render