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 +129 -68 2.23.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 *
@@ -544,9 +565,9 @@
544 565 * @param string $table_id The current table ID.
545 566 * @param int $col_idx The number of the column.
546 567 */
547 568 $attributes = apply_filters( 'tablepress_colgroup_tag_attributes', $attributes, $this->table['id'], $col_idx + 1 );
548 - $colgroup .= "\t<col{$attributes}/>\n";
569 + $colgroup .= "\t<col{$attributes} />\n";
549 570 }
550 571 }
551 572 if ( ! empty( $colgroup ) ) {
552 573 $colgroup = "<colgroup>\n{$colgroup}</colgroup>\n";
@@ -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.
@@ -635,28 +682,42 @@
635 682 */
636 683 $table_attributes = apply_filters( 'tablepress_table_tag_attributes', $table_attributes, $this->table, $this->render_options );
637 684 $table_attributes = $this->_attributes_array_to_string( $table_attributes );
638 685
639 - $output .= "\n<table{$table_attributes}>\n";
640 - $output .= $caption . $colgroup . $thead . $tbody . $tfoot;
641 - $output .= "</table>\n";
686 + $table_html = "<table{$table_attributes}>\n";
687 + $table_html .= $caption . $colgroup . $thead . $tbody . $tfoot;
688 + $table_html .= '</table>';
642 689
690 + /**
691 + * Filters the generated HTML code for the table, without HTML elements around it.
692 + *
693 + * @since 2.4.0
694 + *
695 + * @param string $output The generated HTML for the table, without HTML elements around it.
696 + * @param array<string, mixed> $table The current table.
697 + * @param array<string, mixed> $render_options The render options for the table, without HTML elements around it.
698 + */
699 + $table_html = apply_filters( 'tablepress_table_html', $table_html, $this->table, $this->render_options );
700 +
701 + $output .= "\n{$table_html}\n";
702 + unset( $table_html ); // Unset the potentially large variable to free up memory.
703 +
643 704 // name/description below table (HTML already generated above).
644 705 if ( $this->render_options['print_name'] && 'below' === $this->render_options['print_name_position'] ) {
645 - $output .= $print_name_html;
706 + $output .= $print_name_html; // @phpstan-ignore variable.undefined (The variable is set above.)
646 707 }
647 708 if ( $this->render_options['print_description'] && 'below' === $this->render_options['print_description_position'] ) {
648 - $output .= $print_description_html;
709 + $output .= $print_description_html; // @phpstan-ignore variable.undefined (The variable is set above.)
649 710 }
650 711
651 712 /**
652 - * Filters the generated HTML code for table.
713 + * Filters the generated HTML code for the table and HTML elements around it.
653 714 *
654 715 * @since 1.0.0
655 716 *
656 - * @param string $output The generated HTML for the table.
717 + * @param string $output The generated HTML for the table and HTML elements around it.
657 718 * @param array<string, mixed> $table The current table.
658 - * @param array<string, mixed> $render_options The render options for the table.
719 + * @param array<string, mixed> $render_options The render options for the table and HTML elements around it.
659 720 */
660 721 $this->output = apply_filters( 'tablepress_table_output', $output, $this->table, $this->render_options );
661 722 }
662 723
@@ -677,10 +738,10 @@
677 738
678 739 if ( $this->span_trigger['rowspan'] === $cell_content ) { // There will be a rowspan.
679 740 if ( ! (
680 741 ( 0 === $row_idx ) // No rowspan inside first row.
681 - || ( 1 === $row_idx && $this->render_options['table_head'] ) // No rowspan into table head.
682 - || ( $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.
683 744 ) ) {
684 745 // Increase counter for rowspan in this column.
685 746 ++$this->rowspan[ $col_idx ];
686 747 // Reset counter for colspan in this row, combined col- and rowspan might be happening.
@@ -690,9 +751,10 @@
690 751 // Invalid rowspan, so we set cell content from #rowspan# to empty.
691 752 $cell_content = '';
692 753 } elseif ( $this->span_trigger['colspan'] === $cell_content ) { // There will be a colspan.
693 754 if ( ! (
694 - ( 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.
695 757 || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
696 758 ) ) {
697 759 // Increase counter for colspan in this row.
698 760 ++$this->colspan[ $row_idx ];
@@ -704,10 +766,10 @@
704 766 $cell_content = '';
705 767 } elseif ( $this->span_trigger['span'] === $cell_content ) { // There will be a combined col- and rowspan.
706 768 if ( ! (
707 769 ( 0 === $row_idx ) // No rowspan inside first row.
708 - || ( 1 === $row_idx && $this->render_options['table_head'] ) // No rowspan into table head.
709 - || ( $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.
710 772 ) && ! (
711 773 ( 0 === $col_idx ) // No colspan inside first column.
712 774 || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
713 775 ) ) {
@@ -722,11 +784,19 @@
722 784
723 785 // "colspan" and "rowspan" attributes.
724 786 if ( $this->colspan[ $row_idx ] > 1 ) { // We have colspaned cells.
725 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 + }
726 792 }
727 793 if ( $this->rowspan[ $col_idx ] > 1 ) { // We have rowspaned cells.
728 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 + }
729 799 }
730 800
731 801 // "class" attribute.
732 802 $cell_class = 'column-' . ( $col_idx + 1 );
@@ -782,11 +852,8 @@
782 852 $tr_attributes = array();
783 853
784 854 // "class" attribute.
785 855 $row_classes = 'row-' . ( $row_idx + 1 );
786 - if ( $this->render_options['alternating_row_colors'] ) {
787 - $row_classes .= ( 1 === ( $row_idx % 2 ) ) ? ' even' : ' odd';
788 - }
789 856 /**
790 857 * Filters the CSS classes that are given to a row (HTML tr element) of a table.
791 858 *
792 859 * @since 1.0.0
@@ -838,10 +905,8 @@
838 905
839 906 /**
840 907 * Possibly replace certain HTML entities and replace line breaks with HTML.
841 908 *
842 - * @TODO: Find a better solution than this function, e.g. something like wpautop().
843 - *
844 909 * @since 1.0.0
845 910 *
846 911 * @param string $text The string to process.
847 912 * @return string Processed string for output.
@@ -876,8 +941,9 @@
876 941 public function get_default_render_options(): array {
877 942 // Attention: Array keys have to be lowercase, otherwise they won't match the Shortcode attributes, which will be passed in lowercase by WP.
878 943 return array(
879 944 'alternating_row_colors' => null,
945 + 'block_preview' => false,
880 946 'border' => false,
881 947 'cache_table_output' => true,
882 948 'cellpadding' => false,
883 949 'cellspacing' => false,
@@ -883,8 +949,9 @@
883 949 'cellspacing' => false,
884 950 'column_widths' => '',
885 951 'convert_line_breaks' => true,
886 952 'datatables_custom_commands' => null,
953 + 'datatables_datetime' => '',
887 954 'datatables_filter' => null,
888 955 'datatables_info' => null,
889 956 'datatables_lengthchange' => null,
890 957 'datatables_locale' => get_locale(),
@@ -925,30 +992,24 @@
925 992 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
926 993 $default_css_minified = $tablepress_css->load_default_css_from_file( $is_rtl );
927 994 if ( false === $default_css_minified ) {
928 995 $default_css_minified = '';
929 - } else {
930 - // Change relative URLs to web font files to absolute URLs, as combining the CSS files and saving to another directory breaks the relative URLs.
931 - $absolute_path = plugins_url( 'css/build/tablepress.', TABLEPRESS__FILE__ );
932 - // Make the absolute URL protocol-relative to prevent mixed content warnings.
933 - $absolute_path = str_replace( array( 'http:', 'https:' ), '', $absolute_path );
934 - $default_css_minified = str_replace( 'url(tablepress.', 'url(' . $absolute_path, $default_css_minified );
935 996 }
936 997
937 998 $rtl_direction = $is_rtl ? "\ndirection: rtl;" : '';
938 999
939 1000 return <<<CSS
940 -<style>
941 -/* iframe */
942 -body {
943 - margin: 10px;
944 - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;{$rtl_direction}
945 -}
946 -p {
947 - font-size: 13px;
948 -}
949 -{$default_css_minified}
950 -</style>
951 -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;
952 1013 }
953 1014
954 1015 } // class TablePress_Render