<?php
/**
* @version	$Id: event.php 15374 2012-05-30 15:27:07Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

	defined('FULL_PATH') or die('restricted access!');

	final class kEvent extends kBase {

		/**
		 * Event finished working succsessfully
		 *
		 */
		const erSUCCESS = 0;

		/**
		 * Event finished working, but result is unsuccsessfull
		 *
		 */
		const erFAIL = -1;

		/**
		 * Event experienced FATAL error - no hooks should continue!
		 *
		 */
		const erFATAL = -2;

		/**
		 * Event failed on internal permission checking (user has no permission)
		 *
		 */
		const erPERM_FAIL = -3;

		/**
		 * Event requested to stop processing (don't parse templates)
		 *
		 */
		const erSTOP = -4;

		/**
		 * Reference to event, that created given event
		 *
		 * @var kEvent
		 * @access public
		 */
		public $MasterEvent;

		/**
		 * Event name
		 *
		 * @var string
		 * @access public
		 */
		public $Name;

		/**
		 * Don't execute hooks, before event processing
		 *
		 * @var bool
		 * @access public
		 */
		public $SkipBeforeHooks = false;

		/**
		 * Don't execute hooks, after event processing
		 *
		 * @var bool
		 * @access public
		 */
		public $SkipAfterHooks = false;

		/**
		 * Perform redirect after event processing.
		 * Redirect after event processing allows to prevent same event being present in resulting url.
		 * Also could contain template name, that needs to be shown after redirect.
		 *
		 * @var mixed
		 * @access public
		 */
		public $redirect = true;

		/**
		 * Params, used during redirect url building after event successful processing
		 *
		 * @var bool
		 * @access private
		 */
		private $redirectParams = Array ();

		/**
		 * PHP file to redirect to. Defaults to "index.php"
		 *
		 * @var string
		 * @access public
		 */
		public $redirectScript = null;

		/**
		 * Event processing status
		 *
		 * @var int
		 * @access public
		 */
		public $status = kEvent::erSUCCESS;

		/**
		 * Event parameters
		 * Usually indicate, how particular event should be processed.
		 *
		 * @var Array
		 * @access private
		 */
		private $specificParams = Array ();

		/**
		 * Pseudo class used, to create object, based on event contents
		 *
		 * @var string
		 * @access private
		 */
		private $pseudoClass = '';

		/**
		 * Create event from given prefix, special, name and specific params.
		 * Parameter $params could be be an an array with following  keys: "prefix", "special" (optional), "name".
		 * Parameter $params could be a string in format: "prefix:name" or "prefix.special:name".
		 *
		 * @param mixed $params
		 * @param Array $specific_params event specific params (none by default)
		 * @return kEvent
		 * @access public
		 */
		public function __construct($params = Array(), $specific_params = null)
		{
			parent::__construct();

			if ($params) {
				if ( is_array($params) ) {
					$prefix = isset($params['prefix']) ? $params['prefix'] : false;
					$special = isset($params['special']) ? $params['special'] : false;

					if ($prefix) {
						$this->Init($prefix, $special);
					}

					$this->Name = isset($params['name']) ? $params['name'] : '';
				}
				elseif ( is_string($params) ) {
					if (preg_match('/([^.:]*)[.]{0,1}([^:]*):(.*)/', $params, $regs)) {
						$prefix = $regs[1];
						$special = $regs[2];

						if ($prefix) {
							$this->Init($prefix, $special);
						}

						$this->Name = $regs[3];
					}
					else {
						throw new Exception('Invalid event string: "<strong>' . $params . '</strong>". Should be in "prefix[.special]:OnEvent" format');
					}
				}
			}

			if ( isset($specific_params) ) {
				$this->specificParams = $specific_params;
			}
		}

		/**
		 * Returns joined prefix and special if any
		 *
		 * @param bool $from_submit if true, then joins prefix & special by "_", uses  "." otherwise
		 * @return string
		 * @access public
		 */
		public function getPrefixSpecial($from_submit = false)
		{
			if (!$from_submit) {
				return parent::getPrefixSpecial();
			}

			return rtrim($this->Prefix . '_' . $this->Special, '_');
		}

		/**
		 * Sets event parameter
		 *
		 * @param string $name
		 * @param mixed $value
		 * @access public
		 */
		public function setEventParam($name,$value)
		{
			$this->specificParams[$name] = $value;
		}

		/**
		 * Returns event parameter by name (supports digging)
		 *
		 * @param string $name
		 * @return mixed
		 * @access public
		 */
		public function getEventParam($name)
		{
			$args = func_get_args();

			if (count($args) > 1) {
				kUtil::array_unshift_ref($args, $this->specificParams);

				return call_user_func_array('getArrayValue', $args); // getArrayValue($this->specificParams, $name);
			}

			return array_key_exists($name, $this->specificParams) ? $this->specificParams[$name] : false;
		}

		/**
		 * Returns all event parameters
		 *
		 * @return Array
		 * @access public
		 */
		public function getEventParams()
		{
			return $this->specificParams;
		}

		/**
		 * Set's pseudo class that differs from
		 * the one specified in $Prefix
		 *
		 * @param string $appendix
		 * @access public
		 */
		public function setPseudoClass($appendix)
		{
			$this->pseudoClass = $this->Prefix . $appendix;
		}

		/**
		 * Performs event initialization
		 * Also sets pseudo class same $prefix
		 *
		 * @param string $prefix
		 * @param string $special
		 * @access public
		 */
		public function Init($prefix, $special)
		{
			$this->pseudoClass = $prefix;

			parent::Init($prefix, $special);
		}

		/**
		 * Returns object used in event
		 *
		 * @param Array $params
		 * @return kDBBase
		 * @access public
		 */
		public function getObject(array $params = Array())
		{
			if ( !$this->Application->hasObject($this->prefixSpecial) ) {
				$top_event = $this;

				// when OnSave calls OnPreSave in first line, then this would make sure OnSave is used
				while ( is_object($top_event->MasterEvent) ) {
					$top_event = $top_event->MasterEvent;
				}

				$params['parent_event'] = $top_event;
			}

			return $this->Application->recallObject($this->prefixSpecial, $this->pseudoClass, $params);
		}

		/**
		 * Executes given event in context of current event
		 * Sub-event gets this event in "kEvent::MasterEvent" attribute.
		 * Sub-event execution results (status and redirect* properties) are copied back to current event.
		 *
		 * @param string $name name of callable event (optionally could contain prefix_special as well)
		 * @see kEvent::MasterEvent
		 * @todo Will overwrite master event data with called event data, which makes 'parent_event' useless in most cases
		 */
		public function CallSubEvent($name)
		{
			if ( strpos($name, ':') === false ) {
				// PrefixSpecial not specified -> use from current event
				$name = $this->getPrefixSpecial() . ':' . $name;
			}

			$child_event = new kEvent($name);
			$child_event->copyFrom($this, true);

			$this->Application->HandleEvent($child_event);
			$this->copyFrom($child_event);
			$this->specificParams = $child_event->specificParams;
		}

		/**
		 * Allows to copy data between events
		 *
		 * @param kEvent $source_event
		 * @param bool $inherit
		 * @access public
		 */
		public function copyFrom($source_event, $inherit = false)
		{
			if ( $inherit ) {
				$this->MasterEvent = $source_event;
			}
			else {
				$this->status = $source_event->status;
			}

			$this->redirect = $source_event->redirect;
			$this->redirectParams = $source_event->redirectParams;
			$this->redirectScript = $source_event->redirectScript;
			$this->specificParams = $source_event->specificParams;
		}

		/**
		 * Returns all redirect parameters
		 *
		 * @return Array
		 * @access public
		 */
		public function getRedirectParams()
		{
			return $this->redirectParams;
		}

		/**
		 * Returns redirect parameter
		 *
		 * @param string $name
		 * @return mixed
		 * @access public
		 */
		public function getRedirectParam($name)
		{
			return array_key_exists($name, $this->redirectParams) ? $this->redirectParams[$name] : false;
		}

		/**
		 * Set's redirect param for event
		 *
		 * @param string $name
		 * @param string $value
		 * @access public
		 */
		public function SetRedirectParam($name, $value)
		{
			$this->redirectParams[$name] = $value;
		}

		/**
		 * Allows to merge passed redirect params hash with existing ones
		 *
		 * @param Array $params
		 * @param bool $append
		 * @access public
		 */
		public function setRedirectParams($params, $append = true)
		{
			if ( $append ) {
				// append new parameters to parameters set before
				$params = kUtil::array_merge_recursive($this->redirectParams, $params);
			}

			$this->redirectParams = $params;
		}

		/**
		 * Allows to tell if this event was called some how (e.g. subevent, hook) from event requested
		 *
		 * @param string $event_key event key in format [prefix[.special]:]event_name
		 * @return bool
		 * @access public
		 */
		public function hasAncestor($event_key)
		{
			if ( strpos($event_key, ':') === false ) {
				$event_key = $this->getPrefixSpecial() . ':' . $event_key;
			}

			return $this->Application->EventManager->eventRunning($event_key);
		}

		/**
		 * Returns permission section associated with event
		 *
		 * @return string
		 * @access public
		 */
		public function getSection()
		{
			$perm_section = $this->getEventParam('PermSection');
			if ($perm_section) {
				return $perm_section;
			}

			// 1. get section by current top_prefix
			$top_prefix = $this->getEventParam('top_prefix');
			if ($top_prefix == false) {
				$top_prefix = $this->Application->GetTopmostPrefix($this->Prefix, true);
				$this->setEventParam('top_prefix', $top_prefix);
			}
			$section = $this->Application->getUnitOption($top_prefix.'.main', 'PermSection');

			// 2. check if this section has perm_prefix mapping to other prefix
			$sections_helper = $this->Application->recallObject('SectionsHelper');
			/* @var $sections_helper kSectionsHelper */

			$section_data =& $sections_helper->getSectionData($section);
			if ($section_data && isset($section_data['perm_prefix']) && $section_data['perm_prefix'] != $top_prefix) {
				$this->setEventParam('top_prefix', $section_data['perm_prefix']);
				$section = $this->Application->getUnitOption($section_data['perm_prefix'].'.main', 'PermSection');
			}

			if (!$section) {
				throw new Exception('Permission <strong>section</strong> not specified for prefix <strong>' . $top_prefix . '</strong>');
			}

			return $section;
		}

		public function __toString()
		{
			return $this->getPrefixSpecial() . ':' . $this->Name;
		}
	}