PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.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 All 45 releases
← All changes | classes/class-view.php +273 -129 2.1.8 → 3.4 View file →
@@ -7,8 +7,10 @@
7 7 * @author Tobias Bäthge
8 8 * @since 1.0.0
9 9 */
10 10
11 +declare(strict_types=1);
12 +
11 13 // Prohibit direct script loading.
12 14 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13 15
14 16 /**
@@ -24,51 +26,41 @@
24 26 /**
25 27 * Data for the view.
26 28 *
27 29 * @since 1.0.0
28 - * @var array
30 + * @var array<string, mixed>
29 31 */
30 - protected $data = array();
32 + protected array $data = array();
31 33
32 34 /**
33 35 * Number of screen columns for post boxes.
34 36 *
35 37 * @since 1.0.0
36 - * @var int
37 38 */
38 - protected $screen_columns = 0;
39 + protected int $screen_columns = 0;
39 40
40 41 /**
41 42 * User action for this screen.
42 43 *
43 44 * @since 1.0.0
44 - * @var string
45 45 */
46 - protected $action = '';
46 + protected string $action = '';
47 47
48 48 /**
49 - * Instance of the Admin Page Helper Class, with necessary functions.
50 - *
51 - * @since 1.0.0
52 - * @var TablePress_Admin_Page
53 - */
54 - protected $admin_page;
55 -
56 - /**
57 49 * List of text boxes (similar to post boxes, but just with text and without extra functionality).
58 50 *
59 51 * @since 1.0.0
60 - * @var array
52 + * @var array<string, array<string, array<string, mixed>>>
61 53 */
62 - protected $textboxes = array();
54 + protected array $textboxes = array();
63 55
64 56 /**
65 57 * List of messages that are to be displayed as boxes below the page title.
66 58 *
67 59 * @since 1.0.0
68 - * @var array
60 + * @var string[]
69 61 */
70 - protected $header_messages = array();
62 + protected array $header_messages = array();
71 63
72 64 /**
73 65 * Whether there are post boxes registered for this screen,
74 66 * is automatically set to true, when a meta box is added.
@@ -73,22 +65,21 @@
73 65 * Whether there are post boxes registered for this screen,
74 66 * is automatically set to true, when a meta box is added.
75 67 *
76 68 * @since 1.0.0
77 - * @var bool
78 69 */
79 - protected $has_meta_boxes = false;
70 + protected bool $has_meta_boxes = false;
80 71
81 72 /**
82 73 * List of WP feature pointers for this view.
83 74 *
84 75 * @since 1.0.0
85 - * @var array
76 + * @var string[]
86 77 */
87 - protected $wp_pointers = array();
78 + protected array $wp_pointers = array();
88 79
89 80 /**
90 - * Initialize the View class, by setting the correct screen columns and adding help texts.
81 + * Initializes the View class, by setting the correct screen columns and adding help texts.
91 82 *
92 83 * @since 1.0.0
93 84 */
94 85 public function __construct() {
@@ -93,29 +84,36 @@
93 84 */
94 85 public function __construct() {
95 86 $screen = get_current_screen();
96 87 if ( 0 !== $this->screen_columns ) {
97 - $screen->add_option( 'layout_columns', array( 'max' => $this->screen_columns ) );
88 + $screen->add_option( 'layout_columns', array( 'max' => $this->screen_columns ) ); // @phpstan-ignore method.nonObject
98 89 }
99 90 // Enable two column layout.
100 - add_filter( "get_user_option_screen_layout_{$screen->id}", array( $this, 'set_current_screen_layout_columns' ) );
91 + add_filter( "get_user_option_screen_layout_{$screen->id}", array( $this, 'set_current_screen_layout_columns' ) ); // @phpstan-ignore property.nonObject
101 92
93 + /* translators: %1$s: URL to TablePress website, %2$s: URL to WordPress Plugin Directory */
102 94 $common_content = '<p>' . sprintf( __( 'More information about TablePress can be found on the <a href="%1$s">plugin website</a> or on its page in the <a href="%2$s">WordPress Plugin Directory</a>.', 'tablepress' ), 'https://tablepress.org/', 'https://wordpress.org/plugins/tablepress/' ) . '</p>';
103 - $common_content .= '<p>' . sprintf( __( 'For technical information, please see the <a href="%s">Documentation</a>.', 'tablepress' ), 'https://tablepress.org/documentation/' ) . ' ' . sprintf( __( 'Common questions are answered in the <a href="%s">FAQ</a>.', 'tablepress' ), 'https://tablepress.org/faq/' ) . '</p>';
95 + /* translators: %s: URL to Documentation page */
96 + $common_content .= '<p>' . sprintf( __( 'For technical information, please see the <a href="%s">Documentation</a>.', 'tablepress' ), 'https://tablepress.org/documentation/' ) . ' ';
97 + /* translators: %s: URL to FAQ page */
98 + $common_content .= sprintf( __( 'Common questions are answered in the <a href="%s">FAQ</a>.', 'tablepress' ), 'https://tablepress.org/faq/' ) . '</p>';
104 99
105 100 if ( tb_tp_fs()->is_free_plan() ) {
106 - $common_content .= '<p>' . sprintf( __( '<a href="%1$s">Support</a> is provided through the <a href="%2$s">WordPress Support Forums</a>.', 'tablepress' ), 'https://tablepress.org/support/', 'https://wordpress.org/tags/tablepress' ) . ' '
107 - . sprintf( __( 'Before asking for support, please carefully read the <a href="%s">Frequently Asked Questions</a>, where you will find answers to the most common questions, and search through the forums.', 'tablepress' ), 'https://tablepress.org/faq/' ) . '</p>';
108 - $common_content .= '<p><strong>' . sprintf( __( 'More great features for you and your site’s visitors and priority email support are available with a Premium license plan of TablePress. <a href="%s">Go check them out!</a>', 'tablepress' ), 'https://tablepress.org/premium/' ) . '</strong></p>';
101 + $common_content .= '<p>'
102 + . sprintf( __( '<a href="%1$s">Support</a> is provided through the <a href="%2$s">WordPress Support Forums</a>.', 'tablepress' ), 'https://tablepress.org/support/', 'https://wordpress.org/support/plugin/tablepress/' )
103 + . ' '
104 + . sprintf( __( 'Before asking for support, please carefully read the <a href="%s">Frequently Asked Questions</a>, where you will find answers to the most common questions, and search through the forums.', 'tablepress' ), 'https://tablepress.org/faq/' )
105 + . '</p>';
106 + $common_content .= '<p><strong>' . sprintf( __( 'More great features for you and your site’s visitors and priority email support are available with a Premium license plan of TablePress. <a href="%s">Go check them out!</a>', 'tablepress' ), 'https://tablepress.org/premium/?utm_source=plugin&utm_medium=textlink&utm_content=help-tab' ) . '</strong></p>';
109 107 }
110 108
111 - $screen->add_help_tab( array(
109 + $screen->add_help_tab( array( // @phpstan-ignore method.nonObject
112 110 'id' => 'tablepress-help', // This should be unique for the screen.
113 111 'title' => __( 'TablePress Help', 'tablepress' ),
114 112 'content' => '<p>' . $this->help_tab_content() . '</p>' . $common_content,
115 113 ) );
116 114 // "Sidebar" in the help tab.
117 - $screen->set_help_sidebar(
115 + $screen->set_help_sidebar( // @phpstan-ignore method.nonObject
118 116 '<p><strong>' . __( 'For more information:', 'tablepress' ) . '</strong></p>'
119 117 . '<p><a href="https://tablepress.org/">TablePress Website</a></p>'
120 118 . '<p><a href="https://tablepress.org/faq/">TablePress FAQ</a></p>'
121 119 . '<p><a href="https://tablepress.org/documentation/">TablePress Documentation</a></p>'
@@ -123,52 +121,55 @@
123 121 );
124 122 }
125 123
126 124 /**
127 - * Change the value of the user option "screen_layout_{$screen->id}" through a filter.
125 + * Changes the value of the user option "screen_layout_{$screen->id}" through a filter.
128 126 *
129 127 * @since 1.0.0
130 128 *
131 - * @param int|false $result Current value of the user option.
129 + * @param string|int|false $result Current value of the user option.
132 130 * @return int New value for the user option.
133 131 */
134 - public function set_current_screen_layout_columns( $result ) {
132 + public function set_current_screen_layout_columns( /* string|int|false */ $result ): int {
135 133 if ( false === $result ) {
136 134 // The user option does not yet exist.
137 - $result = $this->screen_columns;
138 - } elseif ( $result > $this->screen_columns ) {
135 + return $this->screen_columns;
136 + }
137 +
138 + $result = (int) $result;
139 + if ( $result > $this->screen_columns ) {
139 140 // The value of the user option is bigger than what is possible on this screen (e.g. because the number of columns was reduced in an update).
140 141 $result = $this->screen_columns;
141 142 }
143 +
142 144 return $result;
143 145 }
144 146
145 147 /**
146 - * Set up the view with data and do things that are necessary for all views.
148 + * Sets up the view with data and do things that are necessary for all views.
147 149 *
148 150 * @since 1.0.0
149 151 *
150 - * @param string $action Action for this view.
151 - * @param array $data Data for this view.
152 + * @param string $action Action for this view.
153 + * @param array<string, mixed> $data Data for this view.
152 154 */
153 - public function setup( $action, array $data ) {
155 + public function setup( /* string */ $action, array $data ) /* : void */ {
156 + // Don't use type hints (except array $data) in method declaration, as the method is extended in some TablePress Extensions which are no longer updated.
157 +
154 158 $this->action = $action;
155 159 $this->data = $data;
156 160
157 161 // Set page title.
158 - $GLOBALS['title'] = sprintf( __( '%1$s &lsaquo; %2$s', 'tablepress' ), $this->data['view_actions'][ $this->action ]['page_title'], 'TablePress' );
162 + $GLOBALS['title'] = sprintf( __( '%1$s &lsaquo; %2$s', 'tablepress' ), $this->data['view_actions'][ $this->action ]['page_title'], 'TablePress' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
159 163
160 - // Admin page helpers, like script/style loading, could be moved to view.
161 - $this->admin_page = TablePress::load_class( 'TablePress_Admin_Page', 'class-admin-page-helper.php', 'classes' );
162 - $this->admin_page->enqueue_style( 'common' );
164 + add_filter( 'admin_footer_text', array( $this, 'add_admin_footer_text' ) );
165 +
166 + TablePress::enqueue_style( 'common', array( 'wp-components' ) );
163 167 // RTL styles for the admin interface.
164 168 if ( is_rtl() ) {
165 - $this->admin_page->enqueue_style( 'common-rtl', array( 'tablepress-common' ) );
169 + TablePress::enqueue_style( 'common-rtl', array( 'tablepress-common' ) );
166 170 }
167 - $this->admin_page->enqueue_script( 'common', array( 'jquery-core', 'postbox' ) );
168 171
169 - $this->admin_page->add_admin_footer_text();
170 -
171 172 // Initialize WP feature pointers for TablePress.
172 173 $this->_init_wp_pointers();
173 174
174 175 // Necessary fields for all views.
@@ -177,9 +178,9 @@
177 178 $this->add_text_box( 'action_field', array( $this, 'action_field' ), 'header', false );
178 179 }
179 180
180 181 /**
181 - * Register a header message for the view.
182 + * Registers a header message for the view.
182 183 *
183 184 * @since 1.0.0
184 185 *
185 186 * @param string $text Text for the header message.
@@ -185,10 +186,10 @@
185 186 * @param string $text Text for the header message.
186 187 * @param string $css_class Optional. Additional CSS class for the header message.
187 188 * @param string $title Optional. Text for the header title.
188 189 */
189 - protected function add_header_message( $text, $css_class = 'notice-success', $title = '' ) {
190 - if ( ! strpos( $css_class, 'not-dismissible' ) ) {
190 + protected function add_header_message( string $text, string $css_class = 'is-success notice-success', string $title = '' ): void {
191 + if ( ! str_contains( $css_class, 'not-dismissible' ) ) {
191 192 $css_class .= ' is-dismissible';
192 193 }
193 194 if ( '' !== $title ) {
194 195 $title = "<h3>{$title}</h3>";
@@ -193,24 +194,24 @@
193 194 if ( '' !== $title ) {
194 195 $title = "<h3>{$title}</h3>";
195 196 }
196 197 // Wrap the message text in HTML <p> tags if it does not already start with one (potentially with attributes), indicating custom message HTML.
197 - if ( '' !== $text && 0 !== strpos( $text, '<p' ) ) {
198 + if ( '' !== $text && ! str_starts_with( $text, '<p' ) ) {
198 199 $text = "<p>{$text}</p>";
199 200 }
200 - $this->header_messages[] = "<div class=\"notice {$css_class}\">{$title}{$text}</div>\n";
201 + $this->header_messages[] = "<div class=\"notice components-notice {$css_class}\"><div class=\"components-notice__content\">{$title}{$text}</div></div>\n";
201 202 }
202 203
203 204 /**
204 - * Process header action messages, i.e. check if a message should be added to the page.
205 + * Processes header action messages, i.e. check if a message should be added to the page.
205 206 *
206 207 * @since 1.0.0
207 208 *
208 - * @param array $action_messages Action messages for the screen.
209 + * @param array<string, string> $action_messages Action messages for the screen.
209 210 */
210 - protected function process_action_messages( array $action_messages ) {
211 + protected function process_action_messages( array $action_messages ): void {
211 212 if ( $this->data['message'] && isset( $action_messages[ $this->data['message'] ] ) ) {
212 - $class = ( 0 === strpos( $this->data['message'], 'error' ) ) ? 'notice-error' : 'notice-success';
213 + $class = ( str_starts_with( $this->data['message'], 'error' ) ) ? 'is-error notice-error' : 'is-success notice-success';
213 214
214 215 if ( '' !== $this->data['error_details'] ) {
215 216 $this->data['error_details'] = '</p><p>' . sprintf( __( 'Error code: %s', 'tablepress' ), '<code>' . esc_html( $this->data['error_details'] ) . '</code>' );
216 217 }
@@ -219,18 +220,18 @@
219 220 }
220 221 }
221 222
222 223 /**
223 - * Register a text box for the view.
224 + * Registers a text box for the view.
224 225 *
225 226 * @since 1.0.0
226 227 *
227 228 * @param string $id Unique HTML ID for the text box container (only visible with $wrap = true).
228 - * @param callback $callback Callback that prints the contents of the text box.
229 + * @param callable $callback Callback that prints the contents of the text box.
229 230 * @param string $context Optional. Context/position of the text box (normal, side, additional, header, submit).
230 231 * @param bool $wrap Whether the content of the text box shall be wrapped in a <div> container.
231 232 */
232 - protected function add_text_box( $id, $callback, $context = 'normal', $wrap = false ) {
233 + protected function add_text_box( string $id, callable $callback, string $context = 'normal', bool $wrap = false ): void {
233 234 if ( ! isset( $this->textboxes[ $context ] ) ) {
234 235 $this->textboxes[ $context ] = array();
235 236 }
236 237
@@ -243,32 +244,32 @@
243 244 );
244 245 }
245 246
246 247 /**
247 - * Register a post meta box for the view, that is drag/droppable with WordPress functionality.
248 + * Registers a post meta box for the view, that is drag/droppable with WordPress functionality.
248 249 *
249 250 * @since 1.0.0
250 251 *
251 - * @param string $id Unique ID for the meta box.
252 - * @param string $title Title for the meta box.
253 - * @param callback $callback Callback that prints the contents of the post meta box.
254 - * @param string $context Optional. Context/position of the post meta box (normal, side, additional).
255 - * @param string $priority Optional. Order of the post meta box for the $context position (high, default, low).
256 - * @param array $callback_args Optional. Additional data for the callback function (e.g. useful when in different class).
252 + * @param string $id Unique ID for the meta box.
253 + * @param string $title Title for the meta box.
254 + * @param callable $callback Callback that prints the contents of the post meta box.
255 + * @param 'normal'|'side'|'additional' $context Optional. Context/position of the post meta box (normal, side, additional).
256 + * @param 'core'|'default'|'high'|'low' $priority Optional. Order of the post meta box for the $context position (high, default, low).
257 + * @param mixed[]|null $callback_args Optional. Additional data for the callback function (e.g. useful when in different class).
257 258 */
258 - protected function add_meta_box( $id, $title, $callback, $context = 'normal', $priority = 'default', $callback_args = null ) {
259 + protected function add_meta_box( string $id, string $title, callable $callback, string $context = 'normal', string $priority = 'default', ?array $callback_args = null ): void {
259 260 $this->has_meta_boxes = true;
260 261 add_meta_box( "tablepress_{$this->action}-{$id}", $title, $callback, null, $context, $priority, $callback_args );
261 262 }
262 263
263 264 /**
264 - * Render all text boxes for the given context.
265 + * Renders all text boxes for the given context.
265 266 *
266 267 * @since 1.0.0
267 268 *
268 269 * @param string $context Context (normal, side, additional, header, submit) for which registered text boxes shall be rendered.
269 270 */
270 - protected function do_text_boxes( $context ) {
271 + protected function do_text_boxes( string $context ): void {
271 272 if ( empty( $this->textboxes[ $context ] ) ) {
272 273 return;
273 274 }
274 275
@@ -283,32 +284,31 @@
283 284 }
284 285 }
285 286
286 287 /**
287 - * Render all post meta boxes for the given context, if there are post meta boxes.
288 + * Renders all post meta boxes for the given context, if there are post meta boxes.
288 289 *
289 290 * @since 1.0.0
290 291 *
291 292 * @param string $context Context (normal, side, additional) for which registered post meta boxes shall be rendered.
292 293 */
293 - protected function do_meta_boxes( $context ) {
294 - if ( ! $this->has_meta_boxes ) {
295 - return;
294 + protected function do_meta_boxes( string $context ): void {
295 + if ( $this->has_meta_boxes ) {
296 + do_meta_boxes( get_current_screen(), $context, $this->data ); // @phpstan-ignore argument.type
296 297 }
297 - do_meta_boxes( null, $context, $this->data );
298 298 }
299 299
300 300 /**
301 - * Print hidden fields with nonces for post meta box AJAX handling, if there are post meta boxes on the screen.
301 + * Prints hidden fields with nonces for post meta box AJAX handling, if there are post meta boxes on the screen.
302 302 *
303 303 * The check is possible as this function is executed after post meta boxes have to be registered.
304 304 *
305 305 * @since 1.0.0
306 306 *
307 - * @param array $data Data for this screen.
308 - * @param array $box Information about the text box.
307 + * @param array<string, mixed> $data Data for this screen.
308 + * @param array<string, mixed> $box Information about the text box.
309 309 */
310 - protected function default_nonce_fields( array $data, array $box ) {
310 + protected function default_nonce_fields( array $data, array $box ): void {
311 311 if ( ! $this->has_meta_boxes ) {
312 312 return;
313 313 }
314 314 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
@@ -317,38 +317,38 @@
317 317 echo "\n";
318 318 }
319 319
320 320 /**
321 - * Print hidden field with a nonce for the screen's action, to be transmitted in HTTP requests.
321 + * Prints hidden field with a nonce for the screen's action, to be transmitted in HTTP requests.
322 322 *
323 323 * @since 1.0.0
324 324 *
325 - * @param array $data Data for this screen.
326 - * @param array $box Information about the text box.
325 + * @param array<string, mixed> $data Data for this screen.
326 + * @param array<string, mixed> $box Information about the text box.
327 327 */
328 - protected function action_nonce_field( array $data, array $box ) {
328 + protected function action_nonce_field( array $data, array $box ): void {
329 329 wp_nonce_field( TablePress::nonce( $this->action ) );
330 330 echo "\n";
331 331 }
332 332
333 333 /**
334 - * Print hidden field with the screen action.
334 + * Prints hidden field with the screen action.
335 335 *
336 336 * @since 1.0.0
337 337 *
338 - * @param array $data Data for this screen.
339 - * @param array $box Information about the text box.
338 + * @param array<string, mixed> $data Data for this screen.
339 + * @param array<string, mixed> $box Information about the text box.
340 340 */
341 - protected function action_field( array $data, array $box ) {
342 - echo "<input type=\"hidden\" name=\"action\" value=\"tablepress_{$this->action}\" />\n";
341 + protected function action_field( array $data, array $box ): void {
342 + echo "<input type=\"hidden\" name=\"action\" value=\"tablepress_{$this->action}\">\n";
343 343 }
344 344
345 345 /**
346 - * Render the current view.
346 + * Renders the current view.
347 347 *
348 348 * @since 1.0.0
349 349 */
350 - public function render() {
350 + public function render(): void {
351 351 ?>
352 352 <div id="tablepress-page" class="wrap">
353 353 <?php
354 354 $this->print_nav_tab_menu();
@@ -353,9 +353,10 @@
353 353 <?php
354 354 $this->print_nav_tab_menu();
355 355 ?>
356 356 <div id="tablepress-body">
357 - <hr class="wp-header-end" />
357 + <hr class="wp-header-end">
358 + <script>document.querySelectorAll('.notice:has(~#tablepress-page)').forEach(e=>document.querySelector('.wp-header-end').after(e));</script>
358 359 <?php
359 360 // Print all header messages.
360 361 foreach ( $this->header_messages as $message ) {
361 362 echo $message;
@@ -362,13 +363,14 @@
362 363 }
363 364 // "Import" screen has file upload.
364 365 $enctype = ( 'import' === $this->action ) ? ' enctype="multipart/form-data"' : '';
365 366 ?>
366 - <form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post"<?php echo $enctype; ?>>
367 + <form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post"<?php echo $enctype; ?> id="tablepress-page-form">
367 368 <?php
368 369 $this->do_text_boxes( 'header' );
370 + $hide_if_no_js = ( in_array( $this->action, array( 'export', 'import' ), true ) ) ? ' class="hide-if-no-js"' : '';
369 371 ?>
370 - <div id="poststuff">
372 + <div id="poststuff"<?php echo $hide_if_no_js; ?>>
371 373 <div id="post-body" class="metabox-holder columns-<?php echo ( isset( $GLOBALS['screen_layout_columns'] ) && ( 2 === $GLOBALS['screen_layout_columns'] ) ) ? '2' : '1'; ?>">
372 374 <div id="postbox-container-2" class="postbox-container">
373 375 <?php
374 376 $this->do_text_boxes( 'normal' );
@@ -388,9 +390,9 @@
388 390 $this->do_meta_boxes( 'side' );
389 391 ?>
390 392 </div>
391 393 </div>
392 - <br class="clear" />
394 + <br class="clear">
393 395 </div>
394 396 </form>
395 397 </div>
396 398 </div>
@@ -397,23 +399,39 @@
397 399 <?php
398 400 }
399 401
400 402 /**
401 - * Render the navigation menu with links to the possible actions, highlighting the current one.
403 + * Renders the navigation menu with links to the possible actions, highlighting the current one.
402 404 *
403 405 * @since 1.0.0
404 406 */
405 - protected function print_nav_tab_menu() {
407 + protected function print_nav_tab_menu(): void {
408 + $milestone_period_ended = ( strtotime( '2026-10-10' ) < strtotime( 'today' ) );
409 + $name = __( 'TablePress', 'tablepress' );
410 + $filename = 'admin/img/tablepress.svg';
406 411 ?>
407 412 <div id="tablepress-header" class="header">
408 - <h1 class="name"><img src="<?php echo plugins_url( 'admin/img/tablepress-icon.png', TABLEPRESS__FILE__ ); ?>" class="tablepress-icon" alt="<?php esc_attr_e( 'TablePress plugin logo', 'tablepress' ); ?>" /><?php _e( 'TablePress', 'tablepress' ); ?><?php echo tb_tp_fs()->is_plan_or_trial( 'pro', true ) ? ' Pro' : ( tb_tp_fs()->is_plan_or_trial( 'max', true ) ? ' Max' : '' ); ?></h1>
409 - <?php if ( tb_tp_fs()->is_free_plan() ) : ?>
410 - <div class="buttons">
411 - <a href="<?php echo 'https://tablepress.org/premium/'; ?>" class="tablepress-button">
412 - <span><?php _e( 'Upgrade to Premium', 'tablepress' ); ?></span>
413 - <span class="dashicons dashicons-arrow-right-alt"></span>
414 - </a>
415 - </div>
413 + <h1 class="name">
414 + <img src="<?php echo plugins_url( $filename, TABLEPRESS__FILE__ ); ?>" alt="<?php esc_attr_e( 'TablePress plugin logo', 'tablepress' ); ?>">
415 + <span class="screen-reader-text"><?php echo $name; ?></span>
416 + </h1>
417 + <?php if ( 'list' !== $this->action || ! $this->data['messages']['plugin_update'] || $milestone_period_ended ) : ?>
418 + <?php if ( ! TABLEPRESS_IS_PLAYGROUND_PREVIEW && tb_tp_fs()->is_free_plan() ) : ?>
419 + <div class="buttons">
420 + <a href="<?php echo $milestone_period_ended ? esc_url( tb_tp_fs()->pricing_url( WP_FS__PERIOD_ANNUALLY, false ) ) : 'https://tablepress.org/premium/?utm_source=plugin&utm_medium=header&utm_campaign=upgrade-message340&utm_content=button'; ?>" class="tablepress-button">
421 + <span>
422 + <?php
423 + if ( $milestone_period_ended ) {
424 + _e( 'Upgrade to Premium', 'tablepress' );
425 + } else {
426 + echo in_array( get_user_locale(), array( 'de_DE', 'de_AT', 'de_CH' ), true ) ? 'Premium: Spare jetzt 25&#8239;%' : 'Premium: Get 25% off now';
427 + }
428 + ?>
429 + </span>
430 + <span class="dashicons dashicons-arrow-right-alt"></span>
431 + </a>
432 + </div>
433 + <?php endif; ?>
416 434 <?php endif; ?>
417 435 </div>
418 436 <nav id="tablepress-nav">
419 437 <ul class="nav-menu">
@@ -441,61 +459,131 @@
441 459 * Prints a notification about JavaScript not being activated in the browser.
442 460 *
443 461 * @since 2.0.0
444 462 *
445 - * @param array $data Data for this screen.
446 - * @param array $box Information about the text box.
463 + * @param array<string, mixed> $data Data for this screen.
464 + * @param array<string, mixed> $box Information about the text box.
447 465 */
448 - public function textbox_no_javascript( array $data, array $box ) {
466 + public function textbox_no_javascript( array $data, array $box ): void {
449 467 ?>
450 - <div class="notice notice-error notice-alt notice-large hide-if-js">
451 - <h3><em>
452 - <?php _e( 'Attention: Unfortunately, there is a problem!', 'tablepress' ); ?>
453 - </em></h3>
454 - <p style="font-size:14px">
455 - <strong><?php _e( 'This screen requires JavaScript. Please enable JavaScript in your browser settings.', 'tablepress' ); ?></strong><br />
456 - <?php _e( 'For help, please follow <a href="https://www.enable-javascript.com/">the instructions on how to enable JavaScript in your browser</a>.', 'tablepress' ); ?>
457 - </p>
458 - <p>
459 - <?php echo '<a href="' . TablePress::url( array( 'action' => 'list' ) ) . '">' . __( 'Back to the List of Tables', 'tablepress' ) . '</a>'; ?>
460 - </p>
468 + <div class="notice components-notice is-error notice-error hide-if-js">
469 + <div class="components-notice__content">
470 + <h3><em>
471 + <?php _e( 'Attention: Unfortunately, there is a problem!', 'tablepress' ); ?>
472 + </em></h3>
473 + <p style="font-size:14px">
474 + <strong><?php _e( 'This screen requires JavaScript. Please enable JavaScript in your browser settings.', 'tablepress' ); ?></strong><br>
475 + <?php _e( 'For help, please follow <a href="https://www.enable-javascript.com/">the instructions on how to enable JavaScript in your browser</a>.', 'tablepress' ); ?>
476 + </p>
477 + <p>
478 + <?php echo '<a href="' . esc_url( TablePress::url( array( 'action' => 'list' ) ) ) . '">' . __( 'Back to the List of Tables', 'tablepress' ) . '</a>'; ?>
479 + </p>
480 + </div>
461 481 </div>
462 482 <?php
463 483 }
464 484
465 485 /**
466 - * Print a submit button (only done when function is used as a callback for a text box).
486 + * Prints a submit button (only done when function is used as a callback for a text box).
467 487 *
488 + * This method is soft-deprecated. It's no longer used in TablePress, but e.g. in the "TablePress Debug Extension".
489 + *
468 490 * @since 1.0.0
469 491 *
470 - * @param array $data Data for this screen.
471 - * @param array $box Information about the text box.
492 + * @param array<string, mixed> $data Data for this screen.
493 + * @param array<string, mixed> $box Information about the text box.
472 494 */
473 - protected function textbox_submit_button( array $data, array $box ) {
474 - $caption = isset( $data['submit_button_caption'] ) ? $data['submit_button_caption'] : __( 'Save Changes', 'tablepress' );
495 + protected function textbox_submit_button( array $data, array $box ): void {
475 496 ?>
476 - <p class="submit"><input type="submit" value="<?php echo esc_attr( $caption ); ?>" class="button button-primary button-large" /></p>
497 + <p class="submit"><input type="submit" class="components-button is-primary button-save-changes" value="<?php esc_attr_e( 'Save Changes', 'tablepress' ); ?>"></p>
477 498 <?php
478 499 }
479 500
480 501 /**
481 - * Return the content for the help tab for this screen.
502 + * Returns a safe JSON representation of a variable for printing inside of JavaScript code.
482 503 *
504 + * @since 2.0.0
505 + *
506 + * @param mixed $data Variable to convert to JSON.
507 + * @return string Safe JSON representation of a variable for printing inside of JavaScript code.
508 + */
509 + public function convert_to_json_parse_output( /* string|array|bool|int|float|null */ $data ): string {
510 + $json = wp_json_encode( $data, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES );
511 + if ( false === $json ) {
512 + // JSON encoding failed, return an error object. Use a prefixed "_error" key to avoid conflicts with intentionally added "error" keys.
513 + $json = '{ "_error": "The data could not be encoded to JSON!" }';
514 + }
515 + // Print the JSON data inside a `JSON.parse()` call in JS for speed gains, with necessary escaping of `\` and `'`.
516 + $json = str_replace( array( '\\', "'" ), array( '\\\\', "\'" ), $json );
517 + return "JSON.parse( '{$json}' )";
518 + }
519 +
520 + /**
521 + * Prints JavaScript variables for the screen.
522 + *
523 + * @since 3.1.0
524 + *
525 + * @param string $variable Name of the JavaScript variable.
526 + * @param array<string, mixed> $data Information about the text box.
527 + */
528 + protected function print_script_data_json( string $variable, array $data ): void {
529 + echo "<script>\n";
530 + echo "window.tp = window.tp || {};\n";
531 + echo "tp.{$variable} = {\n";
532 + foreach ( $data as $key => $value ) {
533 + $value = $this->convert_to_json_parse_output( $value );
534 + echo "\t{$key}: {$value},\n";
535 + }
536 + echo "};\n";
537 + echo "</script>\n";
538 + }
539 +
540 + /**
541 + * Returns the content for the help tab for this screen.
542 + *
483 543 * Has to be implemented for every view that is visible in the WP Dashboard!
484 544 *
485 545 * @since 1.0.0
546 + *
547 + * @return string Help tab content for the view.
486 548 */
487 - protected function help_tab_content() {
549 + protected function help_tab_content(): string {
488 550 // Has to be implemented for every view that is visible in the WP Dashboard!
489 551 return '';
490 552 }
491 553
492 554 /**
493 - * Initialize the WP feature pointers for TablePress.
555 + * Adds a TablePress "Thank You" message to the admin footer content.
494 556 *
495 557 * @since 1.0.0
558 + * @since 3.3.0 This method was moved from the now-removed `TablePress_Admin_Page` class.
559 + *
560 + * @param string $content Current admin footer content.
561 + * @return string New admin footer content.
496 562 */
497 - protected function _init_wp_pointers() {
563 + public function add_admin_footer_text( /* string */ $content ): string {
564 + // Don't use a type hint in the method declaration as many WordPress plugins use the `admin_footer_text` filter without returning a string.
565 +
566 + // Protect against other plugins not returning a string in their filter callbacks.
567 + if ( ! is_string( $content ) ) { // @phpstan-ignore function.alreadyNarrowedType (The `is_string()` check is needed as the input is coming from a filter hook.)
568 + $content = '';
569 + }
570 +
571 + /* translators: %s: URL to TablePress website */
572 + $content .= ' &bull; ' . sprintf( __( 'Thank you for using <a href="%s">TablePress</a>.', 'tablepress' ), 'https://tablepress.org/' );
573 + if ( tb_tp_fs()->is_free_plan() ) {
574 + /* translators: %s: URL to TablePress premium features */
575 + $content .= ' ' . sprintf( __( 'Take a look at the <a href="%s">Premium features</a>!', 'tablepress' ), 'https://tablepress.org/premium/?utm_source=plugin&utm_medium=textlink&utm_content=admin-footer' );
576 + }
577 + return $content;
578 + }
579 +
580 + /**
581 + * Initializes the WP feature pointers for TablePress.
582 + *
583 + * @since 1.0.0
584 + */
585 + protected function _init_wp_pointers(): void {
498 586 // Check if there are WP pointers for this view.
499 587 if ( empty( $this->wp_pointers ) ) {
500 588 return;
501 589 }
@@ -505,9 +593,9 @@
505 593
506 594 $pointers_on_page = false;
507 595 foreach ( array_diff( $this->wp_pointers, $dismissed ) as $pointer ) {
508 596 // Bind pointer print function.
509 - add_action( "admin_footer-{$GLOBALS['hook_suffix']}", array( $this, 'wp_pointer_' . $pointer ) );
597 + add_action( "admin_footer-{$GLOBALS['hook_suffix']}", array( $this, 'wp_pointer_' . $pointer ) ); // @phpstan-ignore argument.type
510 598 $pointers_on_page = true;
511 599 }
512 600
513 601 if ( $pointers_on_page ) {
@@ -513,7 +601,63 @@
513 601 if ( $pointers_on_page ) {
514 602 wp_enqueue_style( 'wp-pointer' );
515 603 wp_enqueue_script( 'wp-pointer' );
516 604 }
605 + }
606 +
607 + /**
608 + * Prints the JavaScript code for a WP feature pointer.
609 + *
610 + * @since 1.0.0
611 + * @since 3.3.0 This method was moved from the now-removed `TablePress_Admin_Page` class.
612 + *
613 + * @param string $pointer_id The pointer ID.
614 + * @param string $selector The HTML elements, on which the pointer should be attached.
615 + * @param array<string, mixed> $args Arguments to be passed to the pointer JS (see wp-pointer.js).
616 + */
617 + public function print_wp_pointer_js( string $pointer_id, string $selector, array $args ): void {
618 + if ( empty( $pointer_id ) || empty( $selector ) || empty( $args['content'] ) ) {
619 + return;
620 + }
621 +
622 + $keyboard_shortcut = '';
623 + if ( 'tp33_edit_quick_navigation' === $pointer_id ) {
624 + $keyboard_shortcut = <<<JS
625 + content: options.content.replace( /%metaKey%/g, window?.navigator?.platform?.includes( 'Mac' ) ? wp.i18n._x( '⌘', 'keyboard shortcut modifier key on a Mac keyboard', 'tablepress' ) : wp.i18n._x( 'Ctrl+', 'keyboard shortcut modifier key on a non-Mac keyboard', 'tablepress' ) ),\n
626 + JS;
627 + }
628 +
629 + /*
630 + * Print JS code for the feature pointers, extended with event handling for opened/closed "Screen Options", so that pointers can
631 + * be repositioned. 210 ms is slightly slower than jQuery's "fast" value, to allow all elements to reach their original position.
632 + */
633 + ?>
634 +<script>
635 +( ( $ ) => {
636 + let options = <?php echo wp_json_encode( $args, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ); ?>;
637 + if ( ! options ) {
638 + return;
639 + }
640 +
641 + options = {
642 + ...options,
643 + <?php echo $keyboard_shortcut; ?>
644 + close() {
645 + $.post( ajaxurl, {
646 + pointer: '<?php echo $pointer_id; ?>',
647 + action: 'dismiss-wp-pointer'
648 + } );
649 + $( this ).pointer( { 'disabled': true } );
650 + },
651 + };
652 +
653 + $( () => setTimeout( () => $( '<?php echo $selector; ?>' ).pointer( options ).pointer( 'open' ), 1 ) );
654 +
655 + $( document ).on( 'screen:options:open screen:options:close', () => {
656 + setTimeout( () => $( '<?php echo $selector; ?>' ).pointer( 'reposition' ), 210 );
657 + } );
658 +} )( jQuery );
659 +</script>
660 + <?php
517 661 }
518 662
519 663 } // class TablePress_View