forked from rebillar/site-accueil-insa
		
	
		
			
				
	
	
		
			508 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			508 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Class implementing the Page Controller pattern for multipage forms
 | |
|  *
 | |
|  * PHP version 5
 | |
|  *
 | |
|  * LICENSE:
 | |
|  *
 | |
|  * Copyright (c) 2006-2010, Alexey Borzov <avb@php.net>,
 | |
|  *                          Bertrand Mansion <golgote@mamasam.com>
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  *
 | |
|  *    * Redistributions of source code must retain the above copyright
 | |
|  *      notice, this list of conditions and the following disclaimer.
 | |
|  *    * Redistributions in binary form must reproduce the above copyright
 | |
|  *      notice, this list of conditions and the following disclaimer in the
 | |
|  *      documentation and/or other materials provided with the distribution.
 | |
|  *    * The names of the authors may not be used to endorse or promote products
 | |
|  *      derived from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | |
|  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | |
|  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | |
|  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | |
|  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | |
|  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | |
|  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | |
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  * @category   HTML
 | |
|  * @package    HTML_QuickForm2
 | |
|  * @author     Alexey Borzov <avb@php.net>
 | |
|  * @author     Bertrand Mansion <golgote@mamasam.com>
 | |
|  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | |
|  * @version    SVN: $Id: Controller.php 295963 2010-03-08 14:33:43Z avb $
 | |
|  * @link       http://pear.php.net/package/HTML_QuickForm2
 | |
|  */
 | |
| 
 | |
| /** The class representing a page of a multipage form */
 | |
| // require_once 'HTML/QuickForm2/Controller/Page.php';
 | |
| 
 | |
| /** Object wrapping around session variable used to store controller data */
 | |
| // require_once 'HTML/QuickForm2/Controller/SessionContainer.php';
 | |
| 
 | |
| /** Class presenting the values stored in session by Controller as submitted ones */
 | |
| // require_once 'HTML/QuickForm2/DataSource/Session.php';
 | |
| 
 | |
| /**
 | |
|  * Class implementing the Page Controller pattern for multipage forms
 | |
|  *
 | |
|  * This class keeps track of pages and (default) action handlers for the form,
 | |
|  * it manages $_SESSION container for the form values, allows setting
 | |
|  * DataSources for the form as a whole and getting its value.
 | |
|  *
 | |
|  * @category   HTML
 | |
|  * @package    HTML_QuickForm2
 | |
|  * @author     Alexey Borzov <avb@php.net>
 | |
|  * @author     Bertrand Mansion <golgote@mamasam.com>
 | |
|  * @version    Release: @package_version@
 | |
|  */
 | |
| class HTML_QuickForm2_Controller implements IteratorAggregate
 | |
