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

1,712 lines 53.6 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.77' );
55 const OPTION_WPDA_CLIENT_VERSION = array( 'wpda_client_version', '1.0.75' );
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( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
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' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
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( esc_attr( '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( esc_attr__( '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 ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
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
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
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
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
1032 } elseif ( 'number' === WPDA::get_type( $column_date_type ) ) {
1033 $where_columns_arr[] = $wpdb->prepare( "`{$column_name}` = '%d'", esc_sql( $column_value ) ); // phpcs:ignore
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
1047 } elseif ( 'number' === WPDA::get_type( $column_date_type ) ) {
1048 $where_columns[] = $wpdb->prepare( "`{$column_name}` = '%d'", esc_sql( $column_value ) ); // phpcs:ignore
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'] ) && 'or' === strtolower( $_REQUEST['wpda_search_column_operator'] ) // phpcs:ignore
1061 ? 'or' : 'and';
1062 return ' (' . implode( " $operator ", $where_columns ) . ') ';
1063 }
1064 }
1065
1066 public static function schema_disabled( $schema_name ) {
1067 if ( 'rdb:' === substr( $schema_name, 0, 4) ) {
1068 $rdb = WPDADB::get_remote_database( $schema_name, true );
1069 if ( false === $rdb ) {
1070 return false;
1071 }
1072 return true === $rdb[ 'disabled' ];
1073 }
1074 return false;
1075 }
1076
1077 /**
1078 * Check if database schema exists (local and remote databases)
1079 *
1080 * @param string $schema_name Database schema
1081 *
1082 * @return bool Exists?
1083 */
1084 public static function schema_exists( $schema_name ) {
1085 if ( self::schema_disabled( $schema_name ) ) {
1086 return false;
1087 }
1088
1089 $wpdadb = WPDADB::get_db_connection( $schema_name );
1090 if ( null === $wpdadb ) {
1091 return false;
1092 }
1093 if ( method_exists( $wpdadb, 'is_connected' ) ) {
1094 if ( ! $wpdadb->is_connected() ) {
1095 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
1096 $msg = new WPDA_Message_Box(
1097 [
1098 /* translators: %s = database name */
1099 'message_text' => sprintf( __( 'Remote database "%s" not available [check connection: Settings > WP Data Access]', 'wp-data-access' ), $schema_name ),
1100 'message_type' => 'error',
1101 'message_is_dismissible' => false,
1102 ]
1103 );
1104 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
1105 $msg->box();
1106
1107 return false;
1108 }
1109 }
1110
1111 $wpdadb->query(
1112 $wpdadb->prepare( '
1113 SELECT TRUE FROM information_schema.schemata WHERE schema_name = %s
1114 ',
1115 [
1116 $wpdadb->dbname,
1117 ]
1118 )
1119 ); // db call ok; no-cache ok.
1120 $wpdadb->get_results();
1121
1122 return 1 === $wpdadb->num_rows;
1123 }
1124
1125 public static function get_user_default_scheme() {
1126 $default_databases = get_option('wpda_default_database');
1127 if ( false !== $default_databases ) {
1128 $user_id = get_current_user_id();
1129 if (
1130 $user_id > 0 &&
1131 isset( $default_databases[ $user_id ] ) &&
1132 self::schema_exists( $default_databases[ $user_id ] )
1133 ) {
1134 return $default_databases[ $user_id ];
1135 }
1136 }
1137
1138 global $wpdb;
1139 return $wpdb->dbname;
1140 }
1141
1142 public static function shortcode_popup() {
1143 ?>
1144 <script type="text/javascript">
1145 jQuery(function() {
1146 var clipboard = new ClipboardJS('.wpda_shortcode_clipboard');
1147 });
1148 </script>
1149 <style type="text/css">
1150 .wpda_shortcode_content {
1151 padding: 0 20px;
1152 }
1153 .wpda_shortcode_text {
1154 text-align: center;
1155 font-size: 105%;
1156 white-space: nowrap;
1157 }
1158 .wpda_shortcode_buttons {
1159 text-align: center;
1160 }
1161 .button.wpda_shortcode_button {
1162 width: 100px !important;
1163 }
1164 .wpda_shortcode_link {
1165 text-decoration: none;
1166 font-weight: bold;
1167 }
1168 </style>
1169 <?php
1170 }
1171
1172 public static function is_editing_post() {
1173 global $pagenow;
1174 if ( $pagenow === 'post.php' || $pagenow === 'edit.php' || $pagenow === 'post-new.php' ) {
1175 // Editing post in classic editor
1176 return '';
1177 }
1178
1179 if ( isset( $_SERVER["CONTENT_TYPE"] ) && 'application/json' === $_SERVER["CONTENT_TYPE"] ) {
1180 // Editing post in Gutenberg editor
1181 return null;
1182 }
1183
1184 return false;
1185 }
1186
1187 /**
1188 * Get all available post types.
1189 *
1190 * @return array
1191 */
1192 public static function get_post_types() {
1193 global $wpdb;
1194 $rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1195 "select distinct post_type from {$wpdb->posts}",
1196 'ARRAY_N'
1197 );
1198
1199 $cpts = array();
1200 foreach ( $rows as $row ) {
1201 $cpts[] = $row[0];
1202 }
1203
1204 return $cpts;
1205 }
1206
1207 public static function get_table_engine( $schema_name, $table_name ) {
1208 $table_info = WPDA::get_table_values( $schema_name, $table_name );
1209 // phpcs:ignore -- 8.1 proof
1210 return (
1211 1 === count( $table_info ) &&
1212 'connect' === strtolower( $table_info[0]['engine'] )
1213 ) ? 'connect' : '';
1214 }
1215
1216 public static function get_table_values( $schema_name, $table_name ) {
1217 $wpdadb = WPDADB::get_db_connection( $schema_name );
1218 return $wpdadb->get_results(
1219 $wpdadb->prepare(
1220 "
1221 select engine as engine,
1222 table_rows as table_rows,
1223 table_type as table_type
1224 from information_schema.tables
1225 where table_schema = %s
1226 and table_name = %s
1227 ",
1228 [
1229 $wpdadb->dbname,
1230 $table_name
1231 ]
1232 ),
1233 'ARRAY_A'
1234 );
1235 }
1236
1237 /**
1238 * Get estimated number of rows in a table
1239 *
1240 * @param $schema_name Schema name
1241 * @param $table_name Table name
1242 * @param $wpda_table_settings Table settings (query WPDA_Table_Settings_Model)
1243 *
1244 * @return int row count estimate or -1 if no estimate available
1245 */
1246 public static function get_row_count_estimate( $schema_name, $table_name, $wpda_table_settings ) {
1247 $row_count = null;
1248 $is_estimate = null;
1249 $do_real_count = null;
1250 $table_info = self::get_table_values( $schema_name, $table_name );
1251 if ( 1 === count( $table_info ) ) { // phpcs:ignore -- 8.1 proof
1252 $row_count = $table_info[0]['table_rows'];
1253
1254 $system_row_count_estimate = null;
1255 $row_count_estimate_value = null;
1256 $row_count_estimate_value_hard = null;
1257
1258 if ( isset( $wpda_table_settings->table_settings->row_count_estimate ) ) {
1259 $system_row_count_estimate = $wpda_table_settings->table_settings->row_count_estimate;
1260 }
1261
1262 if ( isset( $wpda_table_settings->table_settings->row_count_estimate_value ) ) {
1263 $row_count_estimate_value = $wpda_table_settings->table_settings->row_count_estimate_value;
1264 }
1265 if ( isset( $wpda_table_settings->table_settings->row_count_estimate_value_hard ) ) {
1266 $row_count_estimate_value_hard = $wpda_table_settings->table_settings->row_count_estimate_value_hard;
1267 }
1268
1269 if (
1270 'federated' === strtolower( (string) $table_info[0]['engine'] ) ||
1271 'innodb' === strtolower( (string) $table_info[0]['engine'] ) ||
1272 stripos( strtolower( (string) $table_info[0]['table_type'] ), 'view' ) !== false
1273 ) {
1274 // Handle InnoDB tables, views and system views
1275 $wpdadb = WPDADB::get_db_connection( $schema_name );
1276 $view_explain = $wpdadb->get_results(
1277 'explain select count(*) from `' . str_replace( '`', '', $table_name ) . '`',
1278 'ARRAY_A'
1279 );
1280
1281 if ( isset( $view_explain[0]['rows'] ) ) {
1282 $row_count = $view_explain[0]['rows'];
1283 } elseif ( isset( $view_explain[0]['ROWS'] ) ) {
1284 $row_count = $view_explain[0]['ROWS'];
1285 }
1286
1287 if ( true === $system_row_count_estimate ) {
1288 if ( 'hard' === $row_count_estimate_value && null !== $row_count_estimate_value_hard ) {
1289 $row_count = $row_count_estimate_value_hard;
1290 }
1291 $is_estimate = true;
1292 $do_real_count = false;
1293 } elseif ( false === $system_row_count_estimate ) {
1294 $is_estimate = false;
1295 $do_real_count = true;
1296 } else {
1297 if ( intval( $row_count ) > intval( WPDA::get_option( WPDA::OPTION_BE_INNODB_COUNT ) ) ) {
1298 $is_estimate = true;
1299 $do_real_count = false;
1300 } else {
1301 $is_estimate = false;
1302 $do_real_count = true;
1303 }
1304 }
1305 } elseif ( 'connect' === strtolower( $table_info[0]['engine'] ) ) {
1306 $getpk = WPDA_List_Columns_Cache::get_list_columns( $schema_name, $table_name );
1307 $pk = $getpk->get_table_primary_key();
1308 if ( is_array( $pk ) && count( $pk ) > 0 ) { // phpcs:ignore -- 8.1 proof
1309 $sql_rowcount_sql =
1310 'select count(*) from `' . str_replace( '`', '', $table_name ) .
1311 '` where `' . $pk[0] . '` not in ' .
1312 '(select null from `' . str_replace( '`', '', $table_name ) . '` where 1=2)';
1313 $wpdadb = WPDADB::get_db_connection( $schema_name );
1314 $sql_rowcount = $wpdadb->get_results( $sql_rowcount_sql, 'ARRAY_N' );
1315 if ( count( $sql_rowcount ) === 1 ) { // phpcs:ignore -- 8.1 proof
1316 $row_count = $sql_rowcount[0][0];
1317 $is_estimate = false;
1318 $do_real_count = false;
1319 } else {
1320 $is_estimate = false;
1321 $do_real_count = true;
1322 }
1323 } else {
1324 $is_estimate = false;
1325 $do_real_count = true;
1326 }
1327 } else {
1328 // Handle other table types (MyISAM and NDB)
1329 $is_estimate = false;
1330 $do_real_count = false;
1331 }
1332 }
1333
1334 return array(
1335 'row_count' => $row_count,
1336 'is_estimate' => $is_estimate,
1337 'do_real_count' => $do_real_count
1338 );
1339 }
1340
1341 public static function validate_names( $schema_name, $table_name, $column_names = null ) {
1342 if ( ! self::validate_name( $table_name ) ) {
1343 return self::validate_name_failed();
1344 }
1345
1346 if ( 'rdb:' === substr( $schema_name, 0, 4 ) ) {
1347 $wpdadb = WPDADB::get_db_connection( $schema_name );
1348 if ( $wpdadb === null ) {
1349 return self::validate_name_np();
1350 } else {
1351 if ( ! self::validate_name( $wpdadb->dbname ) ) {
1352 return self::validate_name_failed();
1353 }
1354 }
1355 } else {
1356 // Added prefix to schema validation: allowing database names to start with a number
1357 if ( ! self::validate_name( 'prefix_' . $schema_name ) ) {
1358 return self::validate_name_failed();
1359 }
1360 }
1361
1362 if ( null === $column_names ) {
1363 $columns = WPDA_Dictionary_Lists::get_table_columns( $table_name, $schema_name );
1364 $column_names = array_column( $columns, 'column_name' ); // phpcs:ignore -- 8.1 proof
1365 }
1366 if ( count( $column_names ) === 0 ) { // phpcs:ignore -- 8.1 proof
1367 return self::validate_name_np();
1368 }
1369
1370 $is_valid = true;
1371 foreach ( $column_names as $column_name ) {
1372 if ( ! self::validate_name( $column_name ) ) {
1373 $is_valid = false;
1374 break;
1375 }
1376 }
1377
1378 return $is_valid ? '' : self::validate_name_failed();
1379 }
1380
1381 public static function validate_name( $item_name ) {
1382 if (
1383 ! ctype_alpha( substr( $item_name, 0, 1 ) ) ||
1384 ! preg_match("/^[_a-zA-Z0-9]+$/", $item_name)
1385 ){
1386 return false;
1387 }
1388
1389 return true;
1390 }
1391
1392 public static function validate_name_failed() {
1393 $title = __( 'Schema, table or column name(s) restricting plugin features (click to read more and fix)', 'wp-data-access' );
1394 $warning = "
1395 <a href='https://docs.legacy.wpdataaccess.com/docs/naming-conventions/' target='_blank' style='text-decoration:none'>
1396 <span class='dashicons dashicons-flag wpda_tooltip' style='color:red;padding-left:5px' title='$title'></span>
1397 </a>";
1398 return $warning;
1399 }
1400
1401 public static function validate_name_np() {
1402 $title = __( 'Schema, table or column name validation not possible (click to read more and fix)', 'wp-data-access' );
1403 $warning = "
1404 <a href='https://docs.legacy.wpdataaccess.com/docs/naming-conventions/' target='_blank'>
1405 <span class='dashicons dashicons-warning wpda_tooltip' style='padding-left:5px' title='$title'></span>
1406 </a>";
1407 return $warning;
1408 }
1409
1410 public static function get_dbms_var( $var_name = null, $schema_name = null ) {
1411 if ( null === $schema_name ) {
1412 global $wpdb;
1413 $wpdadb = $wpdb;
1414 } else {
1415 $wpdadb = WPDADB::get_db_connection( $schema_name );
1416 }
1417
1418 if ( null === $wpdadb ) {
1419 return null;
1420 }
1421
1422 if ( null !== $var_name ) {
1423 $var_value = $wpdadb->get_results( "SHOW VARIABLES LIKE '$var_name'", 'ARRAY_N' );
1424 if ( is_array( $var_value ) && isset( $var_value[0][1] ) ) {
1425 return $var_value[0][1];
1426 } else {
1427 return null;
1428 }
1429 } else {
1430 return $wpdadb->get_results( 'SHOW VARIABLES', 'ARRAY_N' );
1431 }
1432 }
1433
1434 public static function get_dbms_global( $global_name = null, $schema_name = null ) {
1435 if ( null === $schema_name ) {
1436 global $wpdb;
1437 $wpdadb = $wpdb;
1438 } else {
1439 $wpdadb = WPDADB::get_db_connection( $schema_name );
1440 }
1441
1442 if ( null === $wpdadb ) {
1443 return null;
1444 }
1445
1446 if ( null !== $global_name ) {
1447 $global_value = $wpdadb->get_results( "SHOW GLOBAL STATUS LIKE '$global_name'", 'ARRAY_N' );
1448 if ( is_array( $global_value ) && isset( $global_value[0][1] ) ) {
1449 return $global_value[0][1];
1450 } else {
1451 return null;
1452 }
1453 } else {
1454 return $wpdadb->get_results( 'SHOW GLOBAL STATUS', 'ARRAY_N' );
1455 }
1456 }
1457
1458 public static function secondsToTime( $seconds ) {
1459 $dtF = new \DateTime('@0');
1460 $dtT = new \DateTime("@$seconds");
1461 return $dtF->diff( $dtT )->format('%a days, %h hours, %i minutes and %s seconds');
1462 }
1463
1464 public static function get_sonce_token() {
1465 $encrypt_method = 'AES-256-CBC';
1466 $key = hash( 'sha256', WPDA::get_option( WPDA::OPTION_PLUGIN_SECRET_KEY ) );
1467 $iv = substr( hash( 'sha256', WPDA::get_option( WPDA::OPTION_PLUGIN_SECRET_IV ) ), 0, 16 );
1468
1469 return base64_encode(
1470 openssl_encrypt( WPDA::get_option(
1471 WPDA::OPTION_PLUGIN_SONCE_SEED ) .
1472 (
1473 isset( $_SERVER['REMOTE_ADDR'] )
1474 ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) )
1475 : ''
1476 ),
1477 $encrypt_method,
1478 $key,
1479 0,
1480 $iv
1481 )
1482 );
1483 }
1484
1485 public static function wpda_create_sonce( $action = 'undefined' ) {
1486 $token = self::get_sonce_token();
1487 $i = wp_nonce_tick();
1488
1489 return substr( wp_hash( $i . '|' . $action . '|' . $token, 'nonce' ), -12, 10 );
1490 }
1491
1492 public static function wpda_verify_sonce( $sonce, $action = 'undefined' ) {
1493 $expected = WPDA::wpda_create_sonce($action);
1494
1495 return hash_equals( $expected , $sonce );
1496 }
1497
1498 public static function get_plugin_upload_dir() {
1499 $uploads = wp_upload_dir();
1500
1501 return $uploads['basedir'] . DIRECTORY_SEPARATOR . 'wp-data-access' . DIRECTORY_SEPARATOR;
1502 }
1503
1504 public static function wpda_create_content_folder() {
1505 $upload_dir = WPDA::get_plugin_upload_dir();
1506 if ( ! file_exists( $upload_dir ) ) {
1507 mkdir( $upload_dir, 0755, true ); // phpcs:ignore
1508 $fw = fopen( $upload_dir . ".htaccess", 'w' ); // phpcs:ignore
1509 if ( false !== $fw ) {
1510 fwrite( $fw, "IndexIgnore *" ); // phpcs:ignore
1511 }
1512 fclose( $fw ); // phpcs:ignore
1513 }
1514 }
1515
1516 public static function wpda_delete_content_folder() {
1517 $upload_dir = WPDA::get_plugin_upload_dir();
1518 if ( file_exists( $upload_dir ) ) {
1519 $files = glob( $upload_dir . '*', GLOB_MARK );
1520 foreach ( $files as $file ) {
1521 unlink( $file ); // phpcs:ignore
1522 }
1523 unlink( $upload_dir . '.htaccess' ); // phpcs:ignore
1524 rmdir( $upload_dir ); // phpcs:ignore
1525 }
1526 }
1527
1528 public static function sent_header( $content_type, $cors = null, $attachment = null ) {
1529 if ( null !== $cors ) {
1530 header( "Access-Control-Allow-Origin: {$cors}" );
1531 }
1532
1533 if ( null !== $attachment ) {
1534 header("Content-Disposition: attachment; filename={$attachment}");
1535 }
1536
1537 header( "Content-type: {$content_type}" );
1538 header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' );
1539 header( 'Cache-Control: post-check=0, pre-check=0', false );
1540 header( 'Pragma: no-cache' );
1541 header( 'Expires: 0' );
1542 }
1543
1544 public static function sent_msg( $status, $msg ) {
1545 echo json_encode(
1546 array(
1547 'status' => esc_attr( $status ),
1548 'msg' => $msg, // phpcs:ignore
1549 ), true
1550 ); // phpcs:ignore
1551 }
1552
1553 public static function is_post() {
1554 global $post;
1555 return 'post' === get_post_type( $post );
1556 }
1557
1558 public static function is_page() {
1559 global $post;
1560 return 'page' === get_post_type( $post );
1561 }
1562
1563 public static function can_manage() {
1564 $user_id = get_current_user_id();
1565
1566 $wpda_hide_manage_link = get_option( 'wpda_hide_manage_link' );
1567 if ( is_array( $wpda_hide_manage_link ) ) {
1568 $wpda_hide_manage_list = array_flip( $wpda_hide_manage_link ); // phpcs:ignore -- 8.1 proof
1569 } else {
1570 $wpda_hide_manage_list = array();
1571 }
1572
1573 return ! isset( $wpda_hide_manage_list[ $user_id ] );
1574 }
1575
1576 /**
1577 * Remove backticks from string
1578 *
1579 * @param string $s Subject.
1580 * @return array|string|string[]
1581 */
1582 public static function remove_backticks( $s ) {
1583 return str_replace( '`', '', $s );
1584 }
1585
1586 /**
1587 * Sanitize array:
1588 * uses sanitize_text_field on all array elements
1589 * supports multidimensional arrays
1590 *
1591 * @param mixed $input Any value (initial value is supposed to be an array).
1592 * @param array $is_textarea List of textarea fields.
1593 * @return array|mixed
1594 */
1595 public static function sanitize_text_field_array( $input, $is_textarea = array() ) {
1596 return
1597 is_array( $input ) ?
1598 array_map(
1599 function( $element ) use ( $is_textarea ) { return WPDA::sanitize_text_field_array( $element, $is_textarea ); },
1600 $input
1601 ) :
1602 (
1603 is_scalar( $input ) ?
1604 ( ! isset( $is_textarea[ $input ] ) ? sanitize_text_field( wp_unslash( $input ) ) : sanitize_textarea_field( $input ) ) :
1605 $input
1606 );
1607 }
1608
1609 /**
1610 * Get server IP address
1611 *
1612 * @return mixed|string
1613 */
1614 public static function get_server_address() {
1615 if ( isset( $_SERVER['SERVER_ADDR'] ) ) {
1616 return sanitize_text_field( wp_unslash( $_SERVER['SERVER_ADDR'] ) );
1617 } elseif ( isset( $_SERVER['LOCAL_ADDR'] ) ) {
1618 return sanitize_text_field( wp_unslash( $_SERVER['LOCAL_ADDR'] ) );
1619 } else {
1620 return 'UNKNOWN';
1621 }
1622 }
1623
1624 public static function convert_default_value( $item_default_value ) {
1625 switch( $item_default_value ) {
1626 case '$$USERID$$':
1627 return WPDA::get_current_user_id();
1628 case '$$USER$$':
1629 return WPDA::get_current_user_login();
1630 case '$$EMAIL$$':
1631 return WPDA::get_current_user_email();
1632 case '$$NOW$$':
1633 case '$$NOWDT$$':
1634 global $wpdb;
1635 $now_db = $wpdb->get_var( 'select now()' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1636 $db_format = WPDA::DB_DATETIME_FORMAT;
1637 $convert_date = \DateTime::createFromFormat( $db_format, $now_db );
1638 if ( false !== $convert_date ) {
1639 $date_format = WPDA::get_option( WPDA::OPTION_PLUGIN_DATE_FORMAT );
1640 $time_format = WPDA::get_option( WPDA::OPTION_PLUGIN_TIME_FORMAT );
1641 $item_format = $date_format;
1642 if ( '$$NOWDT$$' === $item_default_value ) {
1643 $item_format .= " {$time_format}";
1644 }
1645 return $convert_date->format( $item_format );
1646 } else {
1647 return '';
1648 }
1649 default:
1650 return $item_default_value;
1651 }
1652 }
1653
1654 /**
1655 * Write error to WordPress log file
1656 *
1657 * @param string $errmsg Error message
1658 * @return void
1659 */
1660 public static function wpda_log_wp_error( $errmsg ) {
1661 $dbt = debug_backtrace(); // phpcs:ignore
1662 $clr = array_shift( $dbt ); // phpcs:ignore -- 8.1 proof
1663
1664 // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r
1665 error_log( "WP Data Access error in {$clr['file']}:{$clr['line']}" );
1666 error_log( print_r( $errmsg, true ) );
1667 // phpcs:enable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r
1668 }
1669
1670 /**
1671 * Get the column data types from a custom query
1672 *
1673 * @param $database Database (schema) name
1674 * @param $query Custom query
1675 * @return array|null Column data type array or null if the query does not return any columns
1676 */
1677 public static function get_columns_from_query( $database, $query ) {
1678 $wpdadb = WPDADB::get_db_connection( $database );
1679 if ( null === $wpdadb ) {
1680 return null;
1681 }
1682
1683 $wpdadb->suppress_errors( true );
1684 $table_name = 'custom' . wp_rand(0, time());
1685
1686 // Create temporary table.
1687 $wpdadb->query(
1688 "CREATE TEMPORARY TABLE `{$table_name}` AS (select * from (" . $query . ') as t limit 0)'
1689 );
1690
1691 if ( '' !== $wpdadb->last_error ) {
1692 return null;
1693 }
1694
1695 // Get query structure.
1696 $rows = $wpdadb->get_results( "desc `{$table_name}`", 'ARRAY_A' );
1697
1698 if ( '' !== $wpdadb->last_error ) {
1699 return null;
1700 }
1701
1702 return $rows;
1703 }
1704
1705 public static function current_user_is_admin() {
1706 return current_user_can( 'manage_options' ) || is_super_admin();
1707 }
1708
1709 }
1710
1711 }
1712