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 | views/view-list.php +309 -646 1.12 → 3.4 View file →
@@ -7,13 +7,16 @@
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 /**
15 17 * List Tables View class
18 + *
16 19 * @package TablePress
17 20 * @subpackage Views
18 21 * @author Tobias Bäthge
19 22 * @since 1.0.0
@@ -23,11 +26,10 @@
23 26 /**
24 27 * Object for the All Tables List Table.
25 28 *
26 29 * @since 1.0.0
27 - * @var TablePress_All_Tables_List_Table
28 30 */
29 - protected $wp_list_table;
31 + protected \TablePress_All_Tables_List_Table $wp_list_table;
30 32
31 33 /**
32 34 * Set up the view with data and do things that are specific for this view.
33 35 *
@@ -32,95 +34,276 @@
32 34 * Set up the view with data and do things that are specific for this view.
33 35 *
34 36 * @since 1.0.0
35 37 *
36 - * @param string $action Action for this view.
37 - * @param array $data Data for this view.
38 + * @param string $action Action for this view.
39 + * @param array<string, mixed> $data Data for this view.
38 40 */
39 - public function setup( $action, array $data ) {
41 + #[\Override]
42 + public function setup( /* string */ $action, array $data ) /* : void */ {
43 + // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
44 +
40 45 parent::setup( $action, $data );
41 46
42 - add_thickbox();
43 - $this->admin_page->enqueue_script( 'list', array( 'jquery' ), array(
44 - 'list' => array(
45 - 'shortcode_popup' => __( 'To embed this table into a post or page, use this Shortcode:', 'tablepress' ),
46 - 'donation-message-already-donated' => __( 'Thank you very much! Your donation is highly appreciated. You just contributed to the further development of TablePress!', 'tablepress' ),
47 - 'donation-message-maybe-later' => sprintf( __( 'No problem! I still hope you enjoy the benefits that TablePress adds to your site. If you should change your mind, you&#8217;ll always find the &#8220;Donate&#8221; button on the <a href="%s">TablePress website</a>.', 'tablepress' ), 'https://tablepress.org/' ),
48 - ),
49 - ) );
47 + TablePress::enqueue_script( 'list' );
50 48
51 - if ( $data['messages']['first_visit'] ) {
52 - $this->add_header_message(
53 - __( 'Thank you for using TablePress for the first time!', 'tablepress' ) . ' '
54 - . sprintf( __( 'If you encounter any questions or problems, please visit the <a href="%1$s">FAQ</a>, the <a href="%2$s">Documentation</a>, and the <a href="%3$s">Support</a> section on the <a href="%4$s">plugin website</a>.', 'tablepress' ), 'https://tablepress.org/faq/', 'https://tablepress.org/documentation/', 'https://tablepress.org/support/', 'https://tablepress.org/' ) . '</p><p>'
55 - . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'first_visit', 'return' => 'list' ), __( 'Hide this message', 'tablepress' ) ),
56 - 'notice-info not-dismissible',
57 - '<em>' . __( 'Welcome!', 'tablepress' ) . '</em>'
49 + $milestone_period_ended = ( strtotime( '2026-10-10' ) < strtotime( 'today' ) );
50 +
51 + if ( $data['messages']['superseded_extensions'] ) {
52 + $superseded_extensions = array(
53 + 'tablepress-advanced-access-rights/tablepress-advanced-access-rights.php' => array(
54 + 'name' => 'TablePress Extension: Advanced Access Rights',
55 + 'compatible' => true,
56 + ),
57 + 'tablepress-auto-export-tables/tablepress-auto-export-tables.php' => array(
58 + 'name' => 'TablePress Extension: Automatic Table Export',
59 + 'compatible' => true,
60 + ),
61 + 'tablepress-cell-highlighting/tablepress-cell-highlighting.php' => array(
62 + 'name' => 'TablePress Extension: Cell Highlighting',
63 + 'compatible' => true,
64 + ),
65 + 'tablepress-datatables-alphabetsearch/tablepress-datatables-alphabetsearch.php' => array(
66 + 'name' => 'TablePress Extension: DataTables AlphabetSearch',
67 + 'compatible' => false,
68 + ),
69 + 'tablepress-datatables-buttons/tablepress-datatables-buttons.php' => array(
70 + 'name' => 'TablePress Extension: DataTables Buttons',
71 + 'compatible' => true,
72 + ),
73 + 'tablepress-datatables-column-filter-widgets/tablepress-datatables-column-filter-widgets.php' => array(
74 + 'name' => 'TablePress Extension: DataTables ColumnFilterWidgets',
75 + 'compatible' => false,
76 + ),
77 + 'tablepress-datatables-columnfilter/tablepress-datatables-columnfilter.php' => array(
78 + 'name' => 'TablePress Extension: DataTables Column Filter',
79 + 'compatible' => false,
80 + ),
81 + 'tablepress-datatables-counter-column/tablepress-datatables-counter-column.php' => array(
82 + 'name' => 'TablePress Extension: DataTables Counter Column',
83 + 'compatible' => true,
84 + ),
85 + 'tablepress-datatables-fixedcolumns/tablepress-datatables-fixedcolumns.php' => array(
86 + 'name' => 'TablePress Extension: DataTables FixedColumns',
87 + 'compatible' => false,
88 + ),
89 + 'tablepress-datatables-fixedheader/tablepress-datatables-fixedheader.php' => array(
90 + 'name' => 'TablePress Extension: DataTables FixedHeader',
91 + 'compatible' => true,
92 + ),
93 + 'tablepress-datatables-inverted-filter/tablepress-datatables-inverted-filter.php' => array(
94 + 'name' => 'TablePress Extension: DataTables Inverted Filter',
95 + 'compatible' => false,
96 + ),
97 + 'tablepress-datatables-row-details/tablepress-datatables-row-details.php' => array(
98 + 'name' => 'TablePress Extension: DataTables Row Details',
99 + 'compatible' => false,
100 + ),
101 + 'tablepress-datatables-rowgroup/tablepress-datatables-rowgroup.php' => array(
102 + 'name' => 'TablePress Extension: DataTables RowGroup',
103 + 'compatible' => false,
104 + ),
105 + 'tablepress-responsive-tables/tablepress-responsive-tables.php' => array(
106 + 'name' => 'TablePress Extension: Responsive Tables',
107 + 'compatible' => false,
108 + ),
109 + 'tablepress-row-filter/tablepress-row-filter.php' => array(
110 + 'name' => 'TablePress Extension: Row Filtering',
111 + 'compatible' => true,
112 + ),
113 + 'tablepress-row-highlighting/tablepress-row-highlighting.php' => array(
114 + 'name' => 'TablePress Extension: Row Highlighting',
115 + 'compatible' => true,
116 + ),
117 + 'tablepress-table-auto-update/tablepress-table-auto-update.php' => array(
118 + 'name' => 'TablePress Extension: Table Auto Update',
119 + 'compatible' => true,
120 + ),
121 + 'tablepress-table-column-order/tablepress-table-column-order.php' => array(
122 + 'name' => 'TablePress Extension: Table Column Order',
123 + 'compatible' => true,
124 + ),
58 125 );
126 +
127 + $active_compatible_superseded_extensions = array();
128 + $active_incompatible_superseded_extensions = array();
129 + foreach ( $superseded_extensions as $plugin => $extension ) {
130 + if ( is_plugin_active( $plugin ) ) {
131 + if ( $extension['compatible'] ) {
132 + $active_compatible_superseded_extensions[] = $extension['name'];
133 + } else {
134 + $active_incompatible_superseded_extensions[] = $extension['name'];
135 + }
136 + }
137 + }
138 +
139 + /*
140 + * If no superseded extensions are used, hide the message, to prevent running the checks on every load of the view.
141 + * This leaves a small risk of an Extension being activated later, but that's acceptable as they are no longer publicly available.
142 + */
143 + if ( empty( $active_incompatible_superseded_extensions ) && ( empty( $active_compatible_superseded_extensions ) || ! tb_tp_fs()->is_free_plan() ) ) {
144 + TablePress::$model_options->update( 'message_superseded_extensions', false );
145 + }
146 +
147 + $message = '';
148 +
149 + $notice_css_classes = 'not-dismissible';
150 +
151 + if ( ! empty( $active_incompatible_superseded_extensions ) ) {
152 + $notice_css_classes .= ' is-error notice-error';
153 +
154 + $message .= '<p style="font-size:16px;">' . __( 'You are using <strong>TablePress Extension plugins</strong> on this website that have been retired many years ago.', 'tablepress' ) . '<br>' . __( 'For technical reasons, some or all features of these outdated plugins <strong>do no longer work</strong>:', 'tablepress' ) . '</p>';
155 + $message .= '<ul style="list-style:disc;margin:0.5em 1em;font-size:16px;">';
156 + foreach ( $active_incompatible_superseded_extensions as $extension ) {
157 + $message .= '<li>' . esc_html( $extension ) . '</li>';
158 + }
159 + $message .= '</ul>';
160 + $message .= '<p style="font-size:16px;"><strong>' . __( 'Keeping them activated can lead to errors on your website.', 'tablepress' ) . '</strong></p>';
161 +
162 + if ( tb_tp_fs()->is_free_plan() ) {
163 + $message .= '<p style="font-size:16px;"><strong>' . sprintf( __( 'To continue using these features, <a href="%s">upgrade to a TablePress Premium license plan</a>!', 'tablepress' ), 'https://tablepress.org/upgrade-extensions/?utm_source=plugin&utm_medium=textlink&utm_content=superseded-extensions-message' ) . '</strong><br>'
164 + . __( 'TablePress Pro and TablePress Max come with updated and heavily improved versions of these features and include direct priority email support.', 'tablepress' ) . '</p>';
165 + }
166 +
167 + if ( ! empty( $active_compatible_superseded_extensions ) && tb_tp_fs()->is_free_plan() ) {
168 + $message .= '<p style="margin-top:1em;font-size:14px;">' . __( 'In addition, these TablePress Extension plugins have been retired and might become incompatible in the future as well:', 'tablepress' ) . '</strong></p>';
169 + $message .= '<ul style="list-style:disc;margin:0.5em 1em;font-size:14px;">';
170 + foreach ( $active_compatible_superseded_extensions as $extension ) {
171 + $message .= '<li>' . esc_html( $extension ) . '</li>';
172 + }
173 + $message .= '</ul>';
174 + }
175 + } elseif ( ! empty( $active_compatible_superseded_extensions ) && tb_tp_fs()->is_free_plan() ) {
176 + $notice_css_classes .= ' is-warning notice-warning';
177 +
178 + $message .= '<p style="font-size:14px;"><strong>' . __( 'You are using TablePress Extension plugins on this website that have been retired and will no longer receive updates or support:', 'tablepress' ) . '</strong></p>';
179 + $message .= '<ul style="list-style:disc;margin:0.5em 1em;">';
180 + foreach ( $active_compatible_superseded_extensions as $extension ) {
181 + $message .= '<li>' . esc_html( $extension ) . '</li>';
182 + }
183 + $message .= '</ul>';
184 + $message .= '<p style="font-size:14px;"><strong>' . __( 'It is possible that these become incompatible with TablePress or WordPress in the future!', 'tablepress' ) . '</strong></p>';
185 + $message .= '<p style="margin-top:1em;font-size:14px;">' . sprintf( __( 'However, their features were heavily improved and are now part of the up-to-date <a href="%s">TablePress Premium versions</a>!', 'tablepress' ), 'https://tablepress.org/upgrade-extensions/?utm_source=plugin&utm_medium=textlink&utm_content=superseded-extensions-message' ) . '</p>';
186 + }
187 +
188 + if ( '' !== $message ) {
189 + if ( tb_tp_fs()->is_free_plan() ) {
190 + $message .= '<p style="font-size:16px;margin-top:1.5em;"><strong>' . __( 'Upgrade to a TablePress Premium license plan now and get:', 'tablepress' ) . '</strong></p>';
191 + $message .= '<ul style="list-style:disc;margin:0.5em 1em;font-size:16px;">';
192 + $message .= '<li>' . __( 'Updated and heavily improved versions of these features!', 'tablepress' ) . '</li>';
193 + $message .= '<li>' . __( 'Direct integration into the user interface!', 'tablepress' ) . '</li>';
194 + $message .= '<li>' . __( 'Regular updates that ensure compatibility with WordPress!', 'tablepress' ) . '</li>';
195 + $message .= '<li>' . __( 'Priority email support!', 'tablepress' ) . '</li>';
196 + $message .= '</ul>';
197 + $message .= '<p style="font-size:16px;"><strong>' . sprintf( __( 'And the best: %s', 'tablepress' ), sprintf( __( 'Use the promo code %1$s during the checkout process for a special offer!', 'tablepress' ), '<code>UPGRADE</code>' ) ) . '</strong></p>';
198 + $message .= '<p style="margin-top:2em;">' . sprintf( '<a href="%s" class="components-button is-primary" style="font-size:14px;margin-right:3em;background:linear-gradient(135deg,#00d184 0%%,#0791e3 100%%);border:none;font-weight:bold;">%s<span class="dashicons dashicons-arrow-right-alt" style="vertical-align:middle;margin:0 0 4px 4px"></span></a>', 'https://tablepress.org/upgrade-extensions/?utm_source=plugin&utm_medium=button&utm_content=superseded-extensions-message', __( 'Upgrade to a TablePress Premium version now!', 'tablepress' ) );
199 + }
200 + $message .= $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'superseded_extensions', 'return' => 'list' ), __( 'Hide this message', 'tablepress' ) ) . '</p>';
201 +
202 + $title = '<em>' . __( 'Important Notice!', 'tablepress' ) . '</em>';
203 +
204 + $this->add_header_message( $message, $notice_css_classes, $title );
205 + }
59 206 }
60 207
61 - if ( $data['messages']['wp_table_reloaded_warning'] ) {
62 - $this->add_header_message(
63 - '<strong>' . __( 'You have activated the plugin WP-Table Reloaded, which can not be used together with TablePress.', 'tablepress' ) . '</strong><br />'
64 - . __( 'It is strongly recommended that you switch from WP-Table Reloaded to TablePress, which not only fixes many problems, but also has more and better features than WP-Table Reloaded.', 'tablepress' ) . '<br />'
65 - . sprintf( __( 'Please follow the <a href="%s">migration guide</a> to move your tables and then deactivate WP-Table Reloaded!', 'tablepress' ), 'https://tablepress.org/migration-from-wp-table-reloaded/' ),
66 - 'notice-error not-dismissible',
67 - '<em>' . __( 'Attention!', 'tablepress' ) . '</em>'
68 - );
208 + if ( $data['messages']['first_visit'] && ( ! $data['messages']['plugin_update'] || $milestone_period_ended ) ) {
209 + $message = '<p style="font-size:14px;"><strong>' . __( 'Thank you for choosing TablePress, the most popular table plugin for WordPress!', 'tablepress' ) . '</strong></p>';
210 + /* translators: %1$s: URL to FAQ, %2$s: URL to Documentation, %3$s: URL to Support, %4$s: URL to plugin website */
211 + $message .= '<p>' . sprintf( __( 'If you encounter any questions or problems, please visit the <a href="%1$s">FAQ</a>, the <a href="%2$s">Documentation</a>, and the <a href="%3$s">Support</a> section on the <a href="%4$s">plugin website</a>.', 'tablepress' ), 'https://tablepress.org/faq/', 'https://tablepress.org/documentation/', 'https://tablepress.org/support/', 'https://tablepress.org/' ) . '</p>';
212 +
213 + if ( tb_tp_fs()->is_free_plan() ) {
214 + $message .= '<p style="font-size:14px;"><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=first-visit-message' ) . '</strong></p>';
215 + }
216 +
217 + $message .= '<p style="margin-top:1em;">' . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'first_visit', 'return' => 'list' ), __( 'Hide this message', 'tablepress' ) ) . '</p>';
218 +
219 + $title = '<em>' . __( 'Welcome!', 'tablepress' ) . '</em>';
220 +
221 + $this->add_header_message( $message, 'notice-info not-dismissible', $title );
69 222 }
70 223
71 - if ( $data['messages']['donation_message'] ) {
72 - $this->add_header_message(
73 - '<img alt="' . esc_attr__( 'Tobias Bäthge, developer of TablePress', 'tablepress' ) . '" src="https://secure.gravatar.com/avatar/50f1cff2e27a1f522b18ce229c057bc5?s=110" height="110" width="110" style="float:left;margin:2px 10px 30px 0;" />'
74 - . __( 'Hi, my name is Tobias, I&#8217;m the developer of the TablePress plugin.', 'tablepress' ) . '</p><p>'
75 - . __( 'Thanks for using it! You&#8217;ve installed TablePress over a month ago.', 'tablepress' ) . ' '
76 - . sprintf( _n( 'If everything works and you are satisfied with the results of managing your %s table, isn&#8217;t that worth a coffee or two?', 'If everything works and you are satisfied with the results of managing your %s tables, isn&#8217;t that worth a coffee or two?', $data['table_count'], 'tablepress' ), esc_html( $data['table_count'] ) ) . '<br />'
77 - . sprintf( __( '<a href="%s">Donations</a> help me to continue user support and development of this <em>free</em> software &mdash; things for which I spend countless hours of my free time! Thank you very much!', 'tablepress' ), 'https://tablepress.org/donate/' ) . '</p><p>'
78 - . __( 'Sincerely, Tobias', 'tablepress' ) . '</p><p style="font-size:14px">'
79 - . sprintf( '<a href="%s" target="_blank" rel="noopener noreferrer"><strong>%s</strong></a>', 'https://tablepress.org/donate/', __( 'Sure, I&#8217;ll buy you a coffee and support TablePress!', 'tablepress' ) ) . '&nbsp;&nbsp;&nbsp;&nbsp;&middot;&nbsp;&nbsp;&nbsp;&nbsp;'
80 - . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'donation_nag', 'return' => 'list', 'target' => 'already-donated' ), __( 'I already donated.', 'tablepress' ) ) . '&nbsp;&nbsp;&nbsp;&nbsp;&middot;&nbsp;&nbsp;&nbsp;&nbsp;'
81 - . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'donation_nag', 'return' => 'list', 'target' => 'maybe-later' ), __( 'No, thanks. Don&#8217;t ask again.', 'tablepress' ) ),
82 - 'notice-success not-dismissible',
83 - '<em>' . __( 'TablePress needs you!', 'tablepress' ) . '</em>'
84 - );
224 + if ( $data['messages']['donation_nag'] && ( ! $data['messages']['plugin_update'] || $milestone_period_ended ) ) {
225 + $message = '<p style="font-size:14px;"><img alt="' . esc_attr__( 'Tobias Bäthge, developer of TablePress', 'tablepress' ) . '" src="https://secure.gravatar.com/avatar/50f1cff2e27a1f522b18ce229c057bc5?s=250" height="125" width="125" style="float:left;margin:2px 15px 30px 0;border-radius:5px;">'
226 + . __( 'Hi, my name is Tobias, I&#8217;m the developer of the TablePress plugin.', 'tablepress' ) . '</p>';
227 + $message .= '<p style="font-size:14px;">' . __( 'Thank you for using it!', 'tablepress' ) . ' ';
228 + if ( $data['table_count'] > 0 ) {
229 + /* translators: %s: Number of tables */
230 + $message .= sprintf( _n( 'I hope that everything works and that you are satisfied with the results of managing your %s table.', 'I hope that everything works and that you are satisfied with the results of managing your %s tables.', $data['table_count'], 'tablepress' ), $data['table_count'] );
231 + } else {
232 + /* translators: %s: URL to TablePress website */
233 + $message .= sprintf( __( 'It looks like you haven’t added a table yet. If you need help to get started, please find more information in the FAQ and Documentation on the <a href="%s">TablePress website</a>.', 'tablepress' ), 'https://tablepress.org/' );
234 + }
235 + $message .= '</p>';
236 + $message .= '<p style="font-size:14px;"><strong>' . sprintf( __( 'I would like to invite you to check out the <a href="%s">Premium versions of TablePress</a>.', 'tablepress' ), 'https://tablepress.org/premium/?utm_source=plugin&utm_medium=textlink&utm_content=upgrade-message' ) . '<br>'
237 + . __( 'The available Pro and Max plans offer user support and many exciting and helpful features for your tables.', 'tablepress' ) . '</strong></p>';
238 + $message .= '<p style="font-size:14px;">' . __( 'Sincerely, Tobias', 'tablepress' ) . '</p>';
239 + $message .= '<p style="margin-top:1em;">' . sprintf( '<a href="%s" class="components-button is-primary" style="font-size:14px;margin-right:3em;color:#ffffff;">%s<span class="dashicons dashicons-arrow-right-alt" style="margin-inline:8px 0"></span></a>', 'https://tablepress.org/premium/?utm_source=plugin&utm_medium=button&utm_content=upgrade-message', __( 'Tell me more about the Premium features', 'tablepress' ) )
240 + . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'donation_nag', 'return' => 'list' ), __( 'Hide this message', 'tablepress' ) ) . '</p>';
241 +
242 + $title = '<em>' . __( 'TablePress has more to offer!', 'tablepress' ) . '</em>';
243 +
244 + $this->add_header_message( $message, 'notice-success not-dismissible', $title );
85 245 }
86 246
87 - if ( $data['messages']['plugin_update_message'] ) {
88 - $this->add_header_message(
89 - sprintf( __( 'Please read the <a href="%s">release announcement</a> for more information.', 'tablepress' ), 'https://tablepress.org/news/' ) . ' '
90 - . sprintf( __( 'If you like the new features and enhancements, <a href="%s">giving a donation</a> towards the further support and development of TablePress is recommended. Thank you!', 'tablepress' ), 'https://tablepress.org/donate/' )
91 - . '</p>'
92 - . '<div style="margin:20px 0;height:45px"><a href="https://wpactivitylog.com/?utm_source=tablepress&utm_medium=referral&utm_campaign=WSAL" target="_blank" rel="noopener noreferrer"><img src="' . plugins_url( 'admin/img/wsal-mark.png', TABLEPRESS__FILE__ ) . '" alt="' . sprintf( esc_attr_x( 'This release of TablePress is supported by %s, the most comprehensive WordPress activity logs plugin.', 'WP Activity Log', 'tablepress' ), 'WP Activity Log' ) . '" style="width:46px;height:45px;float:left;margin-right:10px" /></a>'
93 - . '<div style="font-size:14px;font-weight:bold;max-width:530px;padding:5px 0;line-height:normal">' . sprintf( _x( 'This release of TablePress is supported by %s, the most comprehensive WordPress activity logs plugin.', 'WP Activity Log', 'tablepress' ), '<a href="https://wpactivitylog.com/?utm_source=tablepress&utm_medium=referral&utm_campaign=WSAL" target="_blank" rel="noopener noreferrer">WP Activity Log</a>' ) . '</div></div>'
94 - . '<p>'
95 - . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'plugin_update', 'return' => 'list' ), __( 'Hide this message', 'tablepress' ) ),
96 - 'notice-info not-dismissible',
97 - '<em>' . sprintf( __( 'Thank you for updating to TablePress %s!', 'tablepress' ), TablePress::version ) . '</em>'
98 - );
247 + if ( $data['messages']['plugin_update'] ) {
248 + if ( $milestone_period_ended ) {
249 + $message = '<p>' . sprintf( __( 'To find out more about what’s new, please read the <a href="%s"><strong>release announcement</strong></a>.', 'tablepress' ), 'https://tablepress.org/news/?utm_source=plugin&utm_medium=textlink&utm_content=plugin-update-message' ) . '</p>';
250 +
251 + if ( tb_tp_fs()->is_free_plan() ) {
252 + $message .= '<p><strong>' . sprintf( __( 'More great features and priority email support are available with a Premium license plan. <a href="%s">Check them out!</a>', 'tablepress' ), 'https://tablepress.org/premium/?utm_source=plugin&utm_medium=textlink&utm_content=plugin-update-message' ) . '</strong></p>';
253 + }
254 +
255 + $title = '<em>' . sprintf( __( 'Thank you for updating to TablePress %s!', 'tablepress' ), TablePress::version ) . '</em>';
256 + $class = 'notice-info not-dismissible';
257 + } else {
258 + $texts_german = in_array( get_user_locale(), array( 'de_DE', 'de_AT', 'de_CH' ), true );
259 + if ( $texts_german ) {
260 + $message = '<p style="font-size:16px;">Wir feiern <strong>25 Millionen Downloads</strong> von TablePress aus dem WordPress-Pluginverzeichnis!<br>Mach mit und <strong>spare 25&#8239;%</strong> beim Upgrade auf TablePress Pro oder TablePress Max!</p>';
261 + } else {
262 + $message = '<p style="font-size:16px;">We’re celebrating <strong>25 million downloads</strong> of TablePress from the WordPress Plugin Directory!<br>Join us and <strong>get 25% off</strong> your TablePress Pro or TablePress Max upgrade!</p>';
263 + }
264 + $url = 'https://tablepress.org/premium/?utm_source=plugin&utm_medium=notice&utm_campaign=upgrade-message340&utm_content=button';
265 + $message .= '<p style="margin-top:1em;margin-bottom:24px;">' . sprintf( '<a href="%s" class="components-button is-primary" target="_blank"><span>%s</span><span class="dashicons dashicons-arrow-right-alt" style="margin-inline:8px 0"></span></a>', $url, $texts_german ? 'Spare jetzt 25&#8239;%' : 'Get 25% off now' ) . '</p>';
266 +
267 + $filename = $texts_german ? 'admin/img/dl-25-period.svg' : 'admin/img/dl-25-comma.svg';
268 + $message .= sprintf( '<img src="%1$s" alt="%2$s">', plugins_url( $filename, TABLEPRESS__FILE__ ), $texts_german ? 'Grafik mit einer Tabelle mit „Downloads“ in der Kopfzeile und der Zahl 25.000.000 im Tabellenkörper' : 'Graphic with a table with “Downloads” in the header and the number 25,000,000 in the body' );
269 +
270 + $title = '';
271 + $class = 'notice-milestone not-dismissible';
272 + }
273 +
274 + $message .= '<p style="margin-top:1em;">' . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'plugin_update', 'return' => 'list' ), __( 'Hide this message', 'tablepress' ) ) . '</p>';
275 +
276 + $this->add_header_message( $message, $class, $title );
99 277 }
100 278
101 279 $this->process_action_messages( array(
102 - 'success_delete' => _n( 'The table was deleted successfully.', 'The tables were deleted successfully.', 1, 'tablepress' ),
103 - 'success_delete_plural' => _n( 'The table was deleted successfully.', 'The tables were deleted successfully.', 2, 'tablepress' ),
104 - 'error_delete' => __( 'Error: The table could not be deleted.', 'tablepress' ),
105 - 'error_save' => __( 'Error: The table could not be saved.', 'tablepress' ),
106 - 'success_copy' => _n( 'The table was copied successfully.', 'The tables were copied successfully.', 1, 'tablepress' ) . ( ( false !== $data['table_id'] ) ? ' ' . sprintf( __( 'The copied table has the table ID &#8220;%s&#8221;.', 'tablepress' ), esc_html( $data['table_id'] ) ) : '' ),
107 - 'success_copy_plural' => _n( 'The table was copied successfully.', 'The tables were copied successfully.', 2, 'tablepress' ),
108 - 'error_copy' => __( 'Error: The table could not be copied.', 'tablepress' ),
109 - 'error_no_table' => __( 'Error: You did not specify a valid table ID.', 'tablepress' ),
110 - 'error_load_table' => __( 'Error: This table could not be loaded!', 'tablepress' ),
111 - 'error_bulk_action_invalid' => __( 'Error: This bulk action is invalid!', 'tablepress' ),
112 - 'error_no_selection' => __( 'Error: You did not select any tables!', 'tablepress' ),
113 - 'error_delete_not_all_tables' => __( 'Notice: Not all selected tables could be deleted!', 'tablepress' ),
114 - 'error_copy_not_all_tables' => __( 'Notice: Not all selected tables could be copied!', 'tablepress' ),
115 - 'success_import' => __( 'The tables were imported successfully.', 'tablepress' ),
280 + 'success_delete' => _n( 'The table was deleted successfully.', 'The tables were deleted successfully.', 1, 'tablepress' ),
281 + 'success_delete_plural' => _n( 'The table was deleted successfully.', 'The tables were deleted successfully.', 2, 'tablepress' ),
282 + 'error_delete' => __( 'Error: The table could not be deleted.', 'tablepress' ),
283 + 'success_copy' => _n( 'The table was copied successfully.', 'The tables were copied successfully.', 1, 'tablepress' )
284 + . ( ( false !== $data['table_id'] )
285 + ? ' ' . ( current_user_can( 'tablepress_edit_table', $data['table_id'] )
286 + ? sprintf( __( 'You can now <a href="%1$s">edit the copied table</a>, which has the table ID “%2$s”.', 'tablepress' ), esc_url( TablePress::url( array( 'action' => 'edit', 'table_id' => $data['table_id'] ) ) ), esc_html( $data['table_id'] ) )
287 + : sprintf( __( 'The copied table has the table ID &#8220;%s&#8221;.', 'tablepress' ), esc_html( $data['table_id'] ) ) )
288 + : ''
289 + ),
290 + 'success_copy_plural' => _n( 'The table was copied successfully.', 'The tables were copied successfully.', 2, 'tablepress' ),
291 + 'error_copy' => __( 'Error: The table could not be copied.', 'tablepress' ),
292 + 'error_no_table' => __( 'Error: You did not specify a valid table ID.', 'tablepress' ),
293 + 'error_load_table' => __( 'Error: This table could not be loaded!', 'tablepress' ),
294 + 'error_bulk_action_invalid' => __( 'Error: This bulk action is invalid!', 'tablepress' ),
295 + 'error_no_selection' => __( 'Error: You did not select any tables!', 'tablepress' ),
296 + 'error_delete_not_all_tables' => __( 'Notice: Not all selected tables could be deleted!', 'tablepress' ),
297 + 'error_copy_not_all_tables' => __( 'Notice: Not all selected tables could be copied!', 'tablepress' ),
298 + 'success_import' => __( 'The tables were imported successfully.', 'tablepress' ),
116 299 ) );
117 300
118 301 $this->add_text_box( 'head', array( $this, 'textbox_head' ), 'normal' );
119 302 $this->add_text_box( 'tables-list', array( $this, 'textbox_tables_list' ), 'normal' );
120 303
121 - add_screen_option( 'per_page', array( 'label' => __( 'Tables', 'tablepress' ), 'default' => 20 ) ); // Admin_Controller contains function to allow changes to this in the Screen Options to be saved
122 - $this->wp_list_table = new TablePress_All_Tables_List_Table();
304 + add_screen_option( 'per_page', array( 'label' => __( 'Tables', 'tablepress' ), 'default' => 20 ) ); // Admin_Controller contains function to allow changes to this in the Screen Options to be saved.
305 + $this->wp_list_table = TablePress::load_class( 'TablePress_All_Tables_List_Table', 'class-all-tables-list-table.php', 'views' );
123 306 $this->wp_list_table->set_items( $this->data['table_ids'] );
124 307 $this->wp_list_table->prepare_items();
125 308
126 309 // Cleanup Request URI string, which WP_List_Table uses to generate the sort URLs.
@@ -131,20 +314,26 @@
131 314 * Render the current view (in this view: without form tag).
132 315 *
133 316 * @since 1.0.0
134 317 */
135 - public function render() {
318 + #[\Override]
319 + public function render(): void {
136 320 ?>
137 321 <div id="tablepress-page" class="wrap">
138 322 <?php
139 323 $this->print_nav_tab_menu();
140 - // Print all header messages.
141 - foreach ( $this->header_messages as $message ) {
142 - echo $message;
143 - }
324 + ?>
325 + <div id="tablepress-body">
326 + <hr class="wp-header-end">
327 + <script>document.querySelectorAll('.notice:has(~#tablepress-page)').forEach(e=>document.querySelector('.wp-header-end').after(e));</script>
328 + <?php
329 + // Print all header messages.
330 + foreach ( $this->header_messages as $message ) {
331 + echo $message;
332 + }
144 333
145 - // For this screen, this is done in textbox_tables_list(), to get the fields into the correct <form>:
146 - // $this->do_text_boxes( 'header' );
334 + // For this screen, this is done in textbox_tables_list(), to get the fields into the correct <form>:
335 + // $this->do_text_boxes( 'header' );.
147 336 ?>
148 337 <div id="poststuff">
149 338 <div id="post-body" class="metabox-holder columns-<?php echo ( isset( $GLOBALS['screen_layout_columns'] ) && ( 2 === $GLOBALS['screen_layout_columns'] ) ) ? '2' : '1'; ?>">
150 339 <div id="postbox-container-2" class="postbox-container">
@@ -166,11 +355,12 @@
166 355 $this->do_meta_boxes( 'side' );
167 356 ?>
168 357 </div>
169 358 </div>
170 - <br class="clear" />
359 + <br class="clear">
171 360 </div>
172 361 </div>
362 + </div>
173 363 <?php
174 364 }
175 365
176 366 /**
@@ -177,19 +367,26 @@
177 367 * Print the screen head text.
178 368 *
179 369 * @since 1.0.0
180 370 *
181 - * @param array $data Data for this screen.
182 - * @param array $box Information about the text box.
371 + * @param array<string, mixed> $data Data for this screen.
372 + * @param array<string, mixed> $box Information about the text box.
183 373 */
184 - public function textbox_head( array $data, array $box ) {
185 - ?>
186 - <p>
187 - <?php _e( 'This is a list of your tables.', 'tablepress' ); ?>
188 - <?php printf( __( 'To insert a table into a post or page, copy its Shortcode %s and paste it into a &#8220;Shortcode&#8221; block at the desired place in the block editor.', 'tablepress' ), '<label class="screen-reader-text" for="table-shortcode-inline">' . esc_html__( 'Shortcode text for editor', 'tablepress' ) . '</label>' . '<input type="text" class="table-shortcode table-shortcode-inline" id="table-shortcode-inline" value="' . esc_attr( '[' . TablePress::$shortcode . ' id=<ID> /]' ) . '" readonly="readonly" />' ); ?>
189 - <?php _e( 'Each table has a unique ID that needs to be adjusted in that Shortcode.', 'tablepress' ); ?>
190 - </p>
191 - <?php
374 + public function textbox_head( array $data, array $box ): void {
375 + echo '<p>';
376 + _e( 'This is a list of your tables.', 'tablepress' );
377 + echo ' ';
378 + // Show the instructions string depending on whether the Block Editor is used on the site or not.
379 + if ( 'block' === $data['site_used_editor'] ) {
380 + printf( __( 'To insert a table into a post or page, add a “%1$s” block in the block editor and select the desired table.', 'tablepress' ), __( 'TablePress table', 'tablepress' ) );
381 + } elseif ( 'elementor' === $data['site_used_editor'] ) {
382 + printf( __( 'To insert a table into a post or page, add a “%1$s” widget in the Elementor editor and select the desired table.', 'tablepress' ), __( 'TablePress table', 'tablepress' ) );
383 + } else {
384 + _e( 'To insert a table into a post or page, paste its Shortcode at the desired place in the editor.', 'tablepress' );
385 + echo ' ';
386 + _e( 'Each table has a unique ID that needs to be adjusted in that Shortcode.', 'tablepress' );
387 + }
388 + echo '</p>';
192 389 }
193 390
194 391 /**
195 392 * Print the content of the "All Tables" text box.
@@ -195,32 +392,33 @@
195 392 * Print the content of the "All Tables" text box.
196 393 *
197 394 * @since 1.0.0
198 395 *
199 - * @param array $data Data for this screen.
200 - * @param array $box Information about the text box.
396 + * @param array<string, mixed> $data Data for this screen.
397 + * @param array<string, mixed> $box Information about the text box.
201 398 */
202 - public function textbox_tables_list( array $data, array $box ) {
399 + public function textbox_tables_list( array $data, array $box ): void {
203 400 if ( ! empty( $_GET['s'] ) ) {
204 401 printf( '<span class="subtitle">' . __( 'Search results for &#8220;%s&#8221;', 'tablepress' ) . '</span>', esc_html( wp_unslash( $_GET['s'] ) ) );
205 402 }
206 - ?>
207 -<form method="get" action="">
208 - <?php
209 - if ( isset( $_GET['page'] ) ) {
210 - echo '<input type="hidden" name="page" value="' . esc_attr( $_GET['page'] ) . '" />' . "\n";
211 - }
212 - $this->wp_list_table->search_box( __( 'Search Tables', 'tablepress' ), 'tables_search' );
213 - ?>
403 + ?>
404 +<form method="get">
405 + <?php
406 + if ( isset( $_GET['page'] ) ) {
407 + echo '<input type="hidden" name="page" value="' . esc_attr( $_GET['page'] ) . '">' . "\n";
408 + }
409 + $this->wp_list_table->search_box( __( 'Search Tables', 'tablepress' ), 'tables_search' );
410 + ?>
214 411 </form>
215 412 <form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post">
216 - <?php
413 + <?php
217 414 // This prints the nonce and action fields for this screen (done here instead of render(), due to moved <form>).
218 415 $this->do_text_boxes( 'header' );
219 416 $this->wp_list_table->display();
220 - ?>
417 + ?>
221 418 </form>
222 - <?php
419 +<div id="tablepress-list-screen"></div>
420 + <?php
223 421 }
224 422
225 423 /**
226 424 * Create HTML code for an AJAXified link.
@@ -226,556 +424,21 @@
226 424 * Create HTML code for an AJAXified link.
227 425 *
228 426 * @since 1.0.0
229 427 *
230 - * @param array $params Parameters for the URL.
231 - * @param string $text Text for the link.
428 + * @param array<string, mixed> $params Parameters for the URL.
429 + * @param string $text Text for the link.
232 430 * @return string HTML code for the link.
233 431 */
234 - protected function ajax_link( array $params = array( 'action' => 'list', 'item' => '' ), $text ) {
432 + protected function ajax_link( array $params, string $text ): string {
433 + $class = 'ajax-link';
434 + if ( ! empty( $params['class'] ) ) {
435 + $class .= ' ' . esc_attr( $params['class'] );
436 + }
235 437 $url = TablePress::url( $params, true, 'admin-post.php' );
236 438 $action = esc_attr( $params['action'] );
237 439 $item = esc_attr( $params['item'] );
238 - $target = isset( $params['target'] ) ? esc_attr( $params['target'] ) : '';
239 - return "<a class=\"ajax-link\" href=\"{$url}\" data-action=\"{$action}\" data-item=\"{$item}\" data-target=\"{$target}\">{$text}</a>";
440 + $target = esc_attr( $params['target'] ?? '' );
441 + return "<a class=\"{$class}\" href=\"{$url}\" data-action=\"{$action}\" data-item=\"{$item}\" data-target=\"{$target}\">{$text}</a>";
240 442 }
241 443
242 444 } // class TablePress_List_View
243 -
244 -/**
245 - * TablePress All Tables List Table Class
246 - * @package TablePress
247 - * @subpackage Views
248 - * @author Tobias Bäthge
249 - * @link https://codex.wordpress.org/Class_Reference/WP_List_Table
250 - * @since 1.0.0
251 - */
252 -class TablePress_All_Tables_List_Table extends WP_List_Table {
253 -
254 - /**
255 - * Number of items of the initial data set (before sort, search, and pagination).
256 - *
257 - * @since 1.0.0
258 - * @var int
259 - */
260 - protected $items_count = 0;
261 -
262 - /**
263 - * Initialize the List Table.
264 - *
265 - * @since 1.0.0
266 - */
267 - public function __construct() {
268 - $screen = get_current_screen();
269 -
270 - // Hide "Last Modified By" column by default.
271 - if ( false === get_user_option( 'manage' . $screen->id . 'columnshidden' ) ) {
272 - update_user_option( get_current_user_id(), 'manage' . $screen->id . 'columnshidden', array( 'table_last_modified_by' ), true );
273 - }
274 -
275 - parent::__construct( array(
276 - 'singular' => 'tablepress-table', // Singular name of the listed records.
277 - 'plural' => 'tablepress-all-tables', // Plural name of the listed records.
278 - 'ajax' => false, // Does this list table support AJAX?
279 - 'screen' => $screen, // WP_Screen object.
280 - ) );
281 - }
282 -
283 - /**
284 - * Set the data items (here: tables) that are to be displayed by the List Tables, and their original count.
285 - *
286 - * @since 1.0.0
287 - *
288 - * @param array $items Tables to be displayed in the List Table.
289 - */
290 - public function set_items( array $items ) {
291 - $this->items = $items;
292 - $this->items_count = count( $items );
293 - }
294 -
295 - /**
296 - * Check whether the user has permissions for certain AJAX actions.
297 - * (not used, but must be implemented in this child class)
298 - *
299 - * @since 1.0.0
300 - *
301 - * @return bool true (Default value).
302 - */
303 - public function ajax_user_can() {
304 - return true;
305 - }
306 -
307 - /**
308 - * Get a list of columns in this List Table.
309 - *
310 - * Format: 'internal-name' => 'Column Title'.
311 - *
312 - * @since 1.0.0
313 - *
314 - * @return array List of columns in this List Table.
315 - */
316 - public function get_columns() {
317 - $columns = array(
318 - 'cb' => $this->has_items() ? '<input type="checkbox" />' : '', // Checkbox for "Select all", but only if there are items in the table.
319 - // "name" is special in WP, which is why we prefix every entry here, to be safe!
320 - 'table_id' => __( 'ID', 'tablepress' ),
321 - 'table_name' => __( 'Table Name', 'tablepress' ),
322 - 'table_description' => __( 'Description', 'tablepress' ),
323 - 'table_author' => __( 'Author', 'tablepress' ),
324 - 'table_last_modified_by' => __( 'Last Modified By', 'tablepress' ),
325 - 'table_last_modified' => __( 'Last Modified', 'tablepress' ),
326 - );
327 - return $columns;
328 - }
329 -
330 - /**
331 - * Get a list of columns that are sortable.
332 - *
333 - * Format: 'internal-name' => array( $field for $item[ $field ], true for already sorted ).
334 - *
335 - * @since 1.0.0
336 - *
337 - * @return array List of sortable columns in this List Table.
338 - */
339 - protected function get_sortable_columns() {
340 - // No sorting on the Empty List placeholder.
341 - if ( ! $this->has_items() ) {
342 - return array();
343 - }
344 -
345 - $sortable_columns = array(
346 - 'table_id' => array( 'id', true ), // true means its already sorted
347 - 'table_name' => array( 'name', false ),
348 - 'table_description' => array( 'description', false ),
349 - 'table_author' => array( 'author', false ),
350 - 'table_last_modified_by' => array( 'last_modified_by', false ),
351 - 'table_last_modified' => array( 'last_modified', false ),
352 - );
353 - return $sortable_columns;
354 - }
355 -
356 - /**
357 - * Gets the name of the default primary column.
358 - *
359 - * @since 1.7.0
360 - *
361 - * @return string Name of the default primary column, in this case, the table name.
362 - */
363 - protected function get_default_primary_column_name() {
364 - return 'table_name';
365 - }
366 -
367 - /**
368 - * Render a cell in the "cb" column.
369 - *
370 - * @since 1.0.0
371 - *
372 - * @param array $item Data item for the current row.
373 - * @return string HTML content of the cell.
374 - */
375 - protected function column_cb( /* array */ $item ) { // No `array` type hint to prevent a Strict Standards notice, as the method is inherited.
376 - $user_can_copy_table = current_user_can( 'tablepress_copy_table', $item['id'] );
377 - $user_can_delete_table = current_user_can( 'tablepress_delete_table', $item['id'] );
378 - $user_can_export_table = current_user_can( 'tablepress_export_table', $item['id'] );
379 -
380 - if ( $user_can_copy_table || $user_can_delete_table || $user_can_export_table ) {
381 - return sprintf(
382 - '<label><span class="screen-reader-text">%1$s</span><input type="checkbox" name="table[]" value="%2$s" /></label>',
383 - esc_html__( 'Bulk action selector', 'tablepress' ),
384 - esc_attr( $item['id'] )
385 - );
386 - } else {
387 - return '';
388 - }
389 - }
390 -
391 - /**
392 - * Render a cell in the "table_id" column.
393 - *
394 - * @since 1.0.0
395 - *
396 - * @param array $item Data item for the current row.
397 - * @return string HTML content of the cell.
398 - */
399 - protected function column_table_id( array $item ) {
400 - return esc_html( $item['id'] );
401 - }
402 -
403 - /**
404 - * Render a cell in the "table_name" column.
405 - *
406 - * @since 1.0.0
407 - *
408 - * @param array $item Data item for the current row.
409 - * @return string HTML content of the cell.
410 - */
411 - protected function column_table_name( array $item ) {
412 - $user_can_edit_table = current_user_can( 'tablepress_edit_table', $item['id'] );
413 - $user_can_copy_table = current_user_can( 'tablepress_copy_table', $item['id'] );
414 - $user_can_export_table = current_user_can( 'tablepress_export_table', $item['id'] );
415 - $user_can_delete_table = current_user_can( 'tablepress_delete_table', $item['id'] );
416 - $user_can_preview_table = current_user_can( 'tablepress_preview_table', $item['id'] );
417 -
418 - $edit_url = TablePress::url( array( 'action' => 'edit', 'table_id' => $item['id'] ) );
419 - $copy_url = TablePress::url( array( 'action' => 'copy_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
420 - $export_url = TablePress::url( array( 'action' => 'export', 'table_id' => $item['id'] ) );
421 - $delete_url = TablePress::url( array( 'action' => 'delete_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
422 - $preview_url = TablePress::url( array( 'action' => 'preview_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
423 -
424 - if ( '' === trim( $item['name'] ) ) {
425 - $item['name'] = __( '(no name)', 'tablepress' );
426 - }
427 -
428 - if ( $user_can_edit_table ) {
429 - $row_text = '<strong><a title="' . esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;', 'tablepress' ), esc_attr( $item['name'] ) ) ) . '" class="row-title" href="' . $edit_url . '">' . esc_html( $item['name'] ) . '</a></strong>';
430 - } else {
431 - $row_text = '<strong>' . esc_html( $item['name'] ) . '</strong>';
432 - }
433 -
434 - $row_actions = array();
435 - if ( $user_can_edit_table ) {
436 - $row_actions['edit'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $edit_url, esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), __( 'Edit', 'tablepress' ) );
437 - }
438 - $row_actions['shortcode hide-if-no-js'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', '#', esc_attr( '[' . TablePress::$shortcode . " id={$item['id']} /]" ), __( 'Show Shortcode', 'tablepress' ) );
439 - if ( $user_can_copy_table ) {
440 - $row_actions['copy'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $copy_url, esc_attr( sprintf( __( 'Copy &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), __( 'Copy', 'tablepress' ) );
441 - }
442 - if ( $user_can_export_table ) {
443 - $row_actions['export'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $export_url, esc_attr( sprintf( __( 'Export &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), _x( 'Export', 'row action', 'tablepress' ) );
444 - }
445 - if ( $user_can_delete_table ) {
446 - $row_actions['delete'] = sprintf( '<a href="%1$s" title="%2$s" class="delete-link">%3$s</a>', $delete_url, esc_attr( sprintf( __( 'Delete &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), __( 'Delete', 'tablepress' ) );
447 - }
448 - if ( $user_can_preview_table ) {
449 - $row_actions['table-preview'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $preview_url, esc_attr( sprintf( __( 'Show a preview of &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), __( 'Preview', 'tablepress' ) );
450 - }
451 -
452 - return $row_text . $this->row_actions( $row_actions );
453 - }
454 -
455 - /**
456 - * Render a cell in the "table_description" column.
457 - *
458 - * @since 1.0.0
459 - *
460 - * @param array $item Data item for the current row.
461 - * @return string HTML content of the cell.
462 - */
463 - protected function column_table_description( array $item ) {
464 - if ( '' === trim( $item['description'] ) ) {
465 - $item['description'] = __( '(no description)', 'tablepress' );
466 - }
467 - return esc_html( $item['description'] );
468 - }
469 -
470 - /**
471 - * Render a cell in the "table_author" column.
472 - *
473 - * @since 1.0.0
474 - *
475 - * @param array $item Data item for the current row.
476 - * @return string HTML content of the cell.
477 - */
478 - protected function column_table_author( array $item ) {
479 - return TablePress::get_user_display_name( $item['author'] );
480 - }
481 -
482 - /**
483 - * Render a cell in the "last_modified_by" column.
484 - *
485 - * @since 1.0.0
486 - *
487 - * @param array $item Data item for the current row.
488 - * @return string HTML content of the cell.
489 - */
490 - protected function column_table_last_modified_by( array $item ) {
491 - return TablePress::get_user_display_name( $item['options']['last_editor'] );
492 - }
493 -
494 - /**
495 - * Render a cell in the "table_last_modified" column.
496 - *
497 - * @since 1.0.0
498 - *
499 - * @param array $item Data item for the current row.
500 - * @return string HTML content of the cell.
501 - */
502 - protected function column_table_last_modified( array $item ) {
503 - $modified_timestamp = strtotime( $item['last_modified'] );
504 - $current_timestamp = current_time( 'timestamp' );
505 - $time_diff = $current_timestamp - $modified_timestamp;
506 - // Time difference is only shown up to one day.
507 - if ( $time_diff >= 0 && $time_diff < DAY_IN_SECONDS ) {
508 - $time_diff = sprintf( __( '%s ago', 'tablepress' ), human_time_diff( $modified_timestamp, $current_timestamp ) );
509 - } else {
510 - $time_diff = TablePress::format_datetime( $item['last_modified'], 'mysql', '<br />' );
511 - }
512 -
513 - $readable_time = TablePress::format_datetime( $item['last_modified'], 'mysql', ' ' );
514 - return '<abbr title="' . esc_attr( $readable_time ) . '">' . $time_diff . '</abbr>';
515 - }
516 -
517 - /**
518 - * Handles output for the default column.
519 - *
520 - * @since 1.8.0
521 - *
522 - * @param array $item Data item for the current row.
523 - * @param string $column_name Current column name.
524 - */
525 - protected function column_default( /* array */ $item, $column_name ) { // No `array` type hint to prevent a Strict Standards notice, as the method is inherited.
526 - /**
527 - * Fires inside each custom column of the TablePress list table.
528 - *
529 - * @since 1.8.0
530 - *
531 - * @param array $column_name Current column name.
532 - * @param string $item Data item for the current row.
533 - */
534 - do_action( 'manage_tablepress_list_custom_column', $column_name, $item );
535 - }
536 -
537 - /**
538 - * Get a list (name => title) bulk actions that are available.
539 - *
540 - * @since 1.0.0
541 - *
542 - * @return array Bulk actions for this table.
543 - */
544 - protected function get_bulk_actions() {
545 - $bulk_actions = array();
546 -
547 - if ( current_user_can( 'tablepress_copy_tables' ) ) {
548 - $bulk_actions['copy'] = _x( 'Copy', 'bulk action', 'tablepress' );
549 - }
550 - if ( current_user_can( 'tablepress_export_tables' ) ) {
551 - $bulk_actions['export'] = _x( 'Export', 'bulk action', 'tablepress' );
552 - }
553 - if ( current_user_can( 'tablepress_delete_tables' ) ) {
554 - $bulk_actions['delete'] = _x( 'Delete', 'bulk action', 'tablepress' );
555 - }
556 -
557 - return $bulk_actions;
558 - }
559 -
560 - /**
561 - * Render the bulk actions dropdown.
562 - *
563 - * In comparison with parent class, this has modified HTML (especially no field named "action" as that's being used already)!
564 - *
565 - * @since 1.0.0
566 - *
567 - * @param string $which The location of the bulk actions: 'top' or 'bottom'.
568 - * This is designated as optional for backwards-compatibility.
569 - */
570 - protected function bulk_actions( $which = '' ) {
571 - if ( is_null( $this->_actions ) ) {
572 - $no_new_actions = $this->_actions = $this->get_bulk_actions();
573 - /** This filter is documented in the WordPress function WP_List_Table::bulk_actions() in wp-admin/includes/class-wp-list-table.php */
574 - $this->_actions = apply_filters( 'bulk_actions-' . $this->screen->id, $this->_actions );
575 - $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
576 - $two = '';
577 - } else {
578 - $two = '2';
579 - }
580 -
581 - if ( empty( $this->_actions ) ) {
582 - return;
583 - }
584 -
585 - $name_id = "bulk-action-{$which}";
586 - echo "<label for='{$name_id}' class='screen-reader-text'>" . __( 'Select Bulk Action', 'tablepress' ) . "</label>\n";
587 - echo "<select name='{$name_id}' id='{$name_id}'>\n";
588 - echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions', 'tablepress' ) . "</option>\n";
589 - foreach ( $this->_actions as $name => $title ) {
590 - echo "\t<option value='{$name}'>{$title}</option>\n";
591 - }
592 - echo "</select>\n";
593 - submit_button( __( 'Apply', 'tablepress' ), 'action', '', false, array( 'id' => "doaction{$two}" ) );
594 - echo "\n";
595 - }
596 -
597 - /**
598 - * Holds the message to be displayed when there are no items in the table.
599 - *
600 - * @since 1.0.0
601 - */
602 - public function no_items() {
603 - _e( 'No tables found.', 'tablepress' );
604 - if ( 0 === $this->items_count ) {
605 - $user_can_add_tables = current_user_can( 'tablepress_add_tables' );
606 - $user_can_import_tables = current_user_can( 'tablepress_import_tables' );
607 -
608 - $add_url = TablePress::url( array( 'action' => 'add' ) );
609 - $import_url = TablePress::url( array( 'action' => 'import' ) );
610 -
611 - if ( $user_can_add_tables && $user_can_import_tables ) {
612 - echo ' ' . sprintf( __( 'You should <a href="%1$s">add</a> or <a href="%2$s">import</a> a table to get started!', 'tablepress' ), $add_url, $import_url );
613 - } elseif ( $user_can_add_tables ) {
614 - echo ' ' . sprintf( __( 'You should <a href="%s">add</a> a table to get started!', 'tablepress' ), $add_url );
615 - } elseif ( $user_can_import_tables ) {
616 - echo ' ' . sprintf( __( 'You should <a href="%s">import</a> a table to get started!', 'tablepress' ), $import_url );
617 - }
618 - }
619 - }
620 -
621 - /**
622 - * Generate the elements above or below the table (like bulk actions and pagination).
623 - *
624 - * In comparison with parent class, this has modified HTML (no nonce field), and a check whether there are items.
625 - *
626 - * @since 1.0.0
627 - *
628 - * @param string $which Location ("top" or "bottom").
629 - */
630 - protected function display_tablenav( $which ) {
631 - if ( ! $this->has_items() ) {
632 - return;
633 - }
634 - ?>
635 - <div class="tablenav <?php echo esc_attr( $which ); ?>">
636 - <div class="alignleft actions">
637 - <?php $this->bulk_actions( $which ); ?>
638 - </div>
639 - <?php
640 - $this->extra_tablenav( $which );
641 - $this->pagination( $which );
642 - ?>
643 - <br class="clear" />
644 - </div>
645 - <?php
646 - }
647 -
648 - /**
649 - * Callback to determine whether the given $item contains the search term.
650 - *
651 - * @since 1.0.0
652 - *
653 - * @param string $item Table ID that shall be searched.
654 - * @return bool Whether the search term was found or not.
655 - */
656 - protected function _search_callback( $item ) {
657 - static $term, $json_encoded_term;
658 - if ( is_null( $term ) || is_null( $json_encoded_term ) ) {
659 - $term = wp_unslash( $_GET['s'] );
660 - $json_encoded_term = substr( wp_json_encode( $term, TABLEPRESS_JSON_OPTIONS ), 1, -1 );
661 - }
662 -
663 - static $debug;
664 - if ( is_null( $debug ) ) {
665 - // Set debug variable to allow searching in corrupted tables.
666 - $debug = isset( $_GET['debug'] ) ? ( 'true' === $_GET['debug'] ) : WP_DEBUG;
667 - }
668 -
669 - // load table again, with data and options (for last_editor).
670 - $item = TablePress::$model_table->load( $item, true, true );
671 -
672 - // Don't search corrupted tables, except when debug mode is enabled via $_GET parameter or WP_DEBUG constant.
673 - if ( ! $debug && isset( $item['is_corrupted'] ) && $item['is_corrupted'] ) {
674 - return false;
675 - }
676 -
677 - // Search from easy to hard, so that "expensive" code maybe doesn't have to run.
678 - if ( false !== stripos( $item['id'], $term )
679 - || false !== stripos( $item['name'], $term )
680 - || false !== stripos( $item['description'], $term )
681 - || false !== stripos( TablePress::get_user_display_name( $item['author'] ), $term )
682 - || false !== stripos( TablePress::get_user_display_name( $item['options']['last_editor'] ), $term )
683 - || false !== stripos( TablePress::format_datetime( $item['last_modified'], 'mysql', ' ' ), $term )
684 - || false !== stripos( wp_json_encode( $item['data'], TABLEPRESS_JSON_OPTIONS ), $json_encoded_term ) ) {
685 - return true;
686 - }
687 -
688 - return false;
689 - }
690 -
691 - /**
692 - * Callback to for the array sort function.
693 - *
694 - * @since 1.0.0
695 - *
696 - * @param array $item_a First item that shall be compared to.
697 - * @param array $item_b The second item for the comparison.
698 - * @return int (-1, 0, 1) depending on which item sorts "higher".
699 - */
700 - protected function _order_callback( array $item_a, array $item_b ) {
701 - global $orderby, $order;
702 -
703 - if ( 'last_modified_by' !== $orderby ) {
704 - if ( $item_a[ $orderby ] === $item_b[ $orderby ] ) {
705 - return 0;
706 - }
707 - } else {
708 - if ( $item_a['options']['last_editor'] === $item_b['options']['last_editor'] ) {
709 - return 0;
710 - }
711 - }
712 -
713 - // Certain fields require some extra work before being sortable.
714 - switch ( $orderby ) {
715 - case 'last_modified':
716 - // Compare UNIX timestamps for "last modified", which actually is a mySQL datetime string.
717 - $result = ( strtotime( $item_a['last_modified'] ) > strtotime( $item_b['last_modified'] ) ) ? 1 : -1;
718 - break;
719 - case 'author':
720 - // Get the actual author name, plain value is just the user ID.
721 - $result = strnatcasecmp( TablePress::get_user_display_name( $item_a['author'] ), TablePress::get_user_display_name( $item_b['author'] ) );
722 - break;
723 - case 'last_modified_by':
724 - // Get the actual last editor name, plain value is just the user ID.
725 - $result = strnatcasecmp( TablePress::get_user_display_name( $item_a['options']['last_editor'] ), TablePress::get_user_display_name( $item_b['options']['last_editor'] ) );
726 - break;
727 - default:
728 - // Other fields (ID, name, description) are sorted as strings.
729 - $result = strnatcasecmp( $item_a[ $orderby ], $item_b[ $orderby ] );
730 - }
731 -
732 - return ( 'asc' === $order ) ? $result : - $result;
733 - }
734 -
735 - /**
736 - * Prepares the list of items for displaying, by maybe searching and sorting, and by doing pagination.
737 - *
738 - * @since 1.0.0
739 - */
740 - public function prepare_items() {
741 - global $orderby, $order, $s;
742 - wp_reset_vars( array( 'orderby', 'order', 's' ) );
743 -
744 - // Maybe search in the items.
745 - if ( $s ) {
746 - $this->items = array_filter( $this->items, array( $this, '_search_callback' ) );
747 - }
748 -
749 - // Load actual tables after search for less memory consumption.
750 - foreach ( $this->items as &$item ) {
751 - // Don't load data, but load table options for access to last_editor.
752 - $item = TablePress::$model_table->load( $item, false, true );
753 - }
754 - // Break reference in foreach iterator.
755 - unset( $item );
756 -
757 - // Maybe sort the items.
758 - $_sortable_columns = $this->get_sortable_columns();
759 - if ( $orderby && ! empty( $this->items ) && isset( $_sortable_columns[ "table_{$orderby}" ] ) ) {
760 - usort( $this->items, array( $this, '_order_callback' ) );
761 - }
762 -
763 - // Number of records to show per page.
764 - $per_page = $this->get_items_per_page( 'tablepress_list_per_page', 20 ); // hard-coded, as in filter in Admin_Controller
765 - // Page number the user is currently viewing.
766 - $current_page = $this->get_pagenum();
767 - // Number of records in the array.
768 - $total_items = count( $this->items );
769 -
770 - // Slice items array to hold only items for the current page.
771 - $this->items = array_slice( $this->items, ( ( $current_page - 1 ) * $per_page ), $per_page );
772 -
773 - // Register pagination options and calculation results.
774 - $this->set_pagination_args( array(
775 - 'total_items' => $total_items, // Total number of records/items
776 - 'per_page' => $per_page, // Number of items per page
777 - 'total_pages' => ceil( $total_items / $per_page ), // Total number of pages
778 - ) );
779 - }
780 -
781 -} // class TablePress_All_Tables_List_Table