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 +201 -140 2.1.83.0.4 View file →
@@ -24,25 +24,25 @@
24 24 /**
25 25 * Table data that is rendered.
26 26 *
27 27 * @since 1.0.0
28 - * @var array
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 *
35 35 * @since 1.0.0
36 - * @var array
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 *
43 43 * @since 1.0.0
44 - * @var string|array
44 + * @var string|array<int, array<int, string>>
45 45 */
46 46 protected $output;
47 47
48 48 /**
@@ -48,11 +48,11 @@
48 48 /**
49 49 * Trigger words for colspan, rowspan, or the combination of both.
50 50 *
51 51 * @since 1.0.0
52 - * @var array
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 );
@@ -60,35 +60,40 @@
60 60 /**
61 61 * Buffer to store the counts of rowspan per column, initialized in _render_table().
62 62 *
63 63 * @since 1.0.0
64 - * @var array
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 *
71 71 * @since 1.0.0
72 - * @var array
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 *
@@ -102,12 +107,12 @@
102 107 * Set the table (data, options, visibility, ...) that is to be rendered.
103 108 *
104 109 * @since 1.0.0
105 110 *
106 - * @param array $table Table to be rendered.
107 - * @param array $render_options Options for rendering, from both "Edit" screen and Shortcode.
111 + * @param array<string, mixed> $table Table to be rendered.
112 + * @param array<string, mixed> $render_options Options for rendering, from both "Edit" screen and Shortcode.
108 113 */
109 - public function set_input( array $table, array $render_options ) {
114 + public function set_input( array $table, array $render_options ): void {
110 115 $this->table = $table;
111 116 $this->render_options = $render_options;
112 117 /**
113 118 * Filters the table before the render process.
@@ -113,10 +118,10 @@
113 118 * Filters the table before the render process.
114 119 *
115 120 * @since 1.0.0
116 121 *
117 - * @param array $table The table.
118 - * @param array $render_options The render options for the table.
122 + * @param array<string, mixed> $table The table.
123 + * @param array<string, mixed> $render_options The render options for the table.
119 124 */
120 125 $this->table = apply_filters( 'tablepress_table_raw_render_data', $this->table, $this->render_options );
121 126 }
122 127
@@ -126,11 +131,11 @@
126 131 * @since 1.0.0
127 132 * @since 2.0.0 Add the $format parameter.
128 133 *
129 134 * @param string $format Optional. Output format, 'html' (default) or 'array'.
130 - * @return string|array[] HTML code of the rendered table, or a PHP array, or an error message.
135 + * @return string|array<int, array<int, string>> HTML code of the rendered table, or a PHP array, or an error message.
131 136 */
132 - public function get_output( $format = 'html' ) {
137 + public function get_output( string $format = 'html' ) /* : string|array */ {
133 138 // Evaluate math expressions/formulas.
134 139 $this->_evaluate_table_data();
135 140 // Remove hidden rows and columns.
136 141 $this->_prepare_render_data();
@@ -162,9 +167,9 @@
162 167 * Loop through the table to evaluate math expressions/formulas.
163 168 *
164 169 * @since 1.0.0
165 170 */
166 - protected function _evaluate_table_data() {
171 + protected function _evaluate_table_data(): void {
167 172 $orig_table = $this->table;
168 173
169 174 if ( $this->render_options['evaluate_formulas'] ) {
170 175 $formula_evaluator = TablePress::load_class( 'TablePress_Evaluate', 'class-evaluate.php', 'classes' );
@@ -175,11 +180,11 @@
175 180 * Filters the table after evaluating formulas in the table.
176 181 *
177 182 * @since 1.0.0
178 183 *
179 - * @param array $table The table with evaluated formulas.
180 - * @param array $orig_table The table with unevaluated formulas.
181 - * @param array $render_options The render options for the table.
184 + * @param array<string, mixed> $table The table with evaluated formulas.
185 + * @param array<string, mixed> $orig_table The table with unevaluated formulas.
186 + * @param array<string, mixed> $render_options The render options for the table.
182 187 */
183 188 $this->table = apply_filters( 'tablepress_table_evaluate_data', $this->table, $orig_table, $this->render_options );
184 189 }
185 190
@@ -187,9 +192,9 @@
187 192 * Remove all cells from the data set that shall not be rendered, because they are hidden.
188 193 *
189 194 * @since 1.0.0
190 195 */
191 - protected function _prepare_render_data() {
196 + protected function _prepare_render_data(): void {
192 197 $orig_table = $this->table;
193 198
194 199 $num_rows = count( $this->table['data'] );
195 200 $num_columns = ( $num_rows > 0 ) ? count( $this->table['data'][0] ) : 0;
@@ -277,11 +282,11 @@
277 282 * Filters the table after processing the table visibility information.
278 283 *
279 284 * @since 1.0.0
280 285 *
281 - * @param array $table The processed table.
282 - * @param array $orig_table The unprocessed table.
283 - * @param array $render_options The render options for the table.
286 + * @param array<string, mixed> $table The processed table.
287 + * @param array<string, mixed> $orig_table The unprocessed table.
288 + * @param array<string, mixed> $render_options The render options for the table.
284 289 */
285 290 $this->table = apply_filters( 'tablepress_table_render_data', $this->table, $orig_table, $this->render_options );
286 291 }
287 292
@@ -289,9 +294,9 @@
289 294 * Generate the data that is to be rendered.
290 295 *
291 296 * @since 2.0.0
292 297 */
293 - protected function _process_render_data() {
298 + protected function _process_render_data(): void {
294 299 $orig_table = $this->table;
295 300
296 301 // Deactivate nl2br() for this render process, if "convert_line_breaks" Shortcode parameter is set to false.
297 302 if ( ! $this->render_options['convert_line_breaks'] ) {
@@ -300,13 +305,13 @@
300 305
301 306 foreach ( $this->table['data'] as $row_idx => $row ) {
302 307 foreach ( $row as $col_idx => $cell_content ) {
303 308 // Print formulas that are escaped with '= (like in Excel) as text.
304 - if ( "'=" === substr( $cell_content, 0, 2 ) ) {
309 + if ( str_starts_with( $cell_content, "'=" ) ) {
305 310 $cell_content = substr( $cell_content, 1 );
306 311 }
307 312 $cell_content = $this->safe_output( $cell_content );
308 - if ( false !== strpos( $cell_content, '[' ) ) {
313 + if ( str_contains( $cell_content, '[' ) ) {
309 314 $cell_content = do_shortcode( $cell_content );
310 315 }
311 316 /**
312 317 * Filters the content of a single cell, after formulas have been evaluated, the output has been sanitized, and Shortcodes have been evaluated.
@@ -332,11 +337,11 @@
332 337 * Filters the table after processing the table content handling.
333 338 *
334 339 * @since 2.0.0
335 340 *
336 - * @param array $table The processed table.
337 - * @param array $orig_table The unprocessed table.
338 - * @param array $render_options The render options for the table.
341 + * @param array<string, mixed> $table The processed table.
342 + * @param array<string, mixed> $orig_table The unprocessed table.
343 + * @param array<string, mixed> $render_options The render options for the table.
339 344 */
340 345 $this->table = apply_filters( 'tablepress_table_content_render_data', $this->table, $orig_table, $this->render_options );
341 346 }
342 347
@@ -344,9 +349,9 @@
344 349 * Generate the HTML output of the table.
345 350 *
346 351 * @since 1.0.0
347 352 */
348 - protected function _render_table() {
353 + protected function _render_table(): void {
349 354 $num_rows = count( $this->table['data'] );
350 355 $num_columns = ( $num_rows > 0 ) ? count( $this->table['data'][0] ) : 0;
351 356
352 357 // Check if there are rows and columns in the table (might not be the case after removing hidden rows/columns!).
@@ -363,10 +368,10 @@
363 368 * Filters the trigger keywords for "colspan" and "rowspan"
364 369 *
365 370 * @since 1.0.0
366 371 *
367 - * @param array $span_trigger The trigger keywords for combining table cells.
368 - * @param string $table_id The current table ID.
372 + * @param array<string, string> $span_trigger The trigger keywords for combining table cells.
373 + * @param string $table_id The current table ID.
369 374 */
370 375 $this->span_trigger = apply_filters( 'tablepress_span_trigger_keywords', $this->span_trigger, $this->table['id'] );
371 376
372 377 // Explode from string to array.
@@ -405,11 +410,11 @@
405 410 * Filters the attributes for the table name (HTML h2 element, by default).
406 411 *
407 412 * @since 1.13.0
408 413 *
409 - * @param array $name_attributes The attributes for the table name element.
410 - * @param array $table The current table.
411 - * @param array $render_options The render options for the table.
414 + * @param array<string, string> $name_attributes The attributes for the table name element.
415 + * @param array<string, mixed> $table The current table.
416 + * @param array<string, mixed> $render_options The render options for the table.
412 417 */
413 418 $name_attributes = apply_filters( 'tablepress_table_name_tag_attributes', $name_attributes, $this->table, $this->render_options );
414 419 $name_attributes = $this->_attributes_array_to_string( $name_attributes );
415 420
@@ -444,11 +449,11 @@
444 449 * Filters the attributes for the table description (HTML span element, by default).
445 450 *
446 451 * @since 1.13.0
447 452 *
448 - * @param array $description_attributes The attributes for the table description element.
449 - * @param array $table The current table.
450 - * @param array $render_options The render options for the table.
453 + * @param array<string, string> $description_attributes The attributes for the table description element.
454 + * @param array<string, mixed> $table The current table.
455 + * @param array<string, mixed> $render_options The render options for the table.
451 456 */
452 457 $description_attributes = apply_filters( 'tablepress_table_description_tag_attributes', $description_attributes, $this->table, $this->render_options );
453 458 $description_attributes = $this->_attributes_array_to_string( $description_attributes );
454 459
@@ -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 *
@@ -491,10 +512,10 @@
491 512 * If the "Edit" link for a table is shown, it is also added to the caption element.
492 513 *
493 514 * @since 1.0.0
494 515 *
495 - * @param string $caption The content for the HTML caption element of the table. Default empty.
496 - * @param array $table The current table.
516 + * @param string $caption The content for the HTML caption element of the table. Default empty.
517 + * @param array<string, mixed> $table The current table.
497 518 */
498 519 $caption = apply_filters( 'tablepress_print_caption_text', '', $this->table );
499 520 $caption_style = '';
500 521 $caption_class = '';
@@ -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,16 +611,23 @@
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
581 627 *
582 - * @param array $css_classes The CSS classes for the table element.
583 - * @param string $table_id The current table ID.
628 + * @param string[] $css_classes The CSS classes for the table element.
629 + * @param string $table_id The current table ID.
584 630 */
585 631 $css_classes = apply_filters( 'tablepress_table_css_classes', $css_classes, $this->table['id'] );
586 632 // $css_classes might contain several classes in one array entry.
587 633 $css_classes = explode( ' ', implode( ' ', $css_classes ) );
@@ -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.
@@ -608,10 +655,10 @@
608 655 * The attribute is only added if it is not empty.
609 656 *
610 657 * @since 1.0.0
611 658 *
612 - * @param string $summary The content for the summary attribute of the table. Default empty.
613 - * @param array $table The current table.
659 + * @param string $summary The content for the summary attribute of the table. Default empty.
660 + * @param array<string, mixed> $table The current table.
614 661 */
615 662 $summary = apply_filters( 'tablepress_print_summary_attr', $summary, $this->table );
616 663 if ( ! empty( $summary ) ) {
617 664 $table_attributes['summary'] = esc_attr( $summary );
@@ -628,35 +675,49 @@
628 675 * Filters the attributes for the table (HTML table element).
629 676 *
630 677 * @since 1.4.0
631 678 *
632 - * @param array $table_attributes The attributes for the table element.
633 - * @param array $table The current table.
634 - * @param array $render_options The render options for the table.
679 + * @param array<string, string> $table_attributes The attributes for the table element.
680 + * @param array<string, mixed> $table The current table.
681 + * @param array<string, mixed> $render_options The render options for the table.
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.
657 - * @param array $table The current table.
658 - * @param array $render_options The render options for the table.
717 + * @param string $output The generated HTML for the table and HTML elements around it.
718 + * @param array<string, mixed> $table The current 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
@@ -668,9 +729,9 @@
668 729 * @param int $row_idx Index of the row to be rendered.
669 730 * @param string $tag HTML tag to use for the cells (td or th).
670 731 * @return string HTML for the row.
671 732 */
672 - protected function _render_row( $row_idx, $tag ) {
733 + protected function _render_row( int $row_idx, string $tag ): string {
673 734 $row_cells = array();
674 735 // Loop through cells in reversed order, to search for colspan or rowspan trigger words.
675 736 for ( $col_idx = $this->last_column_idx; $col_idx >= 0; $col_idx-- ) {
676 737 $cell_content = $this->table['data'][ $row_idx ][ $col_idx ];
@@ -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 ) ) {
@@ -721,12 +783,20 @@
721 783 $tag_attributes = array();
722 784
723 785 // "colspan" and "rowspan" attributes.
724 786 if ( $this->colspan[ $row_idx ] > 1 ) { // We have colspaned cells.
725 - $tag_attributes['colspan'] = $this->colspan[ $row_idx ];
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 - $tag_attributes['rowspan'] = $this->rowspan[ $col_idx ];
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 );
@@ -757,15 +827,15 @@
757 827 * Filters the attributes for the table cell (HTML td or th element).
758 828 *
759 829 * @since 1.4.0
760 830 *
761 - * @param array $tag_attributes The attributes for the td or th element.
762 - * @param string $table_id The current table ID.
763 - * @param string $cell_content The cell content.
764 - * @param int $row_idx The row number of the cell.
765 - * @param int $col_idx The column number of the cell.
766 - * @param int $colspan_row The number of combined columns for this cell.
767 - * @param int $rowspan_col The number of combined rows for this cell.
831 + * @param array<string, string> $tag_attributes The attributes for the td or th element.
832 + * @param string $table_id The current table ID.
833 + * @param string $cell_content The cell content.
834 + * @param int $row_idx The row number of the cell.
835 + * @param int $col_idx The column number of the cell.
836 + * @param int $colspan_row The number of combined columns for this cell.
837 + * @param int $rowspan_col The number of combined rows for this cell.
768 838 */
769 839 $tag_attributes = apply_filters( 'tablepress_cell_tag_attributes', $tag_attributes, $this->table['id'], $cell_content, $row_idx + 1, $col_idx + 1, $this->colspan[ $row_idx ], $this->rowspan[ $col_idx ] );
770 840 $tag_attributes = $this->_attributes_array_to_string( $tag_attributes );
771 841
@@ -782,21 +852,18 @@
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
793 860 *
794 - * @param string $row_classes The CSS classes for the row.
795 - * @param string $table_id The current table ID.
796 - * @param array $row_cells The HTML code for the cells of the row.
797 - * @param int $row_idx The row number.
798 - * @param array $row_data The content of the cells of the row.
861 + * @param string $row_classes The CSS classes for the row.
862 + * @param string $table_id The current table ID.
863 + * @param string[] $row_cells The HTML code for the cells of the row.
864 + * @param int $row_idx The row number.
865 + * @param string[] $row_data The content of the cells of the row.
799 866 */
800 867 $row_classes = apply_filters( 'tablepress_row_css_class', $row_classes, $this->table['id'], $row_cells, $row_idx + 1, $this->table['data'][ $row_idx ] );
801 868 if ( ! empty( $row_classes ) ) {
802 869 $tr_attributes['class'] = $row_classes;
@@ -806,12 +873,12 @@
806 873 * Filters the attributes for the table row (HTML tr element).
807 874 *
808 875 * @since 1.4.0
809 876 *
810 - * @param array $tr_attributes The attributes for the tr element.
811 - * @param string $table_id The current table ID.
812 - * @param int $row_idx The row number.
813 - * @param array $row_data The content of the cells of the row.
877 + * @param array<string, mixed> $tr_attributes The attributes for the tr element.
878 + * @param string $table_id The current table ID.
879 + * @param int $row_idx The row number.
880 + * @param string[] $row_data The content of the cells of the row.
814 881 */
815 882 $tr_attributes = apply_filters( 'tablepress_row_tag_attributes', $tr_attributes, $this->table['id'], $row_idx + 1, $this->table['data'][ $row_idx ] );
816 883 $tr_attributes = $this->_attributes_array_to_string( $tr_attributes );
817 884
@@ -824,12 +891,12 @@
824 891 * Convert an array of HTML tag attributes to a string.
825 892 *
826 893 * @since 1.4.0
827 894 *
828 - * @param array $attributes Attributes for the HTML tag in the array keys, and their values in the array values.
895 + * @param array<string, string> $attributes Attributes for the HTML tag in the array keys, and their values in the array values.
829 896 * @return string The attributes as a string for usage in a HTML element.
830 897 */
831 - protected function _attributes_array_to_string( array $attributes ) {
898 + protected function _attributes_array_to_string( array $attributes ): string {
832 899 $attributes_string = '';
833 900 foreach ( $attributes as $attribute => $value ) {
834 901 $attributes_string .= " {$attribute}=\"{$value}\"";
835 902 }
@@ -838,21 +905,19 @@
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.
848 913 */
849 - protected function safe_output( $text ) {
914 + protected function safe_output( string $text ): string {
850 915 /*
851 916 * Replace any & with &amp; that is not already an encoded entity (from function htmlentities2 in WP 2.8).
852 917 * A complete htmlentities2() or htmlspecialchars() would encode <HTML> tags, which we don't want.
853 918 */
854 - $text = preg_replace( '/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,4};)/', '&amp;', $text );
919 + $text = (string) preg_replace( '/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,4};)/', '&amp;', $text );
855 920 /**
856 921 * Filters whether line breaks in the cell content shall be replaced with HTML br tags.
857 922 *
858 923 * @since 1.0.0
@@ -870,14 +935,15 @@
870 935 * Get the default render options, null means: Use option from "Edit" screen.
871 936 *
872 937 * @since 1.0.0
873 938 *
874 - * @return array Default render options.
939 + * @return array<string, mixed> Default render options.
875 940 */
876 - public function get_default_render_options() {
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(),
@@ -919,36 +986,30 @@
919 986 * @since 1.0.0
920 987 *
921 988 * @return string CSS for the Preview iframe.
922 989 */
923 - public function get_preview_css() {
990 + public function get_preview_css(): string {
924 991 $is_rtl = is_rtl();
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