PluginProbe
TablePress – Tables in WordPress made easy / 3.0
TablePress – Tables in WordPress made easy v3.0
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 +104 -59 2.43.0 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
@@ -655,12 +701,12 @@
655 701 unset( $table_html ); // Unset the potentially large variable to free up memory.
656 702
657 703 // name/description below table (HTML already generated above).
658 704 if ( $this->render_options['print_name'] && 'below' === $this->render_options['print_name_position'] ) {
659 - $output .= $print_name_html;
705 + $output .= $print_name_html; // @phpstan-ignore variable.undefined (The variable is set above.)
660 706 }
661 707 if ( $this->render_options['print_description'] && 'below' === $this->render_options['print_description_position'] ) {
662 - $output .= $print_description_html;
708 + $output .= $print_description_html; // @phpstan-ignore variable.undefined (The variable is set above.)
663 709 }
664 710
665 711 /**
666 712 * Filters the generated HTML code for the table and HTML elements around it.
@@ -691,10 +737,10 @@
691 737
692 738 if ( $this->span_trigger['rowspan'] === $cell_content ) { // There will be a rowspan.
693 739 if ( ! (
694 740 ( 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.
741 + || ( $this->render_options['table_head'] === $row_idx ) // No rowspan into table head.
742 + || ( $this->last_row_idx - $this->render_options['table_foot'] + 1 === $row_idx ) // No rowspan out of table foot.
697 743 ) ) {
698 744 // Increase counter for rowspan in this column.
699 745 ++$this->rowspan[ $col_idx ];
700 746 // Reset counter for colspan in this row, combined col- and rowspan might be happening.
@@ -704,9 +750,10 @@
704 750 // Invalid rowspan, so we set cell content from #rowspan# to empty.
705 751 $cell_content = '';
706 752 } elseif ( $this->span_trigger['colspan'] === $cell_content ) { // There will be a colspan.
707 753 if ( ! (
708 - ( 0 === $col_idx ) // No colspan inside first column.
754 + ( ( 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.
755 + || ( 0 === $col_idx ) // No colspan inside first column.
709 756 || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
710 757 ) ) {
711 758 // Increase counter for colspan in this row.
712 759 ++$this->colspan[ $row_idx ];
@@ -718,10 +765,10 @@
718 765 $cell_content = '';
719 766 } elseif ( $this->span_trigger['span'] === $cell_content ) { // There will be a combined col- and rowspan.
720 767 if ( ! (
721 768 ( 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.
769 + || ( $this->render_options['table_head'] === $row_idx ) // No rowspan into table head.
770 + || ( $this->last_row_idx - $this->render_options['table_foot'] + 1 === $row_idx ) // No rowspan out of table foot.
724 771 ) && ! (
725 772 ( 0 === $col_idx ) // No colspan inside first column.
726 773 || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
727 774 ) ) {
@@ -736,11 +783,19 @@
736 783
737 784 // "colspan" and "rowspan" attributes.
738 785 if ( $this->colspan[ $row_idx ] > 1 ) { // We have colspaned cells.
739 786 $tag_attributes['colspan'] = (string) $this->colspan[ $row_idx ];
787 + 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 ) {
788 + // Set flag that there are connected cells in the tbody.
789 + $this->tbody_has_connected_cells = true;
790 + }
740 791 }
741 792 if ( $this->rowspan[ $col_idx ] > 1 ) { // We have rowspaned cells.
742 793 $tag_attributes['rowspan'] = (string) $this->rowspan[ $col_idx ];
794 + 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 ) {
795 + // Set flag that there are connected cells in the tbody.
796 + $this->tbody_has_connected_cells = true;
797 + }
743 798 }
744 799
745 800 // "class" attribute.
746 801 $cell_class = 'column-' . ( $col_idx + 1 );
@@ -796,11 +851,8 @@
796 851 $tr_attributes = array();
797 852
798 853 // "class" attribute.
799 854 $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 855 /**
804 856 * Filters the CSS classes that are given to a row (HTML tr element) of a table.
805 857 *
806 858 * @since 1.0.0
@@ -852,10 +904,8 @@
852 904
853 905 /**
854 906 * Possibly replace certain HTML entities and replace line breaks with HTML.
855 907 *
856 - * @todo Find a better solution than this function, e.g. something like wpautop().
857 - *
858 908 * @since 1.0.0
859 909 *
860 910 * @param string $text The string to process.
861 911 * @return string Processed string for output.
@@ -898,8 +948,9 @@
898 948 'cellspacing' => false,
899 949 'column_widths' => '',
900 950 'convert_line_breaks' => true,
901 951 'datatables_custom_commands' => null,
952 + 'datatables_datetime' => '',
902 953 'datatables_filter' => null,
903 954 'datatables_info' => null,
904 955 'datatables_lengthchange' => null,
905 956 'datatables_locale' => get_locale(),
@@ -940,30 +991,24 @@
940 991 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
941 992 $default_css_minified = $tablepress_css->load_default_css_from_file( $is_rtl );
942 993 if ( false === $default_css_minified ) {
943 994 $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 995 }
951 996
952 997 $rtl_direction = $is_rtl ? "\ndirection: rtl;" : '';
953 998
954 999 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;
1000 + <style>
1001 + /* iframe */
1002 + body {
1003 + margin: 10px;
1004 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;{$rtl_direction}
1005 + }
1006 + p {
1007 + font-size: 13px;
1008 + }
1009 + {$default_css_minified}
1010 + </style>
1011 + CSS;
967 1012 }
968 1013
969 1014 } // class TablePress_Render