array( 'label' => __( 'Title', 'italystrap' ), 'desc' => __( 'Enter the widget title.', 'italystrap' ), 'id' => 'title', 'type' => 'text', 'class' => 'widefat', 'default' => '', 'validate' => 'alpha_dash', 'sanitize' => 'strip_tags|esc_attr', ), ); } /** * Get the fields for widget * * @param array $fields The options array with new fields. * @return array Return an array with all fields */ protected function get_widget_fields( array $fields = array() ) { if ( empty( $fields ) ) { return $this->title_field(); } return array_merge( $this->title_field(), $fields ); } /** * Echoes the widget content. * * Sub-classes should over-ride this function to generate their widget code. * * @since 2.8.0 * @access public * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance The settings for the particular instance of the widget. */ public function widget( $args, $instance ) { if ( $this->get_cached_widget( $args ) ) { return; } $output = ''; $output .= $args['before_widget']; /** * Print the optional widget title */ $output .= $this->widget_title( $args, $instance ); /** * Modify this for outputting the HTML */ $output .= $this->widget_render( $args, $instance ); $output .= $args['after_widget']; echo apply_filters( 'widget_text', $this->cache_widget( $args, $output ) ); // XSS ok. } /** * Dispay the widget content * * @since 2.0.0 * @access public * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance The settings for the particular instance of the widget. */ abstract public function widget_render( $args, $instance ); /** * Dispay the optional title * * @since 2.0.0 * @access public * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance The settings for the particular instance of the widget. * @return string Return the optional title */ protected function widget_title( $args, $instance ) { $output = ''; /** * This filter is documented in wp-includes/widgets/class-wp-widget-pages.php * * @var string */ $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base ); /** * Return the optional widget title with before_title and after_title */ if ( ! $title ) { return $title; } $output .= $args['before_title']; if ( ! empty( $this->args['title_link'] ) ) { $output .= '' . $title . ''; } else { $output .= $title; } $output .= $args['after_title']; return $output; } /** * Create Widget and call PHP5 constructor. * Creates a new widget and sets it's labels, description, fields and options. * * @access public * @param array $args The array with widget configuration. */ protected function create_widget( array $args ) { /** * Settings some defaults options. * * New feauture added from WP 4.5 to add selective refresh on widget, see the link fro more information. * * @see https://make.wordpress.org/core/2016/03/22/implementing-selective-refresh-support-for-widgets/ * * @var array */ $defaults = array( 'label' => '', 'description' => __( '⇒ Add a $args[\'description\'] to your widget', 'italystrap' ), 'fields' => array(), 'widget_options' => array(), 'control_options' => array(), ); /** * Parse and merge args with defaults. * * @var array */ $args = wp_parse_args( (array) $args, (array) $defaults ); $args['fields'] = $this->get_widget_fields( (array) $args['fields'] ); $this->is_valid_args( $args ); /** * Convert $args['label'] spaces with dash '-'. */ $id_base = sanitize_title( $args['label'] ); $name = $args['label']; /** * Set the default widget title */ $args['fields']['title']['default'] = $name; /** * Check options. * * @var array */ $widget_options = array( 'description' => $args['description'] ); $widget_options = wp_parse_args( (array) $args['widget_options'], (array) $widget_options ); /** * The options contains the height, width, and id_base keys. * The height option is never used. * The width option is the width of the fully expanded control form, * but try hard to use the default width. * The id_base is for multi-widgets (widgets which allow multiple instances * such as the text widget), an id_base must be provided. * The widget id will end up looking like {$id_base}-{$unique_number}. * The id_base is optional. See wp_register_widget_control() * https://codex.wordpress.org/Function_Reference/wp_register_widget_control * * Example: array( 'width' => 450 ); * * @var array */ $control_options = $args['control_options']; /** * With this filter you can change the default widget fields * * @var array */ $this->fields = apply_filters( 'italystrap_widget_fields', $args['fields'], $id_base ); /** * Call WP_Widget to create the widget. * * @since 2.8.0 * @access public * * @param string $id_base Optional Base ID for the widget, lowercase and unique. * If left empty, a portion of the widget's class name * will be used Has to be unique. * @param string $name Name for the widget displayed on the configuration page. * @param array $widget_options Optional. Widget options. * See wp_register_sidebar_widget() for information * on accepted arguments. Default empty array. * @param array $control_options Optional. Widget control options. * See wp_register_widget_control() for * information on accepted arguments. Default empty array. */ parent::__construct( $id_base, $name, $widget_options, $control_options ); $this->init_events(); } /** * Init events. */ protected function init_events() { add_action( 'save_post', array( $this, 'flush_widget_cache' ) ); add_action( 'deleted_post', array( $this, 'flush_widget_cache' ) ); add_action( 'switch_theme', array( $this, 'flush_widget_cache' ) ); } /** * Check if it is valid args before init. * * @param array $args The widget arguments. */ protected function is_valid_args( array $args ) { if ( empty( $args['label'] ) || ! is_string( $args['label'] ) ) { throw new InvalidArgumentException( 'The $args[\'label\'] must be not empty. Insert the widget title.' ); } if ( ! is_string( $args['label'] ) ) { throw new InvalidArgumentException( 'The $args[\'label\'] must be a string.' ); } if ( empty( $args['fields'] ) ) { throw new InvalidArgumentException( 'The $args[\'fields\'] must be not empty.' ); } } /** * Outputs the settings update form. * * @access public * * @param array $instance Current settings. */ public function form( $instance ) { $this->instance = $instance; $this->fields_type = new Fields; /** * Creates the settings form. * * @var string */ $form = $this->render_form(); echo $form; // WPCS: XSS OK. } /** * Updates a particular instance of a widget. * * This function should check that `$new_instance` is set correctly. The newly-calculated * value of `$instance` should be returned. If false is returned, the instance won't be * saved/updated. * * @access public * * @param array $new_instance New settings for this instance * as input by the user via WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Settings to save or bool false to cancel saving. */ public function update( $new_instance, $old_instance ) { $instance = $new_instance; $this->before_update_fields(); $this->validation = new Validation; $this->sanitization = new Sanitization; $this->translator = new Translator( 'ItalyStrap' ); foreach ( $this->fields as $field ) { if ( ! isset( $instance[ $field['id'] ] ) ) { $instance[ $field['id'] ] = ''; } /** * Register string for translation */ if ( isset( $field['translate'] ) && true === $field['translate'] ) { $this->translator->register_string( $field['id'], strip_tags( $instance[ $field['id'] ] ) ); } /** * Validate fields if $field['validate'] is set */ if ( isset( $field['validate'] ) ) { if ( false === $this->validation->validate( $field['validate'], $instance[ $field['id'] ] ) ) { $instance[ $field['id'] ] = ''; } } if ( isset( $field['capability'] ) && true === $field['capability'] ) { $instance[ $field['id'] ] = $instance[ $field['id'] ]; } elseif ( isset( $field['sanitize'] ) ) { $instance[ $field['id'] ] = $this->sanitization->sanitize( $field['sanitize'], $instance[ $field['id'] ] ); } else { $instance[ $field['id'] ] = strip_tags( $instance[ $field['id'] ] ); } } $this->flush_widget_cache(); $alloptions = wp_cache_get( 'alloptions', 'options' ); if ( isset( $alloptions[ $this->id ] ) ) { delete_option( $this->id ); } return $this->after_validate_fields( $instance ); } /** * Fire Before Validate Fields * * Allows to hook code on the update. * * @access public */ protected function before_update_fields() { return; } /** * Fire After Validate Fields * * Allows to modify the output after validating the fields. * * @access public * @param string $instance The $instance of widget. * @return string Return the $instance of widget */ protected function after_validate_fields( $instance ) { return $instance; } /** * Create the section array for tab in widget panel * * @param array $fields The fields array. * @return array Return the array for section */ private function make_sections_array( array $fields ) { $sections = array(); foreach ( (array) $fields as $key ) { if ( isset( $key['section'] ) && 'general' !== $key['section'] ) { $sections[ $key['section'] ][ $key['id'] ] = (array) $key; } else { $sections['general'][ $key['id'] ] = (array) $key; } } return $sections; } /** * Get the sections array key * * @param array $sections The sections array. * @return array Return an array with sections key */ private function get_sections_keys( array $sections ) { return array_keys( $sections ); } /** * Create the sections tabs menu in widget panel * * @param array $sections The sections array. * @return string Return the HTML for tabs menu in widget panel */ protected function create_sections_tabs_menu( array $sections ) { $tabs = '