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

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

1,678 lines 51.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Suppress "error - 0 - No summary was found for this file" on phpdoc generation
5 *
6 * @package WPDataAccess
7 */
8
9 namespace WPDataAccess {
10
11 use WP_Data_Access_Admin;
12 use WPDataAccess\Connection\WPDADB;
13 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Lists;
14 use WPDataAccess\Data_Dictionary\WPDA_List_Columns_Cache;
15 use WPDataAccess\Utilities\WPDA_Message_Box;
16 use WPDataProjects\WPDP;
17
18 /**
19 * Class WPDA
20 *
21 * Plugin default values and settings are managed through this class. Every plugin option has a default value
22 * which is maintained in an array together with the option name. Options are only saved in $wpdb->options when
23 * they are changed. Otherwise the default values are used. After reading option values from $wpdb->options the
24 * values are cached as many of them are used in multiple
25 * values are cached as many of them are used in multiple
26 * places during the processing of a request.
27 *
28 * @author Peter Schulz
29 * @since 1.0.0
30 */
31 class WPDA {
32
33 // SAVING SPACE - According to the plugin guidelines it is allowed to include external fonts:
34 // https://developer.wordpress.org/plugins/wordpress-org/detailed-plugin-guidelines/#8-plugins-may-not-send-executable-code-via-third-party-systems
35 const CDN_FONTAWESOME = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/';
36 const GOOGLE_CHARTS = 'https://www.gstatic.com/charts/loader.js';
37
38 /**
39 * Label for WordPress tables
40 */
41 const TABLE_TYPE_WP = 'wordpress table';
42 /**
43 * Label for WPDA plugin tables
44 */
45 const TABLE_TYPE_WPDA = 'plugin table';
46
47 // Options are stored in arrays (OPTION_ARRAYS):
48 // [0] = option name (as saved in wp_options).
49 // [1] = default value (used if option is not available in wp_options).
50 // Application options.
51 /**
52 * Option wpda_version and it's default value
53 */
54 const OPTION_WPDA_VERSION = array( 'wpda_version', '5.5.4' );
55 const OPTION_WPDA_CLIENT_VERSION = array( 'wpda_client_version', '1.0.5' );
56 const OPTION_WPDA_UPGRADED = array( 'wpda_upgraded', false );
57 /**
58 * Option wpda_setup_error and it's default value
59 */
60 const OPTION_WPDA_SETUP_ERROR = array(
61 'wpda_setup_error',
62 ''
63 ); // Values: off = do not display setup errors, other = show.
64 /**
65 * Option wpda_uninstall_tables and it's default value
66 */
67 const OPTION_WPDA_UNINSTALL_TABLES = array( 'wpda_uninstall_tables', 'off' ); // On uninstall drop WPDA tables.
68 /**
69 * Option wpda_uninstall_options and it's default value
70 */
71 const OPTION_WPDA_UNINSTALL_OPTIONS = array( 'wpda_uninstall_options', 'off' ); // On uninstall delete WPDA options.
72 /**
73 * Option wpda_datatables_version and it's default value
74 */
75 const OPTION_WPDA_DATATABLES_VERSION = array( 'wpda_datatables_version', '1.11.3');
76 /**
77 * Option wpda_datatables_responsive_version and it's default value
78 */
79 const OPTION_WPDA_DATATABLES_RESPONSIVE_VERSION = array( 'wpda_datatables_responsive_version', '2.4.0');
80
81 const OPTION_PLUGIN_HIDE_NOTICES = array( 'wpda_plugin_hide_notices', 'on');
82
83 const OPTION_PLUGIN_HIDE_ADMIN_MENU = array( 'wpda_plugin_hide_admin_menu', 'off');
84
85 const OPTION_PLUGIN_PANEL_COOKIES = array( 'wpda_plugin_panel_cookies', 'clear');
86
87 const OPTION_PLUGIN_SECRET_KEY_DEFAULT = 'enter-your-secret-key-here';
88 const OPTION_PLUGIN_SECRET_IV_DEFAULT = 'enter-your-secret-iv-here';
89
90 const OPTION_PLUGIN_SECRET_KEY = array( 'wpda_plugin_secret_key', self::OPTION_PLUGIN_SECRET_KEY_DEFAULT);
91 const OPTION_PLUGIN_SECRET_IV = array( 'wpda_plugin_secret_iv', self::OPTION_PLUGIN_SECRET_IV_DEFAULT);
92
93 const OPTION_PLUGIN_SONCE_SEED = array( 'wpda_plugin_sonce_seed', 'ALSKDFHIUWEALDSKNCNKSDJAKDJHSFAKSDFGFKOJHORITIHGMRTGHMHL');
94
95 const OPTION_PLUGIN_WPDATAACCESS_POST = array( 'wpda_plugin_wpdataaccess_post', 'on');
96 const OPTION_PLUGIN_WPDATAACCESS_PAGE = array( 'wpda_plugin_wpdataaccess_page', 'on');
97 const OPTION_PLUGIN_WPDADIEHARD_POST = array( 'wpda_plugin_wpdadiehard_post', 'on');
98 const OPTION_PLUGIN_WPDADIEHARD_PAGE = array( 'wpda_plugin_wpdadiehard_page', 'on');
99 const OPTION_PLUGIN_WPDADATAFORMS_POST = array( 'wpda_plugin_wpdadataforms_post', 'on');
100 const OPTION_PLUGIN_WPDADATAFORMS_PAGE = array( 'wpda_plugin_wpdadataforms_page', 'on');
101 const OPTION_PLUGIN_WPDADATAFORMS_ALLOW_ANONYMOUS_ACCESS = array( 'wpda_plugin_wpdadataforms_allow_anonymous_access', 'off');
102 const OPTION_PLUGIN_WPDAREPORT_POST = array( 'wpda_plugin_wpdareport_post', 'on');
103 const OPTION_PLUGIN_WPDAREPORT_PAGE = array( 'wpda_plugin_wpdareport_page', 'on');
104
105 // MySQL date and time formats
106 const DB_DATE_FORMAT = 'Y-m-d';
107 const DB_TIME_FORMAT = 'H:i:s';
108 const DB_DATETIME_FORMAT = 'Y-m-d H:i:s';
109
110 // Plugin options. (date and time settings)
111 const OPTION_PLUGIN_DATE_FORMAT = array( 'wpda_plugin_date_format', 'Y-m-d');
112 const OPTION_PLUGIN_DATE_PLACEHOLDER = array( 'wpda_plugin_date_placeholder', 'yyyy-mm-dd');
113 const OPTION_PLUGIN_TIME_FORMAT = array( 'wpda_plugin_time_format', 'H:i');
114 const OPTION_PLUGIN_TIME_PLACEHOLDER = array( 'wpda_plugin_time_placeholder', 'hh:mi');
115 const OPTION_PLUGIN_SET_FORMAT = array( 'wpda_plugin_set_format', 'csv');
116
117 // Plugin debug mode
118 const OPTION_PLUGIN_DEBUG = array( 'wpda_plugin_debug', 'off');
119
120 // Back-end options.
121 /**
122 * Option wpda_be_load_datatables and it's default value
123 */
124 const OPTION_BE_LOAD_DATATABLES = array( 'wpda_be_load_datatables', 'on');
125 /**
126 * Option wpda_be_load_datatables_response and it's default value
127 */
128 const OPTION_BE_LOAD_DATATABLES_RESPONSE = array( 'wpda_be_load_datatables_response', 'on');
129 /**
130 * Option wpda_be_table_access and it's default value
131 */
132 const OPTION_BE_TABLE_ACCESS = array( 'wpda_be_table_access', 'show'); // Values: show, hide and select.
133 /**
134 * Option wpda_be_table_access_selected and it's default value
135 */
136 const OPTION_BE_TABLE_ACCESS_SELECTED = array( 'wpda_be_table_access_selected', ''); // Tables authorized in WPDA.
137 /**
138 * Option wpda_be_view_link and it's default value
139 */
140 const OPTION_BE_VIEW_LINK = array( 'wpda_be_view_link', 'on'); // Show view link in list table.
141 /**
142 * Option wpda_be_allow_insert and it's default value
143 */
144 const OPTION_BE_ALLOW_INSERT = array(
145 'wpda_be_allow_insert',
146 'on'
147 ); // Show insert link in list table (simple form).
148 /**
149 * Option wpda_be_allow_update and it's default value
150 */
151 const OPTION_BE_ALLOW_UPDATE = array(
152 'wpda_be_allow_update',
153 'on'
154 ); // Show update link in list table (simple form).
155 /**
156 * Option wpda_be_allow_delete and it's default value
157 */
158 const OPTION_BE_ALLOW_DELETE = array( 'wpda_be_allow_delete', 'on'); // Show delete link in list table.
159 /**
160 * Option wpda_be_wpda_export_rows and it's default value
161 */
162 const OPTION_BE_EXPORT_ROWS = array(
163 'wpda_be_wpda_export_rows',
164 'on'
165 ); // Show export link in list table (row export).
166 /**
167 * Option wpda_be_wpda_export_variable_prefix and it's default value
168 */
169 const OPTION_BE_EXPORT_VARIABLE_PREFIX = array(
170 'wpda_be_wpda_export_variable_prefix',
171 'on'
172 ); // Allows to import into repository with different wpdb prefix.
173 /**
174 * Option wpda_be_wpda_allow_imports and it's default value
175 */
176 const OPTION_BE_ALLOW_IMPORTS = array( 'wpda_be_wpda_allow_imports', 'on'); // Allow to import data (main only).
177 /**
178 * Option wpda_be_confirm_export and it's default value
179 */
180 const OPTION_BE_CONFIRM_EXPORT = array( 'wpda_be_confirm_export', 'off'); // Ask for confirmation before exporting.
181 /**
182 * Option wpda_be_confirm_view and it's default value
183 */
184 const OPTION_BE_CONFIRM_VIEW = array( 'wpda_be_confirm_view', 'off'); // Ask for confirmation before viewing.
185 /**
186 * Option wpda_be_pagination and it's default value
187 */
188 const OPTION_BE_PAGINATION = array( 'wpda_be_pagination', '10');
189 /**
190 * Option wpda_be_remember_search and it's default value
191 */
192 const OPTION_BE_REMEMBER_SEARCH = array( 'wpda_be_remember_search', 'on');
193 /**
194 * Option wpda_be_innodb_count and it's default value
195 */
196 const OPTION_BE_INNODB_COUNT = array( 'wpda_be_innodb_count', 100000);
197 /**
198 * Option wpda_be_design_mode and it's default value
199 */
200 const OPTION_BE_DESIGN_MODE = array( 'wpda_be_design_mode', 'advanced'); // Default design mode (basic/advanced).
201 /**
202 * Option wpda_be_text_wrap_switch and it's default value
203 */
204 const OPTION_BE_TEXT_WRAP_SWITCH = array( 'wpda_be_text_wrap_switch', 'off');
205 /**
206 * Option wpda_be_text_wrap and it's default value
207 */
208 const OPTION_BE_TEXT_WRAP = array( 'wpda_be_text_wrap', 400);
209 /**
210 * Option wpda_be_hide_button_icons and it's default value
211 */
212 const OPTION_BE_HIDE_BUTTON_ICONS = array( 'wpda_be_hide_button_icons', 'off');
213
214 // Front-end options.
215 /**
216 * Option wpda_fe_load_datatables and it's default value
217 */
218 const OPTION_FE_LOAD_DATATABLES = array( 'wpda_fe_load_datatables', 'on');
219 /**
220 * Option wpda_fe_load_datatables_response and it's default value
221 */
222 const OPTION_FE_LOAD_DATATABLES_RESPONSE = array( 'wpda_fe_load_datatables_response', 'on');
223 /**
224 * Option wpda_fe_table_access and it's default value
225 */
226 const OPTION_FE_TABLE_ACCESS = array( 'wpda_fe_table_access', 'select'); // Values: hide, show, select.
227 /**
228 * Option wpda_fe_table_access_selected and it's default value
229 */
230 const OPTION_FE_TABLE_ACCESS_SELECTED = array( 'wpda_fe_table_access_selected', ''); // Tables authorized in WPDA.
231 /**
232 * Option wpda_fe_pagination and it's default value
233 */
234 const OPTION_FE_PAGINATION = array( 'wpda_fe_pagination', '10');
235 const OPTION_FE_ADD_PROJECTS_TO_TOOLBAR = array( 'wpda_fe_add_projects_to_toolbar', 'on');
236
237 // Manage Repository options.
238 /**
239 * Option wpda_mr_keep_backup_tables and it's default value
240 */
241 const OPTION_MR_KEEP_BACKUP_TABLES = array( 'wpda_mr_keep_backup_tables', 'on');
242 const OPTION_MR_BACKUP_TABLES_KEPT_DEFAULT = '3';
243 const OPTION_MR_BACKUP_TABLES_KEPT = array( 'wpda_mr_backup_tables_kept', self::OPTION_MR_BACKUP_TABLES_KEPT_DEFAULT);
244
245 // Data Backup options.
246 /**
247 * Option wpda_db_local_path and it's default value
248 */
249 const OPTION_DB_LOCAL_PATH = array( 'wpda_db_local_path', '');
250 /**
251 * Option wpda_db_dropbox_path and it's default value
252 */
253 const OPTION_DB_DROPBOX_PATH = array( 'wpda_db_dropbox_path', '');
254
255 // Data Tables options.
256 /**
257 * Option wpda_dp_publication_roles and it's default value
258 */
259 const OPTION_DP_PUBLICATION_ROLES = array( 'wpda_dp_publication_roles', '');
260 const OPTION_DP_LANGUAGE = array( 'wpda_dp_language', 'English');
261 const OPTION_DP_JSON_EDITING = array( 'wpda_dp_json_editing', 'validate');
262 const OPTION_DP_STYLE = array( 'wpda_dp_style', 'default');
263
264 // Role Management
265 const OPTION_WPDA_ENABLE_ROLE_MANAGEMENT = array( 'wpda_rm_enable_role_management', 'off');
266 const OPTION_WPDA_USE_ROLES_IN_SHORTCODE = array( 'wpda_rm_use_roles_in_shortcode', 'off');
267
268 // Prefixes to store backend access for non WP databases
269 const BACKEND_OPTIONNAME_DATABASE_ACCESS = 'WPDA_BE_TABLE_ACCESS_';
270 const BACKEND_OPTIONNAME_DATABASE_SELECTED = 'WPDA_BE_TABLE_SELECTED_';
271
272 // Prefixes to store frontend access for non WP databases
273 const FRONTEND_OPTIONNAME_DATABASE_ACCESS = 'WPDA_FE_TABLE_ACCESS_';
274 const FRONTEND_OPTIONNAME_DATABASE_SELECTED = 'WPDA_FE_TABLE_SELECTED_';
275
276 const WPDA_DT_UI_THEME_DEFAULT = array( 'wpda_dt_ui_theme_default', 'smoothness');
277
278 /**
279 * List of plugin tables
280 */
281 const WPDA_TABLES = array(
282 'wpda_logging' => true,
283 'wpda_menus' => true,
284 'wpda_table_settings' => true,
285 'wpda_table_design' => true,
286 'wpda_publisher' => true, // Old plugin table name: do NOT change!
287 'wpda_media' => true,
288 'wpda_project' => true,
289 'wpda_project_page' => true,
290 'wpda_project_table' => true,
291 'wpda_csv_uploads' => true,
292 'wpda_pro_reports' => true,
293 );
294
295 /**
296 * List containing all plugin tables
297 *
298 * @var array
299 */
300 static protected $wpda_tables = array();
301
302 // Option values once queried from wp_options are stored in an array for re-use during request to prevent
303 // executing same query on wp_options multiple times.
304 /**
305 * Options cache array
306 *
307 * @var array
308 */
309 static protected $option_cache = array();
310
311 /**
312 * List containing all WordPress tables
313 *
314 * @var array
315 */
316 static protected $wp_tables = array();
317
318 static protected $plugin_pages = array(
319 WP_Data_Access_Admin::PAGE_MAIN,
320 WP_Data_Access_Admin::PAGE_APPS,
321 WP_Data_Access_Admin::PAGE_DASHBOARD,
322 WP_Data_Access_Admin::PAGE_SETTINGS,
323 WP_Data_Access_Admin::PAGE_EXPLORER,
324 WP_Data_Access_Admin::PAGE_QUERY_BUILDER,
325 WP_Data_Access_Admin::PAGE_PUBLISHER,
326 WP_Data_Access_Admin::PAGE_DESIGNER,
327 WP_Data_Access_Admin::PAGE_MY_TABLES,
328 WP_Data_Access_Admin::PAGE_CHARTS,
329 WPDP::PAGE_MAIN,
330 WPDP::PAGE_TEMPLATES,
331 );
332
333 public static function is_plugin_page( $page ) {
334 return (
335 is_scalar( $page ) &&
336 (
337 'wpda_wpdp_' === substr( $page, 0, 10 ) ||
338 WP_Data_Access_Admin::PAGE_EXPLORER === substr( $page, 0, 13 ) ||
339 in_array( $page, self::$plugin_pages) //phpcs:ignore - 8.1 proof
340 )
341 );
342 }
343
344 /**
345 * Translated table label
346 *
347 * Returns a translated table label. The label depends on the type of table. Provided through a function to
348 * support internationalization.
349 *
350 * @param string $table_type Table type (use WPDA constants).
351 *
352 * @return string Translated table type.
353 * @since 1.0.0
354 *
355 */
356 public static function get_table_type_text( $table_type ) {
357
358 switch ( $table_type ) {
359
360 case self::TABLE_TYPE_WP:
361 return __( 'WordPress table', 'wp-data-access' );
362
363 case self::TABLE_TYPE_WPDA:
364 return __( 'plugin table', 'wp-data-access' );
365
366 default:
367 return $table_type;
368
369 }
370
371 }
372
373 /**
374 * Get plugin option values
375 *
376 * Get value for pluginoption saved in wp_options. If option is not found in $wpdb->options the default value
377 * for that option is returned. Option values once taken from $wpdb->options are cached to prevent execution
378 * of the same query multiple times during teh processing of a request.
379 *
380 * @param array $option OPTION_ARRAY (use class constants).
381 *
382 * @return mixed Value for OPTION_ARRAY ($option): (1) cached value (2) wp_options value (3) default value.
383 * @since 1.0.0
384 *
385 */
386 public static function get_option( $option ) {
387
388 if ( isset( self::$option_cache[ $option[0] ] ) ) {
389 return self::$option_cache[ $option[0] ]; // Re-use cached value.
390 }
391
392 $option_value = get_option( $option[0] );
393 if ( ! $option_value ) {
394 // Option not found in wp_options: save default value for re-use.
395 self::$option_cache[ $option[0] ] = $option[1];
396 } else {
397 // Option found in wp_options: save for re-use.
398 self::$option_cache[ $option[0] ] = $option_value;
399 }
400
401 return self::$option_cache[ $option[0] ]; // Return saved value.
402
403 }
404
405 /**
406 * Delete all plugin options from table wp_options
407 *
408 * @since 1.0.0
409 */
410 public static function clear_all_options() {
411
412 global $wpdb;
413
414 $wpdb->query(
415 "
416 DELETE FROM wp_options
417 WHERE option_name LIKE 'wpda_%'
418 "
419 ); // db call ok; no-cache ok.
420
421 self::$option_cache = array(); // Reset cache.
422
423 }
424
425 /**
426 * Load all WordPress tables
427 *
428 * @since 1.1.0
429 */
430 public static function load_wp_tables() {
431
432 if ( 0 === count( self::$wp_tables ) ) {//phpcs:ignore - 8.1 proof
433 try {
434 global $wpdb;
435
436 if ( ! is_multisite() ) {
437 foreach ( $wpdb->tables( 'all', true ) as $table ) {
438 self::$wp_tables[ $table ] = $table;
439 }
440 } else {
441 $query = "select blog_id from {$wpdb->blogs}";
442 $blogs = $wpdb->get_results( $query, 'ARRAY_N' );
443 foreach ( $blogs as $blog ) {
444 foreach ( $wpdb->tables( $blog === reset( $blogs ) ? 'all' : 'blog', true, $blog[0] ) as $table ) {//phpcs:ignore - 8.1 proof
445 self::$wp_tables[ $table ] = $table;
446 }
447 }
448 }
449
450 return true;
451 } catch ( \Exception $e ) {
452 wp_die( 'ERROR: ' . $e->getMessage() );
453 }
454 }
455
456 }
457
458 /**
459 * Checks if a table is a WordPress table
460 *
461 * @param string $table_name Table name.
462 *
463 * @return bool TRUE = WordPress table
464 * @since 1.1.0
465 *
466 */
467 public static function is_wp_table( $table_name ) {
468
469 self::load_wp_tables();
470
471 if ( 0 === count( self::$wp_tables ) ) {//phpcs:ignore - 8.1 proof
472 return false;
473 }
474
475 return isset( self::$wp_tables[ $table_name ] );
476
477 }
478
479 /**
480 * List containing all WordPress tables
481 *
482 * @return array
483 * @since 1.1.0
484 *
485 */
486 public static function get_wp_tables() {
487
488 self::load_wp_tables();
489
490 if ( 0 === count( self::$wp_tables ) ) {//phpcs:ignore - 8.1 proof
491 wp_die( __( 'ERROR: No WordPress table found', 'wp-data-access' ) );
492 }
493
494 return self::$wp_tables;
495
496 }
497
498 /**
499 * Returns a list of all plugin tables
500 *
501 * @return array List of plugin tables
502 * @since 1.0.0
503 *
504 */
505 public static function get_wpda_tables() {
506
507 global $wpdb;
508 $wpda_tables = array();
509
510 foreach ( self::WPDA_TABLES as $key => $value ) {
511 $wpda_tables[] = $wpdb->prefix . $key;
512 }
513
514 return $wpda_tables;
515
516 }
517
518 /**
519 * Check if repository column represents a schema name
520 *
521 * @param string $table_name Table name
522 * @param string $column_name Column name
523 *
524 * @return bool
525 */
526 public static function column_is_schema_name( $table_name, $column_name ) {
527 if ( 0 === count( self::$wpda_tables ) ) {//phpcs:ignore - 8.1 proof
528 // Cache schema names
529 global $wpdb;
530 self::$wpda_tables[$wpdb->prefix . 'wpda_media.media_schema_name' ] = true;
531 self::$wpda_tables[$wpdb->prefix . 'wpda_project_page.page_schema_name' ] = true;
532 self::$wpda_tables[$wpdb->prefix . 'wpda_menus.menu_schema_name' ] = true;
533 self::$wpda_tables[$wpdb->prefix . 'wpda_project_table.wpda_schema_name' ] = true;
534 self::$wpda_tables[$wpdb->prefix . 'wpda_publisher.pub_schema_name' ] = true;
535 self::$wpda_tables[$wpdb->prefix . 'wpda_table_design.wpda_schema_name' ] = true;
536 self::$wpda_tables[$wpdb->prefix . 'wpda_table_settings.wpda_schema_name' ] = true;
537 }
538
539 return isset( self::$wpda_tables[ "{$table_name}.{$column_name}" ] );
540 }
541
542 /**
543 * Return the default value for a plugin option
544 *
545 * @param array $option OPTION_ARRAY (use class constants).
546 *
547 * @return mixed Default value for OPTION_ARRAY ($option).
548 * @since 1.0.0
549 *
550 */
551 public static function get_option_default( $option ) {
552
553 return $option[1]; // Default option value.
554
555 }
556
557 /**
558 * Checks if table is plugin table
559 *
560 * NOTE
561 * Variable $phpdoc_supported_solution is a temporary variable that does not add any functionality to this
562 * function. It only serves the purpose to get class WPDA in the documentation!!! If the isset statement in
563 * the return is performed directly on self::WPDA_TABLES, class WPDA will not appear in the phpdoc generated
564 * documentation. To avoid class WPDA to be undocumented, we use $phpdoc_supported_solution.
565 *
566 * @param string $real_table_name Table name to be checked.
567 *
568 * @return bool TRUE = $table_name is a WPDA table, FALSE = $table_name is not a WPDA table.
569 * @since 1.0.0
570 *
571 */
572 public static function is_wpda_table( $real_table_name ) {
573 if ( null === $real_table_name ) {
574 return false;
575 }
576
577 global $wpdb;
578
579 $phpdoc_supported_solution = self::WPDA_TABLES; // DO NOT DELETE THIS TO MAKE THE CODE SIMPLER!!! (read).
580
581 return
582 (
583 isset(
584 $phpdoc_supported_solution[ substr( $real_table_name, strlen( $wpdb->prefix ) ) ]
585 ) && (
586 $wpdb->prefix === substr( $real_table_name, 0, strlen( $wpdb->prefix ) )
587 )
588 );
589 }
590
591 /**
592 * Save plugin option
593 *
594 * Saves a plugin option in $wpdb->options.
595 *
596 * @param array $option OPTION_ARRAYS (use class constants).
597 * @param mixed $value Value to be saved for $option. If null set to default.
598 *
599 * @since 1.0.0
600 *
601 */
602 public static function set_option( $option, $value = null ) {
603
604 try {
605
606 if ( is_null( $value ) ) {
607 $option_value = $option[1]; // Set option to default.
608 } else {
609 $option_value = $value; // Set option value.
610 }
611
612 update_option( $option[0], $option_value ); // Save option value in wp_options.
613
614 self::$option_cache[ $option[0] ] = $option_value; // Save for re-use.
615
616 } catch ( \Exception $e ) {
617
618 die( 'ERROR: ' . esc_html( $e->errorMessage() ) );
619
620 }
621
622 }
623
624 /**
625 * Simplify data type for simple forms
626 *
627 * Data types used in plugin a re simplified for simple form usage.
628 *
629 * @param string $arg Data type as known to the MySQL database.
630 *
631 * @return string Simplified data type (mainly used to recognize when to use quotes).
632 * @since 1.0.0
633 *
634 * @see \WPDataAccess\Simple_Form\WPDA_Simple_Form
635 *
636 */
637 public static function get_type( $arg ) {
638 $type = trim( str_replace( 'unsigned', '', (string) $arg ) );
639 if ( false !== ( $index = strpos( $type, '(' ) ) ) {
640 $type = substr( $type, 0, $index );
641 }
642 switch ( $type ) {
643 case 'tinyint':
644 case 'smallint':
645 case 'mediumint':
646 case 'int':
647 case 'bigint':
648 case 'float':
649 case 'double':
650 case 'decimal':
651 case 'year':
652 case 'number': // DataTables support
653 return 'number';
654 case 'date':
655 case 'datetime':
656 case 'timestamp':
657 return 'date';
658 case 'time':
659 return 'time';
660 case 'enum':
661 return 'enum';
662 case 'set':
663 return 'set';
664 default:
665 return 'string';
666 }
667 }
668
669 public static function get_sb_type( $arg ) {
670 $type = trim( str_replace( 'unsigned', '', (string) $arg ) );
671 if ( false !== ( $index = strpos( $type, '(' ) ) ) {
672 $type = substr( $type, 0, $index );
673 }
674 switch ( $type ) {
675 case 'tinyint':
676 case 'smallint':
677 case 'mediumint':
678 case 'int':
679 case 'bigint':
680 case 'float':
681 case 'double':
682 case 'decimal':
683 case 'year':
684 return 'num';
685 case 'date':
686 case 'datetime':
687 case 'timestamp':
688 return 'date';
689 default:
690 return 'string';
691 }
692 }
693
694 /**
695 * Insert a value or key/value pair after a specific key in an array. If key doesn't exist, value is appended
696 * to the end of the array.
697 *
698 * Source: https://gist.github.com/wpscholar/0deadce1bbfa4adb4e4c
699 *
700 * @param array $array
701 * @param string $key
702 * @param array $new
703 *
704 * @return array
705 */
706 public static function array_insert_after( $array, $key, $new ) {
707 $keys = array_keys( (array) $array ); //phpcs:ignore - 8.1 proof
708 $index = array_search( $key, $keys ); //phpcs:ignore - 8.1 proof
709 $pos = false === $index ? count( $array ) : $index + 1; //phpcs:ignore - 8.1 proof
710
711 return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); //phpcs:ignore - 8.1 proof
712 }
713
714 /**
715 * Log a message in the database
716 *
717 * Use this method to log messages to the database.
718 *
719 * NOTE Don't use $wpdb->insert! You'll miss a lot of information...
720 *
721 * @param $log_id string Id to identify/find logged data.
722 * @param $log_type string Possible values: 'FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'
723 * @param $log_msg string Any text (max length 4096kb).
724 *
725 * @since 2.0.7
726 *
727 */
728 public static function log( $log_id, $log_type, $log_msg ) {
729 global $wpdb;
730
731 $sql =
732 $wpdb->prepare(
733 'INSERT INTO ' . $wpdb->prefix .
734 'wpda_logging (log_time, log_id, log_type, log_msg) VALUES (now(), %s, %s, %s)'
735 , $log_id
736 , $log_type
737 , $log_msg
738 );
739 $wpdb->query( $sql );
740
741 }
742
743 /**
744 * Get user role
745 *
746 * @return mixed Current user roles or FALSE if not logged in.
747 * @since 2.0.8
748 *
749 */
750 public static function get_current_user_roles() {
751 if ( is_user_logged_in() ) {
752 $user = wp_get_current_user();
753 if ( ! is_array( $user->roles ) ) {
754 return ( array ) $user->roles;
755 } else {
756 return $user->roles;
757 }
758 } else {
759 return false;
760 }
761 }
762
763 /**
764 * Get user capability
765 *
766 * @return mixed Current users first capability or FALSE if not logged in.
767 * @since 2.0.8
768 *
769 */
770 public static function get_current_user_capability() {
771 if ( is_user_logged_in() ) {
772 $user = wp_get_current_user();
773 $allcaps = array();
774 foreach ( $user->allcaps as $key => $val ) {
775 array_push( $allcaps, $key );//phpcs:ignore - 8.1 proof
776 }
777
778 return $allcaps[0];
779 } else {
780 return false;
781 }
782 }
783
784 /**
785 * Convert memory value to integer.
786 *
787 * @param $memory_value string Memory value (eg 128M)
788 *
789 * @return integer Converted value in decimal
790 * @since 2.0.8
791 *
792 */
793 public static function convert_memory_to_decimal( $memory_value ) {
794 if ( preg_match( '/^(\d+)(.)$/', $memory_value, $matches ) ) {
795 if ( $matches[2] == 'G' ) {
796 return $matches[1] * 1024 * 1024 * 1024;
797 } else if ( $matches[2] == 'M' ) {
798 return $matches[1] * 1024 * 1024;
799 } else if ( $matches[2] == 'K' ) {
800 return $matches[1] * 1024;
801 }
802 }
803 }
804
805 public static function get_current_user_id( $is_anonymous = false ) {
806 if ( $is_anonymous ) {
807 return -1;
808 }
809
810 global $current_user;
811 if ( isset( $current_user->ID ) ) {
812 return $current_user->ID;
813 } else {
814 if ( function_exists('wp_get_current_user') ) {
815 $wp_user = wp_get_current_user();
816 if ( isset( $wp_user->ID ) ) {
817 return $wp_user->ID;
818 } else {
819 return - 1;
820 }
821 } else {
822 return - 1;
823 }
824 }
825 }
826
827 public static function get_current_user_login() {
828 global $current_user;
829 if ( isset( $current_user->user_login ) ) {
830 return $current_user->user_login;
831 } else {
832 if ( function_exists('wp_get_current_user') ) {
833 $wp_user = wp_get_current_user();
834 if ( isset( $wp_user->data->user_login ) ) {
835 return $wp_user->data->user_login;
836 } else {
837 return 'anonymous';
838 }
839 } else {
840 return 'anonymous';
841 }
842 }
843 }
844
845 public static function get_current_user_email() {
846 global $current_user;
847 if ( isset( $current_user->user_email ) ) {
848 return $current_user->user_email;
849 } else {
850 $wp_user = wp_get_current_user();
851 if ( isset( $wp_user->data->user_email ) ) {
852 return $wp_user->data->user_email;
853 } else {
854 return 'anonymous';
855 }
856 }
857 }
858
859 /**
860 * Replaces environment variables in where clause with appropriate values
861 *
862 * @param $where_clause
863 *
864 * @return string
865 */
866 public static function substitute_environment_vars( $where_clause ) {
867 if ( strpos( $where_clause, '$$USERID$$' ) ) {
868 $user_id = WPDA::get_current_user_id();
869 $where_clause = str_replace( '$$USERID$$', $user_id, $where_clause );
870 }
871
872 if ( strpos( $where_clause, '"$$USER$$"' ) ) {
873 $user_login = WPDA::get_current_user_login();
874 $where_clause = str_replace( '"$$USER$$"', '"' . $user_login . '"', $where_clause );
875 }
876 if ( strpos( $where_clause, "'" . '$$USER$$' . "'" ) ) {
877 $user_login = WPDA::get_current_user_login();
878 $where_clause = str_replace( "'" . '$$USER$$' . "'", "'" . $user_login . "'", $where_clause );
879 }
880 if ( strpos( $where_clause, '$$USER$$' ) ) {
881 $user_login = WPDA::get_current_user_login();
882 $where_clause = str_replace( '$$USER$$', "'" . $user_login . "'", $where_clause );
883 }
884
885 if ( strpos( $where_clause, '"$$EMAIL$$"' ) ) {
886 $user_email = WPDA::get_current_user_email();
887 $where_clause = str_replace( '"$$EMAIL$$"', '"' . $user_email . '"', $where_clause );
888 }
889 if ( strpos( $where_clause, "'" . '$$EMAIL$$' . "'" ) ) {
890 $user_email = WPDA::get_current_user_email();
891 $where_clause = str_replace( "'" . '$$EMAIL$$' . "'", "'" . $user_email . "'", $where_clause );
892 }
893 if ( strpos( $where_clause, '$$EMAIL$$' ) ) {
894 $user_email = WPDA::get_current_user_email();
895 $where_clause = str_replace( '$$EMAIL$$', "'" . $user_email . "'", $where_clause );
896 }
897
898 return $where_clause;
899 }
900
901 /**
902 * Where clause construction (use filter if applied)
903 *
904 * Param $columns = associative array containing the following column info taken from the data dictionary:
905 * $columns['column_name'] => information_schema.tables.column_name
906 * $columns['data_type'] => information_schema.tables.data_type
907 * $columns['extra'] => information_schema.tables.extra
908 * $columns['column_type' => information_schema.tables.column_type
909 * $columns['is_nullable' => information_schema.tables.is_nullable
910 * $columns['column_default'] => information_schema.tables.column_default
911 *
912 * @param string $schema_name Schema name (optional)
913 * @param string $table_name Table name (optional)
914 * @param string $columns Array containing table columns
915 * @param string $search Search string entered by user
916 * @param boolean $is_case_sensitive Case-sensitive search (default = false)
917 * @param boolean $is_dt_request Always skip filter for DataTable requests (default = false)
918 *
919 * @return string Where clause between ()
920 */
921 public static function construct_where_clause(
922 $schema_name,
923 $table_name,
924 $columns,
925 $search,
926 $is_case_sensitive = false,
927 $is_dt_request = false
928 ) {
929 $where_search_args = self::add_wpda_search_args( $columns );
930
931 if (
932 ! $is_case_sensitive &&
933 ! $is_dt_request &&
934 has_filter('wpda_construct_where_clause')
935 ) {
936 global $wpda_search_args;
937 $wpda_search_args = $where_search_args;
938
939 // Use search filter
940 $filter = apply_filters(
941 'wpda_construct_where_clause',
942 '',
943 $schema_name,
944 $table_name,
945 $columns,
946 $search
947 );
948 if ( null !== $filter ) {
949 if ( '' === $where_search_args ) {
950 return $filter;
951 } else {
952 if ( '' === $filter ) {
953 return $where_search_args;
954 } else {
955 return "{$filter} and {$where_search_args}";
956 }
957 }
958 }
959 }
960
961 // Define case-sensitive search
962 $case_sensitive = $is_case_sensitive ? 'binary' : '';
963
964 // Default search behaviour
965 if ( '' === $search || null === $search || ! is_array( $columns ) ) {
966 return $where_search_args;
967 }
968
969 global $wpdb;
970 $where_columns = array();
971
972 foreach ( $columns as $column ) {
973 if ( 'string' === WPDA::get_type( $column['data_type'] ) ) {
974 $where_columns[] = $wpdb->prepare( "`" . str_replace( '`', '', $column['column_name'] ) . "` like {$case_sensitive} '%s'", '%' . esc_sql( $search ) . '%' ); // phpcs:ignore Standard.Category.SniffName.ErrorCode
975 }
976
977 if ( $is_dt_request ) {
978 // Handle numeric and date queries for DataTables.
979 if ( 'number' === WPDA::get_type( $column['data_type'] ) && ( is_numeric( $search ) ) ) {
980 $where_columns[] = $wpdb->prepare( "`" . str_replace( '`', '', $column['column_name'] ) . "` = '%s'", esc_sql( $search ) ); // phpcs:ignore Standard.Category.SniffName.ErrorCode
981 } elseif ( 'date' === WPDA::get_type( $column['data_type'] ) ) {
982 $where_columns[] = $wpdb->prepare( "`" . str_replace( '`', '', $column['column_name'] ) . "` like '%s'", esc_sql( $search ) . '%' ); // phpcs:ignore Standard.Category.SniffName.ErrorCode
983 }
984 }
985 }
986
987 if ( 0 === count( $where_columns ) ) {//phpcs:ignore - 8.1 proof
988 return '' === $where_search_args ? ' (1=2) ' : $where_search_args;
989 }
990
991 if ( '' === $where_search_args ) {
992 return ' (' . implode( ' or ', $where_columns ) . ') ';
993 } else {
994 return ' (' . implode( ' or ', $where_columns ) . ') ' . "and {$where_search_args}";
995 }
996 }
997
998 public static function add_wpda_search_args( $columns ) {
999 $where_columns = array();
1000
1001 if ( is_array( $columns ) ) {
1002 global $wpdb;
1003 $request = array_change_key_case( $_REQUEST ); //phpcs:ignore - 8.1 proof
1004 foreach ( $columns as $column ) {
1005 $column_name = str_replace( '`', '', $column['column_name'] );
1006 $column_name_lwr = strtolower( $column_name );
1007 if ( isset( $request["wpda_search_column_{$column_name_lwr}"] ) ) {
1008 if ( is_array( $request["wpda_search_column_{$column_name_lwr}"] ) ) {
1009 // Handle multiple values for same column with OR
1010 $where_columns_arr = array();
1011 foreach ( $request["wpda_search_column_{$column_name_lwr}"] as $value ) {
1012 $column_date_type = $column['data_type'];
1013 $column_value = wp_strip_all_tags( wp_unslash( $value ) ); // input var okay.
1014 if ( '' !== $column_value ) {
1015 if ( 'string' === WPDA::get_type( $column_date_type ) ) {
1016 $where_columns_arr[] = $wpdb->prepare( "`{$column_name}` like '%s'", esc_sql( $column_value ) ); // phpcs:ignore Standard.Category.SniffName.ErrorCode
1017 } elseif ( 'number' === WPDA::get_type( $column_date_type ) ) {
1018 $where_columns_arr[] = $wpdb->prepare( "`{$column_name}` = '%d'", esc_sql( $column_value ) ); // phpcs:ignore Standard.Category.SniffName.ErrorCode
1019 }
1020 }
1021 }
1022 if ( count( $where_columns_arr ) > 0 ) {//phpcs:ignore - 8.1 proof
1023 $where_columns[] = ' (' . implode( ' or ', $where_columns_arr ) . ') ';//phpcs:ignore - 8.1 proof
1024 }
1025 } else {
1026 // Handle single value
1027 $column_date_type = $column['data_type'];
1028 $column_value = wp_strip_all_tags( wp_unslash( $request[ "wpda_search_column_{$column_name_lwr}" ] ) );
1029 if ( '' !== $column_value ) {
1030 if ( 'string' === WPDA::get_type( $column_date_type ) ) {
1031 $where_columns[] = $wpdb->prepare( "`{$column_name}` like '%s'", esc_sql( $column_value ) ); // phpcs:ignore Standard.Category.SniffName.ErrorCode
1032 } elseif ( 'number' === WPDA::get_type( $column_date_type ) ) {
1033 $where_columns[] = $wpdb->prepare( "`{$column_name}` = '%d'", esc_sql( $column_value ) ); // phpcs:ignore Standard.Category.SniffName.ErrorCode
1034 }
1035 }
1036 }
1037 }
1038 }
1039 }
1040
1041 if ( 0 === count( $where_columns ) ) {//phpcs:ignore - 8.1 proof
1042 return '';
1043 } else {
1044 $operator =
1045 isset( $_REQUEST['wpda_search_column_operator'] ) &&
1046 'or' === strtolower( $_REQUEST['wpda_search_column_operator'] )
1047 ? 'or' : 'and';
1048 return ' (' . implode( " $operator ", $where_columns ) . ') ';
1049 }
1050 }
1051
1052 public static function schema_disabled( $schema_name ) {
1053 if ( 'rdb:' === substr( $schema_name, 0, 4) ) {
1054 $rdb = WPDADB::get_remote_database( $schema_name, true );
1055 if ( false === $rdb ) {
1056 return false;
1057 }
1058 return true === $rdb[ 'disabled' ];
1059 }
1060 return false;
1061 }
1062
1063 /**
1064 * Check if database schema exists (local and remote databases)
1065 *
1066 * @param string $schema_name Database schema
1067 *
1068 * @return bool Exists?
1069 */
1070 public static function schema_exists( $schema_name ) {
1071 if ( self::schema_disabled( $schema_name ) ) {
1072 return false;
1073 }
1074
1075 $wpdadb = WPDADB::get_db_connection( $schema_name );
1076 if ( null === $wpdadb ) {
1077 return false;
1078 }
1079 if ( method_exists( $wpdadb, 'is_connected' ) ) {
1080 if ( ! $wpdadb->is_connected() ) {
1081 $msg = new WPDA_Message_Box(
1082 [
1083 'message_text' => __( "Remote database '{$schema_name}' not available [check connection: Settings > WP Data Access]", 'wp-data-access' ),
1084 'message_type' => 'error',
1085 'message_is_dismissible' => false,
1086 ]
1087 );
1088 $msg->box();
1089
1090 return false;
1091 }
1092 }
1093
1094 $wpdadb->query(
1095 $wpdadb->prepare( '
1096 SELECT TRUE FROM information_schema.schemata WHERE schema_name = %s
1097 ',
1098 [
1099 $wpdadb->dbname,
1100 ]
1101 )
1102 ); // db call ok; no-cache ok.
1103 $wpdadb->get_results(); // phpcs:ignore Standard.Category.SniffName.ErrorCode
1104
1105 return 1 === $wpdadb->num_rows;
1106 }
1107
1108 public static function get_user_default_scheme() {
1109 $default_databases = get_option('wpda_default_database');
1110 if ( false !== $default_databases ) {
1111 $user_id = get_current_user_id();
1112 if (
1113 $user_id > 0 &&
1114 isset( $default_databases[ $user_id ] ) &&
1115 self::schema_exists( $default_databases[ $user_id ] )
1116 ) {
1117 return $default_databases[ $user_id ];
1118 }
1119 }
1120
1121 global $wpdb;
1122 return $wpdb->dbname;
1123 }
1124
1125 public static function shortcode_popup() {
1126 ?>
1127 <script type="text/javascript">
1128 jQuery(function() {
1129 var clipboard = new ClipboardJS('.wpda_shortcode_clipboard');
1130 });
1131 </script>
1132 <style type="text/css">
1133 .wpda_shortcode_content {
1134 padding: 0 20px;
1135 }
1136 .wpda_shortcode_text {
1137 text-align: center;
1138 font-size: 105%;
1139 white-space: nowrap;
1140 }
1141 .wpda_shortcode_buttons {
1142 text-align: center;
1143 }
1144 .button.wpda_shortcode_button {
1145 width: 100px !important;
1146 }
1147 .wpda_shortcode_link {
1148 text-decoration: none;
1149 font-weight: bold;
1150 }
1151 </style>
1152 <?php
1153 }
1154
1155 public static function is_editing_post() {
1156 global $pagenow;
1157 if ( $pagenow === 'post.php' || $pagenow === 'edit.php' || $pagenow === 'post-new.php' ) {
1158 // Editing post in classic editor
1159 return '';
1160 }
1161
1162 if ( isset( $_SERVER["CONTENT_TYPE"] ) && 'application/json' === $_SERVER["CONTENT_TYPE"] ) {
1163 // Editing post in Gutenberg editor
1164 return null;
1165 }
1166
1167 return false;
1168 }
1169
1170 /**
1171 * Get all available post types.
1172 *
1173 * @return array
1174 */
1175 public static function get_post_types() {
1176 global $wpdb;
1177 $rows = $wpdb->get_results(
1178 "select distinct post_type from {$wpdb->posts}",
1179 'ARRAY_N'
1180 );
1181
1182 $cpts = array();
1183 foreach ( $rows as $row ) {
1184 $cpts[] = $row[0];
1185 }
1186
1187 return $cpts;
1188 }
1189
1190 public static function get_table_engine( $schema_name, $table_name ) {
1191 $table_info = WPDA::get_table_values( $schema_name, $table_name );
1192 //phpcs:ignore - 8.1 proof
1193 return (
1194 1 === count( $table_info ) &&
1195 'connect' === strtolower( $table_info[0]['engine'] )
1196 ) ? 'connect' : '';
1197 }
1198
1199 public static function get_table_values( $schema_name, $table_name ) {
1200 $wpdadb = WPDADB::get_db_connection( $schema_name );
1201 return $wpdadb->get_results(
1202 $wpdadb->prepare(
1203 "
1204 select engine as engine,
1205 table_rows as table_rows,
1206 table_type as table_type
1207 from information_schema.tables
1208 where table_schema = %s
1209 and table_name = %s
1210 ",
1211 [
1212 $wpdadb->dbname,
1213 $table_name
1214 ]
1215 ),
1216 'ARRAY_A'
1217 );
1218 }
1219
1220 /**
1221 * Get estimated number of rows in a table
1222 *
1223 * @param $schema_name Schema name
1224 * @param $table_name Table name
1225 * @param $wpda_table_settings Table settings (query WPDA_Table_Settings_Model)
1226 *
1227 * @return int row count estimate or -1 if no estimate available
1228 */
1229 public static function get_row_count_estimate( $schema_name, $table_name, $wpda_table_settings ) {
1230 $row_count = null;
1231 $is_estimate = null;
1232 $do_real_count = null;
1233 $table_info = self::get_table_values( $schema_name, $table_name );
1234 if ( 1 === count( $table_info ) ) {//phpcs:ignore - 8.1 proof
1235 $row_count = $table_info[0]['table_rows'];
1236
1237 $system_row_count_estimate = null;
1238 $row_count_estimate_value = null;
1239 $row_count_estimate_value_hard = null;
1240
1241 if ( isset( $wpda_table_settings->table_settings->row_count_estimate ) ) {
1242 $system_row_count_estimate = $wpda_table_settings->table_settings->row_count_estimate;
1243 }
1244
1245 if ( isset( $wpda_table_settings->table_settings->row_count_estimate_value ) ) {
1246 $row_count_estimate_value = $wpda_table_settings->table_settings->row_count_estimate_value;
1247 }
1248 if ( isset( $wpda_table_settings->table_settings->row_count_estimate_value_hard ) ) {
1249 $row_count_estimate_value_hard = $wpda_table_settings->table_settings->row_count_estimate_value_hard;
1250 }
1251
1252 if (
1253 'federated' === strtolower( (string) $table_info[0]['engine'] ) ||
1254 'innodb' === strtolower( (string) $table_info[0]['engine'] ) ||
1255 stripos( strtolower( (string) $table_info[0]['table_type'] ), 'view' ) !== false
1256 ) {
1257 // Handle InnoDB tables, views and system views
1258 $wpdadb = WPDADB::get_db_connection( $schema_name );
1259 $view_explain = $wpdadb->get_results(
1260 'explain select count(*) from `' . str_replace( '`', '', $table_name ) . '`',
1261 'ARRAY_A'
1262 );
1263
1264 if ( isset( $view_explain[0]['rows'] ) ) {
1265 $row_count = $view_explain[0]['rows'];
1266 } elseif ( isset( $view_explain[0]['ROWS'] ) ) {
1267 $row_count = $view_explain[0]['ROWS'];
1268 }
1269
1270 if ( true === $system_row_count_estimate ) {
1271 if ( 'hard' === $row_count_estimate_value && null !== $row_count_estimate_value_hard ) {
1272 $row_count = $row_count_estimate_value_hard;
1273 }
1274 $is_estimate = true;
1275 $do_real_count = false;
1276 } elseif ( false === $system_row_count_estimate ) {
1277 $is_estimate = false;
1278 $do_real_count = true;
1279 } else {
1280 if ( intval( $row_count ) > intval( WPDA::get_option( WPDA::OPTION_BE_INNODB_COUNT ) ) ) {
1281 $is_estimate = true;
1282 $do_real_count = false;
1283 } else {
1284 $is_estimate = false;
1285 $do_real_count = true;
1286 }
1287 }
1288 } elseif ( 'connect' === strtolower( $table_info[0]['engine'] ) ) {
1289 $getpk = WPDA_List_Columns_Cache::get_list_columns( $schema_name, $table_name );
1290 $pk = $getpk->get_table_primary_key();
1291 if ( is_array( $pk ) && count( $pk ) > 0 ) {//phpcs:ignore - 8.1 proof
1292 $sql_rowcount_sql =
1293 'select count(*) from `' . str_replace( '`', '', $table_name ) .
1294 '` where `' . $pk[0] . '` not in ' .
1295 '(select null from `' . str_replace( '`', '', $table_name ) . '` where 1=2)';
1296 $wpdadb = WPDADB::get_db_connection( $schema_name );
1297 $sql_rowcount = $wpdadb->get_results( $sql_rowcount_sql, 'ARRAY_N' );
1298 if ( count( $sql_rowcount ) === 1 ) {//phpcs:ignore - 8.1 proof
1299 $row_count = $sql_rowcount[0][0];
1300 $is_estimate = false;
1301 $do_real_count = false;
1302 } else {
1303 $is_estimate = false;
1304 $do_real_count = true;
1305 }
1306 } else {
1307 $is_estimate = false;
1308 $do_real_count = true;
1309 }
1310 } else {
1311 // Handle other table types (MyISAM and NDB)
1312 $is_estimate = false;
1313 $do_real_count = false;
1314 }
1315 }
1316
1317 return array(
1318 'row_count' => $row_count,
1319 'is_estimate' => $is_estimate,
1320 'do_real_count' => $do_real_count
1321 );
1322 }
1323
1324 public static function validate_names( $schema_name, $table_name, $column_names = null ) {
1325 if ( ! self::validate_name( $table_name ) ) {
1326 return self::validate_name_failed();
1327 }
1328
1329 if ( 'rdb:' === substr( $schema_name, 0, 4 ) ) {
1330 $wpdadb = WPDADB::get_db_connection( $schema_name );
1331 if ( $wpdadb === null ) {
1332 return self::validate_name_np();
1333 } else {
1334 if ( ! self::validate_name( $wpdadb->dbname ) ) {
1335 return self::validate_name_failed();
1336 }
1337 }
1338 } else {
1339 // Added prefix to schema validation: allowing database names to start with a number
1340 if ( ! self::validate_name( 'prefix_' . $schema_name ) ) {
1341 return self::validate_name_failed();
1342 }
1343 }
1344
1345 if ( null === $column_names ) {
1346 $columns = WPDA_Dictionary_Lists::get_table_columns( $table_name, $schema_name );
1347 $column_names = array_column( $columns, 'column_name' ); //phpcs:ignore - 8.1 proof
1348 }
1349 if ( count( $column_names ) === 0 ) {//phpcs:ignore - 8.1 proof
1350 return self::validate_name_np();
1351 }
1352
1353 $is_valid = true;
1354 foreach ( $column_names as $column_name ) {
1355 if ( ! self::validate_name( $column_name ) ) {
1356 $is_valid = false;
1357 break;
1358 }
1359 }
1360
1361 return $is_valid ? '' : self::validate_name_failed();
1362 }
1363
1364 public static function validate_name( $item_name ) {
1365 if (
1366 ! ctype_alpha( substr( $item_name, 0, 1 ) ) ||
1367 ! preg_match("/^[_a-zA-Z0-9]+$/", $item_name)
1368 ){
1369 return false;
1370 }
1371
1372 return true;
1373 }
1374
1375 public static function validate_name_failed() {
1376 $title = __( 'Schema, table or column name(s) restricting plugin features (click to read more and fix)', 'wp-data-access' );
1377 $warning = "
1378 <a href='https://wpdataaccess.com/docs/data-explorer/naming-conventions/' target='_blank' style='text-decoration:none'>
1379 <span class='dashicons dashicons-flag wpda_tooltip' style='color:red;padding-left:5px' title='$title'></span>
1380 </a>";
1381 return $warning;
1382 }
1383
1384 public static function validate_name_np() {
1385 $title = __( 'Schema, table or column name validation not possible (click to read more and fix)', 'wp-data-access' );
1386 $warning = "
1387 <a href='https://wpdataaccess.com/docs/data-explorer/naming-conventions/' target='_blank'>
1388 <span class='dashicons dashicons-warning wpda_tooltip' style='padding-left:5px' title='$title'></span>
1389 </a>";
1390 return $warning;
1391 }
1392
1393 public static function get_dbms_var( $var_name = null, $schema_name = null ) {
1394 if ( null === $schema_name ) {
1395 global $wpdb;
1396 $wpdadb = $wpdb;
1397 } else {
1398 $wpdadb = WPDADB::get_db_connection( $schema_name );
1399 }
1400
1401 if ( null === $wpdadb ) {
1402 return null;
1403 }
1404
1405 if ( null !== $var_name ) {
1406 $var_value = $wpdadb->get_results( "SHOW VARIABLES LIKE '$var_name'", 'ARRAY_N' );
1407 if ( is_array( $var_value ) && isset( $var_value[0][1] ) ) {
1408 return $var_value[0][1];
1409 } else {
1410 return null;
1411 }
1412 } else {
1413 return $wpdadb->get_results( 'SHOW VARIABLES', 'ARRAY_N' );
1414 }
1415 }
1416
1417 public static function get_dbms_global( $global_name = null, $schema_name = null ) {
1418 if ( null === $schema_name ) {
1419 global $wpdb;
1420 $wpdadb = $wpdb;
1421 } else {
1422 $wpdadb = WPDADB::get_db_connection( $schema_name );
1423 }
1424
1425 if ( null === $wpdadb ) {
1426 return null;
1427 }
1428
1429 if ( null !== $global_name ) {
1430 $global_value = $wpdadb->get_results( "SHOW GLOBAL STATUS LIKE '$global_name'", 'ARRAY_N' );
1431 if ( is_array( $global_value ) && isset( $global_value[0][1] ) ) {
1432 return $global_value[0][1];
1433 } else {
1434 return null;
1435 }
1436 } else {
1437 return $wpdadb->get_results( 'SHOW GLOBAL STATUS', 'ARRAY_N' );
1438 }
1439 }
1440
1441 public static function secondsToTime( $seconds ) {
1442 $dtF = new \DateTime('@0');
1443 $dtT = new \DateTime("@$seconds");
1444 return $dtF->diff( $dtT )->format('%a days, %h hours, %i minutes and %s seconds');
1445 }
1446
1447 public static function get_sonce_token() {
1448 $encrypt_method = 'AES-256-CBC';
1449 $key = hash( 'sha256', WPDA::get_option( WPDA::OPTION_PLUGIN_SECRET_KEY ) );
1450 $iv = substr( hash( 'sha256', WPDA::get_option( WPDA::OPTION_PLUGIN_SECRET_IV ) ), 0, 16 );
1451
1452 return base64_encode( openssl_encrypt( WPDA::get_option( WPDA::OPTION_PLUGIN_SONCE_SEED ) . $_SERVER['REMOTE_ADDR'], $encrypt_method, $key, 0, $iv ) );
1453 }
1454
1455 public static function wpda_create_sonce( $action = 'undefined' ) {
1456 $token = self::get_sonce_token();
1457 $i = wp_nonce_tick();
1458
1459 return substr( wp_hash( $i . '|' . $action . '|' . $token, 'nonce' ), -12, 10 );
1460 }
1461
1462 public static function wpda_verify_sonce( $sonce, $action = 'undefined' ) {
1463 $expected = WPDA::wpda_create_sonce($action);
1464
1465 return hash_equals( $expected , $sonce );
1466 }
1467
1468 public static function get_plugin_upload_dir() {
1469 $uploads = wp_upload_dir();
1470
1471 return $uploads['basedir'] . DIRECTORY_SEPARATOR . 'wp-data-access' . DIRECTORY_SEPARATOR;
1472 }
1473
1474 public static function wpda_create_content_folder() {
1475 $upload_dir = WPDA::get_plugin_upload_dir();
1476 if ( ! file_exists( $upload_dir ) ) {
1477 mkdir( $upload_dir, 0755, true );
1478
1479 $fw = fopen( $upload_dir . ".htaccess", 'w' );
1480 if ( false !== $fw ) {
1481 fwrite( $fw, "IndexIgnore *" );
1482 }
1483 fclose( $fw );
1484 }
1485 }
1486
1487 public static function wpda_delete_content_folder() {
1488 $upload_dir = WPDA::get_plugin_upload_dir();
1489 if ( file_exists( $upload_dir ) ) {
1490 $files = glob( $upload_dir . '*', GLOB_MARK );
1491 foreach ( $files as $file ) {
1492 unlink( $file );
1493 }
1494 unlink( $upload_dir . '.htaccess' );
1495
1496 rmdir( $upload_dir );
1497 }
1498 }
1499
1500 public static function sent_header( $content_type, $cors = null, $attachment = null ) {
1501 if ( null !== $cors ) {
1502 header( "Access-Control-Allow-Origin: {$cors}" );
1503 }
1504
1505 if ( null !== $attachment ) {
1506 header("Content-Disposition: attachment; filename={$attachment}");
1507 }
1508
1509 header( "Content-type: {$content_type}" );
1510 header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' );
1511 header( 'Cache-Control: post-check=0, pre-check=0', false );
1512 header( 'Pragma: no-cache' );
1513 header( 'Expires: 0' );
1514 }
1515
1516 public static function sent_msg( $status, $msg ) {
1517 echo json_encode(
1518 array(
1519 'status' => $status,
1520 'msg' => $msg,
1521 ), true
1522 ); // phpcs:ignore - 8.1 proof
1523 }
1524
1525 public static function is_post() {
1526 global $post;
1527 return 'post' === get_post_type( $post );
1528 }
1529
1530 public static function is_page() {
1531 global $post;
1532 return 'page' === get_post_type( $post );
1533 }
1534
1535 public static function can_manage() {
1536 $user_id = get_current_user_id();
1537
1538 $wpda_hide_manage_link = get_option( 'wpda_hide_manage_link' );
1539 if ( is_array( $wpda_hide_manage_link ) ) {
1540 $wpda_hide_manage_list = array_flip( $wpda_hide_manage_link ); //phpcs:ignore - 8.1 proof
1541 } else {
1542 $wpda_hide_manage_list = array();
1543 }
1544
1545 return ! isset( $wpda_hide_manage_list[ $user_id ] );
1546 }
1547
1548 /**
1549 * Remove backticks from string
1550 *
1551 * @param string $s Subject.
1552 * @return array|string|string[]
1553 */
1554 public static function remove_backticks( $s ) {
1555 return str_replace( '`', '', $s );
1556 }
1557
1558 /**
1559 * Sanitize array:
1560 * uses sanitize_text_field on all array elements
1561 * supports multidimensional arrays
1562 *
1563 * @param mixed $input Any value (initial value is supposed to be an array).
1564 * @param array $is_textarea List of textarea fields.
1565 * @return array|mixed
1566 */
1567 public static function sanitize_text_field_array( $input, $is_textarea = array() ) {
1568 return
1569 is_array( $input ) ?
1570 array_map(
1571 function( $element ) use ( $is_textarea ) { return WPDA::sanitize_text_field_array( $element, $is_textarea ); },
1572 $input
1573 ) :
1574 (
1575 is_scalar( $input ) ?
1576 ( ! isset( $is_textarea[ $input ] ) ? sanitize_text_field( wp_unslash( $input ) ) : sanitize_textarea_field( $input ) ) :
1577 $input
1578 );
1579 }
1580
1581 /**
1582 * Get server IP address
1583 *
1584 * @return mixed|string
1585 */
1586 public static function get_server_address() {
1587 if ( isset( $_SERVER['SERVER_ADDR'] ) ) {
1588 return $_SERVER['SERVER_ADDR'];
1589 } elseif ( isset( $_SERVER['LOCAL_ADDR'] ) ) {
1590 return $_SERVER['LOCAL_ADDR'];
1591 } else {
1592 return 'UNKNOWN';
1593 }
1594 }
1595
1596 public static function convert_default_value( $item_default_value ) {
1597 switch( $item_default_value ) {
1598 case '$$USERID$$':
1599 return WPDA::get_current_user_id();
1600 case '$$USER$$':
1601 return WPDA::get_current_user_login();
1602 case '$$EMAIL$$':
1603 return WPDA::get_current_user_email();
1604 case '$$NOW$$':
1605 case '$$NOWDT$$':
1606 global $wpdb;
1607 $now_db = $wpdb->get_var( 'select now()' );
1608 $db_format = WPDA::DB_DATETIME_FORMAT;
1609 $convert_date = \DateTime::createFromFormat( $db_format, $now_db );
1610 if ( false !== $convert_date ) {
1611 $date_format = WPDA::get_option( WPDA::OPTION_PLUGIN_DATE_FORMAT );
1612 $time_format = WPDA::get_option( WPDA::OPTION_PLUGIN_TIME_FORMAT );
1613 $item_format = $date_format;
1614 if ( '$$NOWDT$$' === $item_default_value ) {
1615 $item_format .= " {$time_format}";
1616 }
1617 return $convert_date->format( $item_format );
1618 } else {
1619 return '';
1620 }
1621 default:
1622 return $item_default_value;
1623 }
1624 }
1625
1626 /**
1627 * Write error to WordPress log file
1628 *
1629 * @param string $errmsg Error message
1630 * @return void
1631 */
1632 public static function wpda_log_wp_error( $errmsg ) {
1633 $dbt = debug_backtrace();
1634 $clr = array_shift( $dbt );//phpcs:ignore - 8.1 proof
1635
1636 error_log( "WP Data Access error in {$clr['file']}:{$clr['line']}" );
1637 error_log( print_r( $errmsg, true ) );
1638 }
1639
1640 /**
1641 * Get the column data types from a custom query
1642 *
1643 * @param $database Database (schema) name
1644 * @param $query Custom query
1645 * @return array|null Column data type array or null if the query does not return any columns
1646 */
1647 public static function get_columns_from_query( $database, $query ) {
1648 $wpdadb = WPDADB::get_db_connection( $database );
1649 if ( null === $wpdadb ) {
1650 return null;
1651 }
1652
1653 $wpdadb->suppress_errors( true );
1654 $table_name = 'custom' . mt_rand(0, time());
1655
1656 // Create temporary table.
1657 $wpdadb->query(
1658 "CREATE TEMPORARY TABLE `{$table_name}` AS (select * from (" . $query . ') as t limit 0)'
1659 );
1660
1661 if ( '' !== $wpdadb->last_error ) {
1662 return null;
1663 }
1664
1665 // Get query structure.
1666 $rows = $wpdadb->get_results( "desc `{$table_name}`", 'ARRAY_A' );
1667
1668 if ( '' !== $wpdadb->last_error ) {
1669 return null;
1670 }
1671
1672 return $rows;
1673 }
1674
1675 }
1676
1677 }
1678