PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.3
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.3
5.5.83 5.5.82 5.5.81 5.5.80 5.5.79 5.5.77 5.5.76 5.5.75 5.5.73 5.5.72 5.5.22 5.5.23 5.5.29 5.5.3 5.5.31 5.5.32 5.5.34 5.5.35 5.5.36 5.5.37 5.5.4 5.5.40 5.5.41 5.5.42 5.5.43 All 159 releases
wp-data-access / WPDataAccess / API / WPDA_API_Core.php

WPDA_API_Core.php in WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards 5.5.3, at WPDataAccess/API/WPDA_API_Core.php

543 lines 22.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDataAccess\API;
4
5 use WPDataAccess\Connection\WPDADB;
6 use WPDataAccess\Plugin_Table_Models\WPDA_Media_Model;
7 use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model;
8 use WPDataAccess\WPDA;
9 abstract class WPDA_API_Core {
10 public abstract function register_rest_routes();
11
12 private static $user_roles = null;
13
14 private static $user_login = null;
15
16 private $params;
17
18 public function __construct() {
19 $this->params = array(
20 'dbs' => array(
21 'required' => true,
22 'type' => 'string',
23 'description' => __( 'Local database name or remote connection string', 'wp-data-access' ),
24 'sanitize_callback' => function ( $param ) {
25 return $this->sanitize_db_identifier( $param );
26 },
27 'validate_callback' => function ( $param ) {
28 return $this->validate_db_identifier( $param );
29 },
30 ),
31 'tbl' => array(
32 'required' => true,
33 'type' => 'string',
34 'description' => __( 'Table or view name', 'wp-data-access' ),
35 'sanitize_callback' => function ( $param ) {
36 return $this->sanitize_db_identifier( $param );
37 },
38 'validate_callback' => function ( $param ) {
39 return $this->validate_db_identifier( $param );
40 },
41 ),
42 'app_id' => array(
43 'required' => true,
44 'type' => 'integer',
45 'description' => __( 'App ID', 'wp-data-access' ),
46 'sanitize_callback' => 'absint',
47 'validate_callback' => 'rest_validate_request_arg',
48 ),
49 'cnt_id' => array(
50 'required' => true,
51 'type' => 'integer',
52 'description' => __( 'Container ID', 'wp-data-access' ),
53 'sanitize_callback' => 'absint',
54 'validate_callback' => 'rest_validate_request_arg',
55 ),
56 'app_name' => array(
57 'required' => true,
58 'type' => 'string',
59 'description' => __( 'App name', 'wp-data-access' ),
60 'sanitize_callback' => 'sanitize_text_field',
61 'validate_callback' => 'rest_validate_request_arg',
62 ),
63 'app_title' => array(
64 'required' => true,
65 'type' => 'string',
66 'description' => __( 'App title', 'wp-data-access' ),
67 'sanitize_callback' => 'sanitize_text_field',
68 'validate_callback' => 'rest_validate_request_arg',
69 ),
70 'app_type' => array(
71 'required' => true,
72 'type' => 'integer',
73 'description' => __( 'App type', 'wp-data-access' ),
74 'sanitize_callback' => 'absint',
75 'validate_callback' => 'rest_validate_request_arg',
76 ),
77 'app_settings' => array(
78 'required' => true,
79 'type' => 'string',
80 'description' => __( 'App settings', 'wp-data-access' ),
81 'sanitize_callback' => 'sanitize_text_field',
82 'validate_callback' => 'rest_validate_request_arg',
83 ),
84 'app_cls' => array(
85 'required' => true,
86 'type' => 'array',
87 'description' => __( 'App columns', 'wp-data-access' ),
88 'sanitize_callback' => function ( $param ) {
89 return $this->sanitize_columns( $param );
90 },
91 'validate_callback' => function ( $param ) {
92 return $this->validate_columns( $param );
93 },
94 ),
95 'col' => array(
96 'required' => true,
97 'type' => 'string',
98 'description' => __( 'Column name', 'wp-data-access' ),
99 'sanitize_callback' => function ( $param ) {
100 return $this->sanitize_db_identifier( $param );
101 },
102 'validate_callback' => function ( $param ) {
103 return $this->validate_db_identifier( $param );
104 },
105 ),
106 'cols' => array(
107 'required' => true,
108 'type' => 'mixed',
109 'description' => __( 'Table or view columns', 'wp-data-access' ),
110 'sanitize_callback' => function ( $param ) {
111 $columns = array();
112 foreach ( rest_sanitize_object( $param ) as $column_name => $queryable ) {
113 $columns[$this->sanitize_db_identifier( $column_name )] = $queryable === true;
114 }
115 return $columns;
116 },
117 'validate_callback' => function ( $param ) {
118 return is_array( $param );
119 },
120 ),
121 'page_index' => array(
122 'required' => false,
123 'type' => 'integer',
124 'description' => __( 'Page number', 'wp-data-access' ),
125 'default' => 1,
126 'minimum' => 0,
127 'sanitize_callback' => 'absint',
128 'validate_callback' => 'rest_validate_request_arg',
129 ),
130 'page_size' => array(
131 'required' => false,
132 'type' => 'integer',
133 'description' => __( 'Rows per page (0=all)', 'wp-data-access' ),
134 'default' => 10,
135 'minimum' => 1,
136 'sanitize_callback' => 'absint',
137 'validate_callback' => 'rest_validate_request_arg',
138 ),
139 'search' => array(
140 'required' => false,
141 'type' => 'string',
142 'description' => __( 'Global search filter', 'wp-data-access' ),
143 'sanitize_callback' => 'sanitize_text_field',
144 'validate_callback' => 'rest_validate_request_arg',
145 ),
146 'search_columns' => array(
147 'required' => false,
148 'type' => 'mixed',
149 'description' => __( 'Column search filters', 'wp-data-access' ),
150 'sanitize_callback' => function ( $param ) {
151 $search = array();
152 foreach ( rest_sanitize_array( $param ) as $value ) {
153 if ( isset( $value['id'], $value['value'] ) ) {
154 $search[] = array(
155 'id' => $this->sanitize_db_identifier( $value['id'] ),
156 'value' => ( is_array( $value['value'] ) ? map_deep( $value['value'], 'sanitize_text_field' ) : sanitize_text_field( $value['value'] ) ),
157 );
158 }
159 }
160 return $search;
161 },
162 'validate_callback' => function ( $param ) {
163 return is_array( $param );
164 },
165 ),
166 'search_column_fns' => array(
167 'required' => false,
168 'description' => __( 'Column search filter modes', 'wp-data-access' ),
169 'sanitize_callback' => function ( $param ) {
170 $search_modes = array();
171 foreach ( $param as $key => $value ) {
172 if ( in_array( $value, WPDA_Table::WPDA_SEARCH_MODES ) ) {
173 // Accepting only valid modes
174 $search_modes[$this->sanitize_db_identifier( $key )] = sanitize_text_field( $value );
175 }
176 }
177 return $search_modes;
178 },
179 'validate_callback' => function ( $param ) {
180 return is_array( $param );
181 },
182 ),
183 'search_column_lov' => array(
184 'required' => false,
185 'type' => 'mixed',
186 'description' => __( 'Search columns for lov support', 'wp-data-access' ),
187 'sanitize_callback' => function ( $param ) {
188 $columns = array();
189 foreach ( rest_sanitize_object( $param ) as $column_name => $value ) {
190 $columns[$this->sanitize_db_identifier( $column_name )] = sanitize_text_field( wp_unslash( $value ) );
191 }
192 return $columns;
193 },
194 'validate_callback' => function ( $param ) {
195 return is_array( $param );
196 },
197 ),
198 'sorting' => array(
199 'required' => false,
200 'description' => __( 'Order by (array of { id and desc })', 'wp-data-access' ),
201 'sanitize_callback' => function ( $param ) {
202 $order_by = array();
203 foreach ( rest_sanitize_object( $param ) as $value ) {
204 if ( isset( $value['id'], $value['desc'] ) ) {
205 $order_by[] = array(
206 'id' => $this->sanitize_db_identifier( $value['id'] ),
207 'desc' => sanitize_text_field( $value['desc'] ),
208 );
209 }
210 }
211 return $order_by;
212 },
213 'validate_callback' => function ( $param ) {
214 if ( !is_array( $param ) ) {
215 return false;
216 }
217 foreach ( $param as $value ) {
218 if ( !isset( $value['id'], $value['desc'] ) ) {
219 return false;
220 }
221 }
222 return true;
223 },
224 ),
225 'row_count' => array(
226 'required' => false,
227 'type' => 'integer',
228 'description' => __( 'Row count', 'wp-data-access' ),
229 'minimum' => 0,
230 'sanitize_callback' => 'absint',
231 'validate_callback' => 'rest_validate_request_arg',
232 ),
233 'row_count_estimate' => array(
234 'required' => false,
235 'type' => 'boolean',
236 'description' => __( 'Calculate row count estimate', 'wp-data-access' ),
237 'sanitize_callback' => 'sanitize_text_field',
238 'validate_callback' => 'rest_validate_request_arg',
239 ),
240 'key' => array(
241 'required' => true,
242 'type' => 'mixed',
243 'description' => __( 'Primary key', 'wp-data-access' ),
244 'sanitize_callback' => function ( $param ) {
245 $primary_keys = array();
246 foreach ( $param as $key => $value ) {
247 $primary_keys[$this->sanitize_db_identifier( $key )] = sanitize_text_field( $value );
248 }
249 return $primary_keys;
250 },
251 'validate_callback' => function ( $param ) {
252 return is_array( $param );
253 },
254 ),
255 'val' => array(
256 'required' => true,
257 'type' => 'mixed',
258 'description' => __( 'Column values', 'wp-data-access' ),
259 'validate_callback' => function ( $param ) {
260 return is_array( $param );
261 },
262 ),
263 'media' => array(
264 'required' => true,
265 'type' => 'mixed',
266 'description' => __( 'Media columns', 'wp-data-access' ),
267 'sanitize_callback' => function ( $param ) {
268 $media = array();
269 foreach ( $param as $key => $value ) {
270 $media[$this->sanitize_db_identifier( $key )] = sanitize_text_field( $value );
271 }
272 return $media;
273 },
274 'validate_callback' => function ( $param ) {
275 return is_array( $param );
276 },
277 ),
278 );
279 }
280
281 protected function get_param( $key ) {
282 if ( isset( $this->params[$key] ) ) {
283 return $this->params[$key];
284 } else {
285 // Force REST API error
286 return false;
287 }
288 }
289
290 protected function get_user_roles() {
291 if ( null === WPDA_API_Core::$user_roles ) {
292 WPDA_API_Core::$user_roles = WPDA::get_current_user_roles();
293 if ( false === WPDA_API_Core::$user_roles ) {
294 WPDA_API_Core::$user_roles = array();
295 }
296 }
297 return WPDA_API_Core::$user_roles;
298 }
299
300 protected function get_user_login() {
301 if ( null === WPDA_API_Core::$user_login ) {
302 WPDA_API_Core::$user_login = WPDA::get_current_user_login();
303 }
304 return WPDA_API_Core::$user_login;
305 }
306
307 protected function current_user_can_access( $admins_only = false ) {
308 return in_array( 'administrator', $this->get_user_roles() );
309 }
310
311 protected function unauthorized() {
312 return new \WP_Error('error', __( 'Unauthorized', 'wp-data-access' ), array(
313 'status' => 401,
314 ));
315 }
316
317 protected function current_user_token_valid( $request, $token_required = false ) {
318 return wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' );
319 }
320
321 protected function invalid_nonce() {
322 return new \WP_Error('rest_cookie_invalid_nonce', 'Cookie check failed', array(
323 'status' => 403,
324 ));
325 }
326
327 protected function bad_request() {
328 return new \WP_Error('error', __( 'Bad request', 'wp-data-access' ), array(
329 'status' => 400,
330 ));
331 }
332
333 protected function invalid_app_settings() {
334 return new \WP_Error('error', __( 'Invalid app settings - contact support', 'wp-data-access' ), array(
335 'status' => 403,
336 ));
337 }
338
339 protected function current_user_can_remote() {
340 return false;
341 }
342
343 protected function sanitize_db_identifier( $param ) {
344 // Preserve starting and trailing spaces
345 $spaces_before = strlen( $param ) - strlen( ltrim( $param ) );
346 $spaces_after = strlen( $param ) - strlen( rtrim( $param ) );
347 return str_repeat( ' ', $spaces_before ) . WPDA::remove_backticks( sanitize_text_field( $param ) ) . str_repeat( ' ', $spaces_after );
348 }
349
350 protected function validate_db_identifier( $param ) {
351 return !empty( WPDA::remove_backticks( $param ) );
352 }
353
354 protected function sanitize_columns( $param ) {
355 $sanitized_param = array();
356 foreach ( $param as $p ) {
357 $sanitized_param[] = array(
358 'columnName' => $this->sanitize_db_identifier( $p['columnName'] ),
359 'isSelected' => $p['isSelected'],
360 );
361 }
362 return $sanitized_param;
363 }
364
365 protected function validate_columns( $param ) {
366 if ( !is_array( $param ) ) {
367 return false;
368 }
369 foreach ( $param as $p ) {
370 if ( !isset( $p['columnName'], $p['isSelected'] ) || !$this->validate_db_identifier( $p['columnName'] ) || 'boolean' !== gettype( $p['isSelected'] ) ) {
371 return false;
372 }
373 }
374 return true;
375 }
376
377 protected function get_wp_roles() {
378 $roles = array();
379 global $wp_roles;
380 foreach ( $wp_roles->roles as $role => $role_object ) {
381 if ( isset( $role_object['name'] ) ) {
382 $roles[$role] = $role_object['name'];
383 }
384 }
385 return $roles;
386 }
387
388 protected function get_wp_users() {
389 $users = array();
390 foreach ( get_users() as $user ) {
391 if ( isset( $user->user_login, $user->display_name ) ) {
392 $users[$user->user_login] = $user->display_name;
393 }
394 }
395 return $users;
396 }
397
398 protected function get_env() {
399 return array(
400 'ip' => $_SERVER['REMOTE_ADDR'],
401 'id' => WPDA::get_current_user_id(),
402 'user' => WPDA::get_current_user_login(),
403 'roles' => WPDA::get_current_user_roles(),
404 'login' => 'anonymous' !== WPDA::get_current_user_login(),
405 );
406 }
407
408 protected function get_table_info( $dbs, $tbl, $default_where = '' ) {
409 $wpdadb = WPDADB::get_db_connection( $dbs );
410 if ( $wpdadb === null ) {
411 return array(
412 'type' => null,
413 'engine' => null,
414 'count' => null,
415 );
416 }
417 $query = $wpdadb->prepare( "\n\t\t\t\t\tselect table_type,\n\t\t\t\t\t engine,\n\t\t\t\t\t table_rows\n\t\t\t\t\t from information_schema.tables\n\t\t\t\t\t where table_schema = %s\n\t\t\t\t\t and table_name = %s\n\t\t\t\t\t order by table_name\n\t\t\t\t", array($wpdadb->dbname, $tbl) );
418 $resultset = $wpdadb->get_results( $query, 'ARRAY_N' );
419 // phpcs:ignore Standard.Category.SniffName.ErrorCode
420 if ( count( $resultset ) === 1 ) {
421 if ( null !== $resultset[0][2] ) {
422 return array(
423 'type' => $resultset[0][0],
424 'engine' => $resultset[0][1],
425 'count' => ( '' === $default_where ? ( $resultset[0][2] === 0 ? null : $resultset[0][2] ) : null ),
426 );
427 } else {
428 $count = $this->get_row_count_estimate( $dbs, $tbl );
429 return array(
430 'type' => $resultset[0][0],
431 'engine' => $resultset[0][1],
432 'count' => ( $count === 0 ? null : $count ),
433 );
434 }
435 } else {
436 return array(
437 'type' => null,
438 'engine' => null,
439 'count' => null,
440 );
441 }
442 }
443
444 protected function get_row_count_estimate( $dbs, $tbl ) {
445 $wpdadb = WPDADB::get_db_connection( $dbs );
446 if ( null === $wpdadb ) {
447 return -1;
448 }
449 $explain = $wpdadb->get_results( 'explain select count(*) from `' . str_replace( '`', '', $tbl ) . '`', 'ARRAY_A' );
450 if ( isset( $explain[0]['rows'] ) ) {
451 return $explain[0]['rows'];
452 } else {
453 // This should never happen
454 return -1;
455 }
456 }
457
458 protected function get_media( $dbs, $tbl, $columns ) {
459 $media = array();
460 $wp_media = array();
461 foreach ( $columns as $column ) {
462 $media_type = WPDA_Media_Model::get_column_media( $tbl, $column['column_name'], $dbs );
463 switch ( $media_type ) {
464 case 'ImageURL':
465 $media[$column['column_name']] = $media_type;
466 break;
467 case 'Hyperlink':
468 // Get table settings.
469 $table_settings_db = WPDA_Table_Settings_Model::query( $dbs, $tbl );
470 if ( isset( $table_settings_db[0]['wpda_table_settings'] ) ) {
471 $table_settings = json_decode( $table_settings_db[0]['wpda_table_settings'], true );
472 } else {
473 $table_settings = null;
474 }
475 // Check hyperlink format.
476 if ( isset( $table_settings['table_settings']['hyperlink_definition'] ) && 'text' === $table_settings['table_settings']['hyperlink_definition'] ) {
477 $media[$column['column_name']] = 'HyperlinkURL';
478 } else {
479 $media[$column['column_name']] = 'HyperlinkObject';
480 }
481 break;
482 default:
483 if ( false !== $media_type ) {
484 // Handle WordPress Media Library integration
485 $media[$column['column_name']] = "WP-{$media_type}";
486 }
487 }
488 $wp_media[$column['column_name']] = $media_type;
489 }
490 return [
491 'media' => $media,
492 'wp_media' => $wp_media,
493 ];
494 }
495
496 /**
497 * Write standard JSON response.
498 *
499 * @param string $message Response text message.
500 * @param mixed $data Response data.
501 * @param mixed $context Context data.
502 * @param mixed $meta Meta data.
503 * @return \WP_REST_Response
504 */
505 protected static function WPDA_Rest_Response(
506 $message = '',
507 $data = null,
508 $context = null,
509 $meta = null
510 ) {
511 // Prepare response.
512 $response = new \WP_REST_Response(array(
513 'code' => 'ok',
514 'message' => $message,
515 'data' => $data,
516 'context' => $context,
517 'meta' => $meta,
518 ), 200);
519 // Disable caching.
520 $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0' );
521 $response->header( 'Pragma', 'no-cache' );
522 $response->header( 'Expires', '0' );
523 return $response;
524 }
525
526 protected static function WPDA_Rest_Response_Info( $message = '' ) {
527 // Prepare response.
528 $response = new \WP_REST_Response(array(
529 'code' => 'info',
530 'message' => $message,
531 'data' => null,
532 'context' => null,
533 'meta' => null,
534 ), 200);
535 // Disable caching.
536 $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0' );
537 $response->header( 'Pragma', 'no-cache' );
538 $response->header( 'Expires', '0' );
539 return $response;
540 }
541
542 }
543