a_Utils */ protected static $_ = null; /** * @var ET_Core_PageResource */ public static $advanced_styles_manager = null; /** * @var ET_Core_Data_Utils */ public static $data_utils = null; public static $field_dependencies = array(); public static $can_reset_element_indexes = true; const DEFAULT_PRIORITY = 10; const HIDE_ON_MOBILE = 'et-hide-mobile'; protected $module_credits; function __construct() { self::$current_module_index++; if ( ! self::$_deprecations ) { self::$_deprecations = require ET_BUILDER_DIR . 'deprecations.php'; self::$_deprecations = self::$_deprecations['classes']['\ET_Builder_Module_Blurb']; } if ( ! self::$settings_migrations_initialized ) { self::$settings_migrations_initialized = true; require_once 'module/settings/Migration.php'; ET_Builder_Module_Settings_Migration::init(); add_filter( 'the_content', array( get_class( $this ), 'reset_element_indexes' ), 9999 ); } if ( self::$loading_backbone_templates || et_admin_backbone_templates_being_loaded() ) { if ( ! self::$loading_backbone_templates ) { self::$loading_backbone_templates = true; } $start_from = (int) sanitize_text_field( $_POST['et_templates_start_from'] ); $post_type = sanitize_text_field( $_POST['et_post_type'] ); if ( 'layout' === $post_type ) { // need - 2 to include the et_pb_section and et_pb_row modules $start_from = ET_Builder_Element::get_modules_count( 'page' ) - 2; } $current_module_index = self::$current_module_index - 1; if ( ! ( $current_module_index >= $start_from && $current_module_index < ( ET_BUILDER_AJAX_TEMPLATES_AMOUNT + $start_from ) ) ) { return; } } if ( null === self::$advanced_styles_manager && ! is_admin() && ! et_fb_is_enabled() ) { self::_setup_advanced_styles_manager(); } if ( null === self::$data_utils ) { self::$_ = self::$data_utils = ET_Core_Data_Utils::instance(); } $this->init(); $this->settings_modal_tabs = $this->get_settings_modal_tabs(); $this->settings_modal_toggles = $this->get_settings_modal_toggles(); $this->custom_css_fields = $this->get_custom_css_fields_config(); $this->_set_advanced_fields_config(); $this->_is_official_module = self::_is_official_module( get_class( $this ) ); $this->make_options_filterable(); $this->set_fields(); $this->set_factory_objects(); $this->_additional_fields_options = array(); $this->_add_additional_fields(); $this->_add_custom_css_fields(); $this->_maybe_add_global_defaults(); $this->_finalize_all_fields(); if ( ! isset( $this->main_css_element ) ) { $this->main_css_element = '%%order_class%%'; } $this->_render_count = 0; $this->type = isset( $this->type ) ? $this->type : ''; $this->decode_entities = isset( $this->decode_entities ) ? (bool) $this->decode_entities : false; $this->_style_priority = (int) self::DEFAULT_PRIORITY; if ( isset( $this->type ) && 'child' === $this->type ) { $this->_style_priority = $this->_style_priority + 1; } else { // add default toggles $default_general_toggles = array( 'admin_label' => array( 'title' => esc_html__( 'Admin Label', 'et_builder' ), 'priority' => 99, ), ); $default_advanced_toggles = array( 'visibility' => array( 'title' => esc_html__( 'Visibility', 'et_builder' ), 'priority' => 99, ), ); $this->_add_settings_modal_toggles( 'general', $default_general_toggles ); $this->_add_settings_modal_toggles( 'custom_css', $default_advanced_toggles ); } $this->main_tabs = $this->get_main_tabs(); $this->custom_css_tab = isset( $this->custom_css_tab ) ? $this->custom_css_tab : true; self::$modules[ $this->slug ] = $this; $post_types = ! empty( $this->post_types ) ? $this->post_types : et_builder_get_builder_post_types(); // all modules should be assigned for et_pb_layout post type to work in the library if ( ! in_array( 'et_pb_layout', $post_types ) ) { $post_types[] = 'et_pb_layout'; } $this->post_types = apply_filters( 'et_builder_module_post_types', $post_types, $this->slug, $this->post_types ); foreach ( $this->post_types as $post_type ) { if ( ! in_array( $post_type, $this->post_types ) ) { $this->register_post_type( $post_type ); } if ( ! isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) { self::$_module_slugs_by_post_type[ $post_type ] = array(); } if ( ! in_array( $this->slug, self::$_module_slugs_by_post_type[ $post_type ] ) ) { self::$_module_slugs_by_post_type[ $post_type ][] = $this->slug; } if ( 'child' == $this->type ) { self::$child_modules[ $post_type ][ $this->slug ] = $this; } else { self::$parent_modules[ $post_type ][ $this->slug ] = $this; } } if ( ! isset( $this->no_render ) ) { $shortcode_slugs = array( $this->slug ); if ( ! empty( $this->additional_shortcode_slugs ) ) { $shortcode_slugs = array_merge( $shortcode_slugs, $this->additional_shortcode_slugs ); } foreach ( $shortcode_slugs as $shortcode_slug ) { add_shortcode( $shortcode_slug, array( $this, '_render' ) ); } if ( isset( $this->additional_shortcode ) ) { add_shortcode( $this->additional_shortcode, array( $this, 'additional_render' ) ); } } if ( isset( $this->icon ) ) { self::$_->array_set( self::$module_icons, "{$this->slug}.icon", $this->icon ); } if ( isset( $this->icon_path ) ) { self::$_->array_set( self::$module_icons, "{$this->slug}.icon_path", $this->icon_path ); } // Push module's help videos to all help videos array if there's any if ( ! empty( $this->help_videos ) ) { // Automatically add design tab and library tutorial. DRY if ( 'et_pb_column' !== $this->slug ) { // Adding next tabs (design & tab) helps $next_tabs_help = array( 'id' => esc_html__( '1iqjhnHVA9Y', 'et_builder' ), 'name' => esc_html__( 'Design Settings and Advanced Module Settings', 'et_builder' ), ); // Adjust row name if ( in_array( $this->slug, array( 'et_pb_row', 'et_pb_row_inner' ) ) ) { $next_tabs_help['name'] = esc_html__( 'Design Settings and Advanced Row Settings', 'et_builder' ); } // Adjust section name if ( 'et_pb_section' === $this->slug ) { $next_tabs_help['name'] = esc_html__( 'Design Settings and Advanced Section Settings', 'et_builder' ); } $this->help_videos[] = $next_tabs_help; // Adding Divi Library helps $this->help_videos[] = array( 'id' => esc_html( 'boNZZ0MYU0E' ), 'name' => esc_html__( 'Saving and loading from the library', 'et_builder' ), ); } self::$module_help_videos[ $this->slug ] = $this->help_videos; } } public function __call( $name, $args ) { $class = get_class( $this ); $message = "You're Doing It Wrong!"; $is_deprecated = array_key_exists( $name, self::$_deprecations['methods'] ); $value = null; $old_method_exists = method_exists( $this, $name ); if ( $old_method_exists && ! $is_deprecated ) { // Inaccessible method (protected or private) that isn't deprecated et_debug( "{$message} Attempted to call {$class}::{$name}() from out of scope.", 4, false ); return $value; } $message .= " {$class}::{$name}()"; if ( ! $is_deprecated ) { $message .= " doesn't exist."; } else { $message .= " is deprecated."; $new_method = self::$_deprecations['methods'][ $name ]; if ( ! is_string( $new_method ) ) { // Default value for a method that has no replacement. $value = $new_method; } else if ( method_exists( $this, $new_method ) && ! $old_method_exists ) { $message .= " Use {$class}::{$new_method}() instead."; $value = call_user_func_array( array( $this, $new_method ), $args ); } else if ( $old_method_exists ) { // Ensure that our current caller is not the same as the method we're about to call. // as that would cause an infinite recursion situation. It happens when a child class // method which has been deprecated calls itself on the parent class (using parent::) $trace = debug_backtrace(); $callers = array( self::$_->array_get( $trace, '1.function' ), self::$_->array_get( $trace, '2.function' ), ); if ( ! in_array( $name, $callers ) ) { // We've used $this->__call() to call a deprecated method from its replacement // method so that a deprecation notice will be output. $message .= " Use {$class}::{$new_method}() instead."; $value = call_user_func_array( array( $this, $name ), $args ); } } } et_debug( $message, 4, false ); return $value; } public function &__get( $name ) { $class = get_class( $this ); $message = "You're Doing It Wrong!"; $is_deprecated = array_key_exists( $name, self::$_deprecations['properties'] ); $value = null; if ( property_exists( $this, $name ) && ! $is_deprecated ) { // Inaccessible property (protected or private) that isn't deprecated et_debug( "{$message} Attempted to access {$class}::\${$name} from out of scope.", 4, false ); return $value; } $message .= " {$class}::\${$name}"; if ( ! $is_deprecated ) { $message .= " doesn't exist."; $should_set_value = true; } else { $message .= " is deprecated."; $new_prop = self::$_deprecations['properties'][ $name ]; if ( $new_prop && is_string( $new_prop ) && property_exists( $this, $new_prop ) ) { $message .= " Use {$class}::\${$new_prop} instead."; $value = &$this->$new_prop; } else if ( ! is_string( $new_prop ) || ! $new_prop ) { // Default value $value = $new_prop; $should_set_value = true; } } if ( isset( $should_set_value ) ) { // Create the property so we can return a reference to it which allows it to be // used like this: $this->name[] = 'something' $this->$name = $value; $value = &$this->$name; } et_debug( $message, 4, false ); return $value; } public function __isset( $name ) { $prop_name = array_key_exists( $name, self::$_deprecations['properties'] ) ? self::$_deprecations['properties'][ $name ] : $name; if ( ! $prop_name || ! is_string( $prop_name ) ) { return false; } return property_exists( $this, $prop_name ); } public function __set( $name, $value ) { $class = get_class( $this ); $message = "You're Doing It Wrong!"; $is_deprecated = array_key_exists( $name, self::$_deprecations['properties'] ); $property_exists = property_exists( $this, $name ); $has_replacement = $property_exists && is_string( self::$_deprecations['properties'][ $name ] ) && self::$_deprecations['properties'][ $name ]; if ( $property_exists && ! $is_deprecated ) { // Inaccessible property (protected or private) that isn't deprecated et_debug( "{$message} Attempted to access {$class}::\${$name} from out of scope.", 4, false ); return; } if ( ( ! $property_exists && ! $is_deprecated ) || ! $has_replacement ) { // Always allow setting values for properties that are undeclared $this->$name = $value; } if ( ! $is_deprecated ) { return; } $message = " {$class}::\${$name} is deprecated."; $replacement = self::$_deprecations['properties'][ $name ]; if ( $replacement && is_string( $replacement ) ) { $message .= " Use {$class}::\${$replacement} instead."; $this->$replacement = $value; // Unset deprecated property so next time it's updated we process it again unset( $this->$name ); } et_debug( $message, 4, false ); } private static function _is_official_module( $class_name ) { try { $reflection = new ReflectionClass( $class_name ); $is_official = self::$_->includes( $reflection->getFileName(), ET_BUILDER_DIR_RESOLVED_PATH ); } catch( Exception $err ) { $is_official = false; } return $is_official; } protected function _set_advanced_fields_config() { $this->advanced_fields = $this->get_advanced_fields_config(); // 3rd-Party Backwards Compatability if ( isset( $this->advanced_fields['custom_margin_padding'] ) ) { $this->advanced_fields['margin_padding'] = $this->advanced_fields['custom_margin_padding']; unset( $this->advanced_fields['custom_margin_padding'] ); } } /** * Retrieve Post ID from 1 of 3 sources depending on which exists: * - get_the_ID() * - $_POST['et_post_id'] * - $_GET['post'] * * @return int|bool */ public function get_the_ID() { $post_id = get_the_ID(); // try to get post id from get_post_ID() if ( false !== $post_id ) { return $post_id; } if ( wp_doing_ajax() ) { // get the post ID if loading data for VB return isset( $_POST['et_post_id'] ) ? absint( $_POST['et_post_id'] ) : false; } // fallback to $_GET['post'] to cover the BB data loading return isset( $_GET['post'] ) ? absint( $_GET['post'] ) : false; } /** * Setup the advanced styles manager * * {@internal * Before the styles manager was implemented, the advanced styles were output inline in the footer. * That resulted in them being the last styles parsed by the browser, thus giving them higher * priority than other styles on the page. With the styles manager, the advanced styles are * enqueued at the very end of the