PluginProbe
TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables / 2.3.0
TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables v2.3.0
2.3.0 2.2.14 2.2.13 2.2.12 2.2.11 2.2.10 2.2.9 2.2.8 2.2.7 2.2.6 2.2.5 2.2.4 2.2.3 trunk 1.0.0 1.0.1 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2
← All changes | includes/Elementor/TablekitWidget.php +230 -124 2.2.7 → 2.3.0 View file →
@@ -1,8 +1,7 @@
1 1 <?php
2 -
3 2 /**
4 - * Elementor widget for TableKit tables.
3 + * Elementor widget for TableKit tables
5 4 *
6 5 * @package TableKit
7 6 */
8 7
@@ -7,19 +6,45 @@
7 6 */
8 7
9 8 use TableBuilder\Shortcode\ShortcodeUtils;
10 9
11 -if (! defined('ABSPATH')) {
10 +if ( ! defined( 'ABSPATH' ) ) {
12 11 exit;
13 12 }
14 13
15 -class TableKit_Elementor_Widget extends \Elementor\Widget_Base
16 -{
14 +/**
15 + * Renders a TableKit table as a native Elementor widget.
16 + */
17 +class TableKit_Elementor_Widget extends \Elementor\Widget_Base {
18 +
17 19 private const ALL_BLOCKS = '';
18 20
21 + /**
22 + * Whether the shared global styles have been printed yet this request.
23 + *
24 + * @var bool
25 + */
19 26 private static bool $global_styles_printed = false;
20 - private static array $printed_block_styles = [];
21 - private static array $printed_block_scripts = [];
27 +
28 + /**
29 + * Block IDs whose per-instance styles have already been printed.
30 + *
31 + * @var array
32 + */
33 + private static array $printed_block_styles = array();
34 +
35 + /**
36 + * Block IDs whose per-instance scripts have already been printed.
37 + *
38 + * @var array
39 + */
40 + private static array $printed_block_scripts = array();
41 +
42 + /**
43 + * Cached table options list, or null if not yet built.
44 + *
45 + * @var array|null
46 + */
22 47 private static ?array $table_options_cache = null;
23 48
24 49 // -------------------------------------------------------------------------
25 50 // Identity
@@ -24,35 +49,61 @@
24 49 // -------------------------------------------------------------------------
25 50 // Identity
26 51 // -------------------------------------------------------------------------
27 52
28 - public function get_name(): string
29 - {
53 + /**
54 + * Elementor widget's internal name/slug.
55 + *
56 + * @return string
57 + */
58 + public function get_name(): string {
30 59 return 'tablekit_table';
31 60 }
32 61
33 - public function get_title(): string
34 - {
35 - return __('TableKit', 'tablekit');
62 + /**
63 + * Elementor widget's display title (shown in the widget panel).
64 + *
65 + * @return string
66 + */
67 + public function get_title(): string {
68 + return __( 'TableKit', 'table-builder-block' );
36 69 }
37 70
38 - public function get_icon(): string
39 - {
71 + /**
72 + * Elementor widget's icon class.
73 + *
74 + * @return string
75 + */
76 + public function get_icon(): string {
40 77 return 'eicon-table';
41 78 }
42 79
43 - public function get_categories(): array
44 - {
45 - return ['general'];
80 + /**
81 + * Elementor category slugs this widget is listed under.
82 + *
83 + * @return string[]
84 + */
85 + public function get_categories(): array {
86 + return array( 'general' );
46 87 }
47 88
48 - public function get_keywords(): array
49 - {
50 - return ['table', 'tablekit', 'data', 'grid'];
89 + /**
90 + * Search keywords Elementor's widget panel matches against.
91 + *
92 + * @return string[]
93 + */
94 + public function get_keywords(): array {
95 + return array( 'table', 'tablekit', 'data', 'grid' );
51 96 }
52 97
53 - public function is_reload_preview_required(): bool
54 - {
98 + /**
99 + * Whether Elementor should force a full preview reload when this widget's
100 + * settings change (true, since table selection/rendering isn't done via
101 + * live-refreshable controls).
102 + *
103 + * @return bool
104 + */
105 + public function is_reload_preview_required(): bool {
55 106 return true;
56 107 }
57 108
58 109 // -------------------------------------------------------------------------
@@ -58,42 +109,46 @@
58 109 // -------------------------------------------------------------------------
59 110 // Controls
60 111 // -------------------------------------------------------------------------
61 112
62 - protected function register_controls(): void
63 - {
113 + /**
114 + * Registers the widget's Elementor editor controls (table picker, block picker).
115 + *
116 + * @return void
117 + */
118 + protected function register_controls(): void {
64 119 $this->start_controls_section(
65 120 'tablekit_table_section',
66 - [
67 - 'label' => __('Table', 'tablekit'),
121 + array(
122 + 'label' => __( 'Table', 'table-builder-block' ),
68 123 'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
69 - ]
124 + )
70 125 );
71 126
72 127 $this->add_control(
73 128 'table_id',
74 - [
75 - 'label' => __('Select Table', 'tablekit'),
129 + array(
130 + 'label' => __( 'Select Table', 'table-builder-block' ),
76 131 'type' => \Elementor\Controls_Manager::SELECT2,
77 132 'options' => $this->get_table_options(),
78 133 'default' => '',
79 134 'render_type' => 'template',
80 135 'label_block' => true,
81 - 'description' => __('Create and manage tables under TableKit → Tables in the WP admin.', 'tablekit'),
82 - ]
136 + 'description' => __( 'Create and manage tables under TableKit → Tables in the WP admin.', 'table-builder-block' ),
137 + )
83 138 );
84 139
85 140 $this->add_control(
86 141 'block_index',
87 - [
88 - 'label' => __('Select Block', 'tablekit'),
142 + array(
143 + 'label' => __( 'Select Block', 'table-builder-block' ),
89 144 'type' => \Elementor\Controls_Manager::SELECT,
90 - 'options' => ['' => __('— All Blocks —', 'tablekit')],
145 + 'options' => array( '' => __( '— All Blocks —', 'table-builder-block' ) ),
91 146 'default' => self::ALL_BLOCKS,
92 147 'render_type' => 'template',
93 148 'label_block' => true,
94 - 'description' => __('Choose a specific block to display. Or select — All Blocks — to show all blocks.', 'tablekit'),
95 - ]
149 + 'description' => __( 'Choose a specific block to display. Or select — All Blocks — to show all blocks.', 'table-builder-block' ),
150 + )
96 151 );
97 152
98 153 $this->end_controls_section();
99 154 }
@@ -101,65 +156,90 @@
101 156 // -------------------------------------------------------------------------
102 157 // Rendering
103 158 // -------------------------------------------------------------------------
104 159
105 - protected function render(): void
106 - {
160 + /**
161 + * Renders the widget on the frontend/editor: resolves the selected table post,
162 + * optionally narrow to one block, print shared/per-table CSS, render the
163 + * table block(s), and print their view scripts.
164 + *
165 + * @return void
166 + */
167 + protected function render(): void {
107 168 $settings = $this->get_settings_for_display();
108 169 $raw_id = $settings['table_id'] ?? 0;
109 170
110 - if (is_array($raw_id)) {
111 - $raw_id = reset($raw_id);
171 + if ( is_array( $raw_id ) ) {
172 + $raw_id = reset( $raw_id );
112 173 }
113 174
114 - $table_id = absint($raw_id);
175 + $table_id = absint( $raw_id );
115 176
116 - if (empty($table_id)) {
117 - $this->render_placeholder(__('Select a table to display.', 'tablekit'));
177 + if ( empty( $table_id ) ) {
178 + $this->render_placeholder( __( 'Select a table to display.', 'table-builder-block' ) );
118 179 return;
119 180 }
120 181
121 - $post = get_post($table_id);
182 + $post = get_post( $table_id );
122 183
123 - if (! $post) {
124 - $this->render_placeholder(__('Table not found.', 'tablekit'));
184 + if ( ! $post ) {
185 + $this->render_placeholder( __( 'Table not found.', 'table-builder-block' ) );
125 186 return;
126 187 }
127 188
128 - if (class_exists('\\TableBuilder\\Config\\Blocks')) {
189 + if ( class_exists( '\\TableBuilder\\Config\\Blocks' ) ) {
129 190 \TableBuilder\Config\Blocks::instance()->enqueue_block_assets();
130 191 }
131 192
132 - $all_blocks = parse_blocks($post->post_content);
133 - $table_blocks = $this->filter_table_blocks($all_blocks);
134 - $block_index = $settings['block_index'] ?? self::ALL_BLOCKS;
193 + $all_blocks = parse_blocks( $post->post_content );
194 + $table_blocks = $this->filter_table_blocks( $all_blocks );
195 + $block_index = $settings['block_index'] ?? self::ALL_BLOCKS;
135 196 $blocks_to_render = $all_blocks;
136 197
137 - if (self::ALL_BLOCKS !== $block_index && '' !== $block_index) {
198 + if ( self::ALL_BLOCKS !== $block_index && '' !== $block_index ) {
138 199 $index = (int) $block_index;
139 - if (isset($table_blocks[$index])) {
140 - $blocks_to_render = array($table_blocks[$index]);
200 + if ( isset( $table_blocks[ $index ] ) ) {
201 + $blocks_to_render = array( $table_blocks[ $index ] );
141 202 }
142 203 }
143 204
144 - $block_names = $this->collect_block_names($blocks_to_render);
205 + $block_names = $this->collect_block_names( $blocks_to_render );
145 206
146 - $this->print_shared_styles($block_names);
147 - $this->print_table_styles($table_id, $post->post_content);
207 + $this->print_shared_styles( $block_names );
208 + $this->print_table_styles( $table_id, $post->post_content );
148 209
210 + // This widget prints its own table CSS above, so the generic late-CSS
211 + // printer (AssetGenerator) must not print it a second time.
212 + $asset_generator = class_exists( '\\TableBuilder\\Hooks\\AssetGenerator' ) ? \TableBuilder\Hooks\AssetGenerator::instance() : null;
213 + if ( $asset_generator ) {
214 + $asset_generator->suspend_late_css();
215 + }
216 +
149 217 echo '<div class="tablekit-elementor-wrap">';
150 - foreach ($blocks_to_render as $block) {
151 - echo (new WP_Block($block))->render();
218 + try {
219 + foreach ( $blocks_to_render as $block ) {
220 + echo ( new WP_Block( $block ) )->render(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- standard core block-rendering output, same as render_block().
221 + }
222 + } finally {
223 + if ( $asset_generator ) {
224 + $asset_generator->resume_late_css();
225 + }
152 226 }
153 227 echo '</div>';
154 228
155 - $this->print_block_scripts($block_names);
229 + $this->print_block_scripts( $block_names );
156 230
157 - if (class_exists('\\TableBuilder\\Shortcode\\ShortcodeUtils')) {
158 - echo \TableBuilder\Shortcode\ShortcodeUtils::get_elementor_init_script();
231 + if ( class_exists( '\\TableBuilder\\Shortcode\\ShortcodeUtils' ) ) {
232 + echo \TableBuilder\Shortcode\ShortcodeUtils::get_elementor_init_script(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static hardcoded <script> string, no user input.
159 233 }
160 234 }
161 235
236 + /**
237 + * No-op: this widget renders server-side only (render() above), Elementor's
238 + * JS live-preview template is intentionally unused.
239 + *
240 + * @return void
241 + */
162 242 protected function content_template(): void {}
163 243
164 244 // -------------------------------------------------------------------------
165 245 // Private helpers
@@ -164,112 +244,134 @@
164 244 // -------------------------------------------------------------------------
165 245 // Private helpers
166 246 // -------------------------------------------------------------------------
167 247
168 - private function render_placeholder(string $message): void
169 - {
248 + /**
249 + * Renders a simple placeholder message in place of the table (used when no
250 + * table is selected, or the selected table post can't be found).
251 + *
252 + * @param string $message Message to display.
253 + * @return void
254 + */
255 + private function render_placeholder( string $message ): void {
170 256 echo '<div class="tablekit-el-placeholder" style="padding:1.5em;border:1px dashed #ccc;text-align:center;color:#999;">'
171 257 . '<span class="eicon-table" style="font-size:2em;display:block;margin-bottom:.5em;opacity:.35;"></span>'
172 - . '<p style="margin:0;">' . esc_html($message) . '</p>'
258 + . '<p style="margin:0;">' . esc_html( $message ) . '</p>'
173 259 . '</div>';
174 260 }
175 261
176 - private function filter_table_blocks(array $blocks): array
177 - {
178 - return ShortcodeUtils::filter_table_blocks($blocks);
262 + /**
263 + * Narrow a parsed block tree down to just the plugin's table blocks.
264 + *
265 + * @param array $blocks Parsed block tree (as from parse_blocks()).
266 + * @return array Table blocks only.
267 + */
268 + private function filter_table_blocks( array $blocks ): array {
269 + return ShortcodeUtils::filter_table_blocks( $blocks );
179 270 }
180 271
181 272 /**
182 - * Print shared CSS once per page load.
273 + * Prints shared CSS once per page load.
274 + *
275 + * @param string[] $block_names Block names appearing in this render, used to
276 + * additionally print each block's registered style once.
277 + * @return void
183 278 */
184 - private function print_shared_styles(array $block_names): void
185 - {
279 + private function print_shared_styles( array $block_names ): void {
186 280 $css = '';
187 281
188 - if (! self::$global_styles_printed) {
189 - foreach (['global.css', 'components.css'] as $filename) {
282 + if ( ! self::$global_styles_printed ) {
283 + foreach ( array( 'global.css', 'components.css' ) as $filename ) {
190 284 $path = TABLE_BUILDER_BLOCK_PLUGIN_DIR . 'build/tablebuilder/' . $filename;
191 - if (file_exists($path)) {
285 + if ( file_exists( $path ) ) {
192 286 $css .= file_get_contents($path) . "\n"; // phpcs:ignore
193 287 }
194 288 }
195 289
196 - $css .= $this->get_registered_style_css('tablebuilder/table-builder');
290 + $css .= $this->get_registered_style_css( 'tablebuilder/table-builder' );
197 291 self::$global_styles_printed = true;
198 292 self::$printed_block_styles['tablebuilder/table-builder'] = true;
199 293 }
200 294
201 - foreach ($block_names as $name) {
202 - if (isset(self::$printed_block_styles[$name])) {
295 + foreach ( $block_names as $name ) {
296 + if ( isset( self::$printed_block_styles[ $name ] ) ) {
203 297 continue;
204 298 }
205 - $css .= $this->get_registered_style_css($name);
206 - self::$printed_block_styles[$name] = true;
299 + $css .= $this->get_registered_style_css( $name );
300 + self::$printed_block_styles[ $name ] = true;
207 301 }
208 302
209 - if ('' !== trim($css)) {
303 + if ( '' !== trim( $css ) ) {
210 304 echo '<style id="tablekit-shared-styles">' . $css . '</style>'; // phpcs:ignore
211 305 }
212 306 }
213 307
214 308 /**
215 - * Print per-table dynamic CSS. Unique ID per table so multiple widgets coexist.
309 + * Prints per-table dynamic CSS. Uses a unique ID per table so multiple widgets coexist.
310 + *
311 + * @param int $table_id Table post ID, used to build a unique <style> element ID.
312 + * @param string $post_content Raw post content to collect block-generated CSS from.
313 + * @return void
216 314 */
217 - private function print_table_styles(int $table_id, string $post_content): void
218 - {
219 - if (! class_exists('\\TableBuilder\\Shortcode\\Shortcode')) {
315 + private function print_table_styles( int $table_id, string $post_content ): void {
316 + if ( ! class_exists( '\\TableBuilder\\Shortcode\\Shortcode' ) ) {
220 317 return;
221 318 }
222 319
223 320 $shortcode = \TableBuilder\Shortcode\Shortcode::instance();
224 321
225 - if (! $shortcode || ! method_exists($shortcode, 'collect_blocks_css_from_content')) {
322 + if ( ! $shortcode || ! method_exists( $shortcode, 'collect_blocks_css_from_content' ) ) {
226 323 return;
227 324 }
228 325
229 - $css = $shortcode->collect_blocks_css_from_content($post_content);
326 + $css = $shortcode->collect_blocks_css_from_content( $post_content );
230 327
231 - if ('' !== trim($css)) {
328 + if ( '' !== trim( $css ) ) {
232 329 echo '<style id="tablekit-el-css-' . esc_attr((string) $table_id) . '">' . $css . '</style>'; // phpcs:ignore
233 330 }
234 331 }
235 332
236 333 /**
237 - * Enqueue + immediately print block view-scripts inline.
334 + * Enqueues and immediately prints block view-scripts inline.
238 335 * Required for the editor iframe where wp_footer never fires.
336 + *
337 + * @param string[] $block_names Block names to print view scripts for, in addition
338 + * to the base "tablebuilder/table-builder" script.
339 + * @return void
239 340 */
240 - private function print_block_scripts(array $block_names): void
241 - {
242 - // FIX 8: only print once per full or partial render cycle
243 - static $printed = [];
341 + private function print_block_scripts( array $block_names ): void {
342 + // FIX 8: only print once per full or partial render cycle.
343 + static $printed = array();
244 344
245 - foreach (array_unique(array_merge(['tablebuilder/table-builder'], $block_names)) as $name) {
246 - $handle = \TableBuilder\Shortcode\ShortcodeUtils::get_block_asset_handle($name, 'viewScript');
247 - if (isset($printed[$handle])) {
345 + foreach ( array_unique( array_merge( array( 'tablebuilder/table-builder' ), $block_names ) ) as $name ) {
346 + $handle = \TableBuilder\Shortcode\ShortcodeUtils::get_block_asset_handle( $name, 'viewScript' );
347 + if ( isset( $printed[ $handle ] ) ) {
248 348 continue;
249 349 }
250 - wp_enqueue_script($handle);
251 - wp_print_scripts($handle);
252 - $printed[$handle] = true;
350 + wp_enqueue_script( $handle );
351 + wp_print_scripts( $handle );
352 + $printed[ $handle ] = true;
253 353 }
254 354 }
255 355
256 356 /**
257 - * Read the CSS file of a registered WP stylesheet handle.
357 + * Reads the CSS file of a registered WP stylesheet handle.
358 + *
359 + * @param string $block_name Block name to resolve the registered "style" asset handle for.
360 + * @return string The stylesheet's contents, or an empty string if not found/registered.
258 361 */
259 - private function get_registered_style_css(string $block_name): string
260 - {
261 - $handle = \TableBuilder\Shortcode\ShortcodeUtils::get_block_asset_handle($block_name, 'style');
262 - $style = wp_styles()->query($handle, 'registered');
362 + private function get_registered_style_css( string $block_name ): string {
363 + $handle = \TableBuilder\Shortcode\ShortcodeUtils::get_block_asset_handle( $block_name, 'style' );
364 + $style = wp_styles()->query( $handle, 'registered' );
263 365
264 - if (! $style || empty($style->src)) {
366 + if ( ! $style || empty( $style->src ) ) {
265 367 return '';
266 368 }
267 369
268 - $path = str_replace(content_url(), WP_CONTENT_DIR, (string) $style->src);
269 - $path = (string) strtok($path, '?');
370 + $path = str_replace( content_url(), WP_CONTENT_DIR, (string) $style->src );
371 + $path = (string) strtok( $path, '?' );
270 372
271 - if (! file_exists($path)) {
373 + if ( ! file_exists( $path ) ) {
272 374 return '';
273 375 }
274 376
275 377 return file_get_contents($path) . "\n"; // phpcs:ignore
@@ -275,51 +377,55 @@
275 377 return file_get_contents($path) . "\n"; // phpcs:ignore
276 378 }
277 379
278 380 /**
279 - * Recursively collect unique block names from a block tree.
381 + * Recursively collects unique block names from a block tree.
382 + *
383 + * @param array $blocks Parsed block tree.
384 + * @return string[] Unique block names found in the tree.
280 385 */
281 - private function collect_block_names(array $blocks): array
282 - {
386 + private function collect_block_names( array $blocks ): array {
283 387 $all = ShortcodeUtils::collect_blocks_recursive(
284 388 $blocks,
285 - static fn(array $block): bool => is_string($block['blockName'] ?? null) && '' !== $block['blockName']
389 + static fn( array $block ): bool => is_string( $block['blockName'] ?? null ) && '' !== $block['blockName']
286 390 );
287 391
288 - return array_values(array_unique(array_column($all, 'blockName')));
392 + return array_values( array_unique( array_column( $all, 'blockName' ) ) );
289 393 }
290 394
291 395 /**
292 - * Build the SELECT2 options array for the table picker control.
396 + * Builds the SELECT2 options array for the table picker control (published
397 + * tablekit_table posts plus any inline-block source posts), memoized per request.
398 + *
399 + * @return array<string,string> Options keyed by post ID (as a string), valued by label.
293 400 */
294 - private function get_table_options(): array
295 - {
296 - if (is_array(self::$table_options_cache)) {
401 + private function get_table_options(): array {
402 + if ( is_array( self::$table_options_cache ) ) {
297 403 return self::$table_options_cache;
298 404 }
299 405
300 - $options = ['' => __('-- Select a Table --', 'tablekit')];
406 + $options = array( '' => __( '-- Select a Table --', 'table-builder-block' ) );
301 407
302 408 $tables = get_posts(
303 - [
409 + array(
304 410 'post_type' => 'tablekit_table',
305 411 'post_status' => 'publish',
306 412 'posts_per_page' => -1,
307 413 'orderby' => 'title',
308 414 'order' => 'ASC',
309 - ]
415 + )
310 416 );
311 417
312 - foreach ($tables as $table) {
313 - $options[(string) $table->ID] = esc_html($table->post_title);
418 + foreach ( $tables as $table ) {
419 + $options[ (string) $table->ID ] = esc_html( $table->post_title );
314 420 }
315 421
316 - if (class_exists('\\TableBuilder\\Config\\CPT\\TableCPT')) {
422 + if ( class_exists( '\\TableBuilder\\Config\\CPT\\TableCPT' ) ) {
317 423 $inline = \TableBuilder\Config\CPT\TableCPT::instance()->get_inline_table_source_options();
318 - foreach ($inline as $id => $label) {
424 + foreach ( $inline as $id => $label ) {
319 425 $key = (string) $id;
320 - if (! isset($options[$key])) {
321 - $options[$key] = esc_html($label);
426 + if ( ! isset( $options[ $key ] ) ) {
427 + $options[ $key ] = esc_html( $label );
322 428 }
323 429 }
324 430 }
325 431