PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.80
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.80
5.5.84 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 All 160 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.80, at WPDataAccess/API/WPDA_Table.php

1,427 lines 56.7 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 $docs = array()
492 ) {
493 $wpdadb = WPDADB::get_db_connection( $dbs );
494 if ( null === $wpdadb ) {
495 // Error connecting.
496 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
497 'status' => 420,
498 ));
499 } else {
500 // Connected, perform queries.
501 $suppress = $wpdadb->suppress_errors( true );
502 $where = '';
503 foreach ( $primary_key as $primary_key_column => $primary_key_value ) {
504 $where = ( '' === $where ? ' where ' : $where . ' and ' );
505 $where .= $wpdadb->prepare( " `%1s` = %s ", array($primary_key_column, $primary_key_value) );
506 }
507 if ( '' !== $default_where ) {
508 if ( '' === $where ) {
509 $where = $default_where;
510 } else {
511 $where .= " and {$default_where} ";
512 }
513 }
514 // Get table column data types
515 $column_list = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
516 $table_columns = $column_list->get_table_columns();
517 // Prepare selected column list
518 $columns_selected = array();
519 $search_data_types = array();
520 foreach ( $table_columns as $table_column ) {
521 $columns_selected[$table_column['column_name']] = true;
522 $search_data_types[$table_column['column_name']] = $table_column['data_type'];
523 }
524 $selected_columns = $this->get_selected_columns( $columns_selected, $search_data_types );
525 $sql = $wpdadb->prepare( "\n select {$selected_columns}\n from `%1s`\n {$where}\n ", array($tbl) );
526 $dataset = $wpdadb->get_results( $sql, 'ARRAY_A' );
527 // Prepare debug info.
528 if ( 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
529 $debug = array(
530 'debug' => array(
531 'sql' => $sql,
532 'where' => $where,
533 ),
534 );
535 } else {
536 $debug = null;
537 }
538 $wpdadb->suppress_errors( $suppress );
539 // Send response.
540 $media = array();
541 if ( is_array( $media_columns ) && 0 < count( $media_columns ) ) {
542 foreach ( $media_columns as $media_column_name => $media_column_type ) {
543 if ( isset( $dataset[0][$media_column_name] ) ) {
544 if ( in_array( $media_column_type, [
545 'WP-Image',
546 'WP-Attachment',
547 'WP-Audio',
548 'WP-Video'
549 ] ) ) {
550 $media[$media_column_name] = WPDA_WP_Media::get_media_url( $dataset[0][$media_column_name] );
551 }
552 }
553 }
554 }
555 $context = array();
556 // Add media
557 $context['media'] = $media;
558 if ( isset( $debug['debug'] ) && 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
559 $context['debug'] = $debug['debug'];
560 }
561 if ( 0 === count( $dataset ) ) {
562 return $this->WPDA_Rest_Response( 'No data found', $dataset, $context );
563 } else {
564 if ( 1 === count( $dataset ) ) {
565 return $this->WPDA_Rest_Response( '', $dataset, $context );
566 } else {
567 return $this->WPDA_Rest_Response( 'Query returned more than one row', $dataset, $context );
568 }
569 }
570 }
571 }
572
573 public function insert( $dbs, $tbl, $column_values ) {
574 $wpdadb = WPDADB::get_db_connection( $dbs );
575 if ( null === $wpdadb ) {
576 // Error connecting.
577 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
578 'status' => 420,
579 ));
580 } else {
581 // Get column default values
582 $column_list = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
583 $table_columns = $column_list->get_table_columns();
584 foreach ( $table_columns as $table_column_type ) {
585 if ( isset( $column_values[$table_column_type['column_name']] ) && $column_values[$table_column_type['column_name']] === $table_column_type['column_default'] ) {
586 // Remove default values if send values equals column default to support defaults using functions
587 unset($column_values[$table_column_type['column_name']]);
588 }
589 }
590 // Sanitize column names and values.
591 $sanitized_column_values = self::sanitize_column_values( $dbs, $tbl, $column_values );
592 if ( false === $sanitized_column_values ) {
593 return new \WP_Error('error', "Invalid arguments", array(
594 'status' => 420,
595 ));
596 }
597 // Insert row.
598 $rows_inserted = $wpdadb->insert( $tbl, $sanitized_column_values );
599 // Send response.
600 if ( 1 === $rows_inserted ) {
601 return $this->WPDA_Rest_Response( __( 'Row successfully inserted', 'wp-data-access' ), null, array(
602 'insert_id' => $wpdadb->insert_id,
603 ) );
604 } else {
605 if ( '' !== $wpdadb->last_error ) {
606 return new \WP_Error('error', $wpdadb->last_error, array(
607 'status' => 420,
608 ));
609 } else {
610 return new \WP_Error('error', 'Insert failed', array(
611 'status' => 420,
612 ));
613 }
614 }
615 }
616 }
617
618 public function update(
619 $dbs,
620 $tbl,
621 $primary_key,
622 $column_values,
623 $column_names = array(),
624 $code_columns = array(),
625 $html_columns = array()
626 ) {
627 $wpdadb = WPDADB::get_db_connection( $dbs );
628 if ( null === $wpdadb ) {
629 // Error connecting.
630 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
631 'status' => 420,
632 ));
633 } else {
634 // Sanitize column names and values.
635 $sanitized_column_values = self::sanitize_column_values(
636 $dbs,
637 $tbl,
638 $column_values,
639 $code_columns,
640 $html_columns
641 );
642 if ( false === $sanitized_column_values ) {
643 return new \WP_Error('error', "Invalid arguments", array(
644 'status' => 420,
645 ));
646 }
647 // Update row.
648 $rows_inserted = $wpdadb->update( $tbl, $sanitized_column_values, $primary_key );
649 // Send response.
650 if ( 0 === $rows_inserted ) {
651 return $this->WPDA_Rest_Response_Info( 'Nothing to update' );
652 } elseif ( 1 === $rows_inserted ) {
653 $context = null;
654 if ( 0 < count( $column_names ) ) {
655 // Return updated values
656 $updated_row = $this->get(
657 $dbs,
658 $tbl,
659 $primary_key,
660 $column_names
661 );
662 if ( isset( $updated_row->data['data'][0] ) ) {
663 $updated_values = $updated_row->data['data'][0];
664 $updated_context = array();
665 foreach ( $updated_values as $key => $value ) {
666 if ( !isset( $column_values[$key] ) ) {
667 $updated_context[$key] = $value;
668 }
669 }
670 if ( 0 < count( $updated_context ) ) {
671 $context = array(
672 'updated' => $updated_context,
673 );
674 }
675 }
676 }
677 return $this->WPDA_Rest_Response( __( 'Row successfully updated', 'wp-data-access' ), null, $context );
678 } else {
679 if ( '' !== $wpdadb->last_error ) {
680 return new \WP_Error('error', $wpdadb->last_error, array(
681 'status' => 420,
682 ));
683 } else {
684 return new \WP_Error('error', 'Update failed', array(
685 'status' => 420,
686 ));
687 }
688 }
689 }
690 }
691
692 public function delete( $dbs, $tbl, $primary_key ) {
693 $wpdadb = WPDADB::get_db_connection( $dbs );
694 if ( null === $wpdadb ) {
695 // Error connecting.
696 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
697 'status' => 420,
698 ));
699 } else {
700 // Delete row.
701 $rows_deleted = $wpdadb->delete( $tbl, $primary_key );
702 // Send response.
703 if ( 0 === $rows_deleted ) {
704 return $this->WPDA_Rest_Response_Info( __( 'No data found', 'wp-data-access' ) );
705 } elseif ( 1 === $rows_deleted ) {
706 return $this->WPDA_Rest_Response( __( 'Row successfully deleted', 'wp-data-access' ) );
707 } else {
708 if ( '' !== $wpdadb->last_error ) {
709 return new \WP_Error('error', $wpdadb->last_error, array(
710 'status' => 420,
711 ));
712 } else {
713 return new \WP_Error('error', 'Delete failed', array(
714 'status' => 420,
715 ));
716 }
717 }
718 }
719 }
720
721 private function generate_lookup_condition(
722 $wpdadb,
723 $lookups,
724 $column_name,
725 $search_values,
726 $search_column_fns,
727 $filter_mode = null,
728 $filter_key = false
729 ) {
730 $lookup = $lookups[$column_name];
731 $lookup_table = $lookup['tbl'];
732 $lookup_key = $lookup['key'];
733 $lookup_columns = explode( ',', $lookup['value'] );
734 $lookup_where = array();
735 if ( $filter_key ) {
736 $filter_columns = array($lookup_key);
737 } else {
738 $filter_columns = $lookup_columns;
739 }
740 foreach ( $filter_columns as $lookup_column ) {
741 foreach ( $search_values as $search_value ) {
742 $lookup_where[] = $this->add_filter(
743 $wpdadb,
744 $lookup_column,
745 ( $filter_mode !== null ? $filter_mode : $search_column_fns[$column_name] ),
746 $search_value
747 );
748 }
749 }
750 if ( 0 < count( $lookup_where ) ) {
751 return $wpdadb->prepare( ' `%1s` in ( select `%1s` from `%1s` where (' . implode( ' or ', $lookup_where ) . ') ) ', array(
752 $column_name,
753 $lookup_key,
754 $lookup_table,
755 $lookup_columns[0],
756 "%{$search_values[0]}%"
757 ) );
758 } else {
759 return null;
760 }
761 }
762
763 public static function remove_where_from_sql( $sql ) {
764 if ( 'where' === substr( trim( $sql ), 0, 5 ) ) {
765 $pos = strpos( $sql, 'where' );
766 if ( false !== $pos ) {
767 $sql = substr_replace(
768 $sql,
769 '',
770 $pos,
771 5
772 );
773 }
774 }
775 return $sql;
776 }
777
778 private function get_md( $md, $wpdadb, $m2m_relationship ) {
779 }
780
781 private function get_global_filter(
782 $wpdadb,
783 $search,
784 $column_names,
785 $lookups,
786 $m2m_relationship
787 ) {
788 $where_global = array();
789 if ( null !== $search && "" !== $search ) {
790 foreach ( $column_names as $column_name => $queryable ) {
791 if ( $queryable ) {
792 if ( isset( $lookups[$column_name] ) ) {
793 // Perform look search.
794 $condition = $this->generate_lookup_condition(
795 $wpdadb,
796 $lookups,
797 $column_name,
798 array($search),
799 array(),
800 'contains'
801 );
802 if ( null !== $condition ) {
803 $where_global[] = $condition;
804 }
805 } else {
806 $where_global[] = $wpdadb->prepare( " `%1s` like '%s' ", array($this->convert_column_name( $m2m_relationship, $column_name ), '%' . esc_sql( $search ) . '%') );
807 }
808 }
809 }
810 }
811 return $where_global;
812 }
813
814 private function get_column_filters(
815 $wpdadb,
816 $search_columns,
817 $search_column_fns,
818 $lookups,
819 $m2m_relationship,
820 $search_data_types
821 ) {
822 }
823
824 private function get_where(
825 $wpdadb,
826 $default_where,
827 $md,
828 $m2m_relationship,
829 $search,
830 $column_names,
831 $lookups,
832 $search_columns,
833 $search_column_fns,
834 $search_data_types,
835 $geo_radius = array(),
836 $operator = 'and'
837 ) {
838 // Default where.
839 if ( '' !== trim( $default_where ) && 'where' !== strtolower( substr( trim( $default_where ), 0, 5 ) ) ) {
840 $where = "where {$default_where}";
841 } else {
842 $where = $default_where;
843 }
844 // Global filter.
845 $where_global = $this->get_global_filter(
846 $wpdadb,
847 $search,
848 $column_names,
849 $lookups,
850 $m2m_relationship
851 );
852 if ( 0 < count( $where_global ) ) {
853 $where .= (( '' === trim( $where ) ? ' where ' : ' and ' )) . $this->add_condition( $where_global, 'or' );
854 }
855 if ( is_array( $geo_radius ) && 0 < count( $geo_radius ) ) {
856 // Add geo radius to query
857 // Variable $geo_radius already sanitized in REST API
858 $unit = ( "km" == $geo_radius['unit'] ? 1000 : 1609.344 );
859 // km versus miles
860 if ( $geo_radius['col']['lat'] === $geo_radius['col']['lng'] ) {
861 // Location stored in GEOMETRY or POINT data type
862 $geocol = $geo_radius['col']['lat'];
863 $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']} ";
864 } else {
865 // Latitude and longitude stored separately
866 $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']} ";
867 }
868 if ( '' === $where ) {
869 $where = " where {$geo_where} ";
870 } else {
871 $where .= " and {$geo_where} ";
872 }
873 }
874 return $where;
875 }
876
877 private function get_selected_columns( $column_names, $search_data_types ) {
878 if ( !is_array( $column_names ) ) {
879 return '*';
880 // select all columns
881 }
882 // Check for geo columns
883 $geometryColumns = array();
884 if ( is_array( $search_data_types ) ) {
885 foreach ( $search_data_types as $column_name => $search_data_type ) {
886 if ( 'geometry' === strtolower( $search_data_type ) || 'point' === strtolower( $search_data_type ) ) {
887 $geometryColumns[] = $column_name;
888 }
889 }
890 }
891 return implode( ",", array_map( function ( $column_name ) use($geometryColumns) {
892 if ( in_array( $column_name, $geometryColumns ) ) {
893 return 'ST_AsText(`' . WPDA::remove_backticks( $column_name ) . '`) ' . " as `{$column_name}` ";
894 // Convert geo data to string
895 } else {
896 return '`' . WPDA::remove_backticks( $column_name ) . '`';
897 }
898 }, array_keys( $column_names ) ) );
899 }
900
901 /**
902 * Perform query and return result as JSON response.
903 *
904 * @param string $dbs Schema name (database).
905 * @param string $tbl Table Name.
906 * @param string $column_names Column Names.
907 * @param string $page_index Page number.
908 * @param string $page_size Rows per page.
909 * @param string $search Filter.
910 * @param string $search_columns Column search filters.
911 * @param string $search_column_fns Column search filter modes.
912 * @param string $Sorting Order by.
913 * @param integer $last_row_count Row count previous request.
914 * @param string $row_count_estimate Indicates if row count estimate should be used.
915 * @param string $media_columns Media columns.
916 * @param string $default_where Defaul where clause
917 * @param string $default_orderby Defaul order by clause
918 * @return \WP_Error|\WP_REST_Response
919 */
920 public function select(
921 $dbs,
922 $tbl,
923 $column_names,
924 $page_index,
925 $page_size,
926 $search,
927 $search_columns,
928 $search_column_fns,
929 $sorting,
930 $last_row_count,
931 $row_count_estimate,
932 $media_columns = array(),
933 $default_where = '',
934 $default_orderby = '',
935 $lookups = array(),
936 $md = array(),
937 $m2m_relationship = array(),
938 $search_data_types = array(),
939 $client_side = false,
940 $geo_radius = array(),
941 $docs = array()
942 ) {
943 $wpdadb = WPDADB::get_db_connection( $dbs );
944 if ( null === $wpdadb ) {
945 // Error connecting.
946 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
947 'status' => 420,
948 ));
949 } else {
950 $suppress = $wpdadb->suppress_errors( true );
951 // Build where clause.
952 $where = $this->get_where(
953 $wpdadb,
954 $default_where,
955 $md,
956 $m2m_relationship,
957 $search,
958 $column_names,
959 $lookups,
960 $search_columns,
961 $search_column_fns,
962 $search_data_types,
963 $geo_radius
964 );
965 // Build order by.
966 $sqlorder = '';
967 if ( is_array( $sorting ) && 0 < count( $sorting ) ) {
968 foreach ( $sorting as $sort ) {
969 if ( '' === $sqlorder ) {
970 $sqlorder = 'order by ';
971 } else {
972 $sqlorder .= ',';
973 }
974 if ( !$client_side && isset( $lookups[$sort['id']] ) ) {
975 // Use lookup table to sort
976 $lookup = $lookups[$sort['id']];
977 $lookup_dbs = $lookup['dbs'];
978 $lookup_wpdadb = ( $dbs === $lookup_dbs ? $wpdadb : WPDADB::get_db_connection( $lookup_dbs ) );
979 if ( $lookup_wpdadb !== null ) {
980 $lookup_tbl = $lookup['tbl'];
981 $lookup_key = $lookup['key'];
982 $lookup_value = $lookup['value'];
983 $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' );
984 $lookup_orderby = 'case `' . $this->convert_column_name( $m2m_relationship, $sort['id'] ) . '` ';
985 foreach ( $lookup_dataset as $index => $value ) {
986 $lookup_orderby .= $lookup_wpdadb->prepare( 'when %s then %d ', array($value[0], $index) );
987 }
988 $lookup_orderby .= 'else `' . $this->convert_column_name( $m2m_relationship, $sort['id'] ) . '` end ' . (( $sort['desc'] ? 'desc' : 'asc' ));
989 $sqlorder .= $lookup_orderby;
990 } else {
991 $sqlorder .= '`' . $this->convert_column_name( $m2m_relationship, $sort['id'] ) . '` ' . (( $sort['desc'] ? 'desc' : 'asc' ));
992 }
993 } else {
994 // Normal sort
995 $sqlorder .= '`' . $this->convert_column_name( $m2m_relationship, $sort['id'] ) . '` ' . (( $sort['desc'] ? 'desc' : 'asc' ));
996 }
997 }
998 }
999 if ( '' === $sqlorder && '' !== trim( $default_orderby ) ) {
1000 $sqlorder = $default_orderby;
1001 }
1002 // Add pagination.
1003 if ( !is_numeric( $page_size ) ) {
1004 $page_size = 10;
1005 }
1006 $offset = $page_index * $page_size;
1007 // Calculate offset.
1008 if ( !is_numeric( $offset ) ) {
1009 $offset = 0;
1010 }
1011 // Prepare query.
1012 $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";
1013 $sql_tables = array($tbl);
1014 // Perpare query.
1015 $sql = $wpdadb->prepare( ( true === $client_side ? $sql : $sql . (( 0 < $page_size ? " limit {$page_size} offset {$offset} " : '' )) ), $sql_tables );
1016 // Prepare debug info.
1017 if ( 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
1018 $debug = array(
1019 'sql' => preg_replace( "/\\s+/", " ", $sql ),
1020 'where' => $where,
1021 'order by' => $sqlorder,
1022 );
1023 } else {
1024 $debug = null;
1025 }
1026 // Perform query.
1027 $dataset = $wpdadb->get_results( $sql, 'ARRAY_A' );
1028 if ( $wpdadb->last_error ) {
1029 // Handle SQL errors.
1030 return new \WP_Error('error', $wpdadb->last_error, array(
1031 'status' => 420,
1032 'debug' => $debug,
1033 ));
1034 }
1035 if ( is_numeric( $last_row_count ) and 0 <= $last_row_count ) {
1036 // Prevents additional unnecessary queries.
1037 $rowcount = $last_row_count;
1038 } else {
1039 if ( true === $client_side ) {
1040 $rowcount = 0;
1041 } else {
1042 $estimate = false;
1043 if ( '1' === $row_count_estimate && '' === $where ) {
1044 // Perform row count estimate
1045 $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' );
1046 if ( isset( $countrows[0]['rowcount'] ) && 0 != $countrows[0]['rowcount'] ) {
1047 $estimate = true;
1048 }
1049 }
1050 if ( !$estimate ) {
1051 if ( !$estimate ) {
1052 // (Re)Count rows.
1053 $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' );
1054 }
1055 }
1056 if ( $wpdadb->last_error ) {
1057 // Handle SQL errors.
1058 return new \WP_Error('error', $wpdadb->last_error, array(
1059 'status' => 420,
1060 ));
1061 }
1062 if ( isset( $countrows[0]['rowcount'] ) ) {
1063 $rowcount = $countrows[0]['rowcount'];
1064 } else {
1065 $rowcount = 0;
1066 }
1067 }
1068 }
1069 // Add context node to response
1070 $context = array();
1071 if ( 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
1072 $context['debug'] = $debug;
1073 }
1074 if ( is_array( $media_columns ) && 0 < count( $media_columns ) ) {
1075 // Handle WP media library
1076 $media = array();
1077 for ($i = 0; $i < count( $dataset ); $i++) {
1078 $media_row = array();
1079 foreach ( $media_columns as $media_column_name => $media_column_type ) {
1080 if ( isset( $dataset[$i][$media_column_name] ) ) {
1081 $media_row[$media_column_name] = WPDA_WP_Media::get_media_url( $dataset[$i][$media_column_name] );
1082 }
1083 }
1084 $media[] = $media_row;
1085 }
1086 // Add media to context node
1087 $context['media'] = $media;
1088 }
1089 $wpdadb->suppress_errors( $suppress );
1090 // Send response.
1091 $response = $this->WPDA_Rest_Response(
1092 '',
1093 $dataset,
1094 $context,
1095 array(
1096 'rowCount' => $rowcount,
1097 )
1098 );
1099 $response->header( 'X-WP-Total', $rowcount );
1100 // Total rows for this query.
1101 if ( 0 < $page_size ) {
1102 $pagecount = floor( $rowcount / $page_size );
1103 if ( $pagecount != $rowcount / $page_size ) {
1104 // phpcs:ignore WordPress.PHP.StrictComparisons
1105 $pagecount++;
1106 }
1107 } else {
1108 // Prevent division by zero
1109 $pagecount = 0;
1110 }
1111 $response->header( 'X-WP-TotalPages', $pagecount );
1112 // Total pages for this query.
1113 return $response;
1114 }
1115 }
1116
1117 private function convert_column_name( $m2m_relationship, $column_name ) {
1118 // Return plain column name.
1119 return $this->sanitize_db_identifier( $column_name );
1120 }
1121
1122 private function map_columns( $prefix, $column_names ) {
1123 return implode( ",", array_map( function ( $v ) use($prefix) {
1124 $c = $this->sanitize_db_identifier( $v );
1125 $r = ( 'd' === $prefix ? static::RELATIONTABLEPREFIX . $c : $c );
1126 return "`{$prefix}`.`{$c}` as \"{$r}\"";
1127 }, array_keys( $column_names ) ) );
1128 }
1129
1130 public function add_filter(
1131 $wpdadb,
1132 $search_column,
1133 $search_column_fns,
1134 $search_value,
1135 $m2m_relationship = array(),
1136 $search_data_types = array()
1137 ) {
1138 }
1139
1140 public static function add_condition( $where_lines, $operand = 'and' ) {
1141 if ( 0 < count( array_filter( $where_lines ) ) ) {
1142 // Apply all searches.
1143 return ' ( (' . implode( ") {$operand} (", array_filter( $where_lines ) ) . ') ) ';
1144 } else {
1145 return "";
1146 }
1147 }
1148
1149 /**
1150 * Get table meta data.
1151 *
1152 * @param string $dbs Database schema name.
1153 * @param string $tbl Database table name.
1154 * @param string $waa With admin actions.
1155 * @return array\object
1156 */
1157 public function get_table_meta_data( $dbs, $tbl, $waa ) {
1158 $sql_create_table = '';
1159 if ( WPDA::current_user_is_admin() ) {
1160 // Admin user has access to all resources
1161 $access = array(
1162 'select' => array('POST'),
1163 'insert' => array('POST'),
1164 'update' => array('POST'),
1165 'delete' => array('POST'),
1166 );
1167 // Get create table script
1168 $wpdadb = WPDADB::get_db_connection( $dbs );
1169 if ( null !== $wpdadb ) {
1170 $suppress_errors = $wpdadb->suppress_errors;
1171 $wpdadb->suppress_errors = true;
1172 // NO_TABLE_OPTIONS is deprecated in V8
1173 // $wpdadb->query( "SET sql_mode = 'NO_TABLE_OPTIONS'" );
1174 $sql = $wpdadb->get_results( $wpdadb->prepare( 'show create table `%1s`', array($tbl) ), 'ARRAY_N' );
1175 if ( isset( $sql[0][1] ) ) {
1176 $sql_create_table = $sql[0][1];
1177 }
1178 $wpdadb->suppress_errors = $suppress_errors;
1179 }
1180 } else {
1181 $access = $this->get_table_access( $dbs, $tbl );
1182 }
1183 $settings = new stdClass();
1184 if ( null !== $access ) {
1185 $columns = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
1186 $settings_db = WPDA_Table_Settings_Model::query( $tbl, $dbs );
1187 if ( isset( $settings_db[0]['wpda_table_settings'] ) ) {
1188 $settings = json_decode( $settings_db[0]['wpda_table_settings'] );
1189 // Remove old settings from response.
1190 unset($settings->form_labels);
1191 unset($settings->list_labels);
1192 unset($settings->custom_settings);
1193 unset($settings->search_settings);
1194 }
1195 $settings->ui = WPDA_Settings::get_admin_settings( $dbs, $tbl );
1196 $rest_api = get_option( WPDA_API::WPDA_REST_API_TABLE_ACCESS );
1197 if ( isset( $rest_api[$dbs][$tbl] ) ) {
1198 $settings->rest_api = $rest_api[$dbs][$tbl];
1199 }
1200 $settings->env = $this->get_env();
1201 $wp_nonce_action_alter = "wpda-alter-{$tbl}";
1202 $wp_nonce_alter = wp_create_nonce( $wp_nonce_action_alter );
1203 $wp_nonce_refresh = null;
1204 $connect = null;
1205 global $wpdb;
1206 $settings->wp = [
1207 'roles' => $this->get_wp_roles(),
1208 'users' => $this->get_wp_users(),
1209 'home' => admin_url( 'admin.php' ),
1210 'homea' => admin_url( 'admin-ajax.php' ),
1211 'tables' => array_values( $wpdb->tables() ),
1212 'date_format' => get_option( 'date_format' ),
1213 'time_format' => get_option( 'time_format' ),
1214 'alter' => $wp_nonce_alter,
1215 'refresh' => $wp_nonce_refresh,
1216 'connect' => $connect,
1217 'copyinprogress' => WPDA_Actions::copy_in_progress(),
1218 'scroll_offset' => WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET ),
1219 'upload' => @ini_get( 'upload_max_filesize' ),
1220 ];
1221 if ( true === $waa ) {
1222 $settings->wp['aonce'] = implode( '-', array(
1223 wp_create_nonce( 'wpda-export-' . json_encode( $tbl ) ),
1224 // Table export
1225 wp_create_nonce( 'wpda-rename-' . $tbl ),
1226 ) );
1227 }
1228 $table_columns = $columns->get_table_columns();
1229 $media = $this->get_media( $dbs, $tbl, $table_columns );
1230 $columns_sorted = array();
1231 foreach ( $table_columns as $column ) {
1232 if ( isset( $column['column_name'] ) ) {
1233 $columns_sorted[$column['column_name']] = $column;
1234 }
1235 }
1236 }
1237 return array(
1238 'columns' => $table_columns,
1239 'columns_sorted' => $columns_sorted,
1240 'table_labels' => $columns->get_table_header_labels(),
1241 'form_labels' => $columns->get_table_column_headers(),
1242 'primary_key' => $columns->get_table_primary_key(),
1243 'access' => $access,
1244 'settings' => $settings,
1245 'media' => $media['media'],
1246 'wp_media' => $media['wp_media'],
1247 'table_info' => $this->get_table_info( $dbs, $tbl ),
1248 'create' => $sql_create_table,
1249 );
1250 }
1251
1252 private function get_table_access( $dbs, $tbl ) {
1253 if ( WPDA::current_user_is_admin() ) {
1254 // Check administrator rights
1255 if ( is_admin() ) {
1256 $access = WPDA_Dictionary_Access::check_table_access_backend( $dbs, $tbl, $done );
1257 } else {
1258 $access = WPDA_Dictionary_Access::check_table_access_frontend( $dbs, $tbl, $done );
1259 }
1260 if ( $access ) {
1261 // Administrator access granted
1262 return array(
1263 'select' => array('POST'),
1264 'insert' => array('POST'),
1265 'update' => array('POST'),
1266 'delete' => array('POST'),
1267 );
1268 }
1269 }
1270 $tables = get_option( WPDA_API::WPDA_REST_API_TABLE_ACCESS );
1271 if ( false !== $tables && isset( $tables[$dbs][$tbl] ) && is_array( $tables[$dbs][$tbl] ) ) {
1272 $table = $tables[$dbs][$tbl];
1273 $table_access = new \stdClass();
1274 $table_access->select = $this->get_table_access_action( $table, 'select' );
1275 $table_access->insert = $this->get_table_access_action( $table, 'insert' );
1276 $table_access->update = $this->get_table_access_action( $table, 'update' );
1277 $table_access->delete = $this->get_table_access_action( $table, 'delete' );
1278 return $table_access;
1279 }
1280 return false;
1281 }
1282
1283 private function get_table_access_action( $table, $action ) {
1284 if ( isset( $table[$action]['authorization'], $table[$action]['methods'] ) && is_array( $table[$action]['methods'] ) && 0 < count( $table[$action]['methods'] ) ) {
1285 if ( 'anonymous' === $table[$action]['authorization'] ) {
1286 return $table[$action]['methods'];
1287 } else {
1288 // Check authorized users
1289 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'] ) ) {
1290 return $table[$action]['methods'];
1291 }
1292 // Check authorized roles
1293 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'] ) ) ) {
1294 return $table[$action]['methods'];
1295 }
1296 }
1297 }
1298 return array();
1299 }
1300
1301 /**
1302 * Check if access is grant for requested database/table.
1303 *
1304 * @param string $dbs Remote or local database connection string.
1305 * @param string $tbl Database table name.
1306 * @param onject $request Request object.
1307 * @param string $action Possible values: select, insert, update, delete.
1308 * @return bool
1309 */
1310 private function check_table_access(
1311 $dbs,
1312 $tbl,
1313 $request,
1314 $action,
1315 &$msg = ''
1316 ) {
1317 if ( WPDA::current_user_is_admin() ) {
1318 // Grant access to administrators always.
1319 return true;
1320 }
1321 $tables = get_option( WPDA_API::WPDA_REST_API_TABLE_ACCESS );
1322 if ( false === $tables ) {
1323 // No tables.
1324 $msg = __( 'Unauthorized', 'wp-data-access' );
1325 return false;
1326 }
1327 if ( !(isset( $tables[$dbs][$tbl][$action]['methods'] ) && is_array( $tables[$dbs][$tbl][$action]['methods'] )) ) {
1328 // No methods.
1329 $msg = __( 'Unauthorized', 'wp-data-access' );
1330 return false;
1331 } else {
1332 if ( !in_array( $request->get_method(), $tables[$dbs][$tbl][$action]['methods'] ) ) {
1333 // phpcs:ignore -- 8.1 proof
1334 $msg = __( 'Unauthorized', 'wp-data-access' );
1335 return false;
1336 }
1337 }
1338 if ( !isset( $tables[$dbs][$tbl][$action]['authorization'] ) ) {
1339 // No authorization.
1340 $msg = __( 'Unauthorized', 'wp-data-access' );
1341 return false;
1342 } else {
1343 if ( 'anonymous' === $tables[$dbs][$tbl][$action]['authorization'] ) {
1344 // Access granted to all users.
1345 return true;
1346 }
1347 }
1348 global $wp_rest_auth_cookie;
1349 if ( true !== $wp_rest_auth_cookie ) {
1350 // No anonymous access.
1351 $msg = __( 'Unauthorized', 'wp-data-access' );
1352 return false;
1353 } else {
1354 if ( 'authorized' !== $tables[$dbs][$tbl][$action]['authorization'] ) {
1355 // Authorization check.
1356 $msg = __( 'Unauthorized', 'wp-data-access' );
1357 return false;
1358 }
1359 // Authorized access requires a valid nonce.
1360 if ( !wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' ) ) {
1361 $msg = 'rest_cookie_invalid_nonce';
1362 return false;
1363 }
1364 if ( !(isset( $tables[$dbs][$tbl][$action]['authorized_users'] ) && is_array( $tables[$dbs][$tbl][$action]['authorized_users'] )) ) {
1365 // No users.
1366 $msg = __( 'Unauthorized', 'wp-data-access' );
1367 return false;
1368 } else {
1369 $requesting_user_login = $this->get_user_login();
1370 if ( 0 < count( $tables[$dbs][$tbl][$action]['authorized_users'] ) && in_array( $requesting_user_login, $tables[$dbs][$tbl][$action]['authorized_users'] ) ) {
1371 return true;
1372 }
1373 }
1374 if ( !(isset( $tables[$dbs][$tbl][$action]['authorized_roles'] ) && is_array( $tables[$dbs][$tbl][$action]['authorized_roles'] )) ) {
1375 // No roles.
1376 $msg = __( 'Unauthorized', 'wp-data-access' );
1377 return false;
1378 } else {
1379 $requesting_user_roles = $this->get_user_roles();
1380 if ( false === $requesting_user_roles ) {
1381 $requesting_user_roles = array();
1382 }
1383 if ( 0 < count( $tables[$dbs][$tbl][$action]['authorized_roles'] ) && 0 < count( array_intersect( $requesting_user_roles, $tables[$dbs][$tbl][$action]['authorized_roles'] ) ) ) {
1384 return true;
1385 }
1386 }
1387 $msg = __( 'Unauthorized', 'wp-data-access' );
1388 return false;
1389 }
1390 }
1391
1392 private function sanitize_column_values(
1393 $dbs,
1394 $tbl,
1395 $column_values,
1396 $code_columns = array(),
1397 $html_columns = array()
1398 ) {
1399 $wpda_list_columns = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
1400 $sanitized_column_values = [];
1401 foreach ( $column_values as $column_name => $column_value ) {
1402 $column_value = $column_values[$column_name];
1403 switch ( $wpda_list_columns->get_column_data_type( $column_name ) ) {
1404 case 'tinytext':
1405 case 'text':
1406 case 'mediumtext':
1407 case 'longtext':
1408 if ( null !== $column_value ) {
1409 if ( in_array( $column_name, $html_columns ) ) {
1410 $column_value = sanitize_textarea_field( $column_value );
1411 } else {
1412 $column_value = wp_kses_post( $column_value );
1413 }
1414 }
1415 break;
1416 default:
1417 if ( null !== $column_value ) {
1418 $column_value = sanitize_text_field( $column_value );
1419 }
1420 }
1421 $sanitized_column_values[$this->sanitize_db_identifier( $column_name )] = $column_value;
1422 }
1423 return $sanitized_column_values;
1424 }
1425
1426 }
1427