symphonycms/symphony-2

View on GitHub
symphony/lib/toolkit/class.filterableevent.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php
/**
 * @package toolkit
 */
/**
 * The `FilterableEvent` class provides methods required to process filters on
 * data entered on the frontend.
 * It is also responsible to notify the proper extension delegates.
 *
 * @since Symphony 3.0.0
 * @link http://getsymphony.com/learn/concepts/view/events/
 */
abstract class FilterableEvent extends Event
{
    /**
     * An associative array of results from the filters that have run
     * on this event.
     * @var array
     */
    public $filter_results = array();

    /**
     * An associative array of errors from the filters that have run
     * on this event.
     * @var array
     */
    public $filter_errors = array();

    /**
     * Processes all extensions attached to the `EventPreSaveFilter` delegate
     *
     * @uses EventPreSaveFilter
     *
     * @param XMLElement $result
     * @param array $fields
     * @param XMLElement $post_values
     * @param integer $entry_id
     * @return boolean
     */
    protected function processPreSaveFilters(XMLElement $result, array &$fields, XMLElement &$post_values, $entry_id = null)
    {
        $can_proceed = true;

        /**
         * Prior to saving entry from the front-end. This delegate will
         * force the Event to terminate if it populates the `$filter_results`
         * array. All parameters are passed by reference.
         *
         * @delegate EventPreSaveFilter
         * @param string $context
         * '/frontend/'
         * @param array $fields
         * @param Event $this
         * @param array $messages
         *  An associative array of array's which contain 4 values,
         *  the name of the filter (string), the status (boolean),
         *  the message (string) an optionally an associative array
         *  of additional attributes to add to the filter element.
         * @param XMLElement $post_values
         * @param integer $entry_id
         *  If editing an entry, this parameter will be an integer,
         *  otherwise null.
         */
        Symphony::ExtensionManager()->notifyMembers(
            'EventPreSaveFilter',
            '/frontend/',
            array(
                'fields' => &$fields,
                'event' => &$this,
                'messages' => &$this->filter_results,
                'post_values' => &$post_values,
                'entry_id' => $entry_id
            )
        );

        // Logic taken from `event.section.php` to fail should any `$this->filter_results`
        // be returned. This delegate can cause the event to exit early.
        if (is_array($this->filter_results) && !empty($this->filter_results)) {
            $can_proceed = true;

            foreach ($this->filter_results as $fr) {
                list($name, $status, $message, $attributes) = array_pad($fr, 4, null);

                $result->appendChild(
                    self::buildFilterElement($name, ($status ? 'passed' : 'failed'), $message, $attributes)
                );

                if ($status === false) {
                    $can_proceed = false;
                }
            }

            if ($can_proceed !== true) {
                $result->appendChild($post_values);
                $result->setAttribute('result', 'error');
                $result->appendChild(new XMLElement('message', __('Entry encountered errors when saving.'), array(
                    'message-id' => EventMessages::FILTER_FAILED
                )));
            }
        }

        // Reset the filter results to prevent duplicates. RE: #2179
        $this->filter_results = array();
        return $can_proceed;
    }

    /**
     * Processes all extensions attached to the `EventPostSaveFilter` delegate
     *
     * @uses EventPostSaveFilter
     *
     * @param XMLElement $result
     * @param array $fields
     * @param Entry $entry
     * @return XMLElement
     */
    protected function processPostSaveFilters(XMLElement $result, array $fields, Entry $entry = null)
    {
        /**
         * After saving entry from the front-end. This delegate will not force
         * the Events to terminate if it populates the `$filter_results` array.
         * Provided with references to this object, the `$_POST` data and also
         * the error array
         *
         * @delegate EventPostSaveFilter
         * @param string $context
         * '/frontend/'
         * @param integer $entry_id
         * @param array $fields
         * @param Entry $entry
         * @param Event $this
         * @param array $messages
         *  An associative array of array's which contain 4 values,
         *  the name of the filter (string), the status (boolean),
         *  the message (string) an optionally an associative array
         *  of additional attributes to add to the filter element.
         */
        Symphony::ExtensionManager()->notifyMembers('EventPostSaveFilter', '/frontend/', array(
            'entry_id' => $entry->get('id'),
            'fields' => $fields,
            'entry' => $entry,
            'event' => &$this,
            'messages' => &$this->filter_results
        ));

        if (is_array($this->filter_results) && !empty($this->filter_results)) {
            foreach ($this->filter_results as $fr) {
                list($name, $status, $message, $attributes) = $fr;

                $result->appendChild(
                    self::buildFilterElement($name, ($status ? 'passed' : 'failed'), $message, $attributes)
                );
            }
        }

        // Reset the filter results to prevent duplicates. RE: #2179
        $this->filter_results = array();
        return $result;
    }

    /**
     * Processes all extensions attached to the `EventFinalSaveFilter` delegate
     *
     * @uses EventFinalSaveFilter
     *
     * @param XMLElement $result
     * @param array $fields
     * @param Entry $entry
     * @return XMLElement
     */
    protected function processFinalSaveFilters(XMLElement $result, array $fields, Entry $entry = null)
    {
        /**
         * This delegate that lets extensions know the final status of the
         * current Event. It is triggered when everything has processed correctly.
         * The `$messages` array contains the results of the previous filters that
         * have executed, and the `$errors` array contains any errors that have
         * occurred as a result of this delegate. These errors cannot stop the
         * processing of the Event, as that has already been done.
         *
         *
         * @delegate EventFinalSaveFilter
         * @param string $context
         * '/frontend/'
         * @param array $fields
         * @param Event $this
         * @param array $messages
         *  An associative array of array's which contain 4 values,
         *  the name of the filter (string), the status (boolean),
         *  the message (string) an optionally an associative array
         *  of additional attributes to add to the filter element.
         * @param array $errors
         *  An associative array of array's which contain 4 values,
         *  the name of the filter (string), the status (boolean),
         *  the message (string) an optionally an associative array
         *  of additional attributes to add to the filter element.
         * @param Entry $entry
         */
        Symphony::ExtensionManager()->notifyMembers('EventFinalSaveFilter', '/frontend/', array(
            'fields'    => $fields,
            'event'     => $this,
            'messages'  => $this->filter_results,
            'errors'    => &$this->filter_errors,
            'entry'     => $entry
        ));

        if (is_array($this->filter_errors) && !empty($this->filter_errors)) {
            foreach ($this->filter_errors as $fr) {
                list($name, $status, $message, $attributes) = $fr;

                $result->appendChild(
                    self::buildFilterElement($name, ($status ? 'passed' : 'failed'), $message, $attributes)
                );
            }
        }

        // Reset the filter results to prevent duplicates. RE: #2179
        $this->filter_results = array();
        return $result;
    }

    /**
     * This method will construct XML that represents the result of
     * an Event filter.
     *
     * @param string $name
     *  The name of the filter
     * @param string $status
     *  The status of the filter, either passed or failed.
     * @param XMLElement|string $message
     *  Optionally, an XMLElement or string to be appended to this
     *  `<filter>` element. XMLElement allows for more complex return
     *  types.
     * @param array $attributes
     *  An associative array of additional attributes to add to this
     *  `<filter>` element
     * @return XMLElement
     */
    public static function buildFilterElement($name, $status, $message = null, array $attributes = null)
    {
        $filter = new XMLElement('filter', (!$message || is_object($message) ? null : $message), array('name' => $name, 'status' => $status));

        if ($message instanceof XMLElement) {
            $filter->appendChild($message);
        }

        if (is_array($attributes)) {
            $filter->setAttributeArray($attributes);
        }

        return $filter;
    }
}