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

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

305 lines 11.7 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 * Initiate 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' );
36 foreach ( $ajax_actions as $action ) {
37 add_action( "wp_ajax_tablepress_{$action}", array( $this, "ajax_action_{$action}" ) );
38 }
39 }
40
41 /**
42 * Hide 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 * Save 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 // Add an error code to the existing WP_Error.
93 $existing_table->add( 'ajax_save_table_load', '', $edit_table['id'] );
94 $error_details = TablePress::get_wp_error_string( $existing_table );
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 // Create a new WP_Error.
103 $empty_data_error = new WP_Error( 'ajax_save_table_data_empty', '', $edit_table['id'] );
104 $error_details = TablePress::get_wp_error_string( $empty_data_error );
105 break;
106 }
107 $edit_table['data'] = (array) json_decode( $edit_table['data'], true );
108 $edit_table['options'] = (array) json_decode( $edit_table['options'], true );
109 $edit_table['visibility'] = (array) json_decode( $edit_table['visibility'], true );
110
111 // Check consistency of new table, and then merge with existing table.
112 $table = TablePress::$model_table->prepare_table( $existing_table, $edit_table, true );
113 if ( is_wp_error( $table ) ) {
114 // Add an error code to the existing WP_Error.
115 $table->add( 'ajax_save_table_prepare', '', $edit_table['id'] );
116 $error_details = TablePress::get_wp_error_string( $table );
117 break;
118 }
119
120 // DataTables Custom Commands can only be edited by trusted users.
121 if ( ! current_user_can( 'unfiltered_html' ) ) {
122 $table['options']['datatables_custom_commands'] = $existing_table['options']['datatables_custom_commands'];
123 }
124
125 // Save updated table.
126 $saved = TablePress::$model_table->save( $table );
127 if ( is_wp_error( $saved ) ) {
128 // Add an error code to the existing WP_Error.
129 $saved->add( 'ajax_save_table_save', '', $table['id'] );
130 $error_details = TablePress::get_wp_error_string( $saved );
131 break;
132 }
133
134 // At this point, the table was saved successfully, possible ID change remains.
135 $success = true;
136 $message = 'success_save';
137
138 // Check if ID change is desired.
139 if ( $table['id'] === $table['new_id'] ) {
140 // If not, we are done.
141 break;
142 }
143
144 // Change table ID.
145 if ( current_user_can( 'tablepress_edit_table_id', $table['id'] ) ) {
146 $id_changed = TablePress::$model_table->change_table_id( $table['id'], $table['new_id'] );
147 if ( ! is_wp_error( $id_changed ) ) {
148 $message = 'success_save_success_id_change';
149 $table['id'] = $table['new_id'];
150 } else {
151 $message = 'success_save_error_id_change';
152 // Add an error code to the existing WP_Error.
153 $id_changed->add( 'ajax_save_table_id_change', '', $table['new_id'] );
154 $error_details = TablePress::get_wp_error_string( $id_changed );
155 }
156 } else {
157 $message = 'success_save_error_id_change';
158 $error_details = 'table_id_could_not_be_changed: capability_check_failed';
159 }
160 } while ( false ); // Do-while-loop through this exactly once, to be able to "break;" early.
161
162 // Generate the response.
163
164 // Common data for all responses.
165 $response = array(
166 'success' => $success,
167 'message' => $message,
168 );
169 if ( $success ) {
170 $response['table_id'] = $table['id'];
171 $response['new_edit_nonce'] = wp_create_nonce( TablePress::nonce( 'edit', $table['id'] ) );
172 $response['new_preview_nonce'] = wp_create_nonce( TablePress::nonce( 'preview_table', $table['id'] ) );
173 $response['last_modified'] = TablePress::format_datetime( $table['last_modified'] );
174 $response['last_editor'] = TablePress::get_user_display_name( $table['options']['last_editor'] );
175 }
176 if ( ! empty( $error_details ) ) {
177 $response['error_details'] = esc_html( $error_details );
178 }
179 // Buffer all outputs, to prevent errors/warnings being printed that make the JSON invalid.
180 $output_buffer = ob_get_clean();
181 if ( ! empty( $output_buffer ) ) {
182 $response['output_buffer'] = $output_buffer;
183 }
184
185 // Send the response.
186 wp_send_json( $response );
187 }
188
189 /**
190 * Return the live preview data of table that has non-saved changes.
191 *
192 * @since 1.0.0
193 */
194 public function ajax_action_preview_table() {
195 if ( empty( $_POST['tablepress']['id'] ) ) {
196 wp_die( '-1' );
197 }
198
199 $preview_table = wp_unslash( $_POST['tablepress'] );
200
201 // Check if the submitted nonce matches the generated nonce we created earlier, dies -1 on failure.
202 TablePress::check_nonce( 'preview_table', $preview_table['id'], '_ajax_nonce', true );
203
204 // Ignore the request if the current user doesn't have sufficient permissions.
205 if ( ! current_user_can( 'tablepress_preview_table', $preview_table['id'] ) ) {
206 wp_die( '-1' );
207 }
208
209 // Default response data.
210 $success = false;
211 do { // to be able to "break;" (allows for better readable code)
212 // Load table, without table data, but with options and visibility settings.
213 $existing_table = TablePress::$model_table->load( $preview_table['id'], false, true );
214 if ( is_wp_error( $existing_table ) ) { // maybe somehow load a new table here? (TablePress::$model_table->get_table_template())?
215 break;
216 }
217
218 // Check and convert data that was transmitted as JSON.
219 if ( empty( $preview_table['data'] )
220 || empty( $preview_table['options'] )
221 || empty( $preview_table['visibility'] ) ) {
222 break;
223 }
224 $preview_table['data'] = (array) json_decode( $preview_table['data'], true );
225 $preview_table['options'] = (array) json_decode( $preview_table['options'], true );
226 $preview_table['visibility'] = (array) json_decode( $preview_table['visibility'], true );
227
228 // Check consistency of new table, and then merge with existing table.
229 $table = TablePress::$model_table->prepare_table( $existing_table, $preview_table, true );
230 if ( is_wp_error( $table ) ) {
231 break;
232 }
233
234 // DataTables Custom Commands can only be edited by trusted users.
235 if ( ! current_user_can( 'unfiltered_html' ) ) {
236 $table['options']['datatables_custom_commands'] = $existing_table['options']['datatables_custom_commands'];
237 }
238
239 // If the ID has changed, and the new ID is valid, render with the new ID (important e.g. for CSS classes/HTML ID).
240 if ( $table['id'] !== $table['new_id'] && 0 === preg_match( '/[^a-zA-Z0-9_-]/', $table['new_id'] ) ) {
241 $table['id'] = $table['new_id'];
242 }
243
244 // Sanitize all table data to remove unsafe HTML from the preview output, if the user is not allowed to work with unfiltered HTML.
245 if ( ! current_user_can( 'unfiltered_html' ) ) {
246 $table = TablePress::$model_table->sanitize( $table );
247 }
248
249 // At this point, the table data is valid and sanitized and can be rendered.
250 $success = true;
251 } while ( false ); // Do-while-loop through this exactly once, to be able to "break;" early.
252
253 if ( $success ) {
254 // Create a render class instance.
255 $_render = TablePress::load_class( 'TablePress_Render', 'class-render.php', 'classes' );
256 // Merge desired options with default render options (see TablePress_Controller_Frontend::shortcode_table()).
257 $default_render_options = $_render->get_default_render_options();
258 /** This filter is documented in controllers/controller-frontend.php */
259 $default_render_options = apply_filters( 'tablepress_shortcode_table_default_shortcode_atts', $default_render_options );
260 $render_options = shortcode_atts( $default_render_options, $table['options'] );
261 /** This filter is documented in controllers/controller-frontend.php */
262 $render_options = apply_filters( 'tablepress_shortcode_table_shortcode_atts', $render_options );
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 } // class TablePress_Admin_AJAX_Controller
305