PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.72
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.72
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_Table.php

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

1,423 lines 56.5 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 stdClass;
6 use WPDataAccess\Connection\WPDADB;
7 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Access;
8 use WPDataAccess\Data_Dictionary\WPDA_List_Columns_Cache;
9 use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model;
10 use WPDataAccess\Utilities\WPDA_WP_Media;
11 use WPDataAccess\WPDA;
12 class WPDA_Table extends WPDA_API_Core {
13 const WPDA_SEARCH_MODES = array(
14 'contains',
15 'startsWith',
16 'endsWith',
17 'equals',
18 'notEquals',
19 'empty',
20 'notEmpty',
21 'between',
22 'betweenInclusive',
23 'greaterThan',
24 'greaterThanOrEqualTo',
25 'lessThan',
26 'lessThanOrEqualTo'
27 );
28
29 const RELATIONTABLEPREFIX = 'relationTableColumn___';
30
31 public function register_rest_routes() {
32 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'table/meta', array(
33 'methods' => array('POST'),
34 'callback' => array($this, 'table_meta'),
35 'permission_callback' => '__return_true',
36 'args' => array(
37 'dbs' => $this->get_param( 'dbs' ),
38 'tbl' => $this->get_param( 'tbl' ),
39 'waa' => array(
40 'required' => false,
41 'type' => 'boolean',
42 'description' => __( 'With admin actions (to support table exports)', 'wp-data-access' ),
43 ),
44 ),
45 ) );
46 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'table/select', array(
47 'methods' => array('GET', 'POST'),
48 'callback' => array($this, 'table_select'),
49 'permission_callback' => '__return_true',
50 'args' => array(
51 'dbs' => $this->get_param( 'dbs' ),
52 'tbl' => $this->get_param( 'tbl' ),
53 'col' => $this->get_param( 'cols' ),
54 'page_index' => $this->get_param( 'page_index' ),
55 'page_size' => $this->get_param( 'page_size' ),
56 'search' => $this->get_param( 'search' ),
57 'search_columns' => $this->get_param( 'search_columns' ),
58 'search_column_fns' => $this->get_param( 'search_column_fns' ),
59 'sorting' => $this->get_param( 'sorting' ),
60 'row_count' => $this->get_param( 'row_count' ),
61 'row_count_estimate' => $this->get_param( 'row_count_estimate' ),
62 'media' => $this->get_param( 'media' ),
63 'client_side' => $this->get_param( 'client_side' ),
64 ),
65 ) );
66 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'table/get', array(
67 'methods' => array('GET', 'POST'),
68 'callback' => array($this, 'table_get'),
69 'permission_callback' => '__return_true',
70 'args' => array(
71 'dbs' => $this->get_param( 'dbs' ),
72 'tbl' => $this->get_param( 'tbl' ),
73 'key' => $this->get_param( 'key' ),
74 'media' => $this->get_param( 'media' ),
75 ),
76 ) );
77 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'table/insert', array(
78 'methods' => array('GET', 'POST'),
79 'callback' => array($this, 'table_insert'),
80 'permission_callback' => '__return_true',
81 'args' => array(
82 'dbs' => $this->get_param( 'dbs' ),
83 'tbl' => $this->get_param( 'tbl' ),
84 'val' => $this->get_param( 'val' ),
85 ),
86 ) );
87 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'table/update', array(
88 'methods' => array('GET', 'POST'),
89 'callback' => array($this, 'table_update'),
90 'permission_callback' => '__return_true',
91 'args' => array(
92 'dbs' => $this->get_param( 'dbs' ),
93 'tbl' => $this->get_param( 'tbl' ),
94 'key' => $this->get_param( 'key' ),
95 'val' => $this->get_param( 'val' ),
96 ),
97 ) );
98 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'table/delete', array(
99 'methods' => array('GET', 'POST'),
100 'callback' => array($this, 'table_delete'),
101 'permission_callback' => '__return_true',
102 'args' => array(
103 'dbs' => $this->get_param( 'dbs' ),
104 'tbl' => $this->get_param( 'tbl' ),
105 'key' => $this->get_param( 'key' ),
106 ),
107 ) );
108 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'table/lov', array(
109 'methods' => array('GET', 'POST'),
110 'callback' => array($this, 'table_lov'),
111 'permission_callback' => '__return_true',
112 'args' => array(
113 'dbs' => $this->get_param( 'dbs' ),
114 'tbl' => $this->get_param( 'tbl' ),
115 'col' => $this->get_param( 'col' ),
116 ),
117 ) );
118 }
119
120 /**
121 * Get table meta info.
122 *
123 * @param WP_REST_Request $request Rest API request.
124 * @return \WP_Error|\WP_REST_Response
125 */
126 public function table_meta( $request ) {
127 $dbs = $request->get_param( 'dbs' );
128 $tbl = $request->get_param( 'tbl' );
129 $waa = $request->get_param( 'waa' );
130 if ( $this->check_table_access(
131 $dbs,
132 $tbl,
133 $request,
134 'select',
135 $msg
136 ) ) {
137 return $this->WPDA_Rest_Response( '', $this->get_table_meta_data( $dbs, $tbl, $waa ) );
138 } else {
139 if ( 'rest_cookie_invalid_nonce' === $msg ) {
140 return $this->invalid_nonce();
141 } else {
142 return new \WP_Error('error', $msg, array(
143 'status' => 401,
144 ));
145 }
146 }
147 }
148
149 /**
150 * Database table query using the full primary key. Must return exactly one row.
151 *
152 * @param WP_REST_Request $request Rest API request.
153 * @return \WP_Error|\WP_REST_Response
154 */
155 public function table_get( $request ) {
156 $dbs = $request->get_param( 'dbs' );
157 $tbl = $request->get_param( 'tbl' );
158 $key = $request->get_param( 'key' );
159 $media = $request->get_param( 'media' );
160 if ( $this->check_table_access(
161 $dbs,
162 $tbl,
163 $request,
164 'select',
165 $msg
166 ) ) {
167 return $this->get(
168 $dbs,
169 $tbl,
170 $key,
171 $media
172 );
173 } else {
174 if ( 'rest_cookie_invalid_nonce' === $msg ) {
175 return $this->invalid_nonce();
176 } else {
177 return new \WP_Error('error', $msg, array(
178 'status' => 401,
179 ));
180 }
181 }
182 }
183
184 /**
185 * Insert one row.
186 *
187 * @param WP_REST_Request $request Rest API request.
188 * @return \WP_Error|\WP_REST_Response
189 */
190 public function table_insert( $request ) {
191 $dbs = $request->get_param( 'dbs' );
192 $tbl = $request->get_param( 'tbl' );
193 $val = $request->get_param( 'val' );
194 if ( $this->check_table_access(
195 $dbs,
196 $tbl,
197 $request,
198 'insert',
199 $msg
200 ) ) {
201 return $this->insert( $dbs, $tbl, $val );
202 } else {
203 if ( 'rest_cookie_invalid_nonce' === $msg ) {
204 return $this->invalid_nonce();
205 } else {
206 return new \WP_Error('error', $msg, array(
207 'status' => 401,
208 ));
209 }
210 }
211 }
212
213 /**
214 * Update uses primary key. Must return exactly one row.
215 *
216 * @param WP_REST_Request $request Rest API request.
217 * @return \WP_Error|\WP_REST_Response
218 */
219 public function table_update( $request ) {
220 $dbs = $request->get_param( 'dbs' );
221 $tbl = $request->get_param( 'tbl' );
222 $key = $request->get_param( 'key' );
223 $val = $request->get_param( 'val' );
224 if ( $this->check_table_access(
225 $dbs,
226 $tbl,
227 $request,
228 'update',
229 $msg
230 ) ) {
231 return $this->update(
232 $dbs,
233 $tbl,
234 $key,
235 $val
236 );
237 } else {
238 if ( 'rest_cookie_invalid_nonce' === $msg ) {
239 return $this->invalid_nonce();
240 } else {
241 return new \WP_Error('error', $msg, array(
242 'status' => 401,
243 ));
244 }
245 }
246 }
247
248 /**
249 * Delete uses primary key. Must return exactly one row.
250 *
251 * @param WP_REST_Request $request Rest API request.
252 * @return \WP_Error|\WP_REST_Response
253 */
254 public function table_delete( $request ) {
255 $dbs = $request->get_param( 'dbs' );
256 $tbl = $request->get_param( 'tbl' );
257 $key = $request->get_param( 'key' );
258 if ( $this->check_table_access(
259 $dbs,
260 $tbl,
261 $request,
262 'delete',
263 $msg
264 ) ) {
265 return $this->delete( $dbs, $tbl, $key );
266 } else {
267 if ( 'rest_cookie_invalid_nonce' === $msg ) {
268 return $this->invalid_nonce();
269 } else {
270 return new \WP_Error('error', $msg, array(
271 'status' => 401,
272 ));
273 }
274 }
275 }
276
277 /**
278 * Database table query to populate a list of values for a specific table/column.
279 *
280 * @param WP_REST_Request $request Rest API request.
281 * @return \WP_Error|\WP_REST_Response
282 */
283 public function table_lov( $request ) {
284 }
285
286 /**
287 * Database table query.
288 *
289 * Supports: searching, ordering and pagination.
290 *
291 * @param WP_REST_Request $request Rest API request.
292 * @return \WP_Error|\WP_REST_Response
293 */
294 public function table_select( $request ) {
295 $dbs = $request->get_param( 'dbs' );
296 $tbl = $request->get_param( 'tbl' );
297 $col = $request->get_param( 'col' );
298 $page_index = $request->get_param( 'page_index' );
299 $page_size = $request->get_param( 'page_size' );
300 $search = $request->get_param( 'search' );
301 $search_columns = $request->get_param( 'search_columns' );
302 $search_column_fns = $request->get_param( 'search_column_fns' );
303 $search_data_types = $request->get_param( 'search_data_types' );
304 $sorting = $request->get_param( 'sorting' );
305 $row_count = $request->get_param( 'row_count' );
306 $row_count_estimate = $request->get_param( 'row_count_estimate' );
307 $media = $request->get_param( 'media' );
308 $client_side = '1' === $request->get_param( 'client_side' );
309 if ( $this->check_table_access(
310 $dbs,
311 $tbl,
312 $request,
313 'select',
314 $msg
315 ) ) {
316 return $this->select(
317 $dbs,
318 $tbl,
319 $col,
320 $page_index,
321 $page_size,
322 $search,
323 $search_columns,
324 $search_column_fns,
325 $sorting,
326 $row_count,
327 $row_count_estimate,
328 $media,
329 '',
330 '',
331 array(),
332 array(),
333 array(),
334 $search_data_types,
335 $client_side
336 );
337 } else {
338 if ( 'rest_cookie_invalid_nonce' === $msg ) {
339 return $this->invalid_nonce();
340 } else {
341 return new \WP_Error('error', $msg, array(
342 'status' => 401,
343 ));
344 }
345 }
346 }
347
348 /**
349 * Perform query and return result as JSON response.
350 *
351 * @param string $dbs Schema name (database).
352 * @param string $tbl Table Name.
353 * @param array $column_name Column name.
354 * @param array $search Global search.
355 * @param array $search_columns Column filters.
356 * @param array $search_column_fns Column filter fns.
357 * @return \WP_Error|\WP_REST_Response
358 */
359 public function lov(
360 $dbs,
361 $tbl,
362 $column_name,
363 $cascade = false,
364 $default_where = '',
365 $search = '',
366 $column_names = array(),
367 $search_columns = array(),
368 $search_column_fns = array(),
369 $lookups = array(),
370 $md = array(),
371 $m2m_relationship = array(),
372 $search_data_types = array()
373 ) {
374 }
375
376 public function lookup(
377 $dbs,
378 $tbl,
379 $column_key,
380 $column_value,
381 $column_dynamic_values,
382 $default_where,
383 $cascade = false,
384 $cascade_table = '',
385 $cascade_column = '',
386 $cascade_where = '',
387 $search = '',
388 $column_names = array(),
389 $search_columns = array(),
390 $search_column_fns = array(),
391 $lookups = array(),
392 $md = array(),
393 $m2m_relationship = array(),
394 $search_data_types = array()
395 ) {
396 $wpdadb = WPDADB::get_db_connection( $dbs );
397 if ( null === $wpdadb ) {
398 // Error connecting.
399 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
400 'status' => 420,
401 ));
402 } else {
403 // Connected, perform queries.
404 $suppress = $wpdadb->suppress_errors( true );
405 $subquery = '';
406 $where = '';
407 if ( '' !== trim( $default_where ) ) {
408 if ( 'where' !== strtolower( substr( trim( $default_where ), 0, 5 ) ) ) {
409 $where = "where {$default_where}";
410 } else {
411 $where = $default_where;
412 }
413 }
414 $dynamic_where = array();
415 if ( is_array( $column_dynamic_values ) && 0 < count( $column_dynamic_values ) ) {
416 foreach ( $column_dynamic_values as $key => $value ) {
417 $dynamic_where[] = $wpdadb->prepare( " `{$key}` = %s ", $value );
418 }
419 $where .= (( '' === $where ? ' where ' : ' and ' )) . ' (' . implode( ' and ', $dynamic_where ) . ') ';
420 }
421 $column_count = ( '' === $subquery ? '' : ", stats.total_rows as 'count'" );
422 if ( strpos( $column_value, ',' ) !== false ) {
423 $columns = explode( ',', $column_value );
424 $columns = array_map( function ( $column ) use($wpdadb, $tbl) {
425 return $wpdadb->prepare( "`%1s`.`%1s`", [$tbl, $column] );
426 }, $columns );
427 $sql = $wpdadb->prepare( "\n\t\t\t\t\t\t\tselect distinct `%1s`.`%1s` as 'key'\n\t\t\t\t\t\t\t, %1s\n\t\t\t\t\t\t\t{$column_count}\n\t\t\t\t\t\t\tfrom `%1s`\n\t\t\t\t\t\t", array(
428 $tbl,
429 $column_key,
430 implode( ',', $columns ),
431 $tbl
432 ) );
433 } else {
434 $sql = $wpdadb->prepare( "\n\t\t\t\t\t\t\tselect distinct `%1s`.`%1s` as 'key'\n\t\t\t\t\t\t\t, `%1s`.`%1s` as 'value'\n\t\t\t\t\t\t\t{$column_count}\n\t\t\t\t\t\t\tfrom `%1s`\n\t\t\t\t\t\t", array(
435 $tbl,
436 $column_key,
437 $tbl,
438 $column_value,
439 $tbl
440 ) );
441 }
442 $orderby = ' order by 2 ';
443 $sql .= " {$where} {$orderby} ";
444 // $where and $orderby already sanitized and prepared
445 $dataset = $wpdadb->get_results( $sql, 'OBJECT' );
446 $wpdadb->suppress_errors( $suppress );
447 // Send response.
448 if ( '' === $wpdadb->last_error ) {
449 // Prepare debug info.
450 if ( 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
451 $debug = array(
452 'debug' => array(
453 'sql' => preg_replace( "/\\s+/", " ", $sql ),
454 'where' => $where ?? '',
455 ),
456 );
457 } else {
458 $debug = null;
459 }
460 // Add context node to response.
461 $context = array();
462 if ( isset( $debug['debug'] ) && 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
463 $context['debug'] = $debug['debug'];
464 }
465 return $this->WPDA_Rest_Response( '', $dataset, $context );
466 } else {
467 return new \WP_Error('error', $wpdadb->last_error, array(
468 'status' => 420,
469 ));
470 }
471 }
472 }
473
474 /**
475 * Perform query and return result as JSON response.
476 *
477 * @param string $dbs Schema name (database).
478 * @param string $tbl Table Name.
479 * @param array $primary Primary (key|value pairs.
480 * @param array $media_columns Media columns.
481 * @param array $column_names Just a plain array containing the column names.
482 * @return \WP_Error|\WP_REST_Response
483 */
484 public function get(
485 $dbs,
486 $tbl,
487 $primary_key,
488 $media_columns = array(),
489 $column_names = array(),
490 $default_where = ''
491 ) {
492 $wpdadb = WPDADB::get_db_connection( $dbs );
493 if ( null === $wpdadb ) {
494 // Error connecting.
495 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
496 'status' => 420,
497 ));
498 } else {
499 // Connected, perform queries.
500 $suppress = $wpdadb->suppress_errors( true );
501 $where = '';
502 foreach ( $primary_key as $primary_key_column => $primary_key_value ) {
503 $where = ( '' === $where ? ' where ' : $where . ' and ' );
504 $where .= $wpdadb->prepare( " `%1s` = %s ", array($primary_key_column, $primary_key_value) );
505 }
506 if ( '' !== $default_where ) {
507 if ( '' === $where ) {
508 $where = $default_where;
509 } else {
510 $where .= " and {$default_where} ";
511 }
512 }
513 // Get table column data types
514 $column_list = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
515 $table_columns = $column_list->get_table_columns();
516 // Prepare selected column list
517 $columns_selected = array();
518 $search_data_types = array();
519 foreach ( $table_columns as $table_column ) {
520 $columns_selected[$table_column['column_name']] = true;
521 $search_data_types[$table_column['column_name']] = $table_column['data_type'];
522 }
523 $selected_columns = $this->get_selected_columns( $columns_selected, $search_data_types );
524 $sql = $wpdadb->prepare( "\n select {$selected_columns}\n from `%1s`\n {$where}\n ", array($tbl) );
525 $dataset = $wpdadb->get_results( $sql, 'ARRAY_A' );
526 // Prepare debug info.
527 if ( 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
528 $debug = array(
529 'debug' => array(
530 'sql' => $sql,
531 'where' => $where,
532 ),
533 );
534 } else {
535 $debug = null;
536 }
537 $wpdadb->suppress_errors( $suppress );
538 // Send response.
539 $media = array();
540 if ( is_array( $media_columns ) && 0 < count( $media_columns ) ) {
541 foreach ( $media_columns as $media_column_name => $media_column_type ) {
542 if ( isset( $dataset[0][$media_column_name] ) ) {
543 if ( in_array( $media_column_type, [
544 'WP-Image',
545 'WP-Attachment',
546 'WP-Audio',
547 'WP-Video'
548 ] ) ) {
549 $media[$media_column_name] = WPDA_WP_Media::get_media_url( $dataset[0][$media_column_name] );
550 }
551 }
552 }
553 }
554 $context = array();
555 $context['media'] = $media;
556 if ( isset( $debug['debug'] ) && 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
557 $context['debug'] = $debug['debug'];
558 }
559 if ( 0 === count( $dataset ) ) {
560 return $this->WPDA_Rest_Response( 'No data found', $dataset, $context );
561 } else {
562 if ( 1 === count( $dataset ) ) {
563 return $this->WPDA_Rest_Response( '', $dataset, $context );
564 } else {
565 return $this->WPDA_Rest_Response( 'Query returned more than one row', $dataset, $context );
566 }
567 }
568 }
569 }
570
571 public function insert( $dbs, $tbl, $column_values ) {
572 $wpdadb = WPDADB::get_db_connection( $dbs );
573 if ( null === $wpdadb ) {
574 // Error connecting.
575 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
576 'status' => 420,
577 ));
578 } else {
579 // Get column default values
580 $column_list = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
581 $table_columns = $column_list->get_table_columns();
582 foreach ( $table_columns as $table_column_type ) {
583 if ( isset( $column_values[$table_column_type['column_name']] ) && $column_values[$table_column_type['column_name']] === $table_column_type['column_default'] ) {
584 // Remove default values if send values equals column default to support defaults using functions
585 unset($column_values[$table_column_type['column_name']]);
586 }
587 }
588 // Sanitize column names and values.
589 $sanitized_column_values = self::sanitize_column_values( $dbs, $tbl, $column_values );
590 if ( false === $sanitized_column_values ) {
591 return new \WP_Error('error', "Invalid arguments", array(
592 'status' => 420,
593 ));
594 }
595 // Insert row.
596 $rows_inserted = $wpdadb->insert( $tbl, $sanitized_column_values );
597 // Send response.
598 if ( 1 === $rows_inserted ) {
599 return $this->WPDA_Rest_Response( __( 'Row successfully inserted', 'wp-data-access' ), null, array(
600 'insert_id' => $wpdadb->insert_id,
601 ) );
602 } else {
603 if ( '' !== $wpdadb->last_error ) {
604 return new \WP_Error('error', $wpdadb->last_error, array(
605 'status' => 420,
606 ));
607 } else {
608 return new \WP_Error('error', 'Insert failed', array(
609 'status' => 420,
610 ));
611 }
612 }
613 }
614 }
615
616 public function update(
617 $dbs,
618 $tbl,
619 $primary_key,
620 $column_values,
621 $column_names = array(),
622 $code_columns = array(),
623 $html_columns = array()
624 ) {
625 $wpdadb = WPDADB::get_db_connection( $dbs );
626 if ( null === $wpdadb ) {
627 // Error connecting.
628 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
629 'status' => 420,
630 ));
631 } else {
632 // Sanitize column names and values.
633 $sanitized_column_values = self::sanitize_column_values(
634 $dbs,
635 $tbl,
636 $column_values,
637 $code_columns,
638 $html_columns
639 );
640 if ( false === $sanitized_column_values ) {
641 return new \WP_Error('error', "Invalid arguments", array(
642 'status' => 420,
643 ));
644 }
645 // Update row.
646 $rows_inserted = $wpdadb->update( $tbl, $sanitized_column_values, $primary_key );
647 // Send response.
648 if ( 0 === $rows_inserted ) {
649 return $this->WPDA_Rest_Response_Info( 'Nothing to update' );
650 } elseif ( 1 === $rows_inserted ) {
651 $context = null;
652 if ( 0 < count( $column_names ) ) {
653 // Return updated values
654 $updated_row = $this->get(
655 $dbs,
656 $tbl,
657 $primary_key,
658 $column_names
659 );
660 if ( isset( $updated_row->data['data'][0] ) ) {
661 $updated_values = $updated_row->data['data'][0];
662 $updated_context = array();
663 foreach ( $updated_values as $key => $value ) {
664 if ( !isset( $column_values[$key] ) ) {
665 $updated_context[$key] = $value;
666 }
667 }
668 if ( 0 < count( $updated_context ) ) {
669 $context = array(
670 'updated' => $updated_context,
671 );
672 }
673 }
674 }
675 return $this->WPDA_Rest_Response( __( 'Row successfully updated', 'wp-data-access' ), null, $context );
676 } else {
677 if ( '' !== $wpdadb->last_error ) {
678 return new \WP_Error('error', $wpdadb->last_error, array(
679 'status' => 420,
680 ));
681 } else {
682 return new \WP_Error('error', 'Update failed', array(
683 'status' => 420,
684 ));
685 }
686 }
687 }
688 }
689
690 public function delete( $dbs, $tbl, $primary_key ) {
691 $wpdadb = WPDADB::get_db_connection( $dbs );
692 if ( null === $wpdadb ) {
693 // Error connecting.
694 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
695 'status' => 420,
696 ));
697 } else {
698 // Delete row.
699 $rows_deleted = $wpdadb->delete( $tbl, $primary_key );
700 // Send response.
701 if ( 0 === $rows_deleted ) {
702 return $this->WPDA_Rest_Response_Info( __( 'No data found', 'wp-data-access' ) );
703 } elseif ( 1 === $rows_deleted ) {
704 return $this->WPDA_Rest_Response( __( 'Row successfully deleted', 'wp-data-access' ) );
705 } else {
706 if ( '' !== $wpdadb->last_error ) {
707 return new \WP_Error('error', $wpdadb->last_error, array(
708 'status' => 420,
709 ));
710 } else {
711 return new \WP_Error('error', 'Delete failed', array(
712 'status' => 420,
713 ));
714 }
715 }
716 }
717 }
718
719 private function generate_lookup_condition(
720 $wpdadb,
721 $lookups,
722 $column_name,
723 $search_values,
724 $search_column_fns,
725 $filter_mode = null,
726 $filter_key = false
727 ) {
728 $lookup = $lookups[$column_name];
729 $lookup_table = $lookup['tbl'];
730 $lookup_key = $lookup['key'];
731 $lookup_columns = explode( ',', $lookup['value'] );
732 $lookup_where = array();
733 if ( $filter_key ) {
734 $filter_columns = array($lookup_key);
735 } else {
736 $filter_columns = $lookup_columns;
737 }
738 foreach ( $filter_columns as $lookup_column ) {
739 foreach ( $search_values as $search_value ) {
740 $lookup_where[] = $this->add_filter(
741 $wpdadb,
742 $lookup_column,
743 ( $filter_mode !== null ? $filter_mode : $search_column_fns[$column_name] ),
744 $search_value
745 );
746 }
747 }
748 if ( 0 < count( $lookup_where ) ) {
749 return $wpdadb->prepare( ' `%1s` in ( select `%1s` from `%1s` where (' . implode( ' or ', $lookup_where ) . ') ) ', array(
750 $column_name,
751 $lookup_key,
752 $lookup_table,
753 $lookup_columns[0],
754 "%{$search_values[0]}%"
755 ) );
756 } else {
757 return null;
758 }
759 }
760
761 public static function remove_where_from_sql( $sql ) {
762 if ( 'where' === substr( trim( $sql ), 0, 5 ) ) {
763 $pos = strpos( $sql, 'where' );
764 if ( false !== $pos ) {
765 $sql = substr_replace(
766 $sql,
767 '',
768 $pos,
769 5
770 );
771 }
772 }
773 return $sql;
774 }
775
776 private function get_md( $md, $wpdadb, $m2m_relationship ) {
777 }
778
779 private function get_global_filter(
780 $wpdadb,
781 $search,
782 $column_names,
783 $lookups,
784 $m2m_relationship
785 ) {
786 $where_global = array();
787 if ( null !== $search && "" !== $search ) {
788 foreach ( $column_names as $column_name => $queryable ) {
789 if ( $queryable ) {
790 if ( isset( $lookups[$column_name] ) ) {
791 // Perform look search.
792 $condition = $this->generate_lookup_condition(
793 $wpdadb,
794 $lookups,
795 $column_name,
796 array($search),
797 array(),
798 'contains'
799 );
800 if ( null !== $condition ) {
801 $where_global[] = $condition;
802 }
803 } else {
804 $where_global[] = $wpdadb->prepare( " `%1s` like '%s' ", array($this->convert_column_name( $m2m_relationship, $column_name ), '%' . esc_sql( $search ) . '%') );
805 }
806 }
807 }
808 }
809 return $where_global;
810 }
811
812 private function get_column_filters(
813 $wpdadb,
814 $search_columns,
815 $search_column_fns,
816 $lookups,
817 $m2m_relationship,
818 $search_data_types
819 ) {
820 }
821
822 private function get_where(
823 $wpdadb,
824 $default_where,
825 $md,
826 $m2m_relationship,
827 $search,
828 $column_names,
829 $lookups,
830 $search_columns,
831 $search_column_fns,
832 $search_data_types,
833 $geo_radius = array(),
834 $operator = 'and'
835 ) {
836 // Default where.
837 if ( '' !== trim( $default_where ) && 'where' !== strtolower( substr( trim( $default_where ), 0, 5 ) ) ) {
838 $where = "where {$default_where}";
839 } else {
840 $where = $default_where;
841 }
842 // Global filter.
843 $where_global = $this->get_global_filter(
844 $wpdadb,
845 $search,
846 $column_names,
847 $lookups,
848 $m2m_relationship
849 );
850 if ( 0 < count( $where_global ) ) {
851 $where .= (( '' === trim( $where ) ? ' where ' : ' and ' )) . $this->add_condition( $where_global, 'or' );
852 }
853 if ( is_array( $geo_radius ) && 0 < count( $geo_radius ) ) {
854 // Add geo radius to query
855 // Variable $geo_radius already sanitized in REST API
856 $unit = ( "km" == $geo_radius['unit'] ? 1000 : 1609.344 );
857 // km versus miles
858 if ( $geo_radius['col']['lat'] === $geo_radius['col']['lng'] ) {
859 // Location stored in GEOMETRY or POINT data type
860 $geocol = $geo_radius['col']['lat'];
861 $geo_where = " ( st_distance_sphere(point(st_y(`{$geocol}`), st_x(`{$geocol}`)), point({$geo_radius['loc']['lng']}, {$geo_radius['loc']['lat']})) / {$unit} ) < {$geo_radius['radius']} ";
862 } else {
863 // Latitude and longitude stored separately
864 $geo_where = " ( st_distance_sphere(point(`{$geo_radius['col']['lng']}`, `{$geo_radius['col']['lat']}`), point({$geo_radius['loc']['lng']}, {$geo_radius['loc']['lat']})) / {$unit} ) < {$geo_radius['radius']} ";
865 }
866 if ( '' === $where ) {
867 $where = " where {$geo_where} ";
868 } else {
869 $where .= " and {$geo_where} ";
870 }
871 }
872 return $where;
873 }
874
875 private function get_selected_columns( $column_names, $search_data_types ) {
876 if ( !is_array( $column_names ) ) {
877 return '*';
878 // select all columns
879 }
880 // Check for geo columns
881 $geometryColumns = array();
882 if ( is_array( $search_data_types ) ) {
883 foreach ( $search_data_types as $column_name => $search_data_type ) {
884 if ( 'geometry' === strtolower( $search_data_type ) || 'point' === strtolower( $search_data_type ) ) {
885 $geometryColumns[] = $column_name;
886 }
887 }
888 }
889 return implode( ",", array_map( function ( $column_name ) use($geometryColumns) {
890 if ( in_array( $column_name, $geometryColumns ) ) {
891 return 'ST_AsText(`' . WPDA::remove_backticks( $column_name ) . '`) ' . " as `{$column_name}` ";
892 // Convert geo data to string
893 } else {
894 return '`' . WPDA::remove_backticks( $column_name ) . '`';
895 }
896 }, array_keys( $column_names ) ) );
897 }
898
899 /**
900 * Perform query and return result as JSON response.
901 *
902 * @param string $dbs Schema name (database).
903 * @param string $tbl Table Name.
904 * @param string $column_names Column Names.
905 * @param string $page_index Page number.
906 * @param string $page_size Rows per page.
907 * @param string $search Filter.
908 * @param string $search_columns Column search filters.
909 * @param string $search_column_fns Column search filter modes.
910 * @param string $Sorting Order by.
911 * @param integer $last_row_count Row count previous request.
912 * @param string $row_count_estimate Indicates if row count estimate should be used.
913 * @param string $media_columns Media columns.
914 * @param string $default_where Defaul where clause
915 * @param string $default_orderby Defaul order by clause
916 * @return \WP_Error|\WP_REST_Response
917 */
918 public function select(
919 $dbs,
920 $tbl,
921 $column_names,
922 $page_index,
923 $page_size,
924 $search,
925 $search_columns,
926 $search_column_fns,
927 $sorting,
928 $last_row_count,
929 $row_count_estimate,
930 $media_columns = array(),
931 $default_where = '',
932 $default_orderby = '',
933 $lookups = array(),
934 $md = array(),
935 $m2m_relationship = array(),
936 $search_data_types = array(),
937 $client_side = false,
938 $geo_radius = array()
939 ) {
940 $wpdadb = WPDADB::get_db_connection( $dbs );
941 if ( null === $wpdadb ) {
942 // Error connecting.
943 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
944 'status' => 420,
945 ));
946 } else {
947 $suppress = $wpdadb->suppress_errors( true );
948 // Build where clause.
949 $where = $this->get_where(
950 $wpdadb,
951 $default_where,
952 $md,
953 $m2m_relationship,
954 $search,
955 $column_names,
956 $lookups,
957 $search_columns,
958 $search_column_fns,
959 $search_data_types,
960 $geo_radius
961 );
962 // Build order by.
963 $sqlorder = '';
964 if ( is_array( $sorting ) && 0 < count( $sorting ) ) {
965 foreach ( $sorting as $sort ) {
966 if ( '' === $sqlorder ) {
967 $sqlorder = 'order by ';
968 } else {
969 $sqlorder .= ',';
970 }
971 if ( !$client_side && isset( $lookups[$sort['id']] ) ) {
972 // Use lookup table to sort
973 $lookup = $lookups[$sort['id']];
974 $lookup_dbs = $lookup['dbs'];
975 $lookup_wpdadb = ( $dbs === $lookup_dbs ? $wpdadb : WPDADB::get_db_connection( $lookup_dbs ) );
976 if ( $lookup_wpdadb !== null ) {
977 $lookup_tbl = $lookup['tbl'];
978 $lookup_key = $lookup['key'];
979 $lookup_value = $lookup['value'];
980 $lookup_dataset = $lookup_wpdadb->get_results( $lookup_wpdadb->prepare( "select `%1s`, `%1s` from `%1s` order by 2", array($lookup_key, $lookup_value, $lookup_tbl) ), 'ARRAY_N' );
981 $lookup_orderby = 'case `' . $this->convert_column_name( $m2m_relationship, $sort['id'] ) . '` ';
982 foreach ( $lookup_dataset as $index => $value ) {
983 $lookup_orderby .= $lookup_wpdadb->prepare( 'when %s then %d ', array($value[0], $index) );
984 }
985 $lookup_orderby .= 'else `' . $this->convert_column_name( $m2m_relationship, $sort['id'] ) . '` end ' . (( $sort['desc'] ? 'desc' : 'asc' ));
986 $sqlorder .= $lookup_orderby;
987 } else {
988 $sqlorder .= '`' . $this->convert_column_name( $m2m_relationship, $sort['id'] ) . '` ' . (( $sort['desc'] ? 'desc' : 'asc' ));
989 }
990 } else {
991 // Normal sort
992 $sqlorder .= '`' . $this->convert_column_name( $m2m_relationship, $sort['id'] ) . '` ' . (( $sort['desc'] ? 'desc' : 'asc' ));
993 }
994 }
995 }
996 if ( '' === $sqlorder && '' !== trim( $default_orderby ) ) {
997 $sqlorder = $default_orderby;
998 }
999 // Add pagination.
1000 if ( !is_numeric( $page_size ) ) {
1001 $page_size = 10;
1002 }
1003 $offset = $page_index * $page_size;
1004 // Calculate offset.
1005 if ( !is_numeric( $offset ) ) {
1006 $offset = 0;
1007 }
1008 // Prepare query.
1009 $sql = "\n\t\t\t\t\tselect " . $this->get_selected_columns( $column_names, $search_data_types ) . "\n\t\t\t\t\tfrom `%1s`\n\t\t\t\t\t{$where}\n\t\t\t\t\t{$sqlorder}\n\t\t\t\t";
1010 $sql_tables = array($tbl);
1011 // Perpare query.
1012 $sql = $wpdadb->prepare( ( true === $client_side ? $sql : $sql . (( 0 < $page_size ? " limit {$page_size} offset {$offset} " : '' )) ), $sql_tables );
1013 // Prepare debug info.
1014 if ( 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
1015 $debug = array(
1016 'sql' => preg_replace( "/\\s+/", " ", $sql ),
1017 'where' => $where,
1018 'order by' => $sqlorder,
1019 );
1020 } else {
1021 $debug = null;
1022 }
1023 // Perform query.
1024 $dataset = $wpdadb->get_results( $sql, 'ARRAY_A' );
1025 if ( $wpdadb->last_error ) {
1026 // Handle SQL errors.
1027 return new \WP_Error('error', $wpdadb->last_error, array(
1028 'status' => 420,
1029 'debug' => $debug,
1030 ));
1031 }
1032 if ( is_numeric( $last_row_count ) and 0 <= $last_row_count ) {
1033 // Prevents additional unnecessary queries.
1034 $rowcount = $last_row_count;
1035 } else {
1036 if ( true === $client_side ) {
1037 $rowcount = 0;
1038 } else {
1039 $estimate = false;
1040 if ( '1' === $row_count_estimate && '' === $where ) {
1041 // Perform row count estimate
1042 $countrows = $wpdadb->get_results( $wpdadb->prepare( "\n\t\t\t\t\t\t\t\t\tselect table_rows as rowcount\n\t\t\t\t\t\t\t\t\t from information_schema.tables\n\t\t\t\t\t\t\t\t\twhere table_schema = %s\n\t\t\t\t\t\t\t\t\t and table_name = %s\n\t\t\t\t\t\t\t\t", [$wpdadb->dbname, $tbl] ), 'ARRAY_A' );
1043 if ( isset( $countrows[0]['rowcount'] ) && 0 != $countrows[0]['rowcount'] ) {
1044 $estimate = true;
1045 }
1046 }
1047 if ( !$estimate ) {
1048 if ( !$estimate ) {
1049 // (Re)Count rows.
1050 $countrows = $wpdadb->get_results( $wpdadb->prepare( "\n\t\t\t\t\t\t\t\t\t\tselect count(1) as rowcount\n\t\t\t\t\t\t\t\t\t\tfrom `%1s`\n\t\t\t\t\t\t\t\t\t\t{$where}\n\t\t\t\t\t\t\t\t\t", array($tbl) ), 'ARRAY_A' );
1051 }
1052 }
1053 if ( $wpdadb->last_error ) {
1054 // Handle SQL errors.
1055 return new \WP_Error('error', $wpdadb->last_error, array(
1056 'status' => 420,
1057 ));
1058 }
1059 if ( isset( $countrows[0]['rowcount'] ) ) {
1060 $rowcount = $countrows[0]['rowcount'];
1061 } else {
1062 $rowcount = 0;
1063 }
1064 }
1065 }
1066 // Add context node to response
1067 $context = array();
1068 if ( 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
1069 $context['debug'] = $debug;
1070 }
1071 if ( is_array( $media_columns ) && 0 < count( $media_columns ) ) {
1072 // Handle WP media library
1073 $media = array();
1074 for ($i = 0; $i < count( $dataset ); $i++) {
1075 $media_row = array();
1076 foreach ( $media_columns as $media_column_name => $media_column_type ) {
1077 if ( isset( $dataset[$i][$media_column_name] ) ) {
1078 $media_row[$media_column_name] = WPDA_WP_Media::get_media_url( $dataset[$i][$media_column_name] );
1079 }
1080 }
1081 $media[] = $media_row;
1082 }
1083 // Add media to context node
1084 $context['media'] = $media;
1085 }
1086 $wpdadb->suppress_errors( $suppress );
1087 // Send response.
1088 $response = $this->WPDA_Rest_Response(
1089 '',
1090 $dataset,
1091 $context,
1092 array(
1093 'rowCount' => $rowcount,
1094 )
1095 );
1096 $response->header( 'X-WP-Total', $rowcount );
1097 // Total rows for this query.
1098 if ( 0 < $page_size ) {
1099 $pagecount = floor( $rowcount / $page_size );
1100 if ( $pagecount != $rowcount / $page_size ) {
1101 // phpcs:ignore WordPress.PHP.StrictComparisons
1102 $pagecount++;
1103 }
1104 } else {
1105 // Prevent division by zero
1106 $pagecount = 0;
1107 }
1108 $response->header( 'X-WP-TotalPages', $pagecount );
1109 // Total pages for this query.
1110 return $response;
1111 }
1112 }
1113
1114 private function convert_column_name( $m2m_relationship, $column_name ) {
1115 // Return plain column name.
1116 return $this->sanitize_db_identifier( $column_name );
1117 }
1118
1119 private function map_columns( $prefix, $column_names ) {
1120 return implode( ",", array_map( function ( $v ) use($prefix) {
1121 $c = $this->sanitize_db_identifier( $v );
1122 $r = ( 'd' === $prefix ? static::RELATIONTABLEPREFIX . $c : $c );
1123 return "`{$prefix}`.`{$c}` as \"{$r}\"";
1124 }, array_keys( $column_names ) ) );
1125 }
1126
1127 public function add_filter(
1128 $wpdadb,
1129 $search_column,
1130 $search_column_fns,
1131 $search_value,
1132 $m2m_relationship = array(),
1133 $search_data_types = array()
1134 ) {
1135 }
1136
1137 public static function add_condition( $where_lines, $operand = 'and' ) {
1138 if ( 0 < count( array_filter( $where_lines ) ) ) {
1139 // Apply all searches.
1140 return ' ( (' . implode( ") {$operand} (", array_filter( $where_lines ) ) . ') ) ';
1141 } else {
1142 return "";
1143 }
1144 }
1145
1146 /**
1147 * Get table meta data.
1148 *
1149 * @param string $dbs Database schema name.
1150 * @param string $tbl Database table name.
1151 * @param string $waa With admin actions.
1152 * @return array\object
1153 */
1154 public function get_table_meta_data( $dbs, $tbl, $waa ) {
1155 $sql_create_table = '';
1156 if ( WPDA::current_user_is_admin() ) {
1157 // Admin user has access to all resources
1158 $access = array(
1159 'select' => array('POST'),
1160 'insert' => array('POST'),
1161 'update' => array('POST'),
1162 'delete' => array('POST'),
1163 );
1164 // Get create table script
1165 $wpdadb = WPDADB::get_db_connection( $dbs );
1166 if ( null !== $wpdadb ) {
1167 $suppress_errors = $wpdadb->suppress_errors;
1168 $wpdadb->suppress_errors = true;
1169 // NO_TABLE_OPTIONS is deprecated in V8
1170 // $wpdadb->query( "SET sql_mode = 'NO_TABLE_OPTIONS'" );
1171 $sql = $wpdadb->get_results( $wpdadb->prepare( 'show create table `%1s`', array($tbl) ), 'ARRAY_N' );
1172 if ( isset( $sql[0][1] ) ) {
1173 $sql_create_table = $sql[0][1];
1174 }
1175 $wpdadb->suppress_errors = $suppress_errors;
1176 }
1177 } else {
1178 $access = $this->get_table_access( $dbs, $tbl );
1179 }
1180 $settings = new stdClass();
1181 if ( null !== $access ) {
1182 $columns = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
1183 $settings_db = WPDA_Table_Settings_Model::query( $tbl, $dbs );
1184 if ( isset( $settings_db[0]['wpda_table_settings'] ) ) {
1185 $settings = json_decode( $settings_db[0]['wpda_table_settings'] );
1186 // Remove old settings from response.
1187 unset($settings->form_labels);
1188 unset($settings->list_labels);
1189 unset($settings->custom_settings);
1190 unset($settings->search_settings);
1191 }
1192 $settings->ui = WPDA_Settings::get_admin_settings( $dbs, $tbl );
1193 $rest_api = get_option( WPDA_API::WPDA_REST_API_TABLE_ACCESS );
1194 if ( isset( $rest_api[$dbs][$tbl] ) ) {
1195 $settings->rest_api = $rest_api[$dbs][$tbl];
1196 }
1197 $settings->env = $this->get_env();
1198 $wp_nonce_action_alter = "wpda-alter-{$tbl}";
1199 $wp_nonce_alter = wp_create_nonce( $wp_nonce_action_alter );
1200 $wp_nonce_refresh = null;
1201 $connect = null;
1202 global $wpdb;
1203 $settings->wp = [
1204 'roles' => $this->get_wp_roles(),
1205 'users' => $this->get_wp_users(),
1206 'home' => admin_url( 'admin.php' ),
1207 'homea' => admin_url( 'admin-ajax.php' ),
1208 'tables' => array_values( $wpdb->tables() ),
1209 'date_format' => get_option( 'date_format' ),
1210 'time_format' => get_option( 'time_format' ),
1211 'alter' => $wp_nonce_alter,
1212 'refresh' => $wp_nonce_refresh,
1213 'connect' => $connect,
1214 'copyinprogress' => WPDA_Actions::copy_in_progress(),
1215 'scroll_offset' => WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET ),
1216 ];
1217 if ( true === $waa ) {
1218 $settings->wp['aonce'] = implode( '-', array(
1219 wp_create_nonce( 'wpda-export-' . json_encode( $tbl ) ),
1220 // Table export
1221 wp_create_nonce( 'wpda-rename-' . $tbl ),
1222 ) );
1223 }
1224 $table_columns = $columns->get_table_columns();
1225 $media = $this->get_media( $dbs, $tbl, $table_columns );
1226 $columns_sorted = array();
1227 foreach ( $table_columns as $column ) {
1228 if ( isset( $column['column_name'] ) ) {
1229 $columns_sorted[$column['column_name']] = $column;
1230 }
1231 }
1232 }
1233 return array(
1234 'columns' => $table_columns,
1235 'columns_sorted' => $columns_sorted,
1236 'table_labels' => $columns->get_table_header_labels(),
1237 'form_labels' => $columns->get_table_column_headers(),
1238 'primary_key' => $columns->get_table_primary_key(),
1239 'access' => $access,
1240 'settings' => $settings,
1241 'media' => $media['media'],
1242 'wp_media' => $media['wp_media'],
1243 'table_info' => $this->get_table_info( $dbs, $tbl ),
1244 'create' => $sql_create_table,
1245 );
1246 }
1247
1248 private function get_table_access( $dbs, $tbl ) {
1249 if ( WPDA::current_user_is_admin() ) {
1250 // Check administrator rights
1251 if ( is_admin() ) {
1252 $access = WPDA_Dictionary_Access::check_table_access_backend( $dbs, $tbl, $done );
1253 } else {
1254 $access = WPDA_Dictionary_Access::check_table_access_frontend( $dbs, $tbl, $done );
1255 }
1256 if ( $access ) {
1257 // Administrator access granted
1258 return array(
1259 'select' => array('POST'),
1260 'insert' => array('POST'),
1261 'update' => array('POST'),
1262 'delete' => array('POST'),
1263 );
1264 }
1265 }
1266 $tables = get_option( WPDA_API::WPDA_REST_API_TABLE_ACCESS );
1267 if ( false !== $tables && isset( $tables[$dbs][$tbl] ) && is_array( $tables[$dbs][$tbl] ) ) {
1268 $table = $tables[$dbs][$tbl];
1269 $table_access = new \stdClass();
1270 $table_access->select = $this->get_table_access_action( $table, 'select' );
1271 $table_access->insert = $this->get_table_access_action( $table, 'insert' );
1272 $table_access->update = $this->get_table_access_action( $table, 'update' );
1273 $table_access->delete = $this->get_table_access_action( $table, 'delete' );
1274 return $table_access;
1275 }
1276 return false;
1277 }
1278
1279 private function get_table_access_action( $table, $action ) {
1280 if ( isset( $table[$action]['authorization'], $table[$action]['methods'] ) && is_array( $table[$action]['methods'] ) && 0 < count( $table[$action]['methods'] ) ) {
1281 if ( 'anonymous' === $table[$action]['authorization'] ) {
1282 return $table[$action]['methods'];
1283 } else {
1284 // Check authorized users
1285 if ( isset( $table[$action]['authorized_users'] ) && is_array( $table[$action]['authorized_users'] ) && 0 < count( $table[$action]['authorized_users'] ) && in_array( (string) $this->get_user_login(), $table[$action]['authorized_users'] ) ) {
1286 return $table[$action]['methods'];
1287 }
1288 // Check authorized roles
1289 if ( isset( $table[$action]['authorized_roles'] ) && is_array( $table[$action]['authorized_roles'] ) && 0 < count( $table[$action]['authorized_roles'] ) && 0 < count( array_intersect( $this->get_user_roles(), $table[$action]['authorized_roles'] ) ) ) {
1290 return $table[$action]['methods'];
1291 }
1292 }
1293 }
1294 return array();
1295 }
1296
1297 /**
1298 * Check if access is grant for requested database/table.
1299 *
1300 * @param string $dbs Remote or local database connection string.
1301 * @param string $tbl Database table name.
1302 * @param onject $request Request object.
1303 * @param string $action Possible values: select, insert, update, delete.
1304 * @return bool
1305 */
1306 private function check_table_access(
1307 $dbs,
1308 $tbl,
1309 $request,
1310 $action,
1311 &$msg = ''
1312 ) {
1313 if ( WPDA::current_user_is_admin() ) {
1314 // Grant access to administrators always.
1315 return true;
1316 }
1317 $tables = get_option( WPDA_API::WPDA_REST_API_TABLE_ACCESS );
1318 if ( false === $tables ) {
1319 // No tables.
1320 $msg = __( 'Unauthorized', 'wp-data-access' );
1321 return false;
1322 }
1323 if ( !(isset( $tables[$dbs][$tbl][$action]['methods'] ) && is_array( $tables[$dbs][$tbl][$action]['methods'] )) ) {
1324 // No methods.
1325 $msg = __( 'Unauthorized', 'wp-data-access' );
1326 return false;
1327 } else {
1328 if ( !in_array( $request->get_method(), $tables[$dbs][$tbl][$action]['methods'] ) ) {
1329 //phpcs:ignore - 8.1 proof
1330 $msg = __( 'Unauthorized', 'wp-data-access' );
1331 return false;
1332 }
1333 }
1334 if ( !isset( $tables[$dbs][$tbl][$action]['authorization'] ) ) {
1335 // No authorization.
1336 $msg = __( 'Unauthorized', 'wp-data-access' );
1337 return false;
1338 } else {
1339 if ( 'anonymous' === $tables[$dbs][$tbl][$action]['authorization'] ) {
1340 // Access granted to all users.
1341 return true;
1342 }
1343 }
1344 global $wp_rest_auth_cookie;
1345 if ( true !== $wp_rest_auth_cookie ) {
1346 // No anonymous access.
1347 $msg = __( 'Unauthorized', 'wp-data-access' );
1348 return false;
1349 } else {
1350 if ( 'authorized' !== $tables[$dbs][$tbl][$action]['authorization'] ) {
1351 // Authorization check.
1352 $msg = __( 'Unauthorized', 'wp-data-access' );
1353 return false;
1354 }
1355 // Authorized access requires a valid nonce.
1356 if ( !wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' ) ) {
1357 $msg = 'rest_cookie_invalid_nonce';
1358 return false;
1359 }
1360 if ( !(isset( $tables[$dbs][$tbl][$action]['authorized_users'] ) && is_array( $tables[$dbs][$tbl][$action]['authorized_users'] )) ) {
1361 // No users.
1362 $msg = __( 'Unauthorized', 'wp-data-access' );
1363 return false;
1364 } else {
1365 $requesting_user_login = $this->get_user_login();
1366 if ( 0 < count( $tables[$dbs][$tbl][$action]['authorized_users'] ) && in_array( $requesting_user_login, $tables[$dbs][$tbl][$action]['authorized_users'] ) ) {
1367 return true;
1368 }
1369 }
1370 if ( !(isset( $tables[$dbs][$tbl][$action]['authorized_roles'] ) && is_array( $tables[$dbs][$tbl][$action]['authorized_roles'] )) ) {
1371 // No roles.
1372 $msg = __( 'Unauthorized', 'wp-data-access' );
1373 return false;
1374 } else {
1375 $requesting_user_roles = $this->get_user_roles();
1376 if ( false === $requesting_user_roles ) {
1377 $requesting_user_roles = array();
1378 }
1379 if ( 0 < count( $tables[$dbs][$tbl][$action]['authorized_roles'] ) && 0 < count( array_intersect( $requesting_user_roles, $tables[$dbs][$tbl][$action]['authorized_roles'] ) ) ) {
1380 return true;
1381 }
1382 }
1383 $msg = __( 'Unauthorized', 'wp-data-access' );
1384 return false;
1385 }
1386 }
1387
1388 private function sanitize_column_values(
1389 $dbs,
1390 $tbl,
1391 $column_values,
1392 $code_columns = array(),
1393 $html_columns = array()
1394 ) {
1395 $wpda_list_columns = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
1396 $sanitized_column_values = [];
1397 foreach ( $column_values as $column_name => $column_value ) {
1398 $column_value = $column_values[$column_name];
1399 switch ( $wpda_list_columns->get_column_data_type( $column_name ) ) {
1400 case 'tinytext':
1401 case 'text':
1402 case 'mediumtext':
1403 case 'longtext':
1404 if ( null !== $column_value ) {
1405 if ( in_array( $column_name, $html_columns ) ) {
1406 $column_value = sanitize_textarea_field( $column_value );
1407 } else {
1408 $column_value = wp_kses_post( $column_value );
1409 }
1410 }
1411 break;
1412 default:
1413 if ( null !== $column_value ) {
1414 $column_value = sanitize_text_field( $column_value );
1415 }
1416 }
1417 $sanitized_column_values[$this->sanitize_db_identifier( $column_name )] = $column_value;
1418 }
1419 return $sanitized_column_values;
1420 }
1421
1422 }
1423