felixarntz/widgets-definitely

View on GitHub
inc/WPWD/Components/Widget.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php
/**
 * WPWD\Components\Widget class
 *
 * @package WPWD
 * @subpackage Components
 * @author Felix Arntz <felix-arntz@leaves-and-love.net>
 * @since 0.5.0
 */

namespace WPWD\Components;

use WPWD\App as App;
use WPDLib\Components\Base as Base;

if ( ! defined( 'ABSPATH' ) ) {
    die();
}

if ( ! class_exists( 'WPWD\Components\Widget' ) ) {
    /**
     * Class for a widget component.
     *
     * A widget denotes just a normal WordPress widget.
     * However, this component is not the actual widget class. The class `WPWD\Widget` handles all widgets created with the plugin.
     * The slug of the widget will be used as the widget's `$id_base`.
     *
     * @internal
     * @since 0.5.0
     */
    class Widget extends Base {

        /**
         * Class constructor.
         *
         * @since 0.5.0
         * @param string $slug the widget slug
         * @param array $args array of widget properties
         */
        public function __construct( $slug, $args ) {
            parent::__construct( $slug, $args );
            $this->validate_filter = 'wpwd_widget_validated';
        }

        /**
         * Registers the widget.
         *
         * @since 0.5.0
         */
        public function register() {
            wpwd_register_widget( $this->slug, $this->args );
        }

        /**
         * Sets the ID and name attributes for all fields of a widget.
         *
         * This function must be executed before any widget form is printed.
         *
         * @since 0.5.0
         * @param WPWD\Widget $widget the widget class instance
         */
        public function set_attributes( $widget ) {
            foreach ( $this->get_children() as $section ) {
                foreach ( $section->get_children() as $field ) {
                    $field->_field->id = $widget->get_field_id( $field->slug );
                    $field->_field->name = $widget->get_field_name( $field->slug );
                }
            }
        }

        /**
         * Renders the widget form.
         *
         * It iterates through all the sections belonging to this widget and calls each one's `render()` function.
         *
         * If no sections are available for this widget, the function will try to call the widget callback function to generate the output.
         *
         * @since 0.5.0
         * @param array $widget_options the widget's widget options with their current values
         */
        public function render_form( $widget_options ) {
            //TODO: of course the following does not work; create manual errors here
            settings_errors( $this->slug );

            /**
             * This action can be used to display additional content on top of this widget form.
             *
             * @since 0.5.0
             * @param string the slug of the current widget
             * @param array the arguments array for the current widget
             */
            do_action( 'wpwd_widget_form_before', $this->slug, $this->args );

            if ( count( $children = $this->get_children() ) > 0 ) {
                foreach ( $children as $section ) {
                    echo '<div class="wpwd-form-section">';
                    $section->render( $widget_options, $this );
                    echo '</div>';
                }
            } elseif ( $this->args['form_callback'] && is_callable( $this->args['form_callback'] ) ) {
                call_user_func( $this->args['form_callback'] );
            } else {
                App::doing_it_wrong( __METHOD__, sprintf( __( 'There are no sections to display for widget %s. Either add some or provide a valid callback function instead.', 'widgets-definitely' ), $this->slug ), '0.5.0' );
            }

            /**
             * This action can be used to display additional content at the bottom of this widget form.
             *
             * @since 0.5.0
             * @param string the slug of the current widget
             * @param array the arguments array for the current widget
             */
            do_action( 'wpwd_widget_form_after', $this->slug, $this->args );
        }

        /**
         * Validates the widget options for this widget.
         *
         * It iterates through all the fields (i.e. widget options) of this widget and validates each one's value.
         * If a field is not set for some reason, its default value is saved.
         *
         * Furthermore this function adds widget settings errors if any occur.
         *
         * @since 0.5.0
         * @param array $widget_options the unvalidated widget options
         * @param array $widget_options_old the currently stored widget options
         * @return array the validated widget options
         */
        public function validate_widget_options( $widget_options, $widget_options_old ) {
            $widget_options_validated = array();

            $errors = array();

            $changes = false;

            foreach ( $this->get_children() as $section ) {
                foreach ( $section->get_children() as $field ) {
                    $widget_option_old = $field->default;
                    if ( isset( $widget_options_old[ $field->slug ] ) ) {
                        $widget_option_old = $widget_options_old[ $field->slug ];
                    } else {
                        $widget_options_old[ $field->slug ] = $widget_option_old;
                    }

                    $widget_option = null;
                    if ( isset( $widget_options[ $field->slug ] ) ) {
                        $widget_option = $widget_options[ $field->slug ];
                    }

                    list( $widget_option_validated, $error, $changed ) = $this->validate_widget_option( $field, $widget_option, $widget_option_old );

                    $widget_options_validated[ $field->slug ] = $widget_option_validated;
                    if ( $error ) {
                        $errors[ $field->slug ] = $error;
                    } elseif ( $changed ) {
                        $changes = true;
                    }
                }
            }

            if ( $changes ) {
                /**
                 * This action can be used to perform additional steps when the widget options of this widget were updated.
                 *
                 * @since 0.5.0
                 * @param array the updated widget option values as $field_slug => $value
                 * @param array the previous widget option values as $field_slug => $value
                 */
                do_action( 'wpwd_update_widget_options_' . $this->slug, $widget_options_validated, $widget_options_old );
            }

            /**
             * This filter can be used by the developer to modify the validated widget options right before they are saved.
             *
             * @since 0.5.0
             * @param array the associative array of widget options and their values
             */
            $widget_options_validated = apply_filters( 'wpwd_validated_widget_options', $widget_options_validated );

            $this->add_settings_message( $errors );

            return $widget_options_validated;
        }

        /**
         * Returns the field defaults for this widget.
         *
         * @since 0.5.0
         * @param array $defaults the defaults array to be filtered
         * @return array the field defaults
         */
        public function get_field_defaults( $defaults = array() ) {
            foreach ( $this->get_children() as $section ) {
                foreach ( $section->get_children() as $field ) {
                    $defaults[ $field->slug ] = $field->default;
                }
            }

            return $defaults;
        }

        /**
         * Enqueues necessary stylesheets and scripts for this widget.
         *
         * @since 0.5.0
         */
        public function enqueue_assets() {
            //TODO: remove this, it must happen in WidgetHandler
            $_fields = array();
            foreach ( $this->get_children() as $section ) {
                foreach ( $section->get_children() as $field ) {
                    $_fields[] = $field->_field;
                }
            }

            FieldManager::enqueue_assets( $_fields );
        }

        /**
         * Validates the arguments array.
         *
         * @since 0.5.0
         * @param null $parent the parent component
         * @return bool|WPDLib\Util\Error an error object if an error occurred during validation, true if it was validated, false if it did not need to be validated
         */
        public function validate( $parent = null ) {
            $status = parent::validate( $parent );

            if ( $status === true ) {

            }

            return $status;
        }

        /**
         * Returns the keys of the arguments array and their default values.
         *
         * Read the plugin guide for more information about the widget arguments.
         *
         * @since 0.5.0
         * @return array
         */
        protected function get_defaults() {
            $defaults = array(
                'title'                => __( 'Widget title', 'widgets-definitely' ),
                'description'        => '',
                'template_file'        => false,
                'template_callback'    => false,
                'enqueue_callback'    => false,
                'form_callback'        => false, //only used if no sections are attached to this widget
            );

            /**
             * This filter can be used by the developer to modify the default values for each widget component.
             *
             * @since 0.5.0
             * @param array the associative array of default values
             */
            return apply_filters( 'wpwd_widget_defaults', $defaults );
        }

        /**
         * Returns whether this component supports multiple parents.
         *
         * @since 0.5.0
         * @return bool
         */
        protected function supports_multiparents() {
            return false;
        }

        /**
         * Returns whether this component supports global slugs.
         *
         * If it does not support global slugs, the function either returns false for the slug to be globally unique
         * or the class name of a parent component to ensure the slug is unique within that parent's scope.
         *
         * @since 0.5.0
         * @return bool|string
         */
        protected function supports_globalslug() {
            return false;
        }

        /**
         * Validates a widget option.
         *
         * @since 0.5.0
         * @param WPWD\Components\Field $field field object to validate the option for
         * @param mixed $widget_option the widget option value to validate
         * @param mixed $widget_option_old the previous widget option value
         * @return array an array containing the validated value, a variable possibly containing a WP_Error object and a boolean value whether the widget option value has changed
         */
        protected function validate_widget_option( $field, $widget_option, $widget_option_old ) {
            $widget_option = $field->validate_widget_option( $widget_option );
            $error = false;
            $changed = false;

            if ( is_wp_error( $widget_option ) ) {
                $error = $widget_option;
                $widget_option = $widget_option_old;
            } elseif ( $widget_option != $widget_option_old ) {
                /**
                 * This action can be used to perform additional steps when the option for a specific field of this tab has been updated.
                 *
                 * @since 0.5.0
                 * @param mixed the updated option value
                 * @param mixed the previous option value
                 */
                do_action( 'wpwd_update_widget_option_' . $this->slug . '_' . $field->slug, $widget_option, $widget_option_old );
                $changed = true;
            }

            return array( $widget_option, $error, $changed );
        }

        /**
         * Adds settings errors and/or updated messages for the current widget.
         *
         * @since 0.5.0
         * @param array $errors an array (possibly) containing validation errors as $field_slug => $wp_error
         */
        protected function add_settings_message( $errors ) {
            $status_text = __( 'Settings successfully saved.', 'widgets-definitely' );

            if ( count( $errors ) > 0 ) {
                $error_text = __( 'Some errors occurred while trying to save the following settings:', 'widgets-definitely' );
                foreach ( $errors as $field_slug => $error ) {
                    $error_text .= '<br/><em>' . $field_slug . '</em>: ' . $error->get_error_message();
                }

                add_settings_error( $this->slug, $this->slug . '-error', $error_text, 'error' );

                $status_text = __( 'Other settings were successfully saved.', 'widgets-definitely' );
            }

            add_settings_error( $this->slug, $this->slug . '-updated', $status_text, 'updated' );
        }
    }
}