<?php
/**
* @version	$Id: sections_helper.php 14668 2011-10-17 14:26:57Z 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!');

	/**
	 * Processes sections info from the configs
	 *
	 */
	class kSectionsHelper extends kHelper {

		/**
		 * Holds information about all sections
		 *
		 * @var Array
		 */
		var $Tree = Array();

		/**
		 * Checks, that we are in debug mode
		 *
		 * @var bool
		 */
		var $debugMode = false;

		/**
		 * Checks, that we are in super admin mode
		 *
		 * @var bool
		 */
		var $superAdminMode = false;

		public function __construct()
		{
			parent::__construct();

			$this->debugMode = $this->Application->isDebugMode();
			$this->superAdminMode = $this->Application->RecallVar('super_admin');
		}

		/**
		 * Set's prefix and special
		 *
		 * @param string $prefix
		 * @param string $special
		 * @access public
		 */
		public function Init($prefix, $special)
		{
			parent::Init($prefix, $special);

			$this->BuildTree();
		}

		/**
		 * Builds xml for tree in left frame in admin
		 *
		 * @return void
		 * @access public
		 */
		public function BuildTree()
		{
			if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
				$data = $this->Application->getCache('master:sections_parsed', false, CacheSettings::$sectionsParsedRebuildTime);
			}
			else {
				$data = $this->Application->getDBCache('sections_parsed', CacheSettings::$sectionsParsedRebuildTime);
			}

			if ( $data ) {
				$this->Tree = unserialize($data);
				return ;
			}

			if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
				$this->Application->rebuildCache('master:sections_parsed', kCache::REBUILD_NOW, CacheSettings::$sectionsParsedRebuildTime);
			}
			else {
				$this->Application->rebuildDBCache('sections_parsed', kCache::REBUILD_NOW, CacheSettings::$sectionsParsedRebuildTime);
			}

			if ( !defined('IS_INSTALL') || !IS_INSTALL ) {
				// don't reread all configs during install, because they are reread on every install step
				$this->Application->UnitConfigReader->ReReadConfigs();
			}

			$this->Tree = Array ();

			// 1. build base tree (don't update parent with children list yet)

			// 1.1. process prefixes without priority
			$prioritized_prefixes = Array ();
			$prefixes = array_keys($this->Application->UnitConfigReader->configData);

			foreach ($prefixes as $prefix) {
				$config =& $this->Application->UnitConfigReader->configData[$prefix];

				if ( array_key_exists('ConfigPriority', $config) ) {
					$prioritized_prefixes[$prefix] = $config['ConfigPriority'];
					continue;
				}

				$this->_processPrefixSections($prefix);
			}

			// 2. process prefixes with priority
			asort($prioritized_prefixes);
			foreach ($prioritized_prefixes as $prefix => $priority) {
				$this->_processPrefixSections($prefix);
			}

			// 2. apply section adjustments
			foreach ($prefixes as $prefix) {
				$config =& $this->Application->UnitConfigReader->configData[$prefix];
				$section_adjustments = getArrayValue($config, 'SectionAdjustments');
				/* @var $section_adjustments Array */

				if ( !$section_adjustments ) {
					continue;
				}

				foreach ($section_adjustments as $section_name => $adjustment_params) {
					if ( is_array($adjustment_params) ) {
						if ( !array_key_exists($section_name, $this->Tree) ) {
							// don't process adjustments for non-existing sections
							continue;
						}

						$this->Tree[$section_name] = kUtil::array_merge_recursive($this->Tree[$section_name], $adjustment_params);
					}
					else {
						// then remove section
						unset($this->Tree[$section_name]);
					}
				}
			}

			// 3.
			foreach ($this->Tree as $section_name => $section_params) {
				// 3.1. update parent -> children references
				$parent_section = $section_params['parent'];
				$section_order = "{$section_params['priority']}";

				if ( !isset($parent_section) ) {
					// don't process parent section of "in-portal:root" section
					continue;
				}

				if ( !array_key_exists('children', $this->Tree[$parent_section]) ) {
					$this->Tree[$parent_section]['children'] = Array ();
				}

				if ( array_key_exists($section_order, $this->Tree[$parent_section]['children']) ) {
					trigger_error('Section "<strong>' . $section_name . '</strong>" has replaced section "<strong>' . $this->Tree[$parent_section]['children'][$section_order] . '</strong>" (parent section: "<strong>' . $parent_section . '</strong>"; duplicate priority: <strong>' . $section_order . '</strong>)', E_USER_WARNING);
				}

				$this->Tree[$parent_section]['children'][$section_order] = $section_name;

				if ( $section_params['type'] == stTAB ) {
					// if this is tab, then mark parent section as TabOnly
					$this->Tree[$parent_section]['tabs_only'] = true;
				}

				// 3.2. process icons here, because they also can be adjusted
				if ( isset($section_params['icon']) && preg_match('/([^:]+):(.*)/', $section_params['icon'], $regs) ) {
					$this->Tree[$section_name]['icon'] = $regs[2];
					$this->Tree[$section_name]['icon_module'] = $regs[1]; // set "icon_module" used in "combined_header" block
					$module_folder = trim($this->Application->findModule('Name', $regs[1], 'Path'), '/');

					if ( $module_folder == '' ) {
						$module_folder = 'core';
					}
				}
				else {
					$module_folder = $this->Application->getUnitOption($section_params['SectionPrefix'], 'ModuleFolder');
					if ( !array_key_exists('icon_module', $section_params) ) {
						// set "icon_module" used in "combined_header" block
						$this->Tree[$section_name]['icon_module'] = $this->Application->findModule('Path', $module_folder . '/', 'Name');
					}
				}

				// this is to display HELP icon instead of missing one.. can be replaced with some other icon to draw attention
				$icon_file = $module_folder . '/admin_templates/img/icons/icon24_' . $this->Tree[$section_name]['icon'];

				/*$core_file = FULL_PATH.'/core/admin_templates/img/icons/icon24_' . $this->Tree[$section_name]['icon'].'.png';
				if ($module_folder != 'core' && file_exists($core_file) && file_exists(FULL_PATH.'/'.$icon_file.'.png')) {
					if (crc32(file_get_contents($core_file)) == crc32(file_get_contents(FULL_PATH.'/'.$icon_file.'.png'))) {
						trigger_error('Section "<strong>' . $section_name . '</strong>" uses icon copy from "Core" module', E_USER_NOTICE);
					}
				}*/

				if ( !file_exists(FULL_PATH . '/' . $icon_file . '.png') ) {
					$this->Tree[$section_name]['icon'] = 'help';
					$this->Tree[$section_name]['icon_module'] = 'core';
				}
			}

			$this->Application->HandleEvent(new kEvent('adm:OnAfterBuildTree'));

			if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
				$this->Application->setCache('master:sections_parsed', serialize($this->Tree));
			}
			else {
				$this->Application->setDBCache('sections_parsed', serialize($this->Tree));
			}
		}

		function _processPrefixSections($prefix)
		{
			$config =& $this->Application->UnitConfigReader->configData[$prefix];
			$sections = getArrayValue($config, 'Sections');
			/* @var $sections Array */

			if ( !$sections ) {
				return ;
			}

			foreach ($sections as $section_name => $section_params) {
				// we could also skip not allowed sections here in future
				if ( isset($section_params['SectionPrefix']) ) {
					$section_prefix = $section_params['SectionPrefix'];
				}
				elseif ( $this->Application->getUnitOption($prefix, 'SectionPrefix') ) {
					$section_prefix = $this->Application->getUnitOption($prefix, 'SectionPrefix');
				}
				else {
					$section_prefix = $prefix;
				}

				if ( is_float($section_params['priority']) ) {
					$section_params['priority'] = (string)$section_params['priority'];
				}

				$section_params['SectionPrefix'] = $section_prefix;
				$section_params['url']['m_opener'] = 'r';
				$section_params['url']['no_pass_through'] = 1;
				$pass_section = getArrayValue($section_params, 'url', 'pass_section');

				if ( $pass_section ) {
					unset($section_params['url']['pass_section']);
					$section_params['url']['section'] = $section_name;

					if ( !isset($section_params['url']['module']) ) {
						$module_name = $this->Application->findModule('Path', $config['ModuleFolder'] . '/', 'Name');
						$section_params['url']['module'] = $module_name;
					}
				}

				if ( !isset($section_params['url']['t']) ) {
					$section_params['url']['t'] = 'index';
				}

				if ( !isset($section_params['onclick']) ) {
					$section_params['onclick'] = 'checkEditMode()';
				}

				if ( !isset($section_params['container']) ) {
					$section_params['container'] = 0; // for js tree printing to xml
				}

				$current_data = isset($this->Tree[$section_name]) ? $this->Tree[$section_name] : Array ();

				if ( $current_data ) {
					trigger_error('Section "<strong>' . $section_name . '</strong>" declaration (originally defined in "<strong>' . $current_data['SectionPrefix'] . '</strong>") was overwriten from "<strong>' . $prefix . '</strong>"', E_USER_WARNING);
				}

				$this->Tree[$section_name] = kUtil::array_merge_recursive($current_data, $section_params);
			}
		}

		/**
		 * Returns details information about section
		 *
		 * @param string $section_name
		 * @return Array
		 */
		function &getSectionData($section_name)
		{
			if (isset($this->Tree[$section_name])) {
				$ret =& $this->Tree[$section_name];
			}
			else {
				$ret = Array();
			}
			return $ret;
		}

		/**
		 * Returns first child, that is not a folder
		 *
		 * @param string $section_name
		 * @param bool $check_permission
		 * @return string
		 * @access public
		 */
		public function getFirstChild($section_name, $check_permission = false)
		{
			$section_data =& $this->getSectionData($section_name);
			$children = isset($section_data['children']) && $section_data['children'] ? $section_data['children'] : false;
			/* @var $children Array */

			if ( $children ) {
				// get 1st child
				ksort($children, SORT_NUMERIC);
				foreach ($children as $child_section) {
					if ( !$this->sectionVisible($child_section, $check_permission) ) {
						continue;
					}

					break;
				}

				return $this->getFirstChild($child_section, $check_permission);
			}

			return $section_name;
		}

		/**
		 * Checks if given section is visible by it's definition and optionally by user permission
		 *
		 * @param string $section_name
		 * @param bool $check_permission
		 * @return bool
		 */
		function sectionVisible($section_name, $check_permission = false)
		{
			$section_data =& $this->getSectionData($section_name);

			if (isset($section_data['show_mode']) && is_numeric($section_data['show_mode'])) {
				$show_mode = $section_data['show_mode'];

				// if super admin section -> show in super admin mode & debug mode
				$show_section = $show_mode == smNORMAL || ((($show_mode & smSUPER_ADMIN) == smSUPER_ADMIN) && ($this->superAdminMode || $this->debugMode));

				if (!$show_section) {
					// if section is in debug mode only && debug mode -> show
					$show_section = (($show_mode & smDEBUG) == smDEBUG) && $this->debugMode;
				}

				if (!$show_section) {
					// visibility by section definition
					return false;
				}
			}

			// visibility by section permission
			if ($check_permission) {
				$perm_section = $this->getPermSection($section_name);

				return $this->Application->CheckPermission($perm_section.'.view');
			}

			return true;
		}

		/**
		 * Returns section for permission checking based on given section
		 *
		 * @param string $section_name
		 * @return string
		 */
		function getPermSection($section_name)
		{
			$ret = $section_name;
			$section_data =& $this->getSectionData($section_name);

			if ($section_data && isset($section_data['perm_prefix'])) {
				// this section uses other section permissions
				$ret = $this->Application->getUnitOption($section_data['perm_prefix'].'.main', 'PermSection');
			}
			return $ret;
		}
	}