class-redux-helpers.php (66804B)
1 <?php 2 /** 3 * Redux Helper Class 4 * 5 * @class Redux_Helpers 6 * @version 3.0.0 7 * @package Redux Framework/Classes 8 */ 9 10 defined( 'ABSPATH' ) || exit; 11 12 // Don't duplicate me! 13 if ( ! class_exists( 'Redux_Helpers', false ) ) { 14 15 /** 16 * Redux Helpers Class 17 * A Class of useful functions that can/should be shared among all Redux files. 18 * 19 * @since 3.0.0 20 */ 21 class Redux_Helpers { 22 23 /** 24 * Resuable supported unit array. 25 * 26 * @var array 27 */ 28 public static $array_units = array( '', '%', 'in', 'cm', 'mm', 'em', 'rem', 'ex', 'pt', 'pc', 'px', 'vh', 'vw', 'vmin', 'vmax', 'ch' ); 29 30 /** 31 * Retrieve section array from field ID. 32 * 33 * @param string $opt_name Panel opt_name. 34 * @param string $field_id Field ID. 35 */ 36 public static function section_from_field_id( string $opt_name = '', string $field_id = '' ) { 37 if ( '' !== $opt_name ) { 38 $redux = Redux::instance( $opt_name ); 39 40 if ( is_object( $redux ) ) { 41 $sections = $redux->sections; 42 43 if ( is_array( $sections ) && ! empty( $sections ) ) { 44 foreach ( $sections as $idx => $section ) { 45 if ( isset( $section['fields'] ) && ! empty( $section['fields'] ) ) { 46 foreach ( $section['fields'] as $i => $field ) { 47 if ( is_array( $field ) && ! empty( $field ) ) { 48 if ( isset( $field['id'] ) && $field['id'] === $field_id ) { 49 return $section; 50 } 51 } 52 } 53 } 54 } 55 } 56 } 57 } 58 } 59 60 /** 61 * Verify integer value. 62 * 63 * @param mixed $val Value to test. 64 * 65 * @return bool|false|int 66 */ 67 public static function is_integer( $val ) { 68 if ( ! is_scalar( $val ) || is_bool( $val ) ) { 69 return false; 70 } 71 72 return is_float( $val ) ? false : preg_match( '~^((?:\+|-)?[0-9]+)$~', $val ); 73 } 74 75 /** 76 * Deprecated. Gets panel tab number from specified field. 77 * 78 * @param object $parent ReduxFramework object. 79 * @param array|string $field Field array. 80 * 81 * @return int|string 82 * @deprecated No longer using camelCase naming convention. 83 */ 84 public static function tabFromField( $parent, $field ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 85 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0', 'Redux_Helpers::tab_from_field( $parent, $field )' ); 86 87 return self::tab_from_field( $parent, $field ); 88 } 89 90 /** 91 * Gets panel tab number from specified field. 92 * 93 * @param object $parent ReduxFramework object. 94 * @param array|string $field Field array. 95 * 96 * @return int|string 97 */ 98 public static function tab_from_field( $parent, $field ) { 99 foreach ( $parent->sections as $k => $section ) { 100 if ( ! isset( $section['title'] ) ) { 101 continue; 102 } 103 104 if ( isset( $section['fields'] ) && ! empty( $section['fields'] ) ) { 105 if ( self::recursive_array_search( $field, $section['fields'] ) ) { 106 return $k; 107 } 108 } 109 } 110 } 111 112 /** 113 * Deprecated. Verifies if specified field type is in use. 114 * 115 * @param array $fields Field arrays. 116 * @param array $field Field array. 117 * 118 * @return bool 119 * @deprecated No longer using camelCase naming convention. 120 */ 121 public static function isFieldInUseByType( array $fields, array $field = array() ): bool { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 122 // phpcs:ignore Squiz.PHP.CommentedOutCode 123 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0', 'Redux_Helpers::tab_from_field( $parent, $field )' ); 124 return self::is_field_in_use_by_type( $fields, $field ); 125 } 126 127 /** 128 * Verifies if specified field type is in use. 129 * 130 * @param array $fields Field arrays. 131 * @param array $field Field arrays to check. 132 * 133 * @return bool 134 */ 135 public static function is_field_in_use_by_type( array $fields, array $field = array() ): bool { 136 foreach ( $field as $name ) { 137 if ( array_key_exists( $name, $fields ) ) { 138 return true; 139 } 140 } 141 142 return false; 143 } 144 145 /** 146 * Deprecated Verifies if field is in use. 147 * 148 * @param object $parent ReduxFramework object. 149 * @param string $field Field type. 150 * 151 * @return bool 152 * @deprecated No longer using camelCase function names. 153 */ 154 public static function isFieldInUse( $parent, string $field ): bool { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 155 // phpcs:ignore Squiz.PHP.CommentedOutCode 156 // _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0', 'Redux_Helpers::is_field_in_use( $parent, $field )' ); 157 return self::is_field_in_use( $parent, $field ); 158 } 159 160 /** 161 * Verifies if field is in use. 162 * 163 * @param object $parent ReduxFramework object. 164 * @param string $field Field type. 165 * 166 * @return bool 167 */ 168 public static function is_field_in_use( $parent, string $field ): bool { 169 if ( empty( $parent->sections ) ) { 170 return false; 171 } 172 173 foreach ( $parent->sections as $k => $section ) { 174 if ( ! isset( $section['title'] ) ) { 175 continue; 176 } 177 178 if ( isset( $section['fields'] ) && ! empty( $section['fields'] ) ) { 179 if ( self::recursive_array_search( $field, $section['fields'] ) ) { 180 return true; 181 } 182 } 183 } 184 185 return false; 186 } 187 188 /** 189 * Returns major version from version number. 190 * 191 * @param string $v Version number. 192 * 193 * @return string 194 */ 195 public static function major_version( string $v ): string { 196 $version = explode( '.', $v ); 197 if ( count( $version ) > 1 ) { 198 return $version[0] . '.' . $version[1]; 199 } else { 200 return $v; 201 } 202 } 203 204 205 /** 206 * Deprecated. Checks for localhost environment. 207 * 208 * @return bool 209 * @deprecated No longer using camelCase naming convention. 210 * @since 4.0 211 */ 212 public static function isLocalHost(): bool { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 213 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0', 'Redux_Helpers::is_local_host()' ); 214 215 return self::is_local_host(); 216 } 217 218 /** 219 * Checks for localhost environment. 220 * 221 * @return bool 222 */ 223 public static function is_local_host(): bool { 224 $is_local = false; 225 226 $domains_to_check = array_unique( 227 array( 228 'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ), 229 'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ), 230 ) 231 ); 232 233 $forbidden_domains = array( 234 'wordpress.com', 235 'localhost', 236 'localhost.localdomain', 237 '127.0.0.1', 238 '::1', 239 'local.wordpress.test', // VVV pattern. 240 'local.wordpress-trunk.test', // VVV pattern. 241 'src.wordpress-develop.test', // VVV pattern. 242 'build.wordpress-develop.test', // VVV pattern. 243 ); 244 245 foreach ( $domains_to_check as $domain ) { 246 // If it's empty, just fail out. 247 if ( ! $domain ) { 248 $is_local = true; 249 break; 250 } 251 252 // None of the explicit localhosts. 253 if ( in_array( $domain, $forbidden_domains, true ) ) { 254 $is_local = true; 255 break; 256 } 257 258 // No .test or .local domains. 259 if ( preg_match( '#\.(test|local)$#i', $domain ) ) { 260 $is_local = true; 261 break; 262 } 263 } 264 265 return $is_local; 266 } 267 268 /** 269 * Deprecated. Checks if WP_DEBUG is enabled. 270 * 271 * @return bool::is_wp_debug() 272 * @deprecated No longer using camelCase naming convention. 273 * @since 4.0 274 */ 275 public static function isWpDebug(): bool { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 276 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0', 'Redux_Functions_Ex::is_wp_debug()' ); 277 278 return self::is_wp_debug(); 279 } 280 281 /** 282 * Checks if WP_DEBUG is enabled. 283 * 284 * @return bool 285 */ 286 public static function is_wp_debug(): bool { 287 return ( defined( 'WP_DEBUG' ) && true === WP_DEBUG ); 288 } 289 290 /** 291 * Deprecated. Return tracking object. 292 * 293 * @return array::get_statistics_object() 294 * @deprecated No longer using camelCase naming convention. 295 */ 296 public static function getTrackingObject(): array { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 297 // phpcs:ignore: Squiz.PHP.CommentedOutCode 298 /* _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::get_statistics_object()' ); */ 299 300 return self::get_statistics_object(); 301 } 302 303 /** 304 * Deprecated. Return tracking object. 305 * 306 * @return array::get_statistics_object() 307 * @deprecated No longer using camelCase naming convention. 308 */ 309 public static function trackingObject(): array { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 310 // phpcs:ignore Squiz.PHP.CommentedOutCode 311 /* _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::get_statistics_object()' ); */ 312 313 return self::get_statistics_object(); 314 } 315 316 /** 317 * Return tracking object. 318 * 319 * @return array 320 */ 321 public static function get_statistics_object(): array { 322 $hash = self::get_hash(); 323 324 global $blog_id, $wpdb; 325 $pts = array(); 326 327 foreach ( get_post_types( array( 'public' => true ) ) as $pt ) { 328 $count = wp_count_posts( $pt ); 329 $pts[ $pt ] = $count->publish; 330 } 331 332 $comments_count = wp_count_comments(); 333 334 if ( ! function_exists( 'get_plugin_data' ) ) { 335 if ( file_exists( ABSPATH . 'wp-admin/includes/plugin.php' ) ) { 336 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 337 } 338 if ( file_exists( ABSPATH . 'wp-admin/includes/admin.php' ) ) { 339 require_once ABSPATH . 'wp-admin/includes/admin.php'; 340 } 341 } 342 343 $plugins = array(); 344 345 foreach ( get_option( 'active_plugins', array() ) as $plugin_path ) { 346 if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin_path ) ) { 347 $plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_path ); 348 $slug = str_replace( '/' . basename( $plugin_path ), '', $plugin_path ); 349 350 $plugins[ $slug ] = array( 351 'version' => $plugin_info['Version'], 352 'name' => $plugin_info['Name'], 353 'plugin_uri' => $plugin_info['PluginURI'], 354 'author' => $plugin_info['AuthorName'], 355 'author_uri' => $plugin_info['AuthorURI'], 356 ); 357 } 358 } 359 360 if ( is_multisite() ) { 361 foreach ( get_option( 'active_sitewide_plugins', array() ) as $plugin_path ) { 362 if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin_path ) ) { 363 $plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_path ); 364 $slug = str_replace( '/' . basename( $plugin_path ), '', $plugin_path ); 365 $plugins[ $slug ] = array( 366 'version' => $plugin_info['Version'], 367 'name' => $plugin_info['Name'], 368 'plugin_uri' => $plugin_info['PluginURI'], 369 'author' => $plugin_info['AuthorName'], 370 'author_uri' => $plugin_info['AuthorURI'], 371 ); 372 } 373 } 374 } 375 376 $user_query = new WP_User_Query( 377 array( 378 'blog_id' => $blog_id, 379 'count_total' => true, 380 ) 381 ); 382 383 $comments_query = new WP_Comment_Query(); 384 385 $demo_mode = get_option( 'ReduxFrameworkPlugin', false ); 386 if ( ! empty( $demo_mode ) ) { 387 $demo_mode = true; 388 } 389 390 $data = array( 391 'hash' => $hash, 392 'wp_version' => get_bloginfo( 'version' ), 393 'multisite' => is_multisite(), 394 'users' => $user_query->get_total(), 395 'lang' => get_locale(), 396 'wp_debug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ), 397 'memory' => WP_MEMORY_LIMIT, 398 'localhost' => self::is_local_host(), 399 'php' => PHP_VERSION, 400 'posts' => $pts, 401 'comments' => array( 402 'total' => $comments_count->total_comments, 403 'approved' => $comments_count->approved, 404 'spam' => $comments_count->spam, 405 'pings' => $comments_query->query( 406 array( 407 'count' => true, 408 'type' => 'pingback', 409 ) 410 ), 411 ), 412 413 // phpcs:ignore WordPress.NamingConventions.ValidHookName 414 'options' => apply_filters( 'redux/tracking/options', array() ), // TODO - What is this?! 415 416 'redux_installed' => Redux_Core::$installed, 417 'redux_version' => Redux_Core::$version, 418 'redux_demo_mode' => $demo_mode, 419 'redux_plugin' => Redux_Core::$as_plugin, 420 'developer' => self::get_developer_keys(), 421 'plugins' => $plugins, 422 ); 423 424 $theme_data = wp_get_theme(); 425 426 $theme = array( 427 'theme_version' => $theme_data->get( 'Version' ), 428 'theme_name' => $theme_data->get( 'Name' ), 429 'theme_author' => $theme_data->get( 'Author' ), 430 'theme_author_uri' => $theme_data->get( 'AuthorURI' ), 431 'theme_uri' => $theme_data->get( 'ThemeURI' ), 432 'theme_template' => $theme_data->get( 'Template' ), 433 ); 434 435 if ( is_child_theme() ) { 436 $parent_theme = wp_get_theme( $theme_data->Template ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName 437 438 $theme['theme_is_child'] = true; 439 $theme['theme_parent'] = $theme_data->Template; // phpcs:ignore WordPress.NamingConventions.ValidVariableName, 440 $theme['theme_parent_name'] = $parent_theme->get( 'Name' ); 441 $theme['theme_parent_version'] = $parent_theme->get( 'Version' ); 442 $theme['theme_parent_author'] = $parent_theme->get( 'Author' ); 443 $theme['theme_parent_author_uri'] = $parent_theme->get( 'AuthorURI' ); 444 $theme['theme_parent_uri'] = $parent_theme->get( 'ThemeURI' ); 445 } 446 447 $data = wp_parse_args( $data, $theme ); 448 $parts = explode( ' ', Redux_Core::$server['SERVER_SOFTWARE'] ); 449 450 foreach ( $parts as $part ) { 451 if ( '(' === $part[0] ) { 452 continue; 453 } 454 455 if ( false !== strpos( $part, '/' ) ) { 456 $chunk = explode( '/', $part ); 457 } 458 } 459 460 $data['server'] = Redux_Core::$server['SERVER_SOFTWARE']; 461 $data['db_version'] = $wpdb->db_version(); 462 463 $data['callers'] = self::process_redux_callers( true ); 464 465 if ( empty( $data['developer'] ) ) { 466 unset( $data['developer'] ); 467 } else { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement 468 // phpcs:disable Squiz.PHP.CommentedOutCode 469 470 /* 471 * print_r($data['developer']); 472 * echo "NOOO"; 473 */ 474 // phpcs:enable Squiz.PHP.CommentedOutCode 475 } 476 477 ksort( $data ); 478 479 $data['extensions'] = self::get_extensions(); 480 481 return $data; 482 } 483 484 /** 485 * Get extensions. 486 * 487 * @param string $opt_name Panel opt_name. 488 * 489 * @return array 490 */ 491 public static function get_extensions( string $opt_name = '' ): array { 492 if ( empty( $opt_name ) ) { 493 $instances = Redux_Instances::get_all_instances(); 494 } else { 495 $instances = array( 496 Redux_Instances::get_instance( $opt_name ), 497 ); 498 } 499 500 $extensions = array(); 501 502 if ( ! empty( $instances ) ) { 503 foreach ( $instances as $instance ) { 504 if ( isset( $instance->extensions ) && is_array( $instance->extensions ) && ! empty( $instance->extensions ) ) { 505 foreach ( $instance->extensions as $key => $extension ) { 506 if ( in_array( 507 $key, 508 array( 509 'import_export', 510 'customizer', 511 'options_object', 512 ), 513 true 514 ) 515 ) { 516 continue; 517 } 518 519 if ( isset( $extension::$version ) ) { 520 $extensions[ $key ] = $extension::$version; 521 } elseif ( isset( $extension->version ) ) { 522 $extensions[ $key ] = $extension->version; 523 } else { 524 $extensions[ $key ] = true; 525 } 526 } 527 } 528 } 529 } 530 531 return $extensions; 532 533 } 534 535 /** 536 * Get encrypted tracking object. 537 * 538 * @return array 539 */ 540 // phpcs:ignore Squiz.PHP.CommentedOutCode 541 // public static function tracking_object() { 542 // $data = wp_remote_post( 543 // 'http://verify.redux.io', 544 // array( 545 // 'body' => array( 546 // 'hash' => $_GET['action'], // phpcs:ignore WordPress.Security.NonceVerification, sanitization ok. 547 // 'site' => esc_url( home_url( '/' ) ), 548 // ), 549 // ) 550 // ); 551 // $data['body'] = urldecode( $data['body'] ); 552 // if ( ! isset( $_GET['code'] ) || $data['body'] !== $_GET['code'] ) { // phpcs:ignore WordPress.Security.NonceVerification 553 // die(); 554 // } 555 // return self::get_statistics_object(); 556 // } . 557 558 /** 559 * Deprecated. Determines if theme is parent. 560 * 561 * @param string $file Path to file. 562 * 563 * @return bool 564 * @deprecated No longer using camelCase naming convention. 565 */ 566 public static function isParentTheme( string $file ): bool { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 567 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::is_parent_theme( $file )' ); 568 569 return self::is_parent_theme( $file ); 570 } 571 572 /** 573 * Determines if theme is parent. 574 * 575 * @param string $file Path to theme dir. 576 * 577 * @return bool 578 */ 579 public static function is_parent_theme( string $file ): bool { 580 $file = Redux_Functions_Ex::wp_normalize_path( $file ); 581 $dir = Redux_Functions_Ex::wp_normalize_path( get_template_directory() ); 582 583 $file = str_replace( '//', '/', $file ); 584 $dir = str_replace( '//', '/', $dir ); 585 586 if ( strpos( $file, $dir ) !== false ) { 587 return true; 588 } 589 590 return false; 591 } 592 593 /** 594 * Deprecated. Moved to another class. 595 * 596 * @param string $file Path to file. 597 * 598 * @return string 599 * @deprecated Moved to another class. 600 */ 601 public static function wp_normalize_path( string $file ): string { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 602 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Functions_Ex::wp_normalize_path( $file )' ); 603 604 return Redux_Functions_Ex::wp_normalize_path( $file ); 605 } 606 607 /** 608 * Deprecated. Determines if theme is child. 609 * 610 * @param string $file Path to file. 611 * 612 * @return bool 613 * @deprecated No longer using camelCase naming convention. 614 */ 615 public static function isChildTheme( string $file ): bool { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 616 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::is_child_theme( $file )' ); 617 618 return self::is_child_theme( $file ); 619 } 620 621 /** 622 * Deprecated. Returns true if Redux is running as a plugin. 623 * 624 * @return bool::() 625 * @deprecated No longer using camelCase naming convention. 626 */ 627 private static function reduxAsPlugin(): bool { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 628 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Core::$as_plugin()' ); 629 630 return Redux_Core::$as_plugin; 631 } 632 633 /** 634 * Determines if theme is child. 635 * 636 * @param string $file Path to theme dir. 637 * 638 * @return bool 639 */ 640 public static function is_child_theme( string $file ): bool { 641 $file = Redux_Functions_Ex::wp_normalize_path( $file ); 642 $dir = Redux_Functions_Ex::wp_normalize_path( get_stylesheet_directory() ); 643 644 $file = str_replace( '//', '/', $file ); 645 $dir = str_replace( '//', '/', $dir ); 646 647 if ( strpos( $file, $dir ) !== false ) { 648 return true; 649 } 650 651 return false; 652 } 653 654 /** 655 * Deprecated. Determines if file is a theme. 656 * 657 * @param string $file Path to file. 658 * 659 * @return bool 660 * @deprecated No longer using camelCase naming convention. 661 */ 662 public static function isTheme( string $file ): bool { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 663 // phpcs:ignore Squiz.PHP.CommentedOutCode 664 // _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::is_theme( $file )' ); 665 666 return self::is_theme( $file ); 667 } 668 669 /** 670 * Determines if file is a theme. 671 * 672 * @param string $file Path to fle to test. 673 * 674 * @return bool 675 */ 676 public static function is_theme( string $file ): bool { 677 if ( true === self::is_child_theme( $file ) || true === self::is_parent_theme( $file ) ) { 678 return true; 679 } 680 681 return false; 682 } 683 684 /** 685 * Determines deep array status. 686 * 687 * @param array|string $needle array to test. 688 * @param array $haystack Array to search. 689 * 690 * @return bool 691 */ 692 public static function array_in_array( $needle, array $haystack ): bool { 693 // Make sure $needle is an array for foreach. 694 if ( ! is_array( $needle ) ) { 695 $needle = array( $needle ); 696 } 697 // For each value in $needle, return TRUE if in $haystack. 698 foreach ( $needle as $pin ) { 699 if ( in_array( $pin, $haystack, true ) ) { 700 return true; 701 } 702 } 703 704 // Return FALSE if none of the values from $needle are found in $haystack. 705 return false; 706 } 707 708 /** 709 * Enum through an entire deep array. 710 * 711 * @param string|array $needle String to search for. 712 * @param array $haystack Array in which to search. 713 * 714 * @return bool 715 */ 716 public static function recursive_array_search( $needle, array $haystack ): bool { 717 foreach ( $haystack as $key => $value ) { 718 if ( $needle === $value || ( is_array( $value ) && self::recursive_array_search( $needle, $value ) !== false ) ) { 719 return true; 720 } 721 } 722 723 return false; 724 } 725 726 /** 727 * Take a path and return it clean. 728 * 729 * @param string $path Path to clean. 730 * 731 * @return string 732 * @deprecated Replaced with wp_normalize_path. 733 * @since 3.1.7 734 */ 735 public static function cleanFilePath( string $path ): string { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 736 // phpcs:ignore Squiz.PHP.CommentedOutCode 737 // _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0', 'Redux_Functions_Ex::wp_normalize_path( $path )' ); 738 return Redux_Functions_Ex::wp_normalize_path( $path ); 739 } 740 741 /** 742 * Create unique hash. 743 * 744 * @return string 745 */ 746 public static function get_hash(): string { 747 $remote_addr = Redux_Core::$server['REMOTE_ADDR'] ?? '127.0.0.1'; 748 return md5( network_site_url() . '-' . $remote_addr ); 749 } 750 751 /** 752 * Return array of installed themes. 753 * 754 * @return array 755 */ 756 public static function get_wp_themes(): array { 757 global $wp_theme_paths; 758 759 $wp_theme_paths = array(); 760 $themes = wp_get_themes(); 761 762 foreach ( $themes as $theme ) { 763 $path = Redux_Functions_Ex::wp_normalize_path( trailingslashit( $theme->get_theme_root() ) . $theme->get_template() ); 764 765 if ( Redux_Functions_Ex::wp_normalize_path( realpath( $path ) ) !== $path ) { 766 $theme_paths[] = Redux_Functions_Ex::wp_normalize_path( realpath( $path ) ); 767 } 768 769 $wp_theme_paths[ $path ] = Redux_Functions_Ex::wp_normalize_path( realpath( $path ) ); 770 } 771 772 return array( 773 'full_paths' => $wp_theme_paths, 774 'theme_paths' => $theme, 775 ); 776 } 777 778 /** 779 * Get info for specified file. 780 * 781 * @param string $file File to check. 782 * 783 * @return array|bool 784 */ 785 public static function path_info( string $file ) { 786 $theme_info = Redux_Functions_Ex::is_inside_theme( $file ); 787 $plugin_info = Redux_Functions_Ex::is_inside_plugin( $file ); 788 789 if ( false !== $theme_info ) { 790 return $theme_info; 791 } elseif ( false !== $plugin_info ) { 792 return $plugin_info; 793 } 794 795 return array(); 796 } 797 798 /** 799 * Compiles caller data for Redux. 800 * 801 * @param bool $simple Mode. 802 * 803 * @return array 804 */ 805 public static function process_redux_callers( bool $simple = false ): array { 806 $data = array(); 807 808 foreach ( Redux_Core::$callers as $opt_name => $callers ) { 809 foreach ( $callers as $caller ) { 810 $plugin_info = self::is_inside_plugin( $caller ); 811 $theme_info = self::is_inside_theme( $caller ); 812 813 if ( $theme_info ) { 814 if ( ! isset( $data['theme'][ $theme_info['slug'] ] ) ) { 815 $data['theme'][ $theme_info['slug'] ] = array(); 816 } 817 if ( ! isset( $data['theme'][ $theme_info['slug'] ][ $opt_name ] ) ) { 818 $data['theme'][ $theme_info['slug'] ][ $opt_name ] = array(); 819 } 820 if ( $simple ) { 821 $data['theme'][ $theme_info['slug'] ][ $opt_name ][] = $theme_info['basename']; 822 } else { 823 $data['theme'][ $theme_info['slug'] ][ $opt_name ][] = $theme_info; 824 } 825 } elseif ( $plugin_info ) { 826 if ( ! isset( $data['plugin'][ $plugin_info['slug'] ] ) ) { 827 $data['plugin'][ $plugin_info['slug'] ] = array(); 828 } 829 if ( ! in_array( $opt_name, $data['plugin'][ $plugin_info['slug'] ], true ) ) { 830 if ( ! isset( $data['plugin'][ $plugin_info['slug'] ][ $opt_name ] ) ) { 831 $data['plugin'][ $plugin_info['slug'] ][ $opt_name ] = array(); 832 } 833 if ( $simple ) { 834 $data['plugin'][ $plugin_info['slug'] ][ $opt_name ][] = $plugin_info['basename']; 835 } else { 836 $data['plugin'][ $plugin_info['slug'] ][ $opt_name ][] = $plugin_info; 837 } 838 } 839 } 840 } 841 } 842 843 return $data; 844 } 845 846 /** 847 * Take a path and delete it 848 * 849 * @param string $dir Dir to remove. 850 * 851 * @since 3.3.3 852 */ 853 public static function rmdir( string $dir ) { 854 if ( is_dir( $dir ) ) { 855 $objects = scandir( $dir ); 856 857 foreach ( $objects as $object ) { 858 if ( '.' !== $object && '..' !== $object ) { 859 if ( filetype( $dir . '/' . $object ) === 'dir' ) { 860 rmdir( $dir . '/' . $object ); 861 } else { 862 unlink( $dir . '/' . $object ); 863 } 864 } 865 } 866 867 reset( $objects ); 868 869 rmdir( $dir ); 870 } 871 } 872 873 /** 874 * Field Render Function. 875 * Takes the color hex value and converts to a rgba. 876 * 877 * @param string $hex Color value. 878 * @param string $alpha Alpha value. 879 * 880 * @since ReduxFramework 3.0.4 881 */ 882 public static function hex2rgba( string $hex, string $alpha = '' ): string { 883 $hex = str_replace( '#', '', $hex ); 884 if ( 3 === strlen( $hex ) ) { 885 $r = hexdec( substr( $hex, 0, 1 ) . substr( $hex, 0, 1 ) ); 886 $g = hexdec( substr( $hex, 1, 1 ) . substr( $hex, 1, 1 ) ); 887 $b = hexdec( substr( $hex, 2, 1 ) . substr( $hex, 2, 1 ) ); 888 } else { 889 $r = hexdec( substr( $hex, 0, 2 ) ); 890 $g = hexdec( substr( $hex, 2, 2 ) ); 891 $b = hexdec( substr( $hex, 4, 2 ) ); 892 } 893 $rgb = $r . ',' . $g . ',' . $b; 894 895 if ( '' === $alpha ) { 896 return $rgb; 897 } else { 898 $alpha = floatval( $alpha ); 899 900 return 'rgba(' . $rgb . ',' . $alpha . ')'; 901 } 902 } 903 904 /** 905 * Deprecated. Returns string boolean value. 906 * 907 * @param mixed $var String to convert to true boolean. 908 * 909 * @return mixed|array 910 * 911 * @deprecated No longer using camelCase naming convention. 912 */ 913 public static function makeBoolStr( $var ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 914 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::make_bool_str( $var )' ); 915 916 return self::make_bool_str( $var ); 917 } 918 919 /** 920 * Returns string boolean value. 921 * 922 * @param mixed $var true|false to convert. 923 * 924 * @return mixed|array 925 */ 926 public static function make_bool_str( $var ) { 927 if ( false === $var || 'false' === $var || 0 === $var || '0' === $var || '' === $var || empty( $var ) ) { 928 return 'false'; 929 } elseif ( true === $var || 'true' === $var || 1 === $var || '1' === $var ) { 930 return 'true'; 931 } else { 932 return $var; 933 } 934 } 935 936 /** 937 * Compile localized array. 938 * 939 * @param array $localize Array of localized strings. 940 * 941 * @return array 942 */ 943 public static function localize( array $localize ): array { 944 $redux = Redux::instance( $localize['args']['opt_name'] ); 945 $nonce = wp_create_nonce( 'redux-ads-nonce' ); 946 $base = admin_url( 'admin-ajax.php' ) . '?t=' . $redux->core_thread . '&action=redux_p&nonce=' . $nonce . '&url='; 947 948 return $localize; 949 } 950 951 /** 952 * Retrieved request headers. 953 * 954 * @param array $args array of headers. 955 * 956 * @return array 957 */ 958 public static function get_request_headers( array $args = array() ): array { 959 $instances = Redux_Instances::get_all_instances(); 960 961 $array = array( 962 'hash' => self::get_hash(), 963 'developers' => wp_json_encode( self::get_developer_keys() ), 964 'redux' => Redux_Core::$version, 965 'installed' => Redux_Core::$installed, 966 'debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, 967 'local' => self::is_local_host(), 968 'wordpress' => get_bloginfo( 'version' ), 969 'site' => esc_url( home_url( '/' ) ), 970 'auto_fonts' => get_option( 'auto_update_redux_google_fonts', false ), 971 'extensions' => join( '|', array_keys( self::get_extensions() ) ), 972 ); 973 974 if ( ! empty( $instances ) ) { 975 $array['opt_names'] = join( '|', array_keys( $instances ) ); 976 } 977 978 if ( ! empty( $args ) ) { 979 return wp_parse_args( $args, $array ); 980 } 981 982 return $array; 983 } 984 985 /** 986 * Check mokama. 987 * 988 * @access public 989 * @since 4.0.0 990 * @return bool 991 */ 992 public static function mokama(): bool { 993 if ( defined( 'RDX_MOKAMA' ) ) { 994 return Redux_Functions_Ex::s(); 995 } 996 return false; 997 } 998 999 /** 1000 * Deprecated. Compiles array of system specs. 1001 * 1002 * @param boolean $json_output Enable/Disable return in JSON format. 1003 * @param boolean $remote_checks Enable/Disable remote URL testing. 1004 * 1005 * @return array::compile_system_status( $json_output, $remote_checks ) 1006 * @deprecated No longer using camelCase naming convention. 1007 */ 1008 public static function compileSystemStatus( bool $json_output, bool $remote_checks ): array { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 1009 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::compile_system_status( $json_output, $remote_checks )' ); 1010 1011 return self::compile_system_status( $json_output, $remote_checks ); 1012 } 1013 1014 /** 1015 * Compiles array of system specs. 1016 * 1017 * @param bool $json_output Do output file as JSON string. 1018 * @param bool $remote_checks Perform remote checks. 1019 * 1020 * @return array 1021 */ 1022 public static function compile_system_status( bool $json_output = false, bool $remote_checks = false ): array { 1023 global $wpdb; 1024 1025 $sysinfo = array(); 1026 1027 $sysinfo['home_url'] = home_url(); 1028 $sysinfo['site_url'] = site_url(); 1029 $sysinfo['redux_ver'] = esc_html( Redux_Core::$version ); 1030 $sysinfo['redux_data_dir'] = Redux_Core::$upload_dir; 1031 1032 $fs = Redux_Filesystem::get_instance(); 1033 $test_file = Redux_Core::$upload_dir . 'test-log.log'; 1034 1035 if ( $fs->is_file( $test_file ) ) { 1036 $res = $fs->unlink( $test_file ); 1037 } else { 1038 $res = $fs->touch( $test_file ); 1039 $fs->unlink( $test_file ); 1040 } 1041 1042 // Only is a file-write check. 1043 $sysinfo['redux_data_writeable'] = $res; 1044 $sysinfo['wp_content_url'] = WP_CONTENT_URL; 1045 $sysinfo['wp_ver'] = get_bloginfo( 'version' ); 1046 $sysinfo['wp_multisite'] = is_multisite(); 1047 $sysinfo['permalink_structure'] = get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default'; 1048 $sysinfo['front_page_display'] = get_option( 'show_on_front' ); 1049 if ( 'page' === $sysinfo['front_page_display'] ) { 1050 $front_page_id = get_option( 'page_on_front' ); 1051 $blog_page_id = get_option( 'page_for_posts' ); 1052 1053 $sysinfo['front_page'] = 0 !== $front_page_id ? get_the_title( $front_page_id ) . ' (#' . $front_page_id . ')' : 'Unset'; 1054 $sysinfo['posts_page'] = 0 !== $blog_page_id ? get_the_title( $blog_page_id ) . ' (#' . $blog_page_id . ')' : 'Unset'; 1055 } 1056 1057 $sysinfo['wp_mem_limit']['raw'] = self::let_to_num( WP_MEMORY_LIMIT ); 1058 $sysinfo['wp_mem_limit']['size'] = size_format( $sysinfo['wp_mem_limit']['raw'] ); 1059 1060 $sysinfo['db_table_prefix'] = 'Length: ' . strlen( $wpdb->prefix ) . ' - Status: ' . ( strlen( $wpdb->prefix ) > 16 ? 'ERROR: Too long' : 'Acceptable' ); 1061 1062 $sysinfo['wp_debug'] = false; 1063 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 1064 $sysinfo['wp_debug'] = true; 1065 } 1066 1067 $sysinfo['wp_lang'] = get_locale(); 1068 1069 if ( ! class_exists( 'Browser' ) ) { 1070 require_once Redux_Core::$dir . 'inc/lib/browser.php'; 1071 } 1072 1073 $browser = new Browser(); 1074 1075 $sysinfo['browser'] = array( 1076 'agent' => $browser->getUserAgent(), 1077 'browser' => $browser->getBrowser(), 1078 'version' => $browser->getVersion(), 1079 'platform' => $browser->getPlatform(), 1080 ); 1081 1082 $sysinfo['server_info'] = esc_html( Redux_Core::$server['SERVER_SOFTWARE'] ); 1083 $sysinfo['localhost'] = self::make_bool_str( self::is_local_host() ); 1084 $sysinfo['php_ver'] = function_exists( 'phpversion' ) ? esc_html( phpversion() ) : 'phpversion() function does not exist.'; 1085 $sysinfo['abspath'] = ABSPATH; 1086 1087 if ( function_exists( 'ini_get' ) ) { 1088 $sysinfo['php_mem_limit'] = size_format( self::let_to_num( ini_get( 'memory_limit' ) ) ); 1089 $sysinfo['php_post_max_size'] = size_format( self::let_to_num( ini_get( 'post_max_size' ) ) ); 1090 $sysinfo['php_time_limit'] = ini_get( 'max_execution_time' ); 1091 $sysinfo['php_max_input_var'] = ini_get( 'max_input_vars' ); 1092 $sysinfo['php_display_errors'] = self::make_bool_str( ini_get( 'display_errors' ) ); 1093 } 1094 1095 $sysinfo['suhosin_installed'] = extension_loaded( 'suhosin' ); 1096 $sysinfo['mysql_ver'] = $wpdb->db_version(); 1097 $sysinfo['max_upload_size'] = size_format( wp_max_upload_size() ); 1098 1099 $sysinfo['def_tz_is_utc'] = true; 1100 if ( date_default_timezone_get() !== 'UTC' ) { 1101 $sysinfo['def_tz_is_utc'] = false; 1102 } 1103 1104 $sysinfo['fsockopen_curl'] = false; 1105 1106 if ( function_exists( 'fsockopen' ) || function_exists( 'curl_init' ) ) { 1107 $sysinfo['fsockopen_curl'] = true; 1108 } 1109 1110 if ( true === $remote_checks ) { 1111 $response = wp_remote_post( 1112 'https://api.redux.io/status', 1113 array( 1114 'sslverify' => true, 1115 'timeout' => 60, 1116 'headers' => self::get_request_headers(), 1117 ) 1118 ); 1119 1120 if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) { 1121 $sysinfo['wp_remote_post'] = true; 1122 $sysinfo['wp_remote_post_error'] = ''; 1123 } else { 1124 $sysinfo['wp_remote_post'] = false; 1125 $sysinfo['wp_remote_post_error'] = $response->get_error_message(); 1126 } 1127 1128 // phpcs:ignore WordPress.PHP.NoSilencedErrors 1129 $response = @wp_remote_get( 'https://raw.githubusercontent.com/dovy/redux-framework/master/CONTRIBUTING.md' ); 1130 1131 if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) { 1132 $sysinfo['wp_remote_get'] = true; 1133 $sysinfo['wp_remote_get_error'] = ''; 1134 } else { 1135 $sysinfo['wp_remote_get'] = false; 1136 $sysinfo['wp_remote_get_error'] = $response->get_error_message(); 1137 } 1138 } 1139 1140 $active_plugins = (array) get_option( 'active_plugins', array() ); 1141 1142 if ( is_multisite() ) { 1143 $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) ); 1144 } 1145 1146 $sysinfo['plugins'] = array(); 1147 1148 foreach ( $active_plugins as $plugin ) { 1149 if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) { 1150 // phpcs:ignore WordPress.PHP.NoSilencedErrors 1151 $plugin_data = @get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); 1152 $plugin_name = esc_html( $plugin_data['Name'] ); 1153 1154 $sysinfo['plugins'][ $plugin_name ] = $plugin_data; 1155 } 1156 } 1157 1158 $redux = Redux::all_instances(); 1159 1160 $sysinfo['redux_instances'] = array(); 1161 1162 if ( ! empty( $redux ) && is_array( $redux ) ) { 1163 foreach ( $redux as $inst => $data ) { 1164 Redux::init( $inst ); 1165 1166 $sysinfo['redux_instances'][ $inst ]['args'] = $data->args; 1167 $sysinfo['redux_instances'][ $inst ]['sections'] = $data->sections; 1168 foreach ( $sysinfo['redux_instances'][ $inst ]['sections'] as $key => $section ) { 1169 if ( isset( $section['fields'] ) && is_array( $section['fields'] ) ) { 1170 foreach ( $section['fields'] as $field_key => $field ) { 1171 if ( isset( $field['validate_callback'] ) ) { 1172 unset( $sysinfo['redux_instances'][ $inst ]['sections'][ $key ]['fields'][ $field_key ]['validate_callback'] ); 1173 } 1174 if ( 'js_button' === $field['type'] ) { 1175 if ( isset( $field['script'] ) && isset( $field['script']['ver'] ) ) { 1176 unset( $sysinfo['redux_instances'][ $inst ]['sections'][ $key ]['fields'][ $field_key ]['script']['ver'] ); 1177 } 1178 } 1179 } 1180 } 1181 } 1182 1183 $sysinfo['redux_instances'][ $inst ]['extensions'] = Redux::get_extensions( $inst ); 1184 1185 if ( isset( $data->extensions[ 'metaboxes' ] ) ) { 1186 $data->extensions[ 'metaboxes' ]->init(); 1187 $sysinfo['redux_instances'][ $inst ][ 'metaboxes' ] = $data->extensions[ 'metaboxes' ]->boxes; 1188 } 1189 1190 if ( isset( $data->args['templates_path'] ) && '' !== $data->args['templates_path'] ) { 1191 $sysinfo['redux_instances'][ $inst ]['templates'] = self::get_redux_templates( $data->args['templates_path'] ); 1192 } 1193 } 1194 } 1195 1196 $active_theme = wp_get_theme(); 1197 1198 $sysinfo['theme']['name'] = $active_theme->Name; // phpcs:ignore WordPress.NamingConventions.ValidVariableName 1199 $sysinfo['theme']['version'] = $active_theme->Version; // phpcs:ignore WordPress.NamingConventions.ValidVariableName 1200 $sysinfo['theme']['author_uri'] = $active_theme->{'Author URI'}; 1201 $sysinfo['theme']['is_child'] = self::make_bool_str( is_child_theme() ); 1202 1203 if ( is_child_theme() ) { 1204 $parent_theme = wp_get_theme( $active_theme->Template ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName 1205 1206 $sysinfo['theme']['parent_name'] = $parent_theme->Name; // phpcs:ignore WordPress.NamingConventions.ValidVariableName 1207 $sysinfo['theme']['parent_version'] = $parent_theme->Version; // phpcs:ignore WordPress.NamingConventions.ValidVariableName 1208 $sysinfo['theme']['parent_author_uri'] = $parent_theme->{'Author URI'}; 1209 } 1210 1211 return $sysinfo; 1212 } 1213 1214 /** 1215 * Deprecated. Returns array of Redux templates. 1216 * 1217 * @param string $custom_template_path The Path to custom template. 1218 * 1219 * @return array::get_redux_templates( $custom_template_path ) 1220 * @deprecated No longer using camelCase naming convention. 1221 */ 1222 private static function getReduxTemplates( string $custom_template_path ): array { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 1223 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::get_redux_templates( $custom_template_path )' ); 1224 1225 return self::get_redux_templates( $custom_template_path ); 1226 } 1227 1228 /** 1229 * Returns array of Redux templates. 1230 * 1231 * @param string $custom_template_path The Path to template dir. 1232 * 1233 * @return array 1234 */ 1235 private static function get_redux_templates( string $custom_template_path ): array { 1236 $filesystem = Redux_Filesystem::get_instance(); 1237 $template_paths = array( 'ReduxFramework' => Redux_Core::$dir . 'templates/panel' ); 1238 $scanned_files = array(); 1239 $found_files = array(); 1240 $outdated_templates = false; 1241 1242 foreach ( $template_paths as $plugin_name => $template_path ) { 1243 $scanned_files[ $plugin_name ] = self::scan_template_files( $template_path ); 1244 } 1245 1246 foreach ( $scanned_files as $plugin_name => $files ) { 1247 foreach ( $files as $file ) { 1248 if ( file_exists( $custom_template_path . '/' . $file ) ) { 1249 $theme_file = $custom_template_path . '/' . $file; 1250 } else { 1251 $theme_file = false; 1252 } 1253 1254 if ( $theme_file ) { 1255 $core_version = self::get_template_version( Redux_Core::$dir . 'templates/panel/' . $file ); 1256 $theme_version = self::get_template_version( $theme_file ); 1257 1258 if ( $core_version && ( empty( $theme_version ) || version_compare( $theme_version, $core_version, '<' ) ) ) { 1259 if ( ! $outdated_templates ) { 1260 $outdated_templates = true; 1261 } 1262 1263 $found_files[ $plugin_name ][] = sprintf( '<code>%s</code> ' . esc_html__( 'version', 'redux-framework' ) . ' <strong style="color:red">%s</strong> ' . esc_html__( 'is out of date. The core version is', 'redux-framework' ) . ' %s', str_replace( WP_CONTENT_DIR . '/themes/', '', $theme_file ), $theme_version ? $theme_version : '-', $core_version ); 1264 } else { 1265 $found_files[ $plugin_name ][] = sprintf( '<code>%s</code>', str_replace( WP_CONTENT_DIR . '/themes/', '', $theme_file ) ); 1266 } 1267 } 1268 } 1269 } 1270 1271 return $found_files; 1272 } 1273 1274 /** 1275 * Deprecated. URL Fix. 1276 * 1277 * @param string $base Base string of site. 1278 * @param string $opt_name Redux instance opt_name. 1279 * 1280 * @return Redux_Helpers::r_url_fix( $base, $opt_name ) 1281 * @deprecated No longer using camelCase naming convention. 1282 */ 1283 public static function rURL_fix( string $base, string $opt_name ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 1284 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::r_url_fix( $base, $opt_name )' ); 1285 1286 return self::r_url_fix( $base, $opt_name ); 1287 } 1288 1289 /** 1290 * URL Fix. 1291 * 1292 * @param string $base Base. 1293 * @param string $opt_name Panel opt_name. 1294 * 1295 * @return mixed|string|void 1296 */ 1297 public static function r_url_fix( string $base, string $opt_name ) { 1298 $url = $base . rawurlencode( 'https://look.redux.io/api/index.php?js&g&1&v=2' ) . '&proxy=' . rawurlencode( $base ) . ''; 1299 1300 return Redux_Functions::tru( $url, $opt_name ); 1301 } 1302 1303 /** 1304 * Scan template files for ver changes. 1305 * 1306 * @param string $template_path The Path to templates. 1307 * 1308 * @return array 1309 */ 1310 private static function scan_template_files( string $template_path ): array { 1311 $files = scandir( $template_path ); 1312 $result = array(); 1313 1314 if ( $files ) { 1315 foreach ( $files as $key => $value ) { 1316 if ( ! in_array( $value, array( '.', '..' ), true ) ) { 1317 if ( is_dir( $template_path . DIRECTORY_SEPARATOR . $value ) ) { 1318 $sub_files = redux_scan_template_files( $template_path . DIRECTORY_SEPARATOR . $value ); 1319 foreach ( $sub_files as $sub_file ) { 1320 $result[] = $value . DIRECTORY_SEPARATOR . $sub_file; 1321 } 1322 } else { 1323 $result[] = $value; 1324 } 1325 } 1326 } 1327 } 1328 1329 return $result; 1330 } 1331 1332 /** 1333 * Retrieves template version. 1334 * 1335 * @param string $file Path to template file. 1336 * 1337 * @return string 1338 */ 1339 public static function get_template_version( string $file ): string { 1340 $filesystem = Redux_Filesystem::get_instance(); 1341 // Avoid notices if file does not exist. 1342 if ( ! file_exists( $file ) ) { 1343 return ''; 1344 } 1345 1346 $data = get_file_data( $file, array( 'version' ), 'plugin' ); 1347 1348 if ( ! empty( $data[0] ) ) { 1349 return $data[0]; 1350 } else { 1351 $file_data = $filesystem->get_contents( $file ); 1352 1353 $file_data = str_replace( "\r", "\n", $file_data ); 1354 $version = '1.0.0'; 1355 1356 if ( preg_match( '/^[ \t\/*#@]*' . preg_quote( '@version', '/' ) . '(.*)$/mi', $file_data, $match ) && $match[1] ) { 1357 $version = _cleanup_header_comment( $match[1] ); 1358 } 1359 1360 return $version; 1361 } 1362 } 1363 1364 /** 1365 * Create HTML attribute string. 1366 * 1367 * @param array $attributes Array of attributes. 1368 */ 1369 public static function html_attributes( array $attributes = array() ) { 1370 $string = join( 1371 ' ', 1372 array_map( 1373 function ( $key ) use ( $attributes ) { 1374 if ( is_bool( $attributes[ $key ] ) ) { 1375 return $attributes[ $key ] ? $key : ''; 1376 } 1377 1378 return $key . '="' . $attributes[ $key ] . '"'; 1379 }, 1380 array_keys( $attributes ) 1381 ) 1382 ) . ' '; 1383 } 1384 1385 /** 1386 * Output filesize based on letter indicator. 1387 * 1388 * @param string $size Size with letter. 1389 * 1390 * @return bool|int|string 1391 */ 1392 private static function let_to_num( string $size ) { 1393 $l = substr( $size, - 1 ); 1394 $ret = substr( $size, 0, - 1 ); 1395 1396 switch ( strtoupper( $l ) ) { 1397 case 'P': 1398 $ret *= 1024; 1399 // Must remain recursive, do not use 'break'. 1400 case 'T': 1401 $ret *= 1024; 1402 // Must remain recursive, do not use 'break'. 1403 case 'G': 1404 $ret *= 1024; 1405 // Must remain recursive, do not use 'break'. 1406 case 'M': 1407 $ret *= 1024; 1408 // Must remain recursive, do not use 'break'. 1409 case 'K': 1410 $ret *= 1024; 1411 } 1412 1413 return $ret; 1414 } 1415 1416 /** 1417 * Normalize extensions dir. 1418 * 1419 * @param string $dir Path to extensions. 1420 * 1421 * @return string 1422 */ 1423 public static function get_extension_dir( string $dir ): string { 1424 return trailingslashit( Redux_Functions_Ex::wp_normalize_path( dirname( $dir ) ) ); 1425 } 1426 1427 /** 1428 * Normalize extensions URL. 1429 * 1430 * @param string $dir Path to extensions. 1431 * 1432 * @return array|string|string[] 1433 */ 1434 public static function get_extension_url( string $dir ) { 1435 $ext_dir = self::get_extension_dir( $dir ); 1436 1437 return str_replace( Redux_Functions_Ex::wp_normalize_path( WP_CONTENT_DIR ), WP_CONTENT_URL, $ext_dir ); 1438 } 1439 1440 /** 1441 * Checks a nested capabilities array or string to determine if the current user meets the requirements. 1442 * 1443 * @param string|array $capabilities Permission string or array to check. See self::user_can() for details. 1444 * 1445 * @return bool Whether the user meets the requirements. False on invalid user. 1446 * @since 3.6.3.4 1447 */ 1448 public static function current_user_can( $capabilities ): bool { 1449 $current_user = wp_get_current_user(); 1450 1451 if ( empty( $current_user ) ) { 1452 return false; 1453 } 1454 1455 $name_arr = func_get_args(); 1456 $args = array_merge( array( $current_user ), $name_arr ); 1457 1458 return call_user_func_array( array( 'self', 'user_can' ), $args ); 1459 } 1460 1461 /** 1462 * Checks a nested capabilities array or string to determine if the user meets the requirements. 1463 * You can pass in a simple string like 'edit_posts' or an array of conditions. 1464 * The capability 'relation' is reserved for controlling the relation mode (AND/OR), which defaults to AND. 1465 * Max depth of 30 levels. False is returned for any conditions exceeding max depth. 1466 * If you want to check meta caps, you must also pass the object ID on which to check against. 1467 * If you get the error: PHP Notice: Undefined offset: 0 in /wp-includes/capabilities.php, you didn't 1468 * pass the required $object_id. 1469 * 1470 * @param int/object $user User ID or WP_User object to check. Defaults to the current user. 1471 * @param string|array $capabilities Capability string or array to check. The array lets you use multiple 1472 * conditions to determine if a user has permission. 1473 * Invalid conditions are skipped (conditions which aren't a string/array/bool/number(cast to bool)). 1474 * Example array where the user needs to have either the 'edit_posts' capability OR doesn't have the 1475 * 'delete_pages' cap OR has the 'update_plugins' AND 'add_users' capabilities. 1476 * array( 1477 * 'relation' => 'OR', // Optional, defaults to AND. 1478 * 'edit_posts', // Equivalent to 'edit_posts' => true, 1479 * 'delete_pages' => false, // Tests that the user DOESN'T have this capability 1480 * array( // Nested conditions array (up to 30 nestings) 1481 * 'update_plugins', 1482 * 'add_users', 1483 * ), 1484 * ). 1485 * @param int|null $object_id (Optional) ID of the specific object to check against if capability is a "meta" cap. 1486 * e.g. 'edit_post', 'edit_user', 'edit_page', etc. 1487 * 1488 * @return bool Whether the user meets the requirements. 1489 * Will always return false for: 1490 * - Invalid/missing user 1491 * - If the $capabilities is not a string or array 1492 * - Max nesting depth exceeded (for that level) 1493 * @since 3.6.3.4 1494 * @example 1495 * user_can( 42, 'edit_pages' ); // Checks if user ID 42 has the 'edit_pages' cap. 1496 * user_can( 42, 'edit_page', 17433 ); // Checks if user ID 42 has the 'edit_page' cap for post ID 17433. 1497 * user_can( 42, array( 'edit_pages', 'edit_posts' ) ); // Checks if user ID 42 has both the 'edit_pages' and 'edit_posts' caps. 1498 */ 1499 public static function user_can( $user, $capabilities, int $object_id = null ): bool { 1500 static $depth = 0; 1501 1502 if ( $depth >= 30 ) { 1503 return false; 1504 } 1505 1506 if ( empty( $user ) ) { 1507 return false; 1508 } 1509 1510 if ( ! is_object( $user ) ) { 1511 $user = get_userdata( $user ); 1512 } 1513 1514 if ( is_string( $capabilities ) ) { 1515 // Simple string capability check. 1516 $args = array( $user, $capabilities ); 1517 1518 if ( null !== $object_id ) { 1519 $args[] = $object_id; 1520 } 1521 1522 return call_user_func_array( 'user_can', $args ); 1523 } else { 1524 // Only strings and arrays are allowed as valid capabilities. 1525 if ( ! is_array( $capabilities ) ) { 1526 return false; 1527 } 1528 } 1529 1530 // Capability array check. 1531 $or = false; 1532 1533 foreach ( $capabilities as $key => $value ) { 1534 if ( 'relation' === $key ) { 1535 if ( 'OR' === $value ) { 1536 $or = true; 1537 } 1538 1539 continue; 1540 } 1541 1542 /** 1543 * Rules can be in 4 different formats: 1544 * [ 1545 * [0] => 'foobar', 1546 * [1] => array(...), 1547 * 'foobar' => false, 1548 * 'foobar' => array(...), 1549 * ] 1550 */ 1551 if ( is_numeric( $key ) ) { 1552 // Numeric key. 1553 if ( is_string( $value ) ) { 1554 // Numeric key with a string value is the capability string to check 1555 // [0] => 'foobar'. 1556 $args = array( $user, $value ); 1557 1558 if ( null !== $object_id ) { 1559 $args[] = $object_id; 1560 } 1561 1562 $expression_result = call_user_func_array( 'user_can', $args ) === true; 1563 } elseif ( is_array( $value ) ) { 1564 $depth ++; 1565 1566 $expression_result = self::user_can( $user, $value, $object_id ); 1567 1568 $depth --; 1569 } else { 1570 // Invalid types are skipped. 1571 continue; 1572 } 1573 } else { 1574 // Non-numeric key. 1575 if ( is_scalar( $value ) ) { 1576 $args = array( $user, $key ); 1577 1578 if ( null !== $object_id ) { 1579 $args[] = $object_id; 1580 } 1581 1582 $expression_result = call_user_func_array( 'user_can', $args ) === (bool) $value; 1583 } elseif ( is_array( $value ) ) { 1584 $depth ++; 1585 1586 $expression_result = self::user_can( $user, $value, $object_id ); 1587 1588 $depth --; 1589 } else { 1590 // Invalid types are skipped. 1591 continue; 1592 } 1593 } 1594 1595 // Check after every evaluation if we know enough to return a definitive answer. 1596 if ( $or ) { 1597 if ( $expression_result ) { 1598 // If the relation is OR, return on the first true expression. 1599 return true; 1600 } 1601 } else { 1602 if ( ! $expression_result ) { 1603 // If the relation is AND, return on the first false expression. 1604 return false; 1605 } 1606 } 1607 } 1608 1609 // If we get this far on an OR, then it failed. 1610 // If we get this far on an AND, then it succeeded. 1611 return ! $or; 1612 } 1613 1614 /** 1615 * Check if Google font update is needed. 1616 * 1617 * @return bool 1618 */ 1619 public static function google_fonts_update_needed(): bool { 1620 1621 $path = trailingslashit( Redux_Core::$upload_dir ) . 'google_fonts.json'; 1622 $now = time(); 1623 $secs = 60 * 60 * 24 * 7; 1624 if ( file_exists( $path ) ) { 1625 if ( ( $now - filemtime( $path ) ) < $secs ) { 1626 return false; 1627 } 1628 } 1629 1630 return true; 1631 } 1632 1633 /** 1634 * Retrieve list of dev keys. 1635 * 1636 * @return array 1637 */ 1638 public static function get_developer_keys(): array { 1639 1640 // TODO - Get shim values for here. 1641 // phpcs:ignore WordPress.NamingConventions.ValidHookName 1642 $data = array( apply_filters( 'redux/tracking/developer', array() ) ); 1643 if ( 1 === count( $data ) ) { 1644 if ( empty( $data[0] ) ) { 1645 $data = array(); 1646 } 1647 } 1648 $instances = Redux_Instances::get_all_instances(); 1649 $data = array(); 1650 if ( ! empty( $instance ) ) { 1651 foreach ( $instances as $instance ) { 1652 if ( isset( $instance->args['developer'] ) && ! empty( $instance->args['developer'] ) ) { 1653 $data[] = $instance->args['developer']; 1654 } 1655 } 1656 } 1657 1658 return $data; 1659 } 1660 1661 /** 1662 * Retrieve updated Google font array. 1663 * 1664 * @param bool $download Flag to download to file. 1665 * 1666 * @return array|WP_Error 1667 */ 1668 public static function google_fonts_array( bool $download = false ) { 1669 if ( ! empty( Redux_Core::$google_fonts ) && ! self::google_fonts_update_needed() ) { 1670 return Redux_Core::$google_fonts; 1671 } 1672 1673 $filesystem = Redux_Filesystem::get_instance(); 1674 1675 $path = trailingslashit( Redux_Core::$upload_dir ) . 'google_fonts.json'; 1676 1677 if ( ! file_exists( $path ) || ( file_exists( $path ) && $download && self::google_fonts_update_needed() ) ) { 1678 if ( $download ) { 1679 // phpcs:ignore WordPress.NamingConventions.ValidHookName 1680 $url = apply_filters( 'redux/typography/google_fonts/url', 'https://api.redux.io/gfonts' ); 1681 1682 $request = wp_remote_get( 1683 $url, 1684 array( 1685 'timeout' => 20, 1686 'headers' => self::get_request_headers(), 1687 ) 1688 ); 1689 1690 if ( ! is_wp_error( $request ) ) { 1691 $body = wp_remote_retrieve_body( $request ); 1692 if ( ! empty( $body ) ) { 1693 $filesystem->put_contents( $path, $body ); 1694 Redux_Core::$google_fonts = json_decode( $body, true ); 1695 } 1696 } else { 1697 return $request; 1698 } 1699 } 1700 } elseif ( file_exists( $path ) ) { 1701 Redux_Core::$google_fonts = json_decode( $filesystem->get_contents( $path ), true ); 1702 if ( empty( Redux_Core::$google_fonts ) ) { 1703 $filesystem->unlink( $path ); 1704 } 1705 } 1706 1707 return Redux_Core::$google_fonts; 1708 } 1709 1710 /** 1711 * Deprecated. Gets all Redux instances 1712 * 1713 * @return array 1714 * @deprecated No longer using camelCase naming convention and moved to a different class. 1715 */ 1716 public static function getReduxInstances(): array { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName 1717 _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'Redux 4.0.0', 'Redux_Instances::get_all_instances()' ); 1718 1719 return Redux_Instances::get_all_instances(); 1720 } 1721 1722 /** 1723 * Is Inside Plugin 1724 * 1725 * @param string $file File name. 1726 * 1727 * @return array|bool 1728 */ 1729 public static function is_inside_plugin( string $file ) { 1730 1731 // phpcs:ignore Squiz.PHP.CommentedOutCode 1732 // if ( substr( strtoupper( $file ), 0, 2 ) === 'C:' ) { 1733 // $file = ltrim( $file, 'C:' ); 1734 // $file = ltrim( $file, 'c:' ); 1735 // } . 1736 // 1737 $plugin_basename = plugin_basename( $file ); 1738 1739 if ( Redux_Functions_Ex::wp_normalize_path( $file ) !== '/' . $plugin_basename ) { 1740 $slug = explode( '/', $plugin_basename ); 1741 $slug = $slug[0]; 1742 1743 return array( 1744 'slug' => $slug, 1745 'basename' => $plugin_basename, 1746 'path' => Redux_Functions_Ex::wp_normalize_path( $file ), 1747 'url' => plugins_url( $plugin_basename ), 1748 'real_path' => Redux_Functions_Ex::wp_normalize_path( dirname( realpath( $file ) ) ), 1749 ); 1750 } 1751 1752 return false; 1753 } 1754 1755 /** 1756 * Is inside theme. 1757 * 1758 * @param string $file File name. 1759 * 1760 * @return array|bool 1761 */ 1762 public static function is_inside_theme( string $file = '' ) { 1763 $theme_paths = array( 1764 Redux_Functions_Ex::wp_normalize_path( get_template_directory() ) => get_template_directory_uri(), 1765 Redux_Functions_Ex::wp_normalize_path( get_stylesheet_directory() ) => get_stylesheet_directory_uri(), 1766 ); 1767 1768 $theme_paths = array_unique( $theme_paths ); 1769 1770 $file_path = Redux_Functions_Ex::wp_normalize_path( $file ); 1771 $filename = explode( '/', $file_path ); 1772 $filename = end( $filename ); 1773 foreach ( $theme_paths as $theme_path => $url ) { 1774 1775 $real_path = Redux_Functions_Ex::wp_normalize_path( realpath( $theme_path ) ); 1776 1777 if ( strpos( $file_path, trailingslashit( $real_path ) ) !== false ) { 1778 $slug = explode( '/', Redux_Functions_Ex::wp_normalize_path( $theme_path ) ); 1779 if ( empty( $slug ) ) { 1780 continue; 1781 } 1782 $slug = end( $slug ); 1783 $relative_path = explode( $slug, dirname( $file_path ) ); 1784 1785 if ( 1 === count( $relative_path ) ) { 1786 $relative_path = $file_path; 1787 } else { 1788 $relative_path = $relative_path[1]; 1789 } 1790 $relative_path = ltrim( $relative_path, '/' ); 1791 1792 $data = array( 1793 'slug' => $slug, 1794 'path' => trailingslashit( trailingslashit( $theme_path ) . $relative_path ) . $filename, 1795 'real_path' => trailingslashit( trailingslashit( $real_path ) . $relative_path ) . $filename, 1796 'url' => trailingslashit( trailingslashit( $url ) . $relative_path ) . $filename, 1797 ); 1798 1799 $basename = explode( $data['slug'], $data['path'] ); 1800 $basename = end( $basename ); 1801 $basename = ltrim( $basename, '/' ); 1802 $data['basename'] = trailingslashit( $data['slug'] ) . $basename; 1803 1804 if ( is_child_theme() ) { 1805 $parent = get_template_directory(); 1806 $data['parent_slug'] = explode( '/', $parent ); 1807 $data['parent_slug'] = end( $data['parent_slug'] ); 1808 if ( $data['slug'] === $data['parent_slug'] ) { 1809 unset( $data['parent_slug'] ); 1810 } 1811 } 1812 1813 return $data; 1814 } 1815 } 1816 1817 return false; 1818 } 1819 1820 /** 1821 * Nonces. 1822 * 1823 * @return array 1824 */ 1825 public static function nonces(): array { 1826 return array( 1827 '9fced129522f128b2445a41fb0b6ef9f', 1828 '70dda5dfb8053dc6d1c492574bce9bfd', 1829 '62933a2951ef01f4eafd9bdf4d3cd2f0', 1830 'a398fb77df76e6153df57cd65fd0a7c5', 1831 '1cb251ec0d568de6a929b520c4aed8d1', 1832 '6394d816bfb4220289a6f4b29cfb1834', 1833 ); 1834 } 1835 1836 /** 1837 * Get plugin options. 1838 * 1839 * @return array|mixed|void 1840 */ 1841 public static function get_plugin_options() { 1842 $defaults = array( 1843 'demo' => false, 1844 ); 1845 $options = array(); 1846 1847 // If multisite is enabled. 1848 if ( is_multisite() ) { 1849 1850 // Get network activated plugins. 1851 $plugins = get_site_option( 'active_sitewide_plugins' ); 1852 1853 foreach ( $plugins as $file => $plugin ) { 1854 if ( strpos( $file, 'redux-framework.php' ) !== false ) { 1855 $plugin_network_activated = true; 1856 $options = get_site_option( 'ReduxFrameworkPlugin', $defaults ); 1857 } 1858 } 1859 } 1860 1861 // If options aren't set, grab them now! 1862 if ( empty( $options ) ) { 1863 $options = get_option( 'ReduxFrameworkPlugin', $defaults ); 1864 } 1865 1866 return $options; 1867 } 1868 1869 /** 1870 * Sanitize array keys and values. 1871 * 1872 * @param array $array Array to sanitize. 1873 */ 1874 public static function sanitize_array( array $array ): array { 1875 return self::array_map_r( 'sanitize_text_field', $array ); 1876 } 1877 1878 /** 1879 * Recursive array map. 1880 * 1881 * @param string $func function to run. 1882 * @param array $arr Array to clean. 1883 * 1884 * @return array 1885 */ 1886 private static function array_map_r( string $func, array $arr ): array { 1887 $new_arr = array(); 1888 1889 foreach ( $arr as $key => $value ) { 1890 $new_arr[ $key ] = ( is_array( $value ) ? self::array_map_r( $func, $value ) : ( is_array( $func ) ? call_user_func_array( $func, $value ) : $func( $value ) ) ); 1891 } 1892 1893 return $new_arr; 1894 } 1895 1896 /** 1897 * AJAX callback. 1898 */ 1899 public static function hash_arg() { 1900 echo esc_html( md5( Redux_Functions_Ex::hash_key() . '-redux' ) ); 1901 die(); 1902 } 1903 1904 /** 1905 * Adds stats parameters for Redux settings. Outside the main class as the class could also be in use in other ways. 1906 * 1907 * @param array $options Stats options. 1908 * 1909 * @return array 1910 */ 1911 public static function redux_stats_additions( array $options ): array { 1912 $options['redux'] = array( 1913 'demo_mode' => get_option( 'ReduxFrameworkPlugin' ), 1914 ); 1915 1916 return $options; 1917 } 1918 1919 /** 1920 * AJAX callback Compile support arg. 1921 */ 1922 public static function support_args() { 1923 header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' ); 1924 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . 'GMT' ); 1925 header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' ); 1926 header( 'Cache-Control: no-store, no-cache, must-revalidate' ); 1927 header( 'Cache-Control: post-check=0, pre-check=0', false ); 1928 header( 'Pragma: no-cache' ); 1929 1930 $instances = Redux::all_instances(); 1931 1932 if ( isset( $_REQUEST['i'] ) && ! empty( $_REQUEST['i'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification 1933 if ( is_array( $instances ) && ! empty( $instances ) ) { 1934 foreach ( $instances as $opt_name => $data ) { 1935 if ( md5( $opt_name . '-debug' ) === $_REQUEST['i'] ) { // phpcs:ignore WordPress.Security.NonceVerification 1936 $array = $data; 1937 } 1938 } 1939 } 1940 1941 if ( isset( $array ) ) { 1942 1943 // We only want the extension names and versions. 1944 $array->extensions = self::get_extensions( $opt_name ); 1945 $to_return = array(); 1946 1947 // Filter out all the unwanted data. 1948 foreach ( $array as $key => $value ) { 1949 if ( in_array( 1950 $key, 1951 array( 1952 // 'fields', 1953 'extensions', 1954 'sections', 1955 'args', 1956 // 'field_types' 1957 ), 1958 true 1959 ) ) { 1960 $to_return[ $key ] = $value; 1961 } else { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement 1962 // phpcs:ignore Squiz.PHP.CommentedOutCode 1963 /* echo $key.PHP_EOL; */ 1964 } 1965 } 1966 $array = $to_return; 1967 } else { 1968 die(); 1969 } 1970 } else { 1971 $array = self::get_statistics_object(); 1972 if ( is_array( $instances ) && ! empty( $instances ) ) { 1973 $array['instances'] = array(); 1974 foreach ( $instances as $opt_name => $data ) { 1975 $array['instances'][] = $opt_name; 1976 } 1977 } 1978 $array['key'] = md5( Redux_Functions_Ex::hash_key() ); 1979 } 1980 1981 ksort( $array ); // Let's make that pretty. 1982 1983 // phpcs:ignored WordPress.PHP.NoSilencedErrors, WordPress.Security.EscapeOutput 1984 echo @htmlspecialchars( @wp_json_encode( $array, true ), ENT_QUOTES ); 1985 1986 die(); 1987 } 1988 1989 /** 1990 * Detect if Gutenberg is running on the current page. 1991 * 1992 * @return bool 1993 */ 1994 public static function is_gutenberg_page(): bool { 1995 if ( function_exists( 'is_gutenberg_page' ) && is_gutenberg_page() ) { 1996 // The Gutenberg plugin is on. 1997 return true; 1998 } 1999 2000 $current_screen = get_current_screen(); 2001 2002 if ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) { 2003 // Gutenberg page on 5+. 2004 return true; 2005 } 2006 return false; 2007 } 2008 2009 /** 2010 * Material design colors. 2011 * 2012 * @param string $context Mode to use. 2013 * 2014 * @return array|mixed 2015 */ 2016 public static function get_material_design_colors( string $context = 'primary' ) { 2017 $colors = array( 2018 'primary' => array( '#FFFFFF', '#000000', '#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B' ), 2019 'red' => array( '#FFEBEE', '#FFCDD2', '#EF9A9A', '#E57373', '#EF5350', '#F44336', '#E53935', '#D32F2F', '#C62828', '#B71C1C', '#FF8A80', '#FF5252', '#FF1744', '#D50000' ), 2020 'pink' => array( '#FCE4EC', '#F8BBD0', '#F48FB1', '#F06292', '#EC407A', '#E91E63', '#D81B60', '#C2185B', '#AD1457', '#880E4F', '#FF80AB', '#FF4081', '#F50057', '#C51162' ), 2021 'purple' => array( '#F3E5F5', '#E1BEE7', '#CE93D8', '#BA68C8', '#AB47BC', '#9C27B0', '#8E24AA', '#7B1FA2', '#6A1B9A', '#4A148C', '#EA80FC', '#E040FB', '#D500F9', '#AA00FF' ), 2022 'deep-purple' => array( '#EDE7F6', '#D1C4E9', '#B39DDB', '#9575CD', '#7E57C2', '#673AB7', '#5E35B1', '#512DA8', '#4527A0', '#311B92', '#B388FF', '#7C4DFF', '#651FFF', '#6200EA' ), 2023 'indigo' => array( '#E8EAF6', '#C5CAE9', '#9FA8DA', '#7986CB', '#5C6BC0', '#3F51B5', '#3949AB', '#303F9F', '#283593', '#1A237E', '#8C9EFF', '#536DFE', '#3D5AFE', '#304FFE' ), 2024 'blue' => array( '#E3F2FD', '#BBDEFB', '#90CAF9', '#64B5F6', '#42A5F5', '#2196F3', '#1E88E5', '#1976D2', '#1565C0', '#0D47A1', '#82B1FF', '#448AFF', '#2979FF', '#2962FF' ), 2025 'light_blue' => array( '#E1F5FE', '#B3E5FC', '#81D4fA', '#4fC3F7', '#29B6FC', '#03A9F4', '#039BE5', '#0288D1', '#0277BD', '#01579B', '#80D8FF', '#40C4FF', '#00B0FF', '#0091EA' ), 2026 'cyan' => array( '#E0F7FA', '#B2EBF2', '#80DEEA', '#4DD0E1', '#26C6DA', '#00BCD4', '#00ACC1', '#0097A7', '#00838F', '#006064', '#84FFFF', '#18FFFF', '#00E5FF', '#00B8D4' ), 2027 'teal' => array( '#E0F2F1', '#B2DFDB', '#80CBC4', '#4DB6AC', '#26A69A', '#009688', '#00897B', '#00796B', '#00695C', '#004D40', '#A7FFEB', '#64FFDA', '#1DE9B6', '#00BFA5' ), 2028 'green' => array( '#E8F5E9', '#C8E6C9', '#A5D6A7', '#81C784', '#66BB6A', '#4CAF50', '#43A047', '#388E3C', '#2E7D32', '#1B5E20', '#B9F6CA', '#69F0AE', '#00E676', '#00C853' ), 2029 'light-green' => array( '#F1F8E9', '#DCEDC8', '#C5E1A5', '#AED581', '#9CCC65', '#8BC34A', '#7CB342', '#689F38', '#558B2F', '#33691E', '#CCFF90', '#B2FF59', '#76FF03', '#64DD17' ), 2030 'lime' => array( '#F9FBE7', '#F0F4C3', '#E6EE9C', '#DCE775', '#D4E157', '#CDDC39', '#C0CA33', '#A4B42B', '#9E9D24', '#827717', '#F4FF81', '#EEFF41', '#C6FF00', '#AEEA00' ), 2031 'yellow' => array( '#FFFDE7', '#FFF9C4', '#FFF590', '#FFF176', '#FFEE58', '#FFEB3B', '#FDD835', '#FBC02D', '#F9A825', '#F57F17', '#FFFF82', '#FFFF00', '#FFEA00', '#FFD600' ), 2032 'amber' => array( '#FFF8E1', '#FFECB3', '#FFE082', '#FFD54F', '#FFCA28', '#FFC107', '#FFB300', '#FFA000', '#FF8F00', '#FF6F00', '#FFE57F', '#FFD740', '#FFC400', '#FFAB00' ), 2033 'orange' => array( '#FFF3E0', '#FFE0B2', '#FFCC80', '#FFB74D', '#FFA726', '#FF9800', '#FB8C00', '#F57C00', '#EF6C00', '#E65100', '#FFD180', '#FFAB40', '#FF9100', '#FF6D00' ), 2034 'deep-orange' => array( '#FBE9A7', '#FFCCBC', '#FFAB91', '#FF8A65', '#FF7043', '#FF5722', '#F4511E', '#E64A19', '#D84315', '#BF360C', '#FF9E80', '#FF6E40', '#FF3D00', '#DD2600' ), 2035 'brown' => array( '#EFEBE9', '#D7CCC8', '#BCAAA4', '#A1887F', '#8D6E63', '#795548', '#6D4C41', '#5D4037', '#4E342E', '#3E2723' ), 2036 'grey' => array( '#FAFAFA', '#F5F5F5', '#EEEEEE', '#E0E0E0', '#BDBDBD', '#9E9E9E', '#757575', '#616161', '#424242', '#212121', '#000000', '#ffffff' ), 2037 'blue-grey' => array( '#ECEFF1', '#CFD8DC', '#B0BBC5', '#90A4AE', '#78909C', '#607D8B', '#546E7A', '#455A64', '#37474F', '#263238' ), 2038 ); 2039 2040 $mui_arr = array( 2041 '50', 2042 '100', 2043 '200', 2044 '300', 2045 '400', 2046 '500', 2047 '600', 2048 '700', 2049 '800', 2050 '900', 2051 'A100', 2052 'A200', 2053 'A400', 2054 'A700', 2055 ); 2056 2057 if ( in_array( $context, $mui_arr, true ) ) { 2058 $key = absint( $context ) / 100; 2059 2060 if ( 'A100' === $context ) { 2061 $key = 10; 2062 unset( $colors['grey'] ); 2063 } elseif ( 'A200' === $context ) { 2064 $key = 11; 2065 unset( $colors['grey'] ); 2066 } elseif ( 'A400' === $context ) { 2067 $key = 12; 2068 unset( $colors['grey'] ); 2069 } elseif ( 'A700' === $context ) { 2070 $key = 13; 2071 unset( $colors['grey'] ); 2072 } 2073 2074 unset( $colors['primary'] ); 2075 2076 $position_colors = array(); 2077 foreach ( $colors as $color_family ) { 2078 if ( isset( $color_family[ $key ] ) ) { 2079 $position_colors[] = $color_family[ $key ]; 2080 } 2081 } 2082 2083 return $position_colors; 2084 } elseif ( 'all' === $context ) { 2085 unset( $colors['primary'] ); 2086 2087 $all_colors = array(); 2088 foreach ( $colors as $color_family ) { 2089 foreach ( $color_family as $color ) { 2090 $all_colors[] = $color; 2091 } 2092 } 2093 2094 return $all_colors; 2095 } elseif ( 'primary' === $context ) { 2096 return $colors['primary']; 2097 } else { 2098 if ( isset( $colors[ $context ] ) ) { 2099 return $colors[ $context ]; 2100 } 2101 2102 return $colors['primary']; 2103 } 2104 } 2105 } 2106 }