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 +404 -298 1.9.23.0.4 View file →
@@ -12,8 +12,9 @@
12 12 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13 13
14 14 /**
15 15 * TablePress Rendering Class
16 + *
16 17 * @package TablePress
17 18 * @subpackage Rendering
18 19 * @author Tobias Bäthge
19 20 * @since 1.0.0
@@ -23,25 +24,25 @@
23 24 /**
24 25 * Table data that is rendered.
25 26 *
26 27 * @since 1.0.0
27 - * @var array
28 + * @var array<string, mixed>
28 29 */
29 - protected $table = array();
30 + protected array $table = array();
30 31
31 32 /**
32 33 * Table options that influence the output result.
33 34 *
34 35 * @since 1.0.0
35 - * @var array
36 + * @var array<string, mixed>
36 37 */
37 - protected $render_options = array();
38 + protected array $render_options = array();
38 39
39 40 /**
40 - * Rendered HTML of the table.
41 + * Rendered HTML code of the table or PHP array.
41 42 *
42 43 * @since 1.0.0
43 - * @var string
44 + * @var string|array<int, array<int, string>>
44 45 */
45 46 protected $output;
46 47
47 48 /**
@@ -47,11 +48,11 @@
47 48 /**
48 49 * Trigger words for colspan, rowspan, or the combination of both.
49 50 *
50 51 * @since 1.0.0
51 - * @var array
52 + * @var array<string, string>
52 53 */
53 - protected $span_trigger = array(
54 + protected array $span_trigger = array(
54 55 'colspan' => '#colspan#',
55 56 'rowspan' => '#rowspan#',
56 57 'span' => '#span#',
57 58 );
@@ -59,35 +60,40 @@
59 60 /**
60 61 * Buffer to store the counts of rowspan per column, initialized in _render_table().
61 62 *
62 63 * @since 1.0.0
63 - * @var array
64 + * @var int[]
64 65 */
65 - protected $rowspan = array();
66 + protected array $rowspan = array();
66 67
67 68 /**
68 69 * Buffer to store the counts of colspan per row, initialized in _render_table().
69 70 *
70 71 * @since 1.0.0
71 - * @var array
72 + * @var int[]
72 73 */
73 - protected $colspan = array();
74 + protected array $colspan = array();
74 75
75 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 + /**
76 84 * Index of the last row of the visible data in the table, set in _render_table().
77 85 *
78 86 * @since 1.0.0
79 - * @var int
80 87 */
81 - protected $last_row_idx;
88 + protected int $last_row_idx;
82 89
83 90 /**
84 91 * Index of the last column of the visible data in the table, set in _render_table().
85 92 *
86 93 * @since 1.0.0
87 - * @var int
88 94 */
89 - protected $last_column_idx;
95 + protected int $last_column_idx;
90 96
91 97 /**
92 98 * Class constructor.
93 99 *
@@ -101,21 +107,21 @@
101 107 * Set the table (data, options, visibility, ...) that is to be rendered.
102 108 *
103 109 * @since 1.0.0
104 110 *
105 - * @param array $table Table to be rendered.
106 - * @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.
107 113 */
108 - public function set_input( array $table, array $render_options ) {
114 + public function set_input( array $table, array $render_options ): void {
109 115 $this->table = $table;
110 116 $this->render_options = $render_options;
111 117 /**
112 - * Filter the table before the render process.
118 + * Filters the table before the render process.
113 119 *
114 120 * @since 1.0.0
115 121 *
116 - * @param array $table The table.
117 - * @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.
118 124 */
119 125 $this->table = apply_filters( 'tablepress_table_raw_render_data', $this->table, $this->render_options );
120 126 }
121 127
@@ -122,18 +128,39 @@
122 128 /**
123 129 * Process the table rendering and return the HTML output.
124 130 *
125 131 * @since 1.0.0
132 + * @since 2.0.0 Add the $format parameter.
126 133 *
127 - * @return string HTML of the rendered table (or an error message).
134 + * @param string $format Optional. Output format, 'html' (default) or 'array'.
135 + * @return string|array<int, array<int, string>> HTML code of the rendered table, or a PHP array, or an error message.
128 136 */
129 - public function get_output() {
137 + public function get_output( string $format = 'html' ) /* : string|array */ {
130 138 // Evaluate math expressions/formulas.
131 139 $this->_evaluate_table_data();
132 140 // Remove hidden rows and columns.
133 141 $this->_prepare_render_data();
134 - // Generate HTML output.
135 - $this->_render_table();
142 +
143 + if ( 'html' !== $format ) {
144 + add_filter( 'tablepress_cell_content', 'wptexturize' );
145 + }
146 +
147 + // Evaluate Shortcodes and escape cell content.
148 + $this->_process_render_data();
149 +
150 + if ( 'html' !== $format ) {
151 + remove_filter( 'tablepress_cell_content', 'wptexturize' );
152 + }
153 +
154 + switch ( $format ) {
155 + case 'html':
156 + $this->_render_table();
157 + break;
158 + case 'array':
159 + $this->output = $this->table['data'];
160 + break;
161 + }
162 +
136 163 return $this->output;
137 164 }
138 165
139 166 /**
@@ -140,21 +167,24 @@
140 167 * Loop through the table to evaluate math expressions/formulas.
141 168 *
142 169 * @since 1.0.0
143 170 */
144 - protected function _evaluate_table_data() {
171 + protected function _evaluate_table_data(): void {
145 172 $orig_table = $this->table;
146 173
147 - $formula_evaluator = TablePress::load_class( 'TablePress_Evaluate', 'class-evaluate.php', 'classes' );
148 - $this->table['data'] = $formula_evaluator->evaluate_table_data( $this->table['data'] );
174 + if ( $this->render_options['evaluate_formulas'] ) {
175 + $formula_evaluator = TablePress::load_class( 'TablePress_Evaluate', 'class-evaluate.php', 'classes' );
176 + $this->table['data'] = $formula_evaluator->evaluate_table_data( $this->table['data'], $this->table['id'] );
177 + }
178 +
149 179 /**
150 - * Filter the table after evaluating formulas in the table.
180 + * Filters the table after evaluating formulas in the table.
151 181 *
152 182 * @since 1.0.0
153 183 *
154 - * @param array $table The table with evaluated formulas.
155 - * @param array $orig_table The table with unevaluated formulas.
156 - * @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.
157 187 */
158 188 $this->table = apply_filters( 'tablepress_table_evaluate_data', $this->table, $orig_table, $this->render_options );
159 189 }
160 190
@@ -162,9 +192,9 @@
162 192 * Remove all cells from the data set that shall not be rendered, because they are hidden.
163 193 *
164 194 * @since 1.0.0
165 195 */
166 - protected function _prepare_render_data() {
196 + protected function _prepare_render_data(): void {
167 197 $orig_table = $this->table;
168 198
169 199 $num_rows = count( $this->table['data'] );
170 200 $num_columns = ( $num_rows > 0 ) ? count( $this->table['data'][0] ) : 0;
@@ -192,13 +222,13 @@
192 222 foreach ( $this->render_options[ "{$action}_{$element}" ] as $key => $value ) {
193 223 $range_dash = strpos( $value, '-' );
194 224 if ( false !== $range_dash ) {
195 225 unset( $this->render_options[ "{$action}_{$element}" ][ $key ] );
196 - $start = substr( $value, 0, $range_dash );
226 + $start = trim( substr( $value, 0, $range_dash ) );
197 227 if ( ! is_numeric( $start ) ) {
198 228 $start = TablePress::letter_to_number( $start );
199 229 }
200 - $end = substr( $value, $range_dash + 1 );
230 + $end = trim( substr( $value, $range_dash + 1 ) );
201 231 if ( ! is_numeric( $end ) ) {
202 232 $end = TablePress::letter_to_number( $end );
203 233 }
204 234 $current_range = range( $start, $end );
@@ -205,13 +235,15 @@
205 235 $range_cells = array_merge( $range_cells, $current_range );
206 236 }
207 237 }
208 238 $this->render_options[ "{$action}_{$element}" ] = array_merge( $this->render_options[ "{$action}_{$element}" ], $range_cells );
239 +
209 240 /*
210 241 * Parse single letters and change from regular numbering to zero-based numbering,
211 - * as rows/columns are indexed from 0 internally, but from 1 externally
242 + * as rows/columns are indexed from 0 internally, but from 1 externally.
212 243 */
213 244 foreach ( $this->render_options[ "{$action}_{$element}" ] as $key => $value ) {
245 + $value = trim( $value );
214 246 if ( ! is_numeric( $value ) ) {
215 247 $value = TablePress::letter_to_number( $value );
216 248 }
217 249 $this->render_options[ "{$action}_{$element}" ][ $key ] = (int) $value - 1;
@@ -224,13 +256,13 @@
224 256 }
225 257
226 258 // Load information about hidden rows and columns.
227 259 // Get indexes of hidden rows (array value of 0).
228 - $hidden_rows = array_keys( $this->table['visibility']['rows'], 0 );
260 + $hidden_rows = array_keys( $this->table['visibility']['rows'], 0, true );
229 261 $hidden_rows = array_merge( $hidden_rows, $this->render_options['hide_rows'] );
230 262 $hidden_rows = array_diff( $hidden_rows, $this->render_options['show_rows'] );
231 263 // Get indexes of hidden columns (array value of 0).
232 - $hidden_columns = array_keys( $this->table['visibility']['columns'], 0 );
264 + $hidden_columns = array_keys( $this->table['visibility']['columns'], 0, true );
233 265 $hidden_columns = array_merge( $hidden_columns, $this->render_options['hide_columns'] );
234 266 $hidden_columns = array_merge( array_diff( $hidden_columns, $this->render_options['show_columns'] ) );
235 267
236 268 // Remove hidden rows and re-index.
@@ -246,25 +278,80 @@
246 278 $this->table['data'][ $row_idx ] = array_merge( $row );
247 279 }
248 280
249 281 /**
250 - * Filter the table after processing the table visibility information.
282 + * Filters the table after processing the table visibility information.
251 283 *
252 284 * @since 1.0.0
253 285 *
254 - * @param array $table The processed table.
255 - * @param array $orig_table The unprocessed table.
256 - * @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.
257 289 */
258 290 $this->table = apply_filters( 'tablepress_table_render_data', $this->table, $orig_table, $this->render_options );
259 291 }
260 292
261 293 /**
294 + * Generate the data that is to be rendered.
295 + *
296 + * @since 2.0.0
297 + */
298 + protected function _process_render_data(): void {
299 + $orig_table = $this->table;
300 +
301 + // Deactivate nl2br() for this render process, if "convert_line_breaks" Shortcode parameter is set to false.
302 + if ( ! $this->render_options['convert_line_breaks'] ) {
303 + add_filter( 'tablepress_apply_nl2br', '__return_false', 9 ); // Priority 9, so that this filter can easily be overwritten at the default priority.
304 + }
305 +
306 + foreach ( $this->table['data'] as $row_idx => $row ) {
307 + foreach ( $row as $col_idx => $cell_content ) {
308 + // Print formulas that are escaped with '= (like in Excel) as text.
309 + if ( str_starts_with( $cell_content, "'=" ) ) {
310 + $cell_content = substr( $cell_content, 1 );
311 + }
312 + $cell_content = $this->safe_output( $cell_content );
313 + if ( str_contains( $cell_content, '[' ) ) {
314 + $cell_content = do_shortcode( $cell_content );
315 + }
316 + /**
317 + * Filters the content of a single cell, after formulas have been evaluated, the output has been sanitized, and Shortcodes have been evaluated.
318 + *
319 + * @since 1.0.0
320 + *
321 + * @param string $cell_content The cell content.
322 + * @param string $table_id The current table ID.
323 + * @param int $row_idx The row number of the cell.
324 + * @param int $col_idx The column number of the cell.
325 + */
326 + $cell_content = apply_filters( 'tablepress_cell_content', $cell_content, $this->table['id'], $row_idx + 1, $col_idx + 1 );
327 + $this->table['data'][ $row_idx ][ $col_idx ] = $cell_content;
328 + }
329 + }
330 +
331 + // Re-instate nl2br() behavior after this render process, if "convert_line_breaks" Shortcode parameter is set to false.
332 + if ( ! $this->render_options['convert_line_breaks'] ) {
333 + remove_filter( 'tablepress_apply_nl2br', '__return_false', 9 ); // Priority 9, so that this filter can easily be overwritten at the default priority.
334 + }
335 +
336 + /**
337 + * Filters the table after processing the table content handling.
338 + *
339 + * @since 2.0.0
340 + *
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.
344 + */
345 + $this->table = apply_filters( 'tablepress_table_content_render_data', $this->table, $orig_table, $this->render_options );
346 + }
347 +
348 + /**
262 349 * Generate the HTML output of the table.
263 350 *
264 351 * @since 1.0.0
265 352 */
266 - protected function _render_table() {
353 + protected function _render_table(): void {
267 354 $num_rows = count( $this->table['data'] );
268 355 $num_columns = ( $num_rows > 0 ) ? count( $this->table['data'][0] ) : 0;
269 356
270 357 // Check if there are rows and columns in the table (might not be the case after removing hidden rows/columns!).
@@ -277,14 +364,14 @@
277 364 $this->rowspan = array_fill( 0, $num_columns, 1 );
278 365 $this->colspan = array_fill( 0, $num_rows, 1 );
279 366
280 367 /**
281 - * Filter the trigger keywords for "colspan" and "rowspan"
368 + * Filters the trigger keywords for "colspan" and "rowspan"
282 369 *
283 370 * @since 1.0.0
284 371 *
285 - * @param array $span_trigger The trigger keywords for combining table cells.
286 - * @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.
287 374 */
288 375 $this->span_trigger = apply_filters( 'tablepress_span_trigger_keywords', $this->span_trigger, $this->table['id'] );
289 376
290 377 // Explode from string to array.
@@ -295,9 +382,9 @@
295 382 $output = '';
296 383
297 384 if ( $this->render_options['print_name'] ) {
298 385 /**
299 - * Filter the HTML tag that wraps the printed table name.
386 + * Filters the HTML tag that wraps the printed table name.
300 387 *
301 388 * @since 1.0.0
302 389 *
303 390 * @param string $tag The HTML tag around the table name. Default h2.
@@ -302,23 +389,41 @@
302 389 *
303 390 * @param string $tag The HTML tag around the table name. Default h2.
304 391 * @param string $table_id The current table ID.
305 392 */
306 - $print_name_html_tag = apply_filters( 'tablepress_print_name_html_tag', 'h2', $this->table['id'] );
393 + $name_html_tag = apply_filters( 'tablepress_print_name_html_tag', 'h2', $this->table['id'] );
394 +
395 + $name_attributes = array();
396 + if ( ! empty( $this->render_options['html_id'] ) ) {
397 + $name_attributes['id'] = "{$this->render_options['html_id']}-name";
398 + }
307 399 /**
308 - * Filter the class attribute for the printed table name.
400 + * Filters the class attribute for the printed table name.
309 401 *
310 402 * @since 1.0.0
403 + * @deprecated 1.13.0 Use {@see 'tablepress_table_name_tag_attributes'} instead.
311 404 *
312 405 * @param string $class The class attribute for the table name that can be used in CSS code.
313 406 * @param string $table_id The current table ID.
314 407 */
315 - $print_name_css_class = apply_filters( 'tablepress_print_name_css_class', "tablepress-table-name tablepress-table-name-id-{$this->table['id']}", $this->table['id'] );
316 - $print_name_html = "<{$print_name_html_tag} class=\"{$print_name_css_class}\">" . $this->safe_output( $this->table['name'] ) . "</{$print_name_html_tag}>\n";
408 + $name_attributes['class'] = apply_filters_deprecated( 'tablepress_print_name_css_class', array( "tablepress-table-name tablepress-table-name-id-{$this->table['id']}", $this->table['id'] ), 'TablePress 1.13.0', 'tablepress_table_name_tag_attributes' );
409 + /**
410 + * Filters the attributes for the table name (HTML h2 element, by default).
411 + *
412 + * @since 1.13.0
413 + *
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.
417 + */
418 + $name_attributes = apply_filters( 'tablepress_table_name_tag_attributes', $name_attributes, $this->table, $this->render_options );
419 + $name_attributes = $this->_attributes_array_to_string( $name_attributes );
420 +
421 + $print_name_html = "<{$name_html_tag}{$name_attributes}>" . $this->safe_output( $this->table['name'] ) . "</{$name_html_tag}>\n";
317 422 }
318 423 if ( $this->render_options['print_description'] ) {
319 424 /**
320 - * Filter the HTML tag that wraps the printed table description.
425 + * Filters the HTML tag that wraps the printed table description.
321 426 *
322 427 * @since 1.0.0
323 428 *
324 429 * @param string $tag The HTML tag around the table description. Default span.
@@ -323,19 +428,37 @@
323 428 *
324 429 * @param string $tag The HTML tag around the table description. Default span.
325 430 * @param string $table_id The current table ID.
326 431 */
327 - $print_description_html_tag = apply_filters( 'tablepress_print_description_html_tag', 'span', $this->table['id'] );
432 + $description_html_tag = apply_filters( 'tablepress_print_description_html_tag', 'span', $this->table['id'] );
433 +
434 + $description_attributes = array();
435 + if ( ! empty( $this->render_options['html_id'] ) ) {
436 + $description_attributes['id'] = "{$this->render_options['html_id']}-description";
437 + }
328 438 /**
329 - * Filter the class attribute for the printed table description.
439 + * Filters the class attribute for the printed table description.
330 440 *
331 441 * @since 1.0.0
442 + * @deprecated 1.13.0 Use {@see 'tablepress_table_description_tag_attributes'} instead.
332 443 *
333 444 * @param string $class The class attribute for the table description that can be used in CSS code.
334 445 * @param string $table_id The current table ID.
335 446 */
336 - $print_description_css_class = apply_filters( 'tablepress_print_description_css_class', "tablepress-table-description tablepress-table-description-id-{$this->table['id']}", $this->table['id'] );
337 - $print_description_html = "<{$print_description_html_tag} class=\"{$print_description_css_class}\">" . $this->safe_output( $this->table['description'] ) . "</{$print_description_html_tag}>\n";
447 + $description_attributes['class'] = apply_filters_deprecated( 'tablepress_print_description_css_class', array( "tablepress-table-description tablepress-table-description-id-{$this->table['id']}", $this->table['id'] ), 'TablePress 1.13.0', 'tablepress_table_description_tag_attributes' );
448 + /**
449 + * Filters the attributes for the table description (HTML span element, by default).
450 + *
451 + * @since 1.13.0
452 + *
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.
456 + */
457 + $description_attributes = apply_filters( 'tablepress_table_description_tag_attributes', $description_attributes, $this->table, $this->render_options );
458 + $description_attributes = $this->_attributes_array_to_string( $description_attributes );
459 +
460 + $print_description_html = "<{$description_html_tag}{$description_attributes}>" . $this->safe_output( $this->table['description'] ) . "</{$description_html_tag}>\n";
338 461 }
339 462
340 463 if ( $this->render_options['print_name'] && 'above' === $this->render_options['print_name_position'] ) {
341 464 $output .= $print_name_html;
@@ -343,56 +466,63 @@
343 466 if ( $this->render_options['print_description'] && 'above' === $this->render_options['print_description_position'] ) {
344 467 $output .= $print_description_html;
345 468 }
346 469
347 - // Deactivate nl2br() for this render process, if "convert_line_breaks" Shortcode parameter is set to false.
348 - if ( ! $this->render_options['convert_line_breaks'] ) {
349 - add_filter( 'tablepress_apply_nl2br', '__return_false', 9 ); // Priority 9, so that this filter can easily be overwritten at the default priority.
350 - }
351 -
352 - $thead = '';
353 - $tfoot = '';
470 + $thead = array();
471 + $tfoot = array();
354 472 $tbody = array();
355 473
356 474 $this->last_row_idx = $num_rows - 1;
357 475 $this->last_column_idx = $num_columns - 1;
476 +
358 477 // Loop through rows in reversed order, to search for rowspan trigger keyword.
359 - for ( $row_idx = $this->last_row_idx; $row_idx >= 0; $row_idx-- ) {
360 - // Last row, need to check for footer (but only if at least two rows).
361 - if ( $this->last_row_idx === $row_idx && $this->render_options['table_foot'] && $num_rows > 1 ) {
362 - $tfoot = $this->_render_row( $row_idx, 'th' );
363 - 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;
364 486 }
365 - // First row, need to check for head (but only if at least two rows).
366 - if ( 0 === $row_idx && $this->render_options['table_head'] && $num_rows > 1 ) {
367 - $thead = $this->_render_row( $row_idx, 'th' );
368 - continue;
369 - }
370 - // 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 ) {
371 494 $tbody[] = $this->_render_row( $row_idx, 'td' );
495 + --$row_idx;
372 496 }
497 + // Reverse rows because we looped through the rows in reverse order.
498 + $tbody = array_reverse( $tbody );
373 499
374 - // Re-instate nl2br() behavior after this render process, if "convert_line_breaks" Shortcode parameter is set to false.
375 - if ( ! $this->render_options['convert_line_breaks'] ) {
376 - remove_filter( 'tablepress_apply_nl2br', '__return_false', 9 ); // Priority 9, so that this filter can easily be overwritten at the default priority.
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;
377 504 }
505 + // Reverse rows because we looped through the rows in reverse order.
506 + $thead = array_reverse( $thead );
378 507
379 508 // <caption> tag.
380 509 /**
381 - * Filter the content for the HTML caption element of the table.
510 + * Filters the content for the HTML caption element of the table.
382 511 *
383 512 * If the "Edit" link for a table is shown, it is also added to the caption element.
384 513 *
385 514 * @since 1.0.0
386 515 *
387 - * @param string $caption The content for the HTML caption element of the table. Default empty.
388 - * @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.
389 518 */
390 519 $caption = apply_filters( 'tablepress_print_caption_text', '', $this->table );
391 - $caption_style = $caption_class = '';
520 + $caption_style = '';
521 + $caption_class = '';
392 522 if ( ! empty( $caption ) ) {
393 523 /**
394 - * Filter the class attribute for the HTML caption element of the table.
524 + * Filters the class attribute for the HTML caption element of the table.
395 525 *
396 526 * @since 1.0.0
397 527 *
398 528 * @param string $class The class attribute for the HTML caption element of the table.
@@ -406,9 +536,9 @@
406 536 $caption_style = ' style="caption-side:bottom;text-align:left;border:none;background:none;margin:0;padding:0;"';
407 537 } else {
408 538 $caption .= '<br />';
409 539 }
410 - $caption .= "<a href=\"{$this->render_options['edit_table_url']}\">" . __( 'Edit', 'default' ) . '</a>';
540 + $caption .= '<a href="' . esc_url( $this->render_options['edit_table_url'] ) . '" rel="nofollow">' . __( 'Edit', 'default' ) . '</a>';
411 541 }
412 542 if ( ! empty( $caption ) ) {
413 543 $caption = "<caption{$caption_class}{$caption_style}>{$caption}</caption>\n";
414 544 }
@@ -415,9 +545,9 @@
415 545
416 546 // <colgroup> tag.
417 547 $colgroup = '';
418 548 /**
419 - * Filter whether the HTML colgroup tag shall be added to the table output.
549 + * Filters whether the HTML colgroup tag shall be added to the table output.
420 550 *
421 551 * @since 1.0.0
422 552 *
423 553 * @param bool $print Whether the colgroup element shall be printed.
@@ -426,9 +556,9 @@
426 556 if ( apply_filters( 'tablepress_print_colgroup_tag', false, $this->table['id'] ) ) {
427 557 for ( $col_idx = 0; $col_idx < $num_columns; $col_idx++ ) {
428 558 $attributes = ' class="colgroup-column-' . ( $col_idx + 1 ) . ' "';
429 559 /**
430 - * Filter the attributes of the HTML col tags in the HTML colgroup tag.
560 + * Filters the attributes of the HTML col tags in the HTML colgroup tag.
431 561 *
432 562 * @since 1.0.0
433 563 *
434 564 * @param string $attributes The attributes in the col element.
@@ -435,9 +565,9 @@
435 565 * @param string $table_id The current table ID.
436 566 * @param int $col_idx The number of the column.
437 567 */
438 568 $attributes = apply_filters( 'tablepress_colgroup_tag_attributes', $attributes, $this->table['id'], $col_idx + 1 );
439 - $colgroup .= "\t<col{$attributes}/>\n";
569 + $colgroup .= "\t<col{$attributes} />\n";
440 570 }
441 571 }
442 572 if ( ! empty( $colgroup ) ) {
443 573 $colgroup = "<colgroup>\n{$colgroup}</colgroup>\n";
@@ -442,18 +572,36 @@
442 572 if ( ! empty( $colgroup ) ) {
443 573 $colgroup = "<colgroup>\n{$colgroup}</colgroup>\n";
444 574 }
445 575
446 - // <thead>, <tfoot>, and <tbody> tags.
576 + /*
577 + * <thead>, <tfoot>, and <tbody> tags.
578 + */
579 +
447 580 if ( ! empty( $thead ) ) {
448 - $thead = "<thead>\n{$thead}</thead>\n";
581 + $thead = "<thead>\n" . implode( '', $thead ) . "</thead>\n";
582 + } else {
583 + $thead = '';
449 584 }
585 +
450 586 if ( ! empty( $tfoot ) ) {
451 - $tfoot = "<tfoot>\n{$tfoot}</tfoot>\n";
587 + $tfoot = "<tfoot>\n" . implode( '', $tfoot ) . "</tfoot>\n";
588 + } else {
589 + $tfoot = '';
452 590 }
453 - $tbody_class = ( $this->render_options['row_hover'] ) ? ' class="row-hover"' : '';
454 - // Reverse rows because we looped through the rows in reverse order.
455 - $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 +
456 604 $tbody = "<tbody{$tbody_class}>\n" . implode( '', $tbody ) . "</tbody>\n";
457 605
458 606 // Attributes for the table (HTML table element).
459 607 $table_attributes = array();
@@ -463,38 +611,54 @@
463 611 $table_attributes['id'] = $this->render_options['html_id'];
464 612 }
465 613
466 614 // "class" attribute.
467 - $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 + }
468 623 /**
469 - * Filter the CSS classes that are given to the HTML table element.
624 + * Filters the CSS classes that are given to the HTML table element.
470 625 *
471 626 * @since 1.0.0
472 627 *
473 - * @param array $css_classes The CSS classes for the table element.
474 - * @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.
475 630 */
476 631 $css_classes = apply_filters( 'tablepress_table_css_classes', $css_classes, $this->table['id'] );
477 632 // $css_classes might contain several classes in one array entry.
478 633 $css_classes = explode( ' ', implode( ' ', $css_classes ) );
479 - $css_classes = array_map( 'sanitize_html_class', $css_classes );
634 + $css_classes = array_map( array( 'TablePress', 'sanitize_css_class' ), $css_classes );
480 635 $css_classes = array_unique( $css_classes );
481 - $css_classes = trim( implode( ' ', $css_classes ) );
482 - if ( ! empty( $css_classes ) ) {
636 + $css_classes = array_filter( $css_classes ); // Remove empty entries.
637 + $css_classes = implode( ' ', $css_classes );
638 + if ( '' !== $css_classes ) {
483 639 $table_attributes['class'] = $css_classes;
484 640 }
485 641
642 + // ARIA label attributes.
643 + if ( $this->render_options['print_name'] && ! empty( $this->render_options['html_id'] ) ) {
644 + $table_attributes['aria-labelledby'] = "{$this->render_options['html_id']}-name";
645 + }
646 + if ( $this->render_options['print_description'] && ! empty( $this->render_options['html_id'] ) ) {
647 + $table_attributes['aria-describedby'] = "{$this->render_options['html_id']}-description";
648 + }
649 +
486 650 // "summary" attribute.
487 651 $summary = '';
488 652 /**
489 - * Filter the content for the summary attribute of the HTML table element.
653 + * Filters the content for the summary attribute of the HTML table element.
490 654 *
491 655 * The attribute is only added if it is not empty.
492 656 *
493 657 * @since 1.0.0
494 658 *
495 - * @param string $summary The content for the summary attribute of the table. Default empty.
496 - * @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.
497 661 */
498 662 $summary = apply_filters( 'tablepress_print_summary_attr', $summary, $this->table );
499 663 if ( ! empty( $summary ) ) {
500 664 $table_attributes['summary'] = esc_attr( $summary );
@@ -502,44 +666,58 @@
502 666
503 667 // Legacy support for attributes that are not encouraged in HTML5.
504 668 foreach ( array( 'cellspacing', 'cellpadding', 'border' ) as $attribute ) {
505 669 if ( false !== $this->render_options[ $attribute ] ) {
506 - $table_attributes[ $attribute ] = intval( $this->render_options[ $attribute ] );
670 + $table_attributes[ $attribute ] = (int) $this->render_options[ $attribute ];
507 671 }
508 672 }
509 673
510 674 /**
511 - * Filter the attributes for the table (HTML table element).
675 + * Filters the attributes for the table (HTML table element).
512 676 *
513 677 * @since 1.4.0
514 678 *
515 - * @param array $table_attributes The attributes for the table element.
516 - * @param array $table The current table.
517 - * @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.
518 682 */
519 683 $table_attributes = apply_filters( 'tablepress_table_tag_attributes', $table_attributes, $this->table, $this->render_options );
520 684 $table_attributes = $this->_attributes_array_to_string( $table_attributes );
521 685
522 - $output .= "\n<table{$table_attributes}>\n";
523 - $output .= $caption . $colgroup . $thead . $tfoot . $tbody;
524 - $output .= "</table>\n";
686 + $table_html = "<table{$table_attributes}>\n";
687 + $table_html .= $caption . $colgroup . $thead . $tbody . $tfoot;
688 + $table_html .= '</table>';
525 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 +
526 704 // name/description below table (HTML already generated above).
527 705 if ( $this->render_options['print_name'] && 'below' === $this->render_options['print_name_position'] ) {
528 - $output .= $print_name_html;
706 + $output .= $print_name_html; // @phpstan-ignore variable.undefined (The variable is set above.)
529 707 }
530 708 if ( $this->render_options['print_description'] && 'below' === $this->render_options['print_description_position'] ) {
531 - $output .= $print_description_html;
709 + $output .= $print_description_html; // @phpstan-ignore variable.undefined (The variable is set above.)
532 710 }
533 711
534 712 /**
535 - * Filter the generated HTML code for table.
713 + * Filters the generated HTML code for the table and HTML elements around it.
536 714 *
537 715 * @since 1.0.0
538 716 *
539 - * @param string $output The generated HTML for the table.
540 - * @param array $table The current table.
541 - * @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.
542 720 */
543 721 $this->output = apply_filters( 'tablepress_table_output', $output, $this->table, $this->render_options );
544 722 }
545 723
@@ -551,40 +729,22 @@
551 729 * @param int $row_idx Index of the row to be rendered.
552 730 * @param string $tag HTML tag to use for the cells (td or th).
553 731 * @return string HTML for the row.
554 732 */
555 - protected function _render_row( $row_idx, $tag ) {
733 + protected function _render_row( int $row_idx, string $tag ): string {
556 734 $row_cells = array();
557 735 // Loop through cells in reversed order, to search for colspan or rowspan trigger words.
558 736 for ( $col_idx = $this->last_column_idx; $col_idx >= 0; $col_idx-- ) {
559 737 $cell_content = $this->table['data'][ $row_idx ][ $col_idx ];
560 738
561 - // Print formulas that are escaped with '= (like in Excel) as text.
562 - if ( "'=" === substr( $cell_content, 0, 2 ) ) {
563 - $cell_content = substr( $cell_content, 1 );
564 - }
565 - $cell_content = do_shortcode( $this->safe_output( $cell_content ) );
566 - /**
567 - * Filter the content of a single cell.
568 - *
569 - * Filter the content of a single cell, after formulas have been evaluated, the output has been sanitized, and Shortcodes have been evaluated.
570 - *
571 - * @since 1.0.0
572 - *
573 - * @param string $cell_content The cell content.
574 - * @param string $table_id The current table ID.
575 - * @param int $row_idx The row number of the cell.
576 - * @param int $col_idx The column number of the cell.
577 - */
578 - $cell_content = apply_filters( 'tablepress_cell_content', $cell_content, $this->table['id'], $row_idx + 1, $col_idx + 1 );
579 -
580 739 if ( $this->span_trigger['rowspan'] === $cell_content ) { // There will be a rowspan.
581 - // Check for #rowspan# in first row, which doesn't make sense.
582 - if ( ( $row_idx > 1 && $row_idx < $this->last_row_idx )
583 - || ( 1 === $row_idx && ! $this->render_options['table_head'] ) // No rowspan into table head.
584 - || ( $this->last_row_idx === $row_idx && ! $this->render_options['table_foot'] ) ) { // No rowspan out of table foot.
740 + if ( ! (
741 + ( 0 === $row_idx ) // No rowspan inside first row.
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.
744 + ) ) {
585 745 // Increase counter for rowspan in this column.
586 - $this->rowspan[ $col_idx ]++;
746 + ++$this->rowspan[ $col_idx ];
587 747 // Reset counter for colspan in this row, combined col- and rowspan might be happening.
588 748 $this->colspan[ $row_idx ] = 1;
589 749 continue;
590 750 }
@@ -590,13 +750,15 @@
590 750 }
591 751 // Invalid rowspan, so we set cell content from #rowspan# to empty.
592 752 $cell_content = '';
593 753 } elseif ( $this->span_trigger['colspan'] === $cell_content ) { // There will be a colspan.
594 - // Check for #colspan# in first column, which doesn't make sense.
595 - if ( $col_idx > 1
596 - || ( 1 === $col_idx && ! $this->render_options['first_column_th'] ) ) { // No colspan into first column head.
754 + if ( ! (
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.
757 + || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
758 + ) ) {
597 759 // Increase counter for colspan in this row.
598 - $this->colspan[ $row_idx ]++;
760 + ++$this->colspan[ $row_idx ];
599 761 // Reset counter for rowspan in this column, combined col- and rowspan might be happening.
600 762 $this->rowspan[ $col_idx ] = 1;
601 763 continue;
602 764 }
@@ -602,22 +764,20 @@
602 764 }
603 765 // Invalid colspan, so we set cell content from #colspan# to empty.
604 766 $cell_content = '';
605 767 } elseif ( $this->span_trigger['span'] === $cell_content ) { // There will be a combined col- and rowspan.
606 - // Check for #span# in first column or first or last row, which is not always possible.
607 - if ( ( $row_idx > 1 && $row_idx < $this->last_row_idx && $col_idx > 1 )
608 - // We are in first, second, or last row or in the first or second column, so more checks are necessary.
609 - || ( ( 1 === $row_idx && ! $this->render_options['table_head'] ) // No rowspan into table head.
610 - && ( $col_idx > 1 || ( 1 === $col_idx && ! $this->render_options['first_column_th'] ) ) ) // And no colspan into first column head.
611 - || ( ( $this->last_row_idx === $row_idx && ! $this->render_options['table_foot'] ) // No rowspan out of table foot.
612 - && ( $col_idx > 1 || ( 1 === $col_idx && ! $this->render_options['first_column_th'] ) ) ) ) { // And no colspan into first column head.
768 + if ( ! (
769 + ( 0 === $row_idx ) // No rowspan inside first row.
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.
772 + ) && ! (
773 + ( 0 === $col_idx ) // No colspan inside first column.
774 + || ( 1 === $col_idx && $this->render_options['first_column_th'] ) // No colspan into first column head.
775 + ) ) {
613 776 continue;
614 777 }
615 778 // Invalid span, so we set cell content from #span# to empty.
616 779 $cell_content = '';
617 - } elseif ( '' === $cell_content && 0 === $row_idx && $this->render_options['table_head'] ) {
618 - // Make empty cells have a space in the table head, to give sorting arrows the correct position in IE9.
619 - $cell_content = '&nbsp;';
620 780 }
621 781
622 782 // Attributes for the table cell (HTML td or th element).
623 783 $tag_attributes = array();
@@ -623,18 +783,26 @@
623 783 $tag_attributes = array();
624 784
625 785 // "colspan" and "rowspan" attributes.
626 786 if ( $this->colspan[ $row_idx ] > 1 ) { // We have colspaned cells.
627 - $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 + }
628 792 }
629 793 if ( $this->rowspan[ $col_idx ] > 1 ) { // We have rowspaned cells.
630 - $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 + }
631 799 }
632 800
633 801 // "class" attribute.
634 802 $cell_class = 'column-' . ( $col_idx + 1 );
635 803 /**
636 - * Filter the CSS classes that are given to a single cell (HTML td element) of a table.
804 + * Filters the CSS classes that are given to a single cell (HTML td element) of a table.
637 805 *
638 806 * @since 1.0.0
639 807 *
640 808 * @param string $cell_class The CSS classes for the cell.
@@ -655,19 +823,19 @@
655 823 $tag_attributes['style'] = 'width:' . preg_replace( '#[^0-9a-z.%]#', '', $this->render_options['column_widths'][ $col_idx ] ) . ';';
656 824 }
657 825
658 826 /**
659 - * Filter the attributes for the table cell (HTML td or th element).
827 + * Filters the attributes for the table cell (HTML td or th element).
660 828 *
661 829 * @since 1.4.0
662 830 *
663 - * @param array $tag_attributes The attributes for the td or th element.
664 - * @param string $table_id The current table ID.
665 - * @param string $cell_content The cell content.
666 - * @param int $row_idx The row number of the cell.
667 - * @param int $col_idx The column number of the cell.
668 - * @param int $colspan_row The number of combined columns for this cell.
669 - * @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.
670 838 */
671 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 ] );
672 840 $tag_attributes = $this->_attributes_array_to_string( $tag_attributes );
673 841
@@ -684,21 +852,18 @@
684 852 $tr_attributes = array();
685 853
686 854 // "class" attribute.
687 855 $row_classes = 'row-' . ( $row_idx + 1 );
688 - if ( $this->render_options['alternating_row_colors'] ) {
689 - $row_classes .= ( 1 === ( $row_idx % 2 ) ) ? ' even' : ' odd';
690 - }
691 856 /**
692 - * Filter the CSS classes that are given to a row (HTML tr element) of a table.
857 + * Filters the CSS classes that are given to a row (HTML tr element) of a table.
693 858 *
694 859 * @since 1.0.0
695 860 *
696 - * @param string $row_classes The CSS classes for the row.
697 - * @param string $table_id The current table ID.
698 - * @param array $row_cells The HTML code for the cells of the row.
699 - * @param int $row_idx The row number.
700 - * @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.
701 866 */
702 867 $row_classes = apply_filters( 'tablepress_row_css_class', $row_classes, $this->table['id'], $row_cells, $row_idx + 1, $this->table['data'][ $row_idx ] );
703 868 if ( ! empty( $row_classes ) ) {
704 869 $tr_attributes['class'] = $row_classes;
@@ -704,16 +869,16 @@
704 869 $tr_attributes['class'] = $row_classes;
705 870 }
706 871
707 872 /**
708 - * Filter the attributes for the table row (HTML tr element).
873 + * Filters the attributes for the table row (HTML tr element).
709 874 *
710 875 * @since 1.4.0
711 876 *
712 - * @param array $tr_attributes The attributes for the tr element.
713 - * @param string $table_id The current table ID.
714 - * @param int $row_idx The row number.
715 - * @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.
716 881 */
717 882 $tr_attributes = apply_filters( 'tablepress_row_tag_attributes', $tr_attributes, $this->table['id'], $row_idx + 1, $this->table['data'][ $row_idx ] );
718 883 $tr_attributes = $this->_attributes_array_to_string( $tr_attributes );
719 884
@@ -726,12 +891,12 @@
726 891 * Convert an array of HTML tag attributes to a string.
727 892 *
728 893 * @since 1.4.0
729 894 *
730 - * @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.
731 896 * @return string The attributes as a string for usage in a HTML element.
732 897 */
733 - protected function _attributes_array_to_string( array $attributes ) {
898 + protected function _attributes_array_to_string( array $attributes ): string {
734 899 $attributes_string = '';
735 900 foreach ( $attributes as $attribute => $value ) {
736 901 $attributes_string .= " {$attribute}=\"{$value}\"";
737 902 }
@@ -740,23 +905,21 @@
740 905
741 906 /**
742 907 * Possibly replace certain HTML entities and replace line breaks with HTML.
743 908 *
744 - * @TODO: Find a better solution than this function, e.g. something like wpautop().
745 - *
746 909 * @since 1.0.0
747 910 *
748 - * @param string $string The string to process.
911 + * @param string $text The string to process.
749 912 * @return string Processed string for output.
750 913 */
751 - protected function safe_output( $string ) {
914 + protected function safe_output( string $text ): string {
752 915 /*
753 916 * Replace any & with &amp; that is not already an encoded entity (from function htmlentities2 in WP 2.8).
754 917 * A complete htmlentities2() or htmlspecialchars() would encode <HTML> tags, which we don't want.
755 918 */
756 - $string = preg_replace( '/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,4};)/', '&amp;', $string );
919 + $text = (string) preg_replace( '/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,4};)/', '&amp;', $text );
757 920 /**
758 - * Filter whether line breaks in the cell content shall be replaced with HTML br tags.
921 + * Filters whether line breaks in the cell content shall be replaced with HTML br tags.
759 922 *
760 923 * @since 1.0.0
761 924 *
762 925 * @param bool $replace Whether to replace line breaks with HTML br tags. Default true.
@@ -762,11 +925,11 @@
762 925 * @param bool $replace Whether to replace line breaks with HTML br tags. Default true.
763 926 * @param string $table_id The current table ID.
764 927 */
765 928 if ( apply_filters( 'tablepress_apply_nl2br', true, $this->table['id'] ) ) {
766 - $string = nl2br( $string );
929 + $text = nl2br( $text );
767 930 }
768 - return $string;
931 + return $text;
769 932 }
770 933
771 934 /**
772 935 * Get the default render options, null means: Use option from "Edit" screen.
@@ -772,46 +935,49 @@
772 935 * Get the default render options, null means: Use option from "Edit" screen.
773 936 *
774 937 * @since 1.0.0
775 938 *
776 - * @return array Default render options.
939 + * @return array<string, mixed> Default render options.
777 940 */
778 - public function get_default_render_options() {
941 + public function get_default_render_options(): array {
779 942 // Attention: Array keys have to be lowercase, otherwise they won't match the Shortcode attributes, which will be passed in lowercase by WP.
780 943 return array(
781 - 'id' => '',
782 - 'column_widths' => '',
783 944 'alternating_row_colors' => null,
784 - 'row_hover' => null,
785 - 'table_head' => null,
786 - 'table_foot' => null,
787 - 'first_column_th' => false,
788 - 'print_name' => null,
789 - 'print_name_position' => null,
790 - 'print_description' => null,
791 - 'print_description_position' => null,
945 + 'block_preview' => false,
946 + 'border' => false,
792 947 'cache_table_output' => true,
948 + 'cellpadding' => false,
949 + 'cellspacing' => false,
950 + 'column_widths' => '',
793 951 'convert_line_breaks' => true,
794 - 'extra_css_classes' => null,
795 - 'use_datatables' => null,
796 - 'datatables_sort' => null,
952 + 'datatables_custom_commands' => null,
953 + 'datatables_datetime' => '',
954 + 'datatables_filter' => null,
955 + 'datatables_info' => null,
956 + 'datatables_lengthchange' => null,
957 + 'datatables_locale' => get_locale(),
797 958 'datatables_paginate' => null,
798 959 'datatables_paginate_entries' => null,
799 - 'datatables_lengthchange' => null,
800 - 'datatables_filter' => null,
801 - 'datatables_info' => null,
802 960 'datatables_scrollx' => null,
803 961 'datatables_scrolly' => false,
804 - 'datatables_custom_commands' => null,
805 - 'datatables_locale' => get_locale(),
806 - 'show_rows' => '',
807 - 'show_columns' => '',
962 + 'datatables_sort' => null,
963 + 'evaluate_formulas' => true,
964 + 'extra_css_classes' => null,
965 + 'first_column_th' => false,
966 + 'hide_columns' => '',
808 967 'hide_rows' => '',
809 - 'hide_columns' => '',
810 - 'cellspacing' => false,
811 - 'cellpadding' => false,
812 - 'border' => false,
968 + 'id' => '',
969 + 'print_description' => null,
970 + 'print_description_position' => null,
971 + 'print_name' => null,
972 + 'print_name_position' => null,
973 + 'row_hover' => null,
813 974 'shortcode_debug' => false,
975 + 'show_columns' => '',
976 + 'show_rows' => '',
977 + 'table_foot' => null,
978 + 'table_head' => null,
979 + 'use_datatables' => null,
814 980 );
815 981 }
816 982
817 983 /**
@@ -820,90 +986,30 @@
820 986 * @since 1.0.0
821 987 *
822 988 * @return string CSS for the Preview iframe.
823 989 */
824 - public function get_preview_css() {
825 - if ( is_rtl() ) {
826 - $rtl = "\ndirection: rtl;";
827 - $rtl_align = 'right';
828 - } else {
829 - $rtl = '';
830 - $rtl_align = 'left';
990 + public function get_preview_css(): string {
991 + $is_rtl = is_rtl();
992 + $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
993 + $default_css_minified = $tablepress_css->load_default_css_from_file( $is_rtl );
994 + if ( false === $default_css_minified ) {
995 + $default_css_minified = '';
831 996 }
997 +
998 + $rtl_direction = $is_rtl ? "\ndirection: rtl;" : '';
999 +
832 1000 return <<<CSS
833 -<style type="text/css">
834 -/* iframe */
835 -body {
836 - margin: 10px;
837 - font-family: sans-serif;{$rtl}
838 -}
839 -/* Inline Shortcodes, in texts */
840 -.table-shortcode-inline {
841 - background: transparent;
842 - border: none;
843 - color: #333333;
844 - width: 110px;
845 - margin: 0;
846 - padding: 0;
847 - -webkit-box-shadow: none;
848 - box-shadow: none;
849 - text-align: center;
850 - font-weight: bold;
851 - font-size: 100%;
852 -}
853 -.table-shortcode {
854 - cursor: text;
855 -}
856 -/* Default table styling */
857 -.tablepress {
858 - border-collapse: collapse;
859 - border-spacing: 0;
860 - width: 100%;
861 - margin-bottom: 1em;
862 - border: none;
863 -}
864 -.tablepress td,
865 -.tablepress th {
866 - padding: 8px;
867 - border: none;
868 - background: none;
869 - text-align: {$rtl_align};
870 -}
871 -.tablepress tbody td {
872 - vertical-align: top;
873 -}
874 -.tablepress tbody tr td,
875 -.tablepress tfoot tr th {
876 - border-top: 1px solid #dddddd;
877 -}
878 -.tablepress tbody tr:first-child td {
879 - border-top: 0;
880 -}
881 -.tablepress thead tr th {
882 - border-bottom: 1px solid #dddddd;
883 -}
884 -.tablepress thead th,
885 -.tablepress tfoot th {
886 - background-color: #d9edf7;
887 - font-weight: bold;
888 - vertical-align: middle;
889 -}
890 -.tablepress tbody tr.odd td {
891 - background-color: #f9f9f9;
892 -}
893 -.tablepress tbody tr.even td {
894 - background-color: #ffffff;
895 -}
896 -.tablepress .row-hover tr:hover td {
897 - background-color: #f3f3f3;
898 -}
899 -.tablepress img {
900 - margin: 0;
901 - padding: 0;
902 - border: none;
903 - max-width: none;
904 -}
905 -</style>
906 -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;
907 1013 }
908 1014
909 1015 } // class TablePress_Render