opt_name = $redux; } else { parent::__construct( $redux ); } } /** * Get the data. * * @param string|array $type Type. * @param array|string $args Args. * @param string $opt_name Opt name. * @param string|int $current_value Current value. * @param bool $ajax Tells if this is an AJAX call. * * @return array|mixed|string */ public function get( $type, $args = array(), string $opt_name = '', $current_value = '', bool $ajax = false ) { $opt_name = $this->opt_name; // We don't want to run this, it's not a string value. Send it back! if ( is_array( $type ) ) { return $type; } /** * Filter 'redux/options/{opt_name}/pre_data/{type}' * * @param string $data */ $pre_data = apply_filters( "redux/options/$opt_name/pre_data/$type", null ); // phpcs:ignore WordPress.NamingConventions.ValidHookName if ( null !== $pre_data || empty( $type ) ) { return $pre_data; } if ( isset( $args['data_sortby'] ) && in_array( $args['data_sortby'], array( 'value', 'key' ), true ) ) { $data_sort = $args['data_sortby']; unset( $args['data_sortby'] ); } else { $data_sort = 'value'; } if ( isset( $args['data_order'] ) && in_array( $args['data_order'], array( 'asc', 'desc' ), true ) ) { $data_order = $args['data_order']; unset( $args['data_order'] ); } else { $data_order = 'asc'; } $this->maybe_get_translation( $type, $current_value, $args ); $current_data = array(); if ( empty( $current_value ) && ! Redux_Helpers::is_integer( $current_value ) ) { $current_value = null; } else { // Get the args to grab the current data. $current_data_args = $this->get_current_data_args( $type, $args, $current_value ); // Let's make a unique key for this arg array. $current_data_args_key = md5( maybe_serialize( $current_data_args ) ); // Check to make sure we haven't already run this call before. $current_data = $this->wp_data[ $type . $current_data_args_key ] ?? $this->get_data( $type, $current_data_args, $current_value ); } // If ajax is enabled AND $current_data is empty, set a dummy value for the init. if ( $ajax && ! wp_doing_ajax() ) { // Dummy is needed otherwise empty. if ( empty( $current_data ) ) { $current_data = array( 'dummy' => '', ); } return $current_data; } // phpcs:ignore Squiz.PHP.CommentedOutCode $args_key = md5( maybe_serialize( $args ) ); // Data caching. if ( isset( $this->wp_data[ $type . $args_key ] ) ) { $data = $this->wp_data[ $type . $args_key ]; } else { /** * Use data from WordPress to populate options array. * */ $data = $this->get_data( $type, $args, $current_value ); } if ( ! empty( $current_data ) ) { $data += $current_data; } if ( ! empty( $data ) ) { $data = $this->order_data( $data, $data_sort, $data_order ); $this->wp_data[ $type . $args_key ] = $data; } /** * Filter 'redux/options/{opt_name}/data/{type}' * * @param string $data */ // phpcs:ignore WordPress.NamingConventions.ValidHookName return apply_filters( "redux/options/$opt_name/data/$type", $data ); } /** * Process the results into a proper array, fetching the data elements needed for each data type. * * @param array|WP_Error $results Results to process in the data array. * @param string|bool $id_key Key on object/array that represents the ID. * @param string|bool $name_key Key on object/array that represents the name/text. * @param bool $add_key If true, the display key will appear in the text. * @param string|bool $secondary_key If a data type you'd rather display a different ID as the display key. * * @return array */ private function process_results( $results = array(), $id_key = '', $name_key = '', bool $add_key = true, $secondary_key = 'slug' ): array { $data = array(); if ( ! empty( $results ) && ! is_a( $results, 'WP_Error' ) ) { foreach ( $results as $k => $v ) { if ( empty( $id_key ) ) { $key = $k; } else { if ( is_object( $v ) ) { $key = $v->$id_key; } elseif ( is_array( $v ) ) { $key = $v[ $id_key ]; } else { $key = $k; } } if ( empty( $name_key ) ) { $value = $v; } else { if ( is_object( $v ) ) { $value = $v->$name_key; } elseif ( is_array( $v ) ) { $value = $v[ $name_key ]; } else { $value = $v; } } $display_key = $key; if ( is_object( $v ) && isset( $v->$secondary_key ) ) { $display_key = $v->$secondary_key; } elseif ( ! is_object( $v ) && isset( $v[ $secondary_key ] ) ) { $display_key = $v[ $secondary_key ]; } $data[ $key ] = $value; if ( $display_key !== $value && $add_key ) { $data[ $key ] = $data[ $key ] . ' [' . $display_key . ']'; } } } return $data; } /** * Order / Sort the data. * * @param array $data Data to sort. * @param string $sort Way to sort. Accepts: key|value. * @param string $order Order of the sort. Accepts: asc|desc. * * @return array */ private function order_data( array $data = array(), string $sort = 'value', string $order = 'asc' ): array { if ( 'key' === $sort ) { if ( 'asc' === $order ) { ksort( $data ); } else { krsort( $data ); } } elseif ( 'value' === $sort ) { if ( 'asc' === $order ) { asort( $data ); } else { arsort( $data ); } } return $data; } /** * Fetch the data for a given type. * * @param string $type The data type we're fetching. * @param array|string $args Arguments to pass. * @param mixed|array $current_value If a current value already set in the database. * * @return array|null|string */ private function get_data( string $type, $args, $current_value ) { $args = $this->get_arg_defaults( $type, $args ); $opt_name = $this->opt_name; if ( empty( $args ) ) { $args = array(); } $data = array(); if ( isset( $args['args'] ) && empty( $args['args'] ) ) { unset( $args['args'] ); } $display_keys = false; if ( isset( $args['display_keys'] ) ) { $display_keys = true; unset( $args['display_keys'] ); } $secondary_key = 'slug'; if ( isset( $args['secondary_key'] ) ) { $secondary_key = $args['secondary_key']; unset( $args['secondary_key'] ); } switch ( $type ) { case 'categories': case 'category': case 'terms': case 'term': if ( isset( $args['taxonomies'] ) ) { $args['taxonomy'] = $args['taxonomies']; unset( $args['taxonomies'] ); } $results = get_terms( $args ); $data = $this->process_results( $results, 'term_id', 'name', $display_keys, $secondary_key ); break; case 'pages': case 'page': $results = get_pages( $args ); $data = $this->process_results( $results, 'ID', 'post_title', $display_keys, $secondary_key ); break; case 'tags': case 'tag': $results = get_tags( $args ); $data = $this->process_results( $results, 'term_id', 'name', $display_keys, $secondary_key ); break; case 'menus': case 'menu': $results = wp_get_nav_menus( $args ); $data = $this->process_results( $results, 'term_id', 'name', $display_keys, $secondary_key ); break; case 'posts': case 'post': $results = get_posts( $args ); $data = $this->process_results( $results, 'ID', 'post_title', $display_keys, $secondary_key ); break; case 'users': case 'user': $results = get_users( $args ); $data = $this->process_results( $results, 'ID', 'display_name', $display_keys, $secondary_key ); break; case 'sites': case 'site': $sites = get_sites(); if ( isset( $sites ) ) { $results = array(); foreach ( $sites as $site ) { $site = (array) $site; $k = $site['blog_id']; $v = $site['domain'] . $site['path']; $name = get_blog_option( $k, 'blogname' ); if ( ! empty( $name ) ) { $v .= ' - [' . $name . ']'; } $results[ $k ] = $v; } $data = $this->process_results( $results, '', '', $display_keys, $secondary_key ); } break; case 'taxonomies': case 'taxonomy': case 'tax': $results = get_taxonomies( $args ); $data = $this->process_results( $results, '', '', $display_keys, $secondary_key ); break; case 'post_types': case 'post_type': global $wp_post_types; $output = $args['output']; unset( $args['output'] ); $operator = $args['operator']; unset( $args['operator'] ); $post_types = get_post_types( $args, $output, $operator ); foreach ( $post_types as $name => $title ) { if ( isset( $wp_post_types[ $name ]->labels->menu_name ) ) { $data[ $name ] = $wp_post_types[ $name ]->labels->menu_name; } else { $data[ $name ] = ucfirst( $name ); } } break; case 'menu_locations': case 'menu_location': global $_wp_registered_nav_menus; foreach ( $_wp_registered_nav_menus as $k => $v ) { $data[ $k ] = $v; if ( ! has_nav_menu( $k ) ) { $data[ $k ] .= ' ' . __( '[unassigned]', 'redux-framework' ); } } break; case 'image_sizes': case 'image_size': global $_wp_additional_image_sizes; $results = array(); foreach ( $_wp_additional_image_sizes as $size_name => $size_attrs ) { $results[ $size_name ] = $size_name . ' - ' . $size_attrs['width'] . ' x ' . $size_attrs['height']; } $data = $this->process_results( $results, '', '', $display_keys, $secondary_key ); break; case 'elusive-icons': case 'elusive-icon': case 'elusive': case 'icons': case 'font-icon': case 'font-icons': $fs = Redux_Filesystem::get_instance(); $fonts = $fs->get_contents( Redux_Core::$dir . 'assets/css/vendor/elusive-icons.css' ); if ( ! empty( $fonts ) ) { preg_match_all( '@\.el-(\w+)::before@', $fonts, $matches ); foreach ( $matches[1] as $item ) { if ( 'before' === $item ) { continue; } $data[ 'el el-' . $item ] = $item; } } /** * Filter 'redux/font-icons' * * @param array $font_icons array of elusive icon classes * * @deprecated */ // phpcs:ignore WordPress.NamingConventions.ValidHookName $font_icons = apply_filters_deprecated( 'redux/font-icons', array( $data ), '4.3', 'redux/$opt_name/field/font/icons' ); /** * Filter 'redux/{opt_name}/field/font/icons' * * @param array $font_icons array of elusive icon classes */ // phpcs:ignore WordPress.NamingConventions.ValidHookName $data = apply_filters( "redux/$opt_name/field/font/icons", $font_icons ); break; case 'dashicons': case 'dashicon': case 'dash': $fs = Redux_Filesystem::get_instance(); $fonts = $fs->get_contents( ABSPATH . WPINC . '/css/dashicons.css' ); if ( ! empty( $fonts ) ) { preg_match_all( '@\.dashicons-(\w+):before@', $fonts, $matches ); foreach ( $matches[1] as $item ) { if ( 'before' === $item ) { continue; } $data[ 'dashicons dashicons-' . $item ] = $item; } } break; case 'roles': case 'role': global $wp_roles; $results = $wp_roles->get_names(); $data = $this->process_results( $results, '', '', $display_keys, $secondary_key ); break; case 'sidebars': case 'sidebar': global $wp_registered_sidebars; $data = $this->process_results( $wp_registered_sidebars, '', 'name', $display_keys, $secondary_key ); break; case 'capabilities': case 'capability': global $wp_roles; $results = array(); foreach ( $wp_roles->roles as $role ) { foreach ( $role['capabilities'] as $key => $cap ) { $results[ $key ] = ucwords( str_replace( '_', ' ', $key ) ); } } $data = $this->process_results( $results, '', '', $display_keys, $secondary_key ); break; case 'capabilities_grouped': case 'capability_grouped': case 'capabilities_group': case 'capability_group': global $wp_roles; foreach ( $wp_roles->roles as $role ) { $caps = array(); foreach ( $role['capabilities'] as $key => $cap ) { $caps[ $key ] = ucwords( str_replace( '_', ' ', $key ) ); } asort( $caps ); $data[ $role['name'] ] = $caps; } break; case 'callback': if ( ! empty( $args ) && is_string( $args ) && function_exists( $args ) ) { $data = call_user_func( $args, $current_value ); } break; } return $data; } /** * Router for translation based on the given post type. * * @param string $type Type of data request. * @param mixed|array $current_value Current value stored in DB. * @param array|string $args Arguments for the call. */ private function maybe_get_translation( string $type, &$current_value = '', $args = array() ) { switch ( $type ) { case 'categories': case 'category': $this->maybe_translate( $current_value, 'category' ); break; case 'pages': case 'page': $this->maybe_translate( $current_value, 'page' ); break; case 'terms': case 'term': $this->maybe_translate( $current_value, $args['taxonomy'] ?? '' ); break; case 'tags': case 'tag': $this->maybe_translate( $current_value, 'post_tag' ); break; case 'menus': case 'menu': $this->maybe_translate( $current_value, 'nav_menu' ); break; case 'post': case 'posts': $this->maybe_translate( $current_value, 'post' ); break; default: $this->maybe_translate( $current_value, '' ); } } /** * Maybe translate the values. * * @param mixed|array $value Value. * @param mixed|array $post_type Post type. */ private function maybe_translate( &$value, $post_type ) { // phpcs:ignore WordPress.NamingConventions.ValidHookName $value = apply_filters( "redux/options/$this->opt_name/wordpress_data/translate/post_type_value", $value, $post_type ); // WPML Integration, see https://wpml.org/documentation/support/creating-multilingual-wordpress-themes/language-dependent-ids/. if ( function_exists( 'icl_object_id' ) ) { if ( has_filter( 'wpml_object_id' ) ) { if ( Redux_Helpers::is_integer( $value ) ) { $value = apply_filters( 'wpml_object_id', $value, $post_type, true ); } elseif ( is_array( $value ) ) { $value = array_map( function ( $val ) use ( $post_type ) { return apply_filters( 'wpml_object_id', $val, $post_type, true ); }, $value ); } } } } /** * Set the default arguments for a current query (existing data). * * @param string $type Type of data request. * @param array|string $args Arguments for the call. * @param mixed|array $current_value Current value stored in DB. * * @return array */ private function get_current_data_args( string $type, $args, $current_value ): array { // In this section we set the default arguments for each data type. switch ( $type ) { case 'categories': case 'category': case 'pages': case 'page': case 'terms': case 'term': case 'users': case 'user': $args['include'] = $current_value; break; case 'tags': case 'tag': $args['get'] = 'all'; break; case 'menus': case 'menu': $args['object_ids'] = $current_value; break; case 'post': case 'posts': if ( ! empty( $current_value ) ) { $args['post__in'] = is_array( $current_value ) ? $current_value : array( $current_value ); } break; default: $args = array(); } return $args; } /** * Get default arguments for a given data type. * * @param string $type Type of data request. * @param array|string $args Arguments for the call. * * @return array|string */ private function get_arg_defaults( string $type, $args = array() ) { // In this section we set the default arguments for each data type. switch ( $type ) { case 'categories': case 'category': $args = wp_parse_args( $args, array( 'taxonomy' => 'category', ) ); break; case 'pages': case 'page': $args = wp_parse_args( $args, array( 'display_keys' => true, 'posts_per_page' => 20, ) ); break; case 'post_type': case 'post_types': $args = wp_parse_args( $args, array( 'public' => true, 'exclude_from_search' => false, 'output' => 'names', 'operator' => 'and', ) ); break; case 'tag': case 'tags': $args = wp_parse_args( $args, array( 'get' => 'all', 'display_keys' => true, ) ); break; case 'sidebars': case 'sidebar': case 'capabilities': case 'capability': $args = wp_parse_args( $args, array( 'display_keys' => true, ) ); break; case 'capabilities_grouped': case 'capability_grouped': case 'capabilities_group': case 'capability_group': $args = wp_parse_args( $args, array( 'data_sortby' => '', ) ); break; } return $args; } } }