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 +126 -66 2.33.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 *
@@ -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
@@ -635,28 +681,42 @@
635 681 */
636 682 $table_attributes = apply_filters( 'tablepress_table_tag_attributes', $table_attributes, $this->table, $this->render_options );
637 683 $table_attributes = $this->_attributes_array_to_string( $table_attributes );
638 684
639 - $output .= "\n<table{$table_attributes}>\n";
640 - $output .= $caption . $colgroup . $thead . $tbody . $tfoot;
641 - $output .= "</table>\n";
685 + $table_html = "<table{$table_attributes}>\n";
686 + $table_html .= $caption . $colgroup . $thead . $tbody . $tfoot;
687 + $table_html .= '</table>';
642 688
689 + /**
690 + * Filters the generated HTML code for the table, without HTML elements around it.
691 + *
692 + * @since 2.4.0
693 + *
694 + * @param string $output The generated HTML for the table, without HTML elements around it.
695 + * @param array<string, mixed> $table The current table.
696 + * @param array<string, mixed> $render_options The render options for the table, without HTML elements around it.
697 + */
698 + $table_html = apply_filters( 'tablepress_table_html', $table_html, $this->table, $this->render_options );
699 +
700 + $output .= "\n{$table_html}\n";
701 + unset( $table_html ); // Unset the potentially large variable to free up memory.
702 +
643 703 // name/description below table (HTML already generated above).
644 704 if ( $this->render_options['print_name'] && 'below' === $this->render_options['print_name_position'] ) {
645 - $output .= $print_name_html;
705 + $output .= $print_name_html; // @phpstan-ignore variable.undefined (The variable is set above.)
646 706 }
647 707 if ( $this->render_options['print_description'] && 'below' === $this->render_options['print_description_position'] ) {
648 - $output .= $print_description_html;
708 + $output .= $print_description_html; // @phpstan-ignore variable.undefined (The variable is set above.)
649 709 }
650 710
651 711 /**
652 - * Filters the generated HTML code for table.
712 + * Filters the generated HTML code for the table and HTML elements around it.
653 713 *
654 714 * @since 1.0.0
655 715 *
656 - * @param string $output The generated HTML for the table.
716 + * @param string $output The generated HTML for the table and HTML elements around it.
657 717 * @param array<string, mixed> $table The current table.
658 - * @param array<string, mixed> $render_options The render options for the table.
718 + * @param array<string, mixed> $render_options The render options for the table and HTML elements around it.
659 719 */
660 720 $this->output = apply_filters( 'tablepress_table_output', $output, $this->table, $this->render_options );
661 721 }
662 722
@@ -677,10 +737,10 @@
677 737
678 738 if ( $this->span_trigger['rowspan'] === $cell_content ) { // There will be a rowspan.
679 739 if ( ! (
680 740 ( 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.
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.
683 743 ) ) {
684 744 // Increase counter for rowspan in this column.
685 745 ++$this->rowspan[ $col_idx ];
686 746 // Reset counter for colspan in this row, combined col- and rowspan might be happening.
@@ -690,9 +750,10 @@
690 750 // Invalid rowspan, so we set cell content from #rowspan# to empty.
691 751 $cell_content = '';
692 752 } elseif ( $this->span_trigger['colspan'] === $cell_content ) { // There will be a colspan.
693 753 if ( ! (
694 - ( 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.
695 756 || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
696 757 ) ) {
697 758 // Increase counter for colspan in this row.
698 759 ++$this->colspan[ $row_idx ];
@@ -704,10 +765,10 @@
704 765 $cell_content = '';
705 766 } elseif ( $this->span_trigger['span'] === $cell_content ) { // There will be a combined col- and rowspan.
706 767 if ( ! (
707 768 ( 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.
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.
710 771 ) && ! (
711 772 ( 0 === $col_idx ) // No colspan inside first column.
712 773 || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
713 774 ) ) {
@@ -722,11 +783,19 @@
722 783
723 784 // "colspan" and "rowspan" attributes.
724 785 if ( $this->colspan[ $row_idx ] > 1 ) { // We have colspaned cells.
725 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 + }
726 791 }
727 792 if ( $this->rowspan[ $col_idx ] > 1 ) { // We have rowspaned cells.
728 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 + }
729 798 }
730 799
731 800 // "class" attribute.
732 801 $cell_class = 'column-' . ( $col_idx + 1 );
@@ -782,11 +851,8 @@
782 851 $tr_attributes = array();
783 852
784 853 // "class" attribute.
785 854 $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 855 /**
790 856 * Filters the CSS classes that are given to a row (HTML tr element) of a table.
791 857 *
792 858 * @since 1.0.0
@@ -838,10 +904,8 @@
838 904
839 905 /**
840 906 * Possibly replace certain HTML entities and replace line breaks with HTML.
841 907 *
842 - * @todo Find a better solution than this function, e.g. something like wpautop().
843 - *
844 908 * @since 1.0.0
845 909 *
846 910 * @param string $text The string to process.
847 911 * @return string Processed string for output.
@@ -876,8 +940,9 @@
876 940 public function get_default_render_options(): array {
877 941 // Attention: Array keys have to be lowercase, otherwise they won't match the Shortcode attributes, which will be passed in lowercase by WP.
878 942 return array(
879 943 'alternating_row_colors' => null,
944 + 'block_preview' => false,
880 945 'border' => false,
881 946 'cache_table_output' => true,
882 947 'cellpadding' => false,
883 948 'cellspacing' => false,
@@ -883,8 +948,9 @@
883 948 'cellspacing' => false,
884 949 'column_widths' => '',
885 950 'convert_line_breaks' => true,
886 951 'datatables_custom_commands' => null,
952 + 'datatables_datetime' => '',
887 953 'datatables_filter' => null,
888 954 'datatables_info' => null,
889 955 'datatables_lengthchange' => null,
890 956 'datatables_locale' => get_locale(),
@@ -925,30 +991,24 @@
925 991 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
926 992 $default_css_minified = $tablepress_css->load_default_css_from_file( $is_rtl );
927 993 if ( false === $default_css_minified ) {
928 994 $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 995 }
936 996
937 997 $rtl_direction = $is_rtl ? "\ndirection: rtl;" : '';
938 998
939 999 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;
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;
952 1012 }
953 1013
954 1014 } // class TablePress_Render