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

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