PluginProbe
TablePress – Tables in WordPress made easy / 2.1.7
TablePress – Tables in WordPress made easy v2.1.7
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
tablepress / controllers / controller-admin_ajax.php

controller-admin_ajax.php in TablePress – Tables in WordPress made easy 2.1.7, at controllers/controller-admin_ajax.php

353 lines 13.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Admin AJAX Controller for TablePress with functionality for the AJAX backend
4 *
5 * @package TablePress
6 * @subpackage Controllers
7 * @author Tobias Bäthge
8 * @since 1.0.0
9 */
10
11 // Prohibit direct script loading.
12 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13
14 /**
15 * Admin AJAX Controller class, extends Base Controller Class
16 *
17 * @package TablePress
18 * @subpackage Controllers
19 * @author Tobias Bäthge
20 * @since 1.0.0
21 */
22 class TablePress_Admin_AJAX_Controller extends TablePress_Controller {
23
24 /**
25 * Initiates the Admin AJAX functionality.
26 *
27 * @since 1.0.0
28 */
29 public function __construct() {
30 // Buffer all outputs, to prevent errors/warnings being printed that make the JSON invalid.
31 ob_start();
32
33 parent::__construct();
34
35 $ajax_actions = array( 'hide_message', 'save_table', 'preview_table', 'save_screen_options' );
36 foreach ( $ajax_actions as $action ) {
37 add_action( "wp_ajax_tablepress_{$action}", array( $this, "ajax_action_{$action}" ) );
38 }
39 }
40
41 /**
42 * Hides a header message on an admin screen.
43 *
44 * @since 1.0.0
45 */
46 public function ajax_action_hide_message() {
47 if ( empty( $_GET['item'] ) ) {
48 wp_die( '0' );
49 }
50
51 $message_item = $_GET['item'];
52
53 TablePress::check_nonce( 'hide_message', $message_item, '_wpnonce', true );
54
55 if ( ! current_user_can( 'tablepress_list_tables' ) ) {
56 wp_die( '-1' );
57 }
58
59 TablePress::$model_options->update( "message_{$message_item}", false );
60
61 wp_die( '1' );
62 }
63
64 /**
65 * Saves the table after the "Save Changes" button on the "Edit" screen has been clicked.
66 *
67 * @since 1.0.0
68 */
69 public function ajax_action_save_table() {
70 if ( empty( $_POST['tablepress']['id'] ) ) {
71 wp_die( '-1' );
72 }
73
74 $edit_table = wp_unslash( $_POST['tablepress'] );
75
76 // Check if the submitted nonce matches the generated nonce we created earlier, dies -1 on failure.
77 TablePress::check_nonce( 'edit', $edit_table['id'], '_ajax_nonce', true );
78
79 // Ignore the request if the current user doesn't have sufficient permissions.
80 if ( ! current_user_can( 'tablepress_edit_table', $edit_table['id'] ) ) {
81 wp_die( '-1' );
82 }
83
84 // Default response data.
85 $success = false;
86 $message = 'error_save';
87 $error_details = '';
88 do { // to be able to "break;" (allows for better readable code)
89 // Load table, without table data, but with options and visibility settings.
90 $existing_table = TablePress::$model_table->load( $edit_table['id'], false, true );
91 if ( is_wp_error( $existing_table ) ) { // maybe somehow load a new table here? (TablePress::$model_table->get_table_template())?
92 $error = new WP_Error( 'ajax_save_table_load', '', $edit_table['id'] );
93 $error->merge_from( $existing_table );
94 $error_details = TablePress::get_wp_error_string( $error );
95 break;
96 }
97
98 // Check and convert data that was transmitted as JSON.
99 if ( empty( $edit_table['data'] )
100 || empty( $edit_table['options'] )
101 || empty( $edit_table['visibility'] ) ) {
102 $error = new WP_Error( 'ajax_save_table_data_empty', '', $edit_table['id'] );
103 $error_details = TablePress::get_wp_error_string( $error );
104 break;
105 }
106 $edit_table['data'] = (array) json_decode( $edit_table['data'], true );
107 $edit_table['options'] = (array) json_decode( $edit_table['options'], true );
108 $edit_table['visibility'] = (array) json_decode( $edit_table['visibility'], true );
109
110 // Check consistency of new table, and then merge with existing table.
111 $table = TablePress::$model_table->prepare_table( $existing_table, $edit_table, true );
112 if ( is_wp_error( $table ) ) {
113 $error = new WP_Error( 'ajax_save_table_prepare', '', $edit_table['id'] );
114 $error->merge_from( $table );
115 $error_details = TablePress::get_wp_error_string( $error );
116 break;
117 }
118
119 // DataTables Custom Commands can only be edited by trusted users.
120 if ( ! current_user_can( 'unfiltered_html' ) ) {
121 $table['options']['datatables_custom_commands'] = $existing_table['options']['datatables_custom_commands'];
122 }
123
124 // Save updated table.
125 $saved = TablePress::$model_table->save( $table );
126 if ( is_wp_error( $saved ) ) {
127 $error = new WP_Error( 'ajax_save_table_save', '', $table['id'] );
128 $error->merge_from( $saved );
129 $error_details = TablePress::get_wp_error_string( $error );
130 break;
131 }
132
133 // At this point, the table was saved successfully, possible ID change remains.
134 $success = true;
135 $message = 'success_save';
136
137 // Check if ID change is desired.
138 if ( $table['id'] === $table['new_id'] ) {
139 // If not, we are done.
140 break;
141 }
142
143 // Change table ID.
144 if ( current_user_can( 'tablepress_edit_table_id', $table['id'] ) ) {
145 $id_changed = TablePress::$model_table->change_table_id( $table['id'], $table['new_id'] );
146 if ( ! is_wp_error( $id_changed ) ) {
147 $message = 'success_save_success_id_change';
148 $table['id'] = $table['new_id'];
149 } else {
150 $message = 'success_save_error_id_change';
151 $error = new WP_Error( 'ajax_save_table_id_change', '', $table['new_id'] );
152 $error->merge_from( $id_changed );
153 $error_details = TablePress::get_wp_error_string( $error );
154 }
155 } else {
156 $message = 'success_save_error_id_change';
157 $error_details = 'table_id_could_not_be_changed: capability_check_failed';
158 }
159 } while ( false ); // Do-while-loop through this exactly once, to be able to "break;" early.
160
161 // Generate the response.
162
163 // Common data for all responses.
164 $response = array(
165 'success' => $success,
166 'message' => $message,
167 );
168 if ( $success ) {
169 $response['table_id'] = $table['id'];
170 $response['new_edit_nonce'] = wp_create_nonce( TablePress::nonce( 'edit', $table['id'] ) );
171 $response['new_preview_nonce'] = wp_create_nonce( TablePress::nonce( 'preview_table', $table['id'] ) );
172 $response['last_modified'] = TablePress::format_datetime( $table['last_modified'] );
173 $response['last_editor'] = TablePress::get_user_display_name( $table['options']['last_editor'] );
174 }
175 if ( ! empty( $error_details ) ) {
176 $response['error_details'] = esc_html( $error_details );
177 }
178 // Buffer all outputs, to prevent errors/warnings being printed that make the JSON invalid.
179 $output_buffer = ob_get_clean();
180 if ( ! empty( $output_buffer ) ) {
181 $response['output_buffer'] = $output_buffer;
182 }
183
184 // Send the response.
185 wp_send_json( $response );
186 }
187
188 /**
189 * Returns the live preview data of table that has non-saved changes.
190 *
191 * @since 1.0.0
192 */
193 public function ajax_action_preview_table() {
194 if ( empty( $_POST['tablepress']['id'] ) ) {
195 wp_die( '-1' );
196 }
197
198 $preview_table = wp_unslash( $_POST['tablepress'] );
199
200 // Check if the submitted nonce matches the generated nonce we created earlier, dies -1 on failure.
201 TablePress::check_nonce( 'preview_table', $preview_table['id'], '_ajax_nonce', true );
202
203 // Ignore the request if the current user doesn't have sufficient permissions.
204 if ( ! current_user_can( 'tablepress_preview_table', $preview_table['id'] ) ) {
205 wp_die( '-1' );
206 }
207
208 // Default response data.
209 $success = false;
210 do { // to be able to "break;" (allows for better readable code)
211 // Load table, without table data, but with options and visibility settings.
212 $existing_table = TablePress::$model_table->load( $preview_table['id'], false, true );
213 if ( is_wp_error( $existing_table ) ) { // maybe somehow load a new table here? (TablePress::$model_table->get_table_template())?
214 break;
215 }
216
217 // Check and convert data that was transmitted as JSON.
218 if ( empty( $preview_table['data'] )
219 || empty( $preview_table['options'] )
220 || empty( $preview_table['visibility'] ) ) {
221 break;
222 }
223 $preview_table['data'] = (array) json_decode( $preview_table['data'], true );
224 $preview_table['options'] = (array) json_decode( $preview_table['options'], true );
225 $preview_table['visibility'] = (array) json_decode( $preview_table['visibility'], true );
226
227 // Check consistency of new table, and then merge with existing table.
228 $table = TablePress::$model_table->prepare_table( $existing_table, $preview_table, true );
229 if ( is_wp_error( $table ) ) {
230 break;
231 }
232
233 // DataTables Custom Commands can only be edited by trusted users.
234 if ( ! current_user_can( 'unfiltered_html' ) ) {
235 $table['options']['datatables_custom_commands'] = $existing_table['options']['datatables_custom_commands'];
236 }
237
238 // If the ID has changed, and the new ID is valid, render with the new ID (important e.g. for CSS classes/HTML ID).
239 if ( $table['id'] !== $table['new_id'] && 0 === preg_match( '/[^a-zA-Z0-9_-]/', $table['new_id'] ) ) {
240 $table['id'] = $table['new_id'];
241 }
242
243 // Sanitize all table data to remove unsafe HTML from the preview output, if the user is not allowed to work with unfiltered HTML.
244 if ( ! current_user_can( 'unfiltered_html' ) ) {
245 $table = TablePress::$model_table->sanitize( $table );
246 }
247
248 // At this point, the table data is valid and sanitized and can be rendered.
249 $success = true;
250 } while ( false ); // Do-while-loop through this exactly once, to be able to "break;" early.
251
252 if ( $success ) {
253 // Create a render class instance.
254 $_render = TablePress::load_class( 'TablePress_Render', 'class-render.php', 'classes' );
255 // Merge desired options with default render options (see TablePress_Controller_Frontend::shortcode_table()).
256 $default_render_options = $_render->get_default_render_options();
257 /** This filter is documented in controllers/controller-frontend.php */
258 $default_render_options = apply_filters( 'tablepress_shortcode_table_default_shortcode_atts', $default_render_options );
259 $render_options = shortcode_atts( $default_render_options, $table['options'] );
260 /** This filter is documented in controllers/controller-frontend.php */
261 $render_options = apply_filters( 'tablepress_shortcode_table_shortcode_atts', $render_options );
262 $render_options['html_id'] = "tablepress-{$table['id']}";
263 $_render->set_input( $table, $render_options );
264 $head_html = $_render->get_preview_css();
265 $custom_css = TablePress::$model_options->get( 'custom_css' );
266 $use_custom_css = ( TablePress::$model_options->get( 'use_custom_css' ) && '' !== $custom_css );
267 if ( $use_custom_css ) {
268 $head_html .= "<style>\n{$custom_css}\n</style>\n";
269 }
270
271 $body_html = '<div id="tablepress-page"><p>'
272 . __( 'This is a preview of your table.', 'tablepress' ) . ' '
273 . __( 'Because of CSS styling in your theme, the table might look different on your page!', 'tablepress' ) . ' '
274 . __( 'The Table Features for Site Visitors, like sorting, filtering, and pagination, are also not available in this preview!', 'tablepress' ) . '<br />';
275 // Show the instructions string depending on whether the Block Editor is used on the site or not.
276 if ( TablePress::site_uses_block_editor() ) {
277 $body_html .= sprintf( __( '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' ) );
278 } else {
279 $body_html .= __( 'To insert a table into a post or page, paste its Shortcode at the desired place in the editor.', 'tablepress' ) . ' '
280 . __( 'Each table has a unique ID that needs to be adjusted in that Shortcode.', 'tablepress' );
281 }
282 $body_html .= '</p>' . $_render->get_output() . '</div>';
283 } else {
284 $head_html = '';
285 $body_html = __( 'The preview could not be loaded.', 'tablepress' );
286 }
287
288 // Generate the response.
289 $response = array(
290 'success' => $success,
291 'head_html' => $head_html,
292 'body_html' => $body_html,
293 );
294 // Buffer all outputs, to prevent errors/warnings being printed that make the JSON invalid.
295 $output_buffer = ob_get_clean();
296 if ( ! empty( $output_buffer ) ) {
297 $response['output_buffer'] = $output_buffer;
298 }
299
300 // Send the response.
301 wp_send_json( $response );
302 }
303
304 /**
305 * Saves the screen options on the "Edit" screen when they are changed.
306 *
307 * @since 2.1.0
308 */
309 public function ajax_action_save_screen_options() {
310 // Check if the submitted nonce matches the generated nonce we created earlier, dies -1 on failure.
311 TablePress::check_nonce( 'screen_options', false, '_ajax_nonce', true );
312
313 if ( empty( $_POST['tablepress'] ) ) {
314 wp_die( '-1' );
315 }
316 $screen_options = wp_unslash( $_POST['tablepress'] );
317
318 // Sanitize and limit values to a minimum and a maximum.
319 $new_screen_options = array();
320
321 if ( isset( $screen_options['table_editor_column_width'] ) ) {
322 $new_screen_options['table_editor_column_width'] = absint( $screen_options['table_editor_column_width'] );
323 $new_screen_options['table_editor_column_width'] = max( $new_screen_options['table_editor_column_width'], 30 ); // Minimum width: 30 pixels.
324 $new_screen_options['table_editor_column_width'] = min( $new_screen_options['table_editor_column_width'], 9999 ); // Maximum width: 9999 pixels.
325 }
326
327 if ( isset( $screen_options['table_editor_line_clamp'] ) ) {
328 $new_screen_options['table_editor_line_clamp'] = absint( $screen_options['table_editor_line_clamp'] );
329 $new_screen_options['table_editor_line_clamp'] = min( $new_screen_options['table_editor_line_clamp'], 999 ); // Maximum lines: 999. Minimum of 0 (for all lines) is ensured by absint().
330 }
331
332 if ( empty( $new_screen_options ) ) {
333 wp_die( '-1' );
334 }
335 TablePress::$model_options->update( $new_screen_options );
336
337 // Generate the response.
338 $response = array(
339 'success' => true,
340 );
341
342 // Buffer all outputs, to prevent errors/warnings being printed that make the JSON invalid.
343 $output_buffer = ob_get_clean();
344 if ( ! empty( $output_buffer ) ) {
345 $response['output_buffer'] = $output_buffer;
346 }
347
348 // Send the response.
349 wp_send_json( $response );
350 }
351
352 } // class TablePress_Admin_AJAX_Controller
353