| {
 | |
|    /**
 | |
|     * Key in $_REQUEST array that contains the ID of the Controller
 | |
|     */
 | |
|     const KEY_ID = '_qfc_id';
 | |
| 
 | |
|    /**
 | |
|     * Key in $_SESSION array that contains the Controller data (needs ID substituted via sprintf())
 | |
|     */
 | |
|     const KEY_CONTAINER = '_%s_container';
 | |
| 
 | |
|    /**
 | |
|     * Whether the form is a wizard
 | |
|     * @var  boolean
 | |
|     */
 | |
|     protected $wizard = true;
 | |
| 
 | |
|    /**
 | |
|     * Whether Controller ID should be sent in GET and POST parameters
 | |
|     * @var  boolean
 | |
|     */
 | |
|     protected $propagate = true;
 | |
| 
 | |
|    /**
 | |
|     * Controller ID
 | |
|     * @var string
 | |
|     */
 | |
|     protected $id = null;
 | |
| 
 | |
|    /**
 | |
|     * Contains the pages (instances of HTML_QuickForm2_Controller_Page) of the multipage form
 | |
|     * @var array
 | |
|     */
 | |
|     protected $pages = array();
 | |
| 
 | |
|    /**
 | |
|     * Contains the mapping of action names to handlers (objects implementing HTML_QuickForm2_Controller_Action)
 | |
|     * @var array
 | |
|     */
 | |
|     protected $handlers = array();
 | |
| 
 | |
|    /**
 | |
|     * The action extracted from HTTP request: array('page', 'action')
 | |
|     * @var array
 | |
|     */
 | |
|     protected $actionName = null;
 | |
| 
 | |
|    /**
 | |
|     * A wrapper around session variable used to store form data
 | |
|     * @var HTML_QuickForm2_Controller_SessionContainer
 | |
|     */
 | |
|     protected $sessionContainer = null;
 | |
| 
 | |
|    /**
 | |
|     * Finds a controller name in $_REQUEST
 | |
|     *
 | |
|     * @return string|null   Returns nulle if either a KEY_ID is not present
 | |
|     *                       in $_REQUEST or KEY_CONTAINER is not present in
 | |
|     *                       $_SESSION
 | |
|     */
 | |
|     public static function findControllerID()
 | |
|     {
 | |
|         if (empty($_REQUEST[self::KEY_ID])
 | |
|             || empty($_SESSION[sprintf(self::KEY_CONTAINER, $_REQUEST[self::KEY_ID])])
 | |
|         ) {
 | |
|             return null;
 | |
|         } else {
 | |
|             return $_REQUEST[self::KEY_ID];
 | |
|         }
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Class constructor
 | |
|     *
 | |
|     * Sets the form ID, whether to send this ID in POST and GET parameters,
 | |
|     * wizard / non-wizard behaviour.
 | |
|     *
 | |
|     * Different forms should be given different IDs, as they are used to store
 | |
|     * values in session. If $id is empty, the controller will try to find it
 | |
|     * in $_REQUEST, throwing the exception if this fails.
 | |
|     *
 | |
|     * Wizard forms only allow going to the next page if all the previous ones
 | |
|     * are valid.
 | |
|     *
 | |
|     * @param    string      Form ID
 | |
|     * @param    boolean     Whether the form is a wizard
 | |
|     * @param    boolean     Whether form's ID should be sent with GET and POST parameters
 | |
|     * @throws   HTML_QuickForm2_NotFoundException   if ID is not given and cannot
 | |
|     *               be found in $_REQUEST, or session container is empty
 | |
|     */
 | |
|     public function __construct($id = null, $wizard = true, $propagateId = false)
 | |
|     {
 | |
|         if (empty($id)) {
 | |
|             $propagateId = true;
 | |
|             $id          = self::findControllerID();
 | |
|         }
 | |
|         if (empty($id)) {
 | |
|             throw new HTML_QuickForm2_NotFoundException(
 | |
|                 'Controller ID not available in $_REQUEST or session ' .
 | |
|                 'container is empty, please provide ID to constructor'
 | |
|             );
 | |
|         }
 | |
|         $this->id        = $id;
 | |
|         $this->wizard    = (bool)$wizard;
 | |
|         $this->propagate = (bool)$propagateId;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns whether the form is a wizard
 | |
|     *
 | |
|     * @return   boolean
 | |
|     */
 | |
|     public function isWizard()
 | |
|     {
 | |
|         return $this->wizard;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns the form ID
 | |
|     *
 | |
|     * @return   string
 | |
|     */
 | |
|     public function getId()
 | |
|     {
 | |
|         return $this->id;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns whether to send form id with GET and POST parameters
 | |
|     *
 | |
|     * @return   boolean
 | |
|     */
 | |
|     public function propagateId()
 | |
|     {
 | |
|         return $this->propagate;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns the session container with the controller data
 | |
|     *
 | |
|     * @return   HTML_QuickForm2_Controller_SessionContainer
 | |
|     */
 | |
|     public function getSessionContainer()
 | |
|     {
 | |
|         if (empty($this->sessionContainer)) {
 | |
|             $this->sessionContainer = new HTML_QuickForm2_Controller_SessionContainer($this);
 | |
|         }
 | |
|         return $this->sessionContainer;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Removes the session variable containing the controller data
 | |
|     */
 | |
|     public function destroySessionContainer()
 | |
|     {
 | |
|         unset($_SESSION[sprintf(self::KEY_CONTAINER, $this->id)]);
 | |
|         $this->sessionContainer = null;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Extracts the name of the page and the action to perform with it from HTTP request data
 | |
|     *
 | |
|     * @return array     first element is page name, second is action name
 | |
|     */
 | |
|     public function getActionName()
 | |
|     {
 | |
|         if (is_array($this->actionName)) {
 | |
|             return $this->actionName;
 | |
|         }
 | |
|         if (empty($this->pages)) {
 | |
|             throw new HTML_QuickForm2_NotFoundException('No pages added to the form');
 | |
|         }
 | |
|         $names = array_map('preg_quote', array_keys($this->pages));
 | |
|         $regex = '/^_qf_(' . implode('|', $names) . ')_(.+?)(_x)?$/';
 | |
|         foreach (array_keys($_REQUEST) as $key) {
 | |
|             if (preg_match($regex, $key, $matches)) {
 | |
|                 $this->actionName = array($matches[1], $matches[2]);
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         if (!is_array($this->actionName)) {
 | |
|             reset($this->pages);
 | |
|             $this->actionName = array(key($this->pages), 'display');
 | |
|         }
 | |
|         return $this->actionName;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Processes the request
 | |
|     *
 | |
|     * This finds the page, the action to perform with it and passes the action
 | |
|     * to the page's handle() method.
 | |
|     *
 | |
|     * @throws HTML_QuickForm2_Exception
 | |
|     */
 | |
|     public function run()
 | |
|     {
 | |
|         list($page, $action) = $this->getActionName();
 | |
|         return $this->pages[$page]->handle($action);
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Adds a handler for a specific action
 | |
|     *
 | |
|     * @param  string                            action name
 | |
|     * @param  HTML_QuickForm2_Controller_Action the handler for the action
 | |
|     */
 | |
|     public function addHandler($actionName, HTML_QuickForm2_Controller_Action $action)
 | |
|     {
 | |
|         $this->handlers[$actionName] = $action;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Handles an action
 | |
|     *
 | |
|     * This will be called if the page itself does not have a handler for a
 | |
|     * specific action. The method also loads and uses default handlers for
 | |
|     * common actions, if specific ones were not added.
 | |
|     *
 | |
|     * @param    HTML_QuickForm2_Controller_Page form page
 | |
|     * @param    string                          action name
 | |
|     * @throws   HTML_QuickForm2_NotFoundException   if handler for an action is missing
 | |
|     */
 | |
|     public function handle(HTML_QuickForm2_Controller_Page $page, $actionName)
 | |
|     {
 | |
|         if (!isset($this->handlers[$actionName])
 | |
|             && in_array($actionName, array('next', 'back', 'submit', 'display', 'jump'))
 | |
|         ) {
 | |
|             $className = 'HTML_QuickForm2_Controller_Action_' . ucfirst($actionName);
 | |
|             if (!class_exists($className)) {
 | |
|                 HTML_QuickForm2_Loader::loadClass($className);
 | |
|             }
 | |
|             $this->addHandler($actionName, new $className());
 | |
|         }
 | |
|         if (isset($this->handlers[$actionName])) {
 | |
|             return $this->handlers[$actionName]->perform($page, $actionName);
 | |
|         } else {
 | |
|             throw new HTML_QuickForm2_NotFoundException(
 | |
|                 "Unhandled action '{$actionName}' for page '{$page->getForm()->getId()}'"
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Adds a new page to the form
 | |
|     *
 | |
|     * @param    HTML_QuickForm2_Controller_Page
 | |
|     */
 | |
|     public function addPage(HTML_QuickForm2_Controller_Page $page)
 | |
|     {
 | |
|         $pageId = $page->getForm()->getId();
 | |
|         if (!empty($this->pages[$pageId])) {
 | |
|             throw new HTML_QuickForm2_InvalidArgumentException(
 | |
|                 "Duplicate page ID '{$pageId}'"
 | |
|             );
 | |
|         }
 | |
|         $page->setController($this);
 | |
|         $this->pages[$pageId] = $page;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns a page
 | |
|     *
 | |
|     * @param  string    Page ID
 | |
|     * @return HTML_QuickForm2_Controller_Page
 | |
|     * @throws HTML_QuickForm2_NotFoundException if there is no page with
 | |
|     *           the given ID
 | |
|     */
 | |
|     public function getPage($pageId)
 | |
|     {
 | |
|         if (!empty($this->pages[$pageId])) {
 | |
|             return $this->pages[$pageId];
 | |
|         } else {
 | |
|             throw new HTML_QuickForm2_NotFoundException(
 | |
|                 "Unknown page '{$pageId}'"
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns the page preceding the given one
 | |
|     *
 | |
|     * @param    HTML_QuickForm2_Controller_Page
 | |
|     * @return   HTML_QuickForm2_Controller_Page|null
 | |
|     */
 | |
|     public function previousPage(HTML_QuickForm2_Controller_Page $reference)
 | |
|     {
 | |
|         $previous = null;
 | |
|         foreach ($this->pages as $page) {
 | |
|             if ($page === $reference) {
 | |
|                 return $previous;
 | |
|             }
 | |
|             $previous = $page;
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns the page following the given one
 | |
|     *
 | |
|     * @param    HTML_QuickForm2_Controller_Page
 | |
|     * @return   HTML_QuickForm2_Controller_Page|null
 | |
|     */
 | |
|     public function nextPage(HTML_QuickForm2_Controller_Page $reference)
 | |
|     {
 | |
|         $previous = null;
 | |
|         foreach ($this->pages as $page) {
 | |
|             if ($previous === $reference) {
 | |
|                 return $page;
 | |
|             }
 | |
|             $previous = $page;
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Checks whether the pages of the controller are valid
 | |
|     *
 | |
|     * @param  HTML_QuickForm2_Controller_Page   If given, check only the pages
 | |
|     *                                           before (not including) that page
 | |
|     * @return bool
 | |
|     */
 | |
|     public function isValid(HTML_QuickForm2_Controller_Page $reference = null)
 | |
|     {
 | |
|         $container = $this->getSessionContainer();
 | |
|         foreach ($this->pages as $id => $page) {
 | |
|             if ($reference === $page) {
 | |
|                 return true;
 | |
|             }
 | |
|             if (!$container->getValidationStatus($id)) {
 | |
|                 // We should handle the possible situation when the user has never
 | |
|                 // seen a page of a non-modal multipage form
 | |
|                 if (!$this->isWizard()
 | |
|                     && null === $container->getValidationStatus($id)
 | |
|                 ) {
 | |
|                     // Empty Session datasource makes the form look submitted
 | |
|                     $page->getForm()->setDatasources(array_merge(
 | |
|                         $container->getDatasources(),
 | |
|                         array(new HTML_QuickForm2_DataSource_Session(array()))
 | |
|                     ));
 | |
|                     // This will store the "submitted" values in session and
 | |
|                     // return validation status
 | |
|                     if ($page->storeValues()) {
 | |
|                         continue;
 | |
|                     }
 | |
|                 }
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns the first page that failed validation
 | |
|     *
 | |
|     * @return   HTML_QuickForm2_Controller_Page|null
 | |
|     */
 | |
|     public function getFirstInvalidPage()
 | |
|     {
 | |
|         foreach ($this->pages as $id => $page) {
 | |
|             if (!$this->getSessionContainer()->getValidationStatus($id)) {
 | |
|                 return $page;
 | |
|             }
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Adds a new data source to the Controller
 | |
|     *
 | |
|     * Note that Controller data sources are stored in session, so your data source
 | |
|     * implementation should properly handle its (un)serialization.
 | |
|     *
 | |
|     * @param    HTML_QuickForm2_DataSource  Data source
 | |
|     */
 | |
|     public function addDataSource(HTML_QuickForm2_DataSource $datasource)
 | |
|     {
 | |
|         $this->getSessionContainer()->storeDatasources(
 | |
|             array_merge($this->getSessionContainer()->getDatasources(),
 | |
|                         array($datasource))
 | |
|         );
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns the form's values
 | |
|     *
 | |
|     * @return   array
 | |
|     */
 | |
|     public function getValue()
 | |
|     {
 | |
|         $values = array();
 | |
|         foreach (array_keys($this->pages) as $id) {
 | |
|             $pageValues = $this->getSessionContainer()->getValues($id);
 | |
|             // skip elements representing actions
 | |
|             foreach ($pageValues as $key => $value) {
 | |
|                 if (0 !== strpos($key, '_qf')) {
 | |
|                     if (isset($values[$key]) && is_array($value)) {
 | |
|                         $values[$key] = self::arrayMerge($values[$key], $value);
 | |
|                     } else {
 | |
|                         $values[$key] = $value;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return $values;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Merges two arrays
 | |
|     *
 | |
|     * Merges two arrays like the PHP function array_merge_recursive does,
 | |
|     * the difference being that existing integer keys will not be renumbered.
 | |
|     *
 | |
|     * @param    array
 | |
|     * @param    array
 | |
|     * @return   array   resulting array
 | |
|     */
 | |
|     protected static function arrayMerge($a, $b)
 | |
|     {
 | |
|         foreach ($b as $k => $v) {
 | |
|             if (!is_array($v) || isset($a[$k]) && !is_array($a[$k])) {
 | |
|                 $a[$k] = $v;
 | |
|             } else {
 | |
|                 $a[$k] = self::arrayMerge(isset($a[$k])? $a[$k]: array(), $v);
 | |
|             }
 | |
|         }
 | |
|         return $a;
 | |
|     }
 | |
| 
 | |
|    /**
 | |
|     * Returns an Iterator for the form's pages
 | |
|     *
 | |
|     * @return   ArrayIterator
 | |
|     */
 | |
|     public function getIterator(): ArrayIterator
 | |
|     {
 | |
|         return new ArrayIterator($this->pages);
 | |
|     }
 | |
| }
 | |
| ?>
 |