PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.4
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.4
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.4, at WPDataAccess/API/WPDA_API_Core.php

556 lines 22.9 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 'typ' => array(
264 'required' => true,
265 'type' => 'integer',
266 'description' => __( 'Type = 0, view = 1', 'wp-data-access' ),
267 'minimum' => 0,
268 'maximum' => 1,
269 'sanitize_callback' => 'absint',
270 'validate_callback' => 'rest_validate_request_arg',
271 ),
272 'media' => array(
273 'required' => true,
274 'type' => 'mixed',
275 'description' => __( 'Media columns', 'wp-data-access' ),
276 'sanitize_callback' => function ( $param ) {
277 $media = array();
278 foreach ( $param as $key => $value ) {
279 $media[$this->sanitize_db_identifier( $key )] = sanitize_text_field( $value );
280 }
281 return $media;
282 },
283 'validate_callback' => function ( $param ) {
284 return is_array( $param );
285 },
286 ),
287 );
288 }
289
290 protected function get_param( $key, $description = null ) {
291 if ( isset( $this->params[$key] ) ) {
292 $param = $this->params[$key];
293 if ( null !== $description ) {
294 $param['description'] = $description;
295 }
296 return $param;
297 } else {
298 // Force REST API error
299 return false;
300 }
301 }
302
303 protected function get_user_roles() {
304 if ( null === WPDA_API_Core::$user_roles ) {
305 WPDA_API_Core::$user_roles = WPDA::get_current_user_roles();
306 if ( false === WPDA_API_Core::$user_roles ) {
307 WPDA_API_Core::$user_roles = array();
308 }
309 }
310 return WPDA_API_Core::$user_roles;
311 }
312
313 protected function get_user_login() {
314 if ( null === WPDA_API_Core::$user_login ) {
315 WPDA_API_Core::$user_login = WPDA::get_current_user_login();
316 }
317 return WPDA_API_Core::$user_login;
318 }
319
320 protected function current_user_can_access( $admins_only = false ) {
321 return in_array( 'administrator', $this->get_user_roles() );
322 }
323
324 protected function unauthorized() {
325 return new \WP_Error('error', __( 'Unauthorized', 'wp-data-access' ), array(
326 'status' => 401,
327 ));
328 }
329
330 protected function current_user_token_valid( $request, $token_required = false ) {
331 return wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' );
332 }
333
334 protected function invalid_nonce() {
335 return new \WP_Error('rest_cookie_invalid_nonce', 'Cookie check failed', array(
336 'status' => 403,
337 ));
338 }
339
340 protected function bad_request() {
341 return new \WP_Error('error', __( 'Bad request', 'wp-data-access' ), array(
342 'status' => 400,
343 ));
344 }
345
346 protected function invalid_app_settings() {
347 return new \WP_Error('error', __( 'Invalid app settings - contact support', 'wp-data-access' ), array(
348 'status' => 403,
349 ));
350 }
351
352 protected function current_user_can_remote() {
353 return false;
354 }
355
356 protected function sanitize_db_identifier( $param ) {
357 // Preserve starting and trailing spaces
358 $spaces_before = strlen( $param ) - strlen( ltrim( $param ) );
359 $spaces_after = strlen( $param ) - strlen( rtrim( $param ) );
360 return str_repeat( ' ', $spaces_before ) . WPDA::remove_backticks( sanitize_text_field( $param ) ) . str_repeat( ' ', $spaces_after );
361 }
362
363 protected function validate_db_identifier( $param ) {
364 return !empty( WPDA::remove_backticks( $param ) );
365 }
366
367 protected function sanitize_columns( $param ) {
368 $sanitized_param = array();
369 foreach ( $param as $p ) {
370 $sanitized_param[] = array(
371 'columnName' => $this->sanitize_db_identifier( $p['columnName'] ),
372 'isSelected' => $p['isSelected'],
373 );
374 }
375 return $sanitized_param;
376 }
377
378 protected function validate_columns( $param ) {
379 if ( !is_array( $param ) ) {
380 return false;
381 }
382 foreach ( $param as $p ) {
383 if ( !isset( $p['columnName'], $p['isSelected'] ) || !$this->validate_db_identifier( $p['columnName'] ) || 'boolean' !== gettype( $p['isSelected'] ) ) {
384 return false;
385 }
386 }
387 return true;
388 }
389
390 protected function get_wp_roles() {
391 $roles = array();
392 global $wp_roles;
393 foreach ( $wp_roles->roles as $role => $role_object ) {
394 if ( isset( $role_object['name'] ) ) {
395 $roles[$role] = $role_object['name'];
396 }
397 }
398 return $roles;
399 }
400
401 protected function get_wp_users() {
402 $users = array();
403 foreach ( get_users() as $user ) {
404 if ( isset( $user->user_login, $user->display_name ) ) {
405 $users[$user->user_login] = $user->display_name;
406 }
407 }
408 return $users;
409 }
410
411 protected function get_env() {
412 return array(
413 'ip' => $_SERVER['REMOTE_ADDR'],
414 'id' => WPDA::get_current_user_id(),
415 'user' => WPDA::get_current_user_login(),
416 'roles' => WPDA::get_current_user_roles(),
417 'login' => 'anonymous' !== WPDA::get_current_user_login(),
418 );
419 }
420
421 protected function get_table_info( $dbs, $tbl, $default_where = '' ) {
422 $wpdadb = WPDADB::get_db_connection( $dbs );
423 if ( $wpdadb === null ) {
424 return array(
425 'type' => null,
426 'engine' => null,
427 'count' => null,
428 );
429 }
430 $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) );
431 $resultset = $wpdadb->get_results( $query, 'ARRAY_N' );
432 // phpcs:ignore Standard.Category.SniffName.ErrorCode
433 if ( count( $resultset ) === 1 ) {
434 if ( null !== $resultset[0][2] ) {
435 return array(
436 'type' => $resultset[0][0],
437 'engine' => $resultset[0][1],
438 'count' => ( '' === $default_where ? ( $resultset[0][2] === 0 ? null : $resultset[0][2] ) : null ),
439 );
440 } else {
441 $count = $this->get_row_count_estimate( $dbs, $tbl );
442 return array(
443 'type' => $resultset[0][0],
444 'engine' => $resultset[0][1],
445 'count' => ( $count === 0 ? null : $count ),
446 );
447 }
448 } else {
449 return array(
450 'type' => null,
451 'engine' => null,
452 'count' => null,
453 );
454 }
455 }
456
457 protected function get_row_count_estimate( $dbs, $tbl ) {
458 $wpdadb = WPDADB::get_db_connection( $dbs );
459 if ( null === $wpdadb ) {
460 return -1;
461 }
462 $explain = $wpdadb->get_results( 'explain select count(*) from `' . str_replace( '`', '', $tbl ) . '`', 'ARRAY_A' );
463 if ( isset( $explain[0]['rows'] ) ) {
464 return $explain[0]['rows'];
465 } else {
466 // This should never happen
467 return -1;
468 }
469 }
470
471 protected function get_media( $dbs, $tbl, $columns ) {
472 $media = array();
473 $wp_media = array();
474 foreach ( $columns as $column ) {
475 $media_type = WPDA_Media_Model::get_column_media( $tbl, $column['column_name'], $dbs );
476 switch ( $media_type ) {
477 case 'ImageURL':
478 $media[$column['column_name']] = $media_type;
479 break;
480 case 'Hyperlink':
481 // Get table settings.
482 $table_settings_db = WPDA_Table_Settings_Model::query( $dbs, $tbl );
483 if ( isset( $table_settings_db[0]['wpda_table_settings'] ) ) {
484 $table_settings = json_decode( $table_settings_db[0]['wpda_table_settings'], true );
485 } else {
486 $table_settings = null;
487 }
488 // Check hyperlink format.
489 if ( isset( $table_settings['table_settings']['hyperlink_definition'] ) && 'text' === $table_settings['table_settings']['hyperlink_definition'] ) {
490 $media[$column['column_name']] = 'HyperlinkURL';
491 } else {
492 $media[$column['column_name']] = 'HyperlinkObject';
493 }
494 break;
495 default:
496 if ( false !== $media_type ) {
497 // Handle WordPress Media Library integration
498 $media[$column['column_name']] = "WP-{$media_type}";
499 }
500 }
501 $wp_media[$column['column_name']] = $media_type;
502 }
503 return [
504 'media' => $media,
505 'wp_media' => $wp_media,
506 ];
507 }
508
509 /**
510 * Write standard JSON response.
511 *
512 * @param string $message Response text message.
513 * @param mixed $data Response data.
514 * @param mixed $context Context data.
515 * @param mixed $meta Meta data.
516 * @return \WP_REST_Response
517 */
518 protected static function WPDA_Rest_Response(
519 $message = '',
520 $data = null,
521 $context = null,
522 $meta = null
523 ) {
524 // Prepare response.
525 $response = new \WP_REST_Response(array(
526 'code' => 'ok',
527 'message' => $message,
528 'data' => $data,
529 'context' => $context,
530 'meta' => $meta,
531 ), 200);
532 // Disable caching.
533 $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0' );
534 $response->header( 'Pragma', 'no-cache' );
535 $response->header( 'Expires', '0' );
536 return $response;
537 }
538
539 protected static function WPDA_Rest_Response_Info( $message = '' ) {
540 // Prepare response.
541 $response = new \WP_REST_Response(array(
542 'code' => 'info',
543 'message' => $message,
544 'data' => null,
545 'context' => null,
546 'meta' => null,
547 ), 200);
548 // Disable caching.
549 $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0' );
550 $response->header( 'Pragma', 'no-cache' );
551 $response->header( 'Expires', '0' );
552 return $response;
553 }
554
555 }
556