PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.40
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.40
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.40, at WPDataAccess/WPDA.php

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