$options['name'], 'class_name' => $options['class_name'] ); $this->base_id = $options['base_id']; // lets filter the options before we do anything $options = apply_filters( "wp_super_duper_options", $options ); $options = apply_filters( "wp_super_duper_options_{$this->base_id}", $options ); $options = $this->add_name_from_key( $options ); $this->options = $options; $this->base_id = $options['base_id']; $this->arguments = isset( $options['arguments'] ) ? $options['arguments'] : array(); // init parent parent::__construct( $options['base_id'], $options['name'], $options['widget_ops'] ); if ( isset( $options['class_name'] ) ) { // register widget $this->class_name = $options['class_name']; // register shortcode $this->register_shortcode(); // register block add_action( 'admin_enqueue_scripts', array( $this, 'register_block' ) ); } // add the CSS and JS we need ONCE global $sd_widget_scripts; if ( ! $sd_widget_scripts ) { wp_add_inline_script( 'admin-widgets', $this->widget_js() ); wp_add_inline_script( 'customize-controls', $this->widget_js() ); wp_add_inline_style( 'widgets', $this->widget_css() ); $sd_widget_scripts = true; // add shortcode insert button once add_action( 'media_buttons', array( $this, 'shortcode_insert_button' ) ); // generatepress theme sections compatibility if ( function_exists( 'generate_sections_sections_metabox' ) ) { add_action( 'generate_sections_metabox', array( $this, 'shortcode_insert_button_script' ) ); } if ( $this->is_preview() ) { add_action( 'wp_footer', array( $this, 'shortcode_insert_button_script' ) ); // this makes the insert button work for elementor add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'shortcode_insert_button_script' ) ); // for elementor } // this makes the insert button work for cornerstone add_action('wp_print_footer_scripts',array( __CLASS__, 'maybe_cornerstone_builder' )); add_action( 'wp_ajax_super_duper_get_widget_settings', array( __CLASS__, 'get_widget_settings' ) ); add_action( 'wp_ajax_super_duper_get_picker', array( __CLASS__, 'get_picker' ) ); // add generator text to admin head add_action( 'admin_head', array( $this, 'generator' ) ); } do_action( 'wp_super_duper_widget_init', $options, $this ); } /** * Maybe insert the shortcode inserter button in the footer if we are in the cornerstone builder */ public static function maybe_cornerstone_builder(){ if(did_action('cornerstone_before_boot_app')){ self::shortcode_insert_button_script(); } } /** * A function to ge the shortcode builder picker html. * * @param string $editor_id * * @return string */ public static function get_picker( $editor_id = '' ) { ob_start(); if ( isset( $_POST['editor_id'] ) ) { $editor_id = esc_attr( $_POST['editor_id'] ); } elseif ( isset( $_REQUEST['et_fb'] ) ) { $editor_id = 'main_content_content_vb_tiny_mce'; } global $sd_widgets; ?>
'; echo ""; foreach ( $sd_widgets as $shortcode => $class ) { echo ""; } echo ""; } ?>
version . '" />'; } /** * Get widget settings. * * @since 1.0.0 */ public static function get_widget_settings() { global $sd_widgets; $shortcode = isset( $_REQUEST['shortcode'] ) && $_REQUEST['shortcode'] ? sanitize_title_with_dashes( $_REQUEST['shortcode'] ) : ''; if ( ! $shortcode ) { wp_die(); } $widget_args = isset( $sd_widgets[ $shortcode ] ) ? $sd_widgets[ $shortcode ] : ''; if ( ! $widget_args ) { wp_die(); } $class_name = isset( $widget_args['class_name'] ) && $widget_args['class_name'] ? $widget_args['class_name'] : ''; if ( ! $class_name ) { wp_die(); } // invoke an instance method $widget = new $class_name; ob_start(); $widget->form( array() ); $form = ob_get_clean(); echo "
" . $form . "
"; echo ""; echo ""; ?> '; } echo self::shortcode_button( 'this', 'true' ); // see opening note if ( function_exists( 'cornerstone_plugin_init' ) && ! is_admin() ) { echo ''; // end #insert-media-button } // Add separate script for generatepress theme sections if ( function_exists( 'generate_sections_sections_metabox' ) && did_action( 'generate_sections_metabox' ) ) { } else { self::shortcode_insert_button_script( $editor_id, $insert_shortcode_function ); } $shortcode_insert_button_once = true; } /** * Gets the shortcode insert button html. * * @param string $id * @param string $search_for_id * * @return mixed */ public static function shortcode_button( $id = '', $search_for_id = '' ) { ob_start(); ?> );" href="#TB_inline?width=100%&height=550&inlineId=super-duper-content-ajaxed" class="thickbox button super-duper-content-open" title="Add Shortcode"> tags for code highlighting, so we strip them from the output. */ return str_replace( array( '' ), '', $output ); } /** * Output the JS and CSS for the shortcode insert button. * * @since 1.0.6 * * @param string $editor_id * @param string $insert_shortcode_function */ public static function shortcode_insert_button_script( $editor_id = '', $insert_shortcode_function = '' ) { ?> " . self::siteorigin_js() . ""; } ?> tags for code highlighting, so we strip them from the output. */ return str_replace( array( '' ), '', $output ); } /** * Gets some JS for the widgets screen. * * @return mixed */ public function widget_js() { ob_start(); ?> tags for code highlighting, so we strip them from the output. */ return str_replace( array( '' ), '', $output ); } /** * Set the name from the argument key. * * @param $options * * @return mixed */ private function add_name_from_key( $options, $arguments = false ) { if ( ! empty( $options['arguments'] ) ) { foreach ( $options['arguments'] as $key => $val ) { $options['arguments'][ $key ]['name'] = $key; } } elseif ( $arguments && is_array( $options ) && ! empty( $options ) ) { foreach ( $options as $key => $val ) { $options[ $key ]['name'] = $key; } } return $options; } /** * Register the parent shortcode. * * @since 1.0.0 */ public function register_shortcode() { add_shortcode( $this->base_id, array( $this, 'shortcode_output' ) ); add_action( 'wp_ajax_super_duper_output_shortcode', array( __CLASS__, 'render_shortcode' ) ); } /** * Render the shortcode via ajax so we can return it to Gutenberg. * * @since 1.0.0 */ public static function render_shortcode() { check_ajax_referer( 'super_duper_output_shortcode', '_ajax_nonce', true ); if ( ! current_user_can( 'manage_options' ) ) { wp_die(); } // we might need the $post value here so lets set it. if ( isset( $_POST['post_id'] ) && $_POST['post_id'] ) { $post_obj = get_post( absint( $_POST['post_id'] ) ); if ( ! empty( $post_obj ) && empty( $post ) ) { global $post; $post = $post_obj; } } if ( isset( $_POST['shortcode'] ) && $_POST['shortcode'] ) { $shortcode_name = sanitize_title_with_dashes( $_POST['shortcode'] ); $attributes_array = isset( $_POST['attributes'] ) && $_POST['attributes'] ? $_POST['attributes'] : array(); $attributes = ''; if ( ! empty( $attributes_array ) ) { foreach ( $attributes_array as $key => $value ) { $attributes .= " " . sanitize_title_with_dashes( $key ) . "='" . wp_slash( $value ) . "' "; } } $shortcode = "[" . $shortcode_name . " " . $attributes . "]"; echo do_shortcode( $shortcode ); } wp_die(); } /** * Output the shortcode. * * @param array $args * @param string $content * * @return string */ public function shortcode_output( $args = array(), $content = '' ) { $args = self::argument_values( $args ); // add extra argument so we know its a output to gutenberg //$args $args = $this->string_to_bool( $args ); $calss = isset( $this->options['widget_ops']['classname'] ) ? esc_attr( $this->options['widget_ops']['classname'] ) : ''; $calss = apply_filters( 'wp_super_duper_div_classname', $calss, $args, $this ); $calss = apply_filters( 'wp_super_duper_div_classname_' . $this->base_id, $calss, $args, $this ); $attrs = apply_filters( 'wp_super_duper_div_attrs', '', $args, $this ); $attrs = apply_filters( 'wp_super_duper_div_attrs_' . $this->base_id, '', $args, $this ); $shortcode_args = array(); $output = ''; $no_wrap = isset( $this->options['no_wrap'] ) && $this->options['no_wrap'] ? true : false; $main_content = $this->output( $args, $shortcode_args, $content ); if ( $main_content && ! $no_wrap ) { // wrap the shortcode in a dive with the same class as the widget $output .= '
'; if ( ! empty( $args['title'] ) ) { // if its a shortcode and there is a title try to grab the title wrappers $shortcode_args = array( 'before_title' => '', 'after_title' => '' ); if ( empty( $instance ) ) { global $wp_registered_sidebars; if ( ! empty( $wp_registered_sidebars ) ) { foreach ( $wp_registered_sidebars as $sidebar ) { if ( ! empty( $sidebar['before_title'] ) ) { $shortcode_args['before_title'] = $sidebar['before_title']; $shortcode_args['after_title'] = $sidebar['after_title']; break; } } } } $output .= $this->output_title( $shortcode_args, $args ); } $output .= $main_content; $output .= '
'; } elseif ( $main_content && $no_wrap ) { $output .= $main_content; } // if preview show a placeholder if empty if ( $this->is_preview() && $output == '' ) { $output = $this->preview_placeholder_text( "[{" . $this->base_id . "}]" ); } return apply_filters( 'wp_super_duper_widget_output', $output, $args, $shortcode_args, $this ); } /** * Placeholder text to show if output is empty and we are on a preview/builder page. * * @param string $name * * @return string */ public function preview_placeholder_text( $name = '' ) { return "
" . sprintf( __( 'Placeholder for: %s' ), $name ) . "
"; } /** * Sometimes booleans values can be turned to strings, so we fix that. * * @param $options * * @return mixed */ public function string_to_bool( $options ) { // convert bool strings to booleans foreach ( $options as $key => $val ) { if ( $val == 'false' ) { $options[ $key ] = false; } elseif ( $val == 'true' ) { $options[ $key ] = true; } } return $options; } /** * Get the argument values that are also filterable. * * @param $instance * * @since 1.0.12 Don't set checkbox default value if the value is empty. * * @return array */ public function argument_values( $instance ) { $argument_values = array(); // set widget instance $this->instance = $instance; if ( empty( $this->arguments ) ) { $this->arguments = $this->get_arguments(); } if ( ! empty( $this->arguments ) ) { foreach ( $this->arguments as $key => $args ) { // set the input name from the key $args['name'] = $key; // $argument_values[ $key ] = isset( $instance[ $key ] ) ? $instance[ $key ] : ''; if($args['type']=='checkbox' && $argument_values[ $key ] == ''){ // don't set default for an empty checkbox } elseif ( $argument_values[ $key ] == '' && isset( $args['default'] ) ) { $argument_values[ $key ] = $args['default']; } } } return $argument_values; } /** * Set arguments in super duper. * * @since 1.0.0 * * @return array Set arguments. */ public function set_arguments() { return $this->arguments; } /** * Get arguments in super duper. * * @since 1.0.0 * * @return array Get arguments. */ public function get_arguments() { if ( empty( $this->arguments ) ) { $this->arguments = $this->set_arguments(); } $this->arguments = apply_filters( 'wp_super_duper_arguments', $this->arguments, $this->options, $this->instance ); $this->arguments = $this->add_name_from_key( $this->arguments, true ); return $this->arguments; } /** * This is the main output class for all 3 items, widget, shortcode and block, it is extended in the calling class. * * @param array $args * @param array $widget_args * @param string $content */ public function output( $args = array(), $widget_args = array(), $content = '' ) { } /** * Add the dynamic block code inline when the wp-block in enqueued. */ public function register_block() { wp_add_inline_script( 'wp-blocks', $this->block() ); if ( class_exists( 'SiteOrigin_Panels' ) ) { wp_add_inline_script( 'wp-blocks', $this->siteorigin_js() ); } } /** * Check if we need to show advanced options. * * @return bool */ public function block_show_advanced() { $show = false; $arguments = $this->arguments; if ( empty( $arguments ) ) { $arguments = $this->get_arguments(); } if ( ! empty( $arguments ) ) { foreach ( $arguments as $argument ) { if ( isset( $argument['advanced'] ) && $argument['advanced'] ) { $show = true; } } } return $show; } /** * Output the JS for building the dynamic Guntenberg block. * * @since 1.0.4 Added block_wrap property which will set the block wrapping output element ie: div, span, p or empty for no wrap. * @since 1.0.9 Save numbers as numbers and not strings. * @return mixed */ public function block() { ob_start(); ?> tags for code highlighting, so we strip them from the output. */ return str_replace( array( '' ), '', $output ); } /** * Convert an array of attributes to block string. * * @todo there is prob a faster way to do this, also we could add some validation here. * * @param $custom_attributes * * @return string */ public function array_to_attributes( $custom_attributes, $html = false ) { $attributes = ''; if ( ! empty( $custom_attributes ) ) { if ( $html ) { foreach ( $custom_attributes as $key => $val ) { $attributes .= " $key='$val' "; } } else { foreach ( $custom_attributes as $key => $val ) { $attributes .= "'$key': '$val',"; } } } return $attributes; } /** * A self looping function to create the output for JS block elements. * * This is what is output in the WP Editor visual view. * * @param $args */ public function block_element( $args ) { if ( ! empty( $args ) ) { foreach ( $args as $element => $new_args ) { if ( is_array( $new_args ) ) { // its an element if ( isset( $new_args['element'] ) ) { if ( isset( $new_args['element_require'] ) ) { echo str_replace( array( "'+", "+'" ), '', $this->block_props_replace( $new_args['element_require'] ) ) . " && "; unset( $new_args['element_require'] ); } echo "\n el( '" . $new_args['element'] . "', {"; // get the attributes foreach ( $new_args as $new_key => $new_value ) { if ( $new_key == 'element' || $new_key == 'content' || $new_key == 'element_require' || $new_key == 'element_repeat' || is_array( $new_value ) ) { // do nothing } else { echo $this->block_element( array( $new_key => $new_value ) ); } } echo "},";// end attributes // get the content $first_item = 0; foreach ( $new_args as $new_key => $new_value ) { if ( $new_key === 'content' || is_array( $new_value ) ) { if ( $new_key === 'content' ) { echo "'" . $this->block_props_replace( wp_slash( $new_value ) ) . "'"; } if ( is_array( $new_value ) ) { if ( isset( $new_value['element_require'] ) ) { echo str_replace( array( "'+", "+'" ), '', $this->block_props_replace( $new_value['element_require'] ) ) . " && "; unset( $new_value['element_require'] ); } if ( isset( $new_value['element_repeat'] ) ) { $x = 1; while ( $x <= absint( $new_value['element_repeat'] ) ) { $this->block_element( array( '' => $new_value ) ); $x ++; } } else { $this->block_element( array( '' => $new_value ) ); } } $first_item ++; } } echo ")";// end content echo ", \n"; } } else { if ( substr( $element, 0, 3 ) === "if_" ) { echo str_replace( "if_", "", $element ) . ": " . $this->block_props_replace( $new_args, true ) . ","; } elseif ( $element == 'style' ) { echo $element . ": " . $this->block_props_replace( $new_args ) . ","; } else { echo $element . ": '" . $this->block_props_replace( $new_args ) . "',"; } } } } } /** * Replace block attributes placeholders with the proper naming. * * @param $string * * @return mixed */ public function block_props_replace( $string, $no_wrap = false ) { if ( $no_wrap ) { $string = str_replace( array( "[%", "%]" ), array( "props.attributes.", "" ), $string ); } else { $string = str_replace( array( "[%", "%]" ), array( "'+props.attributes.", "+'" ), $string ); } return $string; } /** * Outputs the content of the widget * * @param array $args * @param array $instance */ public function widget( $args, $instance ) { // get the filtered values $argument_values = $this->argument_values( $instance ); $argument_values = $this->string_to_bool( $argument_values ); $output = $this->output( $argument_values, $args ); ob_start(); if ( $output ) { // Before widget $before_widget = $args['before_widget']; $before_widget = apply_filters( 'wp_super_duper_before_widget', $before_widget, $args, $instance, $this ); $before_widget = apply_filters( 'wp_super_duper_before_widget_' . $this->base_id, $before_widget, $args, $instance, $this ); // After widget $after_widget = $args['after_widget']; $after_widget = apply_filters( 'wp_super_duper_after_widget', $after_widget, $args, $instance, $this ); $after_widget = apply_filters( 'wp_super_duper_after_widget_' . $this->base_id, $after_widget, $args, $instance, $this ); echo $before_widget; // elementor strips the widget wrapping div so we check for and add it back if needed if ( $this->is_elementor_widget_output() ) { echo ! empty( $this->options['widget_ops']['classname'] ) ? "" : ''; } echo $this->output_title( $args, $instance ); echo $output; if ( $this->is_elementor_widget_output() ) { echo ! empty( $this->options['widget_ops']['classname'] ) ? "" : ''; } echo $after_widget; } elseif ( $this->is_preview() && $output == '' ) {// if preview show a placeholder if empty $output = $this->preview_placeholder_text( "{{" . $this->base_id . "}}" ); echo $output; } $output = ob_get_clean(); $output = apply_filters( 'wp_super_duper_widget_output', $output, $instance, $args, $this ); echo $output; } /** * Tests if the current output is inside a elementor container. * * @since 1.0.4 * @return bool */ public function is_elementor_widget_output() { $result = false; if ( defined( 'ELEMENTOR_VERSION' ) && isset( $this->number ) && $this->number == 'REPLACE_TO_ID' ) { $result = true; } return $result; } /** * Tests if the current output is inside a elementor preview. * * @since 1.0.4 * @return bool */ public function is_elementor_preview() { $result = false; if ( isset( $_REQUEST['elementor-preview'] ) || ( is_admin() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor' ) || ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor_ajax' ) ) { $result = true; } return $result; } /** * Tests if the current output is inside a Divi preview. * * @since 1.0.6 * @return bool */ public function is_divi_preview() { $result = false; if ( isset( $_REQUEST['et_fb'] ) || isset( $_REQUEST['et_pb_preview'] ) || ( is_admin() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor' ) ) { $result = true; } return $result; } /** * Tests if the current output is inside a Beaver builder preview. * * @since 1.0.6 * @return bool */ public function is_beaver_preview() { $result = false; if ( isset( $_REQUEST['fl_builder'] ) ) { $result = true; } return $result; } /** * Tests if the current output is inside a siteorigin builder preview. * * @since 1.0.6 * @return bool */ public function is_siteorigin_preview() { $result = false; if ( ! empty( $_REQUEST['siteorigin_panels_live_editor'] ) ) { $result = true; } return $result; } /** * Tests if the current output is inside a cornerstone builder preview. * * @since 1.0.8 * @return bool */ public function is_cornerstone_preview() { $result = false; if ( ! empty( $_REQUEST['cornerstone_preview'] ) || basename( $_SERVER['REQUEST_URI'] ) == 'cornerstone-endpoint' ) { $result = true; } return $result; } /** * General function to check if we are in a preview situation. * * @since 1.0.6 * @return bool */ public function is_preview() { $preview = false; if ( $this->is_divi_preview() ) { $preview = true; } elseif ( $this->is_elementor_preview() ) { $preview = true; } elseif ( $this->is_beaver_preview() ) { $preview = true; } elseif ( $this->is_siteorigin_preview() ) { $preview = true; } elseif ( $this->is_cornerstone_preview() ) { $preview = true; } return $preview; } /** * Output the super title. * * @param $args * @param array $instance * * @return string */ public function output_title( $args, $instance = array() ) { $output = ''; if ( ! empty( $instance['title'] ) ) { /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ); $output = $args['before_title'] . $title . $args['after_title']; } return $output; } /** * Outputs the options form inputs for the widget. * * @param array $instance The widget options. */ public function form( $instance ) { // set widget instance $this->instance = $instance; // set it as a SD widget echo $this->widget_advanced_toggle(); echo "

" . esc_attr( $this->options['widget_ops']['description'] ) . "

"; $arguments = $this->get_arguments(); if ( is_array( $arguments ) ) { foreach ( $arguments as $key => $args ) { $this->widget_inputs( $args, $instance ); } } } /** * Get the hidden input that when added makes the advanced button show on widget settings. * * @return string */ public function widget_advanced_toggle() { $output = ''; if ( $this->block_show_advanced() ) { $val = 1; } else { $val = 0; } $output .= ""; return $output; } /** * Convert require element. * * @since 1.0.0 * * @param string $input Input element. * * @return string $output */ public function convert_element_require( $input ) { $input = str_replace( "'", '"', $input );// we only want double quotes $output = esc_attr( str_replace( array( "[%", "%]" ), array( "jQuery(form).find('[data-argument=\"", "\"]').find('input,select').val()" ), $input ) ); return $output; } /** * Builds the inputs for the widget options. * * @param $args * @param $instance */ public function widget_inputs( $args, $instance ) { $class = ""; $element_require = ""; $custom_attributes = ""; // get value if ( isset( $instance[ $args['name'] ] ) ) { $value = $instance[ $args['name'] ]; } elseif ( ! isset( $instance[ $args['name'] ] ) && ! empty( $args['default'] ) ) { $value = is_array( $args['default'] ) ? array_map( "esc_html", $args['default'] ) : esc_html( $args['default'] ); } else { $value = ''; } // get placeholder if ( ! empty( $args['placeholder'] ) ) { $placeholder = "placeholder='" . esc_html( $args['placeholder'] ) . "'"; } else { $placeholder = ''; } // get if advanced if ( isset( $args['advanced'] ) && $args['advanced'] ) { $class .= " sd-advanced-setting "; } // element_require if ( isset( $args['element_require'] ) && $args['element_require'] ) { $element_require = $args['element_require']; } // custom_attributes if ( isset( $args['custom_attributes'] ) && $args['custom_attributes'] ) { $custom_attributes = $this->array_to_attributes( $args['custom_attributes'], true ); } // before wrapper ?>

' data-element_require='convert_element_require( $element_require ); } ?>' > class="widefat" id="get_field_id( $args['name'] ) ); ?>" name="get_field_name( $args['name'] ) ); ?>" type="" value=""> class="widefat" id="get_field_id( $args['name'] ) ); ?>" name="get_field_name( $args['name'] ) ); ?>" type="checkbox" value="1">

desc_tip( $args['desc'] ); } else { $description = '' . wp_kses_post( $args['desc'] ) . ''; } } return $description; } /** * Get the tool tip html. * * @param $tip * @param bool $allow_html * * @return string */ function desc_tip( $tip, $allow_html = false ) { if ( $allow_html ) { $tip = $this->sanitize_tooltip( $tip ); } else { $tip = esc_attr( $tip ); } return ''; } /** * Sanitize a string destined to be a tooltip. * * @param string $var * * @return string */ public function sanitize_tooltip( $var ) { return htmlspecialchars( wp_kses( html_entity_decode( $var ), array( 'br' => array(), 'em' => array(), 'strong' => array(), 'small' => array(), 'span' => array(), 'ul' => array(), 'li' => array(), 'ol' => array(), 'p' => array(), ) ) ); } /** * Processing widget options on save * * @param array $new_instance The new options * @param array $old_instance The previous options * * @return array * @todo we should add some sanitation here. */ public function update( $new_instance, $old_instance ) { //save the widget $instance = array_merge( (array) $old_instance, (array) $new_instance ); // set widget instance $this->instance = $instance; if ( empty( $this->arguments ) ) { $this->get_arguments(); } // check for checkboxes if ( ! empty( $this->arguments ) ) { foreach ( $this->arguments as $argument ) { if ( isset( $argument['type'] ) && $argument['type'] == 'checkbox' && ! isset( $new_instance[ $argument['name'] ] ) ) { $instance[ $argument['name'] ] = '0'; } } } return $instance; } /** * Checks if the current call is a ajax call to get the block content. * * This can be used in your widget to return different content as the block content. * * @since 1.0.3 * @return bool */ public function is_block_content_call() { $result = false; if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'super_duper_output_shortcode' ) { $result = true; } return $result; } } }