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

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

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