Index: branches/5.1.x/core/kernel/utility/unit_config_reader.php =================================================================== --- branches/5.1.x/core/kernel/utility/unit_config_reader.php (revision 13701) +++ branches/5.1.x/core/kernel/utility/unit_config_reader.php (revision 13702) @@ -1,1041 +1,1041 @@ <?php /** * @version $Id$ * @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!'); class kUnitConfigReader extends kBase { /** * Configs readed * * @var Array * @access private */ var $configData = Array(); var $configFiles = Array(); var $CacheExpired = false; var $prefixFiles = array(); var $ProcessAllConfigs = false; var $FinalStage = false; var $StoreCache = false; var $AfterConfigProcessed = array(); /** * Escaped directory separator for using in regular expressions * * @var string */ var $_directorySeparator = ''; /** * Regular expression for detecting module folder * * @var string */ var $_moduleFolderRegExp = ''; /** * Folders to skip during unit config search * * @var Array */ var $_skipFolders = Array ('CVS', '.svn', 'admin_templates', 'libchart'); /** * Scan kernel and user classes * for available configs * * @access protected */ function Init($prefix,$special) { parent::Init($prefix,$special); $this->_directorySeparator = preg_quote(DIRECTORY_SEPARATOR); $editor_path = explode('/', trim(EDITOR_PATH, '/')); $this->_skipFolders[] = array_pop($editor_path); // last of cmseditor folders $this->_moduleFolderRegExp = '#' . $this->_directorySeparator . '(core|modules' . $this->_directorySeparator . '.*?)' . $this->_directorySeparator . '#'; } function CacheParsedData() { $event_manager =& $this->Application->recallObject('EventManager'); $aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray'); $config_vars = Array ( // session related 'SessionTimeout', 'SessionCookieName', 'SessionCookieDomains', 'SessionBrowserSignatureCheck', 'SessionIPAddressCheck', 'CookieSessions', 'KeepSessionOnBrowserClose', 'User_GuestGroup', 'User_LoggedInGroup', // output related 'UseModRewrite', 'UseContentLanguageNegotiation', 'UseOutputCompression', 'OutputCompressionLevel', 'Config_Site_Time', 'SystemTagCache', // tracking related 'UseChangeLog', 'UseVisitorTracking', 'ModRewriteUrlEnding', 'ForceModRewriteUrlEnding', 'UseCronForRegularEvent', ); foreach ($config_vars as $var) { $this->Application->ConfigValue($var); } $cache = Array( 'Factory.Files' => $this->Application->Factory->Files, 'Factory.realClasses' => $this->Application->Factory->realClasses, 'Factory.Dependencies' => $this->Application->Factory->Dependencies, 'ConfigReader.prefixFiles' => $this->prefixFiles, 'EventManager.buildEvents' => $event_manager->buildEvents, 'EventManager.beforeRegularEvents' => $event_manager->beforeRegularEvents, 'EventManager.afterRegularEvents' => $event_manager->afterRegularEvents, 'EventManager.beforeHooks' => $event_manager->beforeHooks, 'EventManager.afterHooks' => $event_manager->afterHooks, 'TagsAggregator.data' => $aggregator->_Array, // the following caches should be reset based on admin interaction (adjusting config, enabling modules etc) 'Application.Caches.ConfigVariables' => $this->Application->Caches['ConfigVariables'], 'Application.ConfigCacheIds' => $this->Application->ConfigCacheIds, 'Application.ConfigHash' => $this->Application->ConfigHash, 'Application.ReplacementTemplates' => $this->Application->ReplacementTemplates, 'Application.RewriteListeners' => $this->Application->RewriteListeners, 'Application.ModuleInfo' => $this->Application->ModuleInfo, ); if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $this->Application->setCache('master:configs_parsed', serialize($cache)); $this->Application->setCache('master:config_files', serialize($this->configFiles)); } else { $this->Application->setDBCache('configs_parsed', serialize($cache)); $this->Application->setDBCache('config_files', serialize($this->configFiles)); } $cache_rebuild_by = SERVER_NAME . ' (' . getenv('REMOTE_ADDR') . ') - ' . adodb_date('d/m/Y H:i:s'); $this->Application->setDBCache('last_cache_rebuild', $cache_rebuild_by); unset($this->configFiles); } function RestoreParsedData() { $conn =& $this->Application->GetADODBConnection(); if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $data = $this->Application->getCache('master:configs_parsed', false); } else { $data = $this->Application->getDBCache('configs_parsed'); } if ($data) { $cache = unserialize($data); $this->Application->Factory->Files = $cache['Factory.Files']; $this->Application->Factory->realClasses = $cache['Factory.realClasses']; $this->Application->Factory->Dependencies = $cache['Factory.Dependencies']; $this->prefixFiles = $cache['ConfigReader.prefixFiles']; $event_manager =& $this->Application->recallObject('EventManager'); $event_manager->buildEvents = $cache['EventManager.buildEvents']; $event_manager->beforeRegularEvents = $cache['EventManager.beforeRegularEvents']; $event_manager->afterRegularEvents = $cache['EventManager.afterRegularEvents']; $event_manager->beforeHooks = $cache['EventManager.beforeHooks']; $event_manager->afterHooks = $cache['EventManager.afterHooks']; $aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray'); $aggregator->_Array = $cache['TagsAggregator.data']; $this->Application->ConfigHash = $cache['Application.ConfigHash']; $this->Application->Caches['ConfigVariables'] = $cache['Application.ConfigCacheIds']; $this->Application->ConfigCacheIds = $cache['Application.ConfigCacheIds']; $this->Application->ReplacementTemplates = $cache['Application.ReplacementTemplates']; $this->Application->RewriteListeners = $cache['Application.RewriteListeners']; $this->Application->ModuleInfo = $cache['Application.ModuleInfo']; return true; } else return false; } function ResetParsedData($include_sections = false) { $conn =& $this->Application->GetADODBConnection(); if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $this->Application->deleteCache('master:configs_parsed'); } else { $this->Application->deleteDBCache('configs_parsed'); } if ($include_sections) { if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $this->Application->deleteCache('master:sections_parsed'); } else { $this->Application->deleteDBCache('sections_parsed'); } } } function scanModules($folderPath, $cache = true) { if (defined('IS_INSTALL') && IS_INSTALL && !defined('FORCE_CONFIG_CACHE')) { // disable config caching during installation $cache = false; } if ($cache) { $restored = $this->RestoreParsedData(); if ($restored) return; } $this->ProcessAllConfigs = true; $this->includeConfigFiles($folderPath, $cache); $this->ParseConfigs(); // tell AfterConfigRead to store cache if neede // can't store it here beacuse AfterConfigRead needs ability to change config data $this->StoreCache = $cache; } function findConfigFiles($folderPath, $level = 0) { /*if ($level == 0) { if ($this->Application->isDebugMode()) { $start_time = getmicrotime(); $this->Application->Debugger->appendHTML('kUnitConfigReader::findConfigFiles("' . $folderPath . '")'); $this->Application->Debugger->appendTrace(); } }*/ // if FULL_PATH = "/" ensure, that all "/" in $folderPath are not deleted $reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/'; $folderPath = preg_replace($reg_exp, '', $folderPath, 1); // this make sense, since $folderPath may NOT contain FULL_PATH $base_folder = FULL_PATH . $folderPath . DIRECTORY_SEPARATOR; $sub_folders = glob($base_folder . '*', GLOB_ONLYDIR); if (!$sub_folders) { return ; } if ($level == 0) { // don't scan Front-End themes because of extensive directory structure $sub_folders = array_diff($sub_folders, Array ($base_folder . 'themes', $base_folder . 'tools')); } foreach ($sub_folders as $full_path) { $sub_folder = substr($full_path, strlen($base_folder)); if (in_array($sub_folder, $this->_skipFolders)) { continue; } if (preg_match('/^\./', $sub_folder)) { // don't scan ".folders" continue; } $config_name = $this->getConfigName($folderPath . DIRECTORY_SEPARATOR . $sub_folder); if (file_exists(FULL_PATH . $config_name)) { $this->configFiles[] = $config_name; } $this->findConfigFiles($full_path, $level + 1); } /*if ($level == 0) { if ($this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('kUnitConfigReader::findConfigFiles("' . FULL_PATH . $folderPath . '"): ' . (getmicrotime() - $start_time)); } }*/ } function includeConfigFiles($folderPath, $cache = true) { $this->Application->refreshModuleInfo(); if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $data = $this->Application->getCache('master:config_files', false); } else { $data = $this->Application->getDBCache('config_files'); } if ($cache && $data) { $this->configFiles = unserialize($data); shuffle($this->configFiles); } else { - $this->findConfigFiles(FULL_PATH . '/core'); // search from core directory + $this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // search from core directory $this->findConfigFiles($folderPath); // search from modules directory } foreach ($this->configFiles as $filename) { $prefix = $this->PreloadConfigFile($filename); if (!$prefix) { trigger_error('Prefix not defined in config file ' . $filename, E_USER_ERROR); } } } /** * Process all read config files - called ONLY when there is no cache! * */ function ParseConfigs() { // 1. process normal configs and their dependencies $prioritized_configs = array(); foreach ($this->configData as $prefix => $config) { if (isset($config['ConfigPriority'])) { $prioritized_configs[$prefix] = $config['ConfigPriority']; continue; } $this->parseConfig($prefix); } foreach ($this->configData as $prefix => $config) { $this->ProcessDependencies($prefix); $this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix'); $clones = $this->postProcessConfig($prefix, 'Clones', 'prefix'); } // 2. process prioritized configs and their dependencies asort($prioritized_configs); foreach ($prioritized_configs as $prefix => $priority) { $this->parseConfig($prefix); } foreach ($prioritized_configs as $prefix => $priority) { $this->ProcessDependencies($prefix); } } function AfterConfigRead($store_cache = null) { // if (!$this->ProcessAllConfigs) return ; $this->FinalStage = true; foreach ($this->configData as $prefix => $config) { $this->runAfterConfigRead($prefix); } if (!isset($store_cache)) { // store cache not overrided -> use global setting $store_cache = $this->StoreCache; } if ($store_cache || (defined('IS_INSTALL') && IS_INSTALL)) { // cache is not stored during install, but dynamic clones should be processed in any case $this->processDynamicClones(); $this->retrieveCollections(); } if ($store_cache) { $this->_sortRewriteListeners(); $this->CacheParsedData(); if (defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS) { // validate configs here to have changes from OnAfterConfigRead hooks to prefixes foreach ($this->configData as $prefix => $config) { if (!isset($config['TableName'])) continue; $this->ValidateConfig($prefix); } } $after_event = new kEvent('adm:OnAfterCacheRebuild'); $this->Application->HandleEvent($after_event); } } /** * Sort rewrite listeners according to RewritePriority (non-prioritized listeners goes first) * */ function _sortRewriteListeners() { $listeners = Array (); $prioritized_listeners = Array (); // process non-prioritized listeners foreach ($this->Application->RewriteListeners as $prefix => $listener_data) { if ($listener_data['priority'] === false) { $listeners[$prefix] = $listener_data; } else { $prioritized_listeners[$prefix] = $listener_data['priority']; } } // process prioritized listeners asort($prioritized_listeners, SORT_NUMERIC); foreach ($prioritized_listeners as $prefix => $priority) { $listeners[$prefix] = $this->Application->RewriteListeners[$prefix]; } $this->Application->RewriteListeners = $listeners; } /** * Re-reads all configs * */ function ReReadConfigs() { // clear restored cache (not in db) $this->Application->Factory->Files = Array (); $this->Application->Factory->realClasses = Array (); $this->Application->Factory->Dependencies = Array (); $this->Application->EventManager->beforeRegularEvents = Array (); $this->Application->EventManager->afterRegularEvents = Array (); $this->Application->EventManager->beforeHooks = Array (); $this->Application->EventManager->afterHooks = Array (); // otherwise ModulesHelper indirectly used from includeConfigFiles won't work $this->Application->RegisterDefaultClasses(); // parse all configs $this->ProcessAllConfigs = true; $this->AfterConfigProcessed = Array (); $this->includeConfigFiles(MODULES_PATH, false); $this->ParseConfigs(); $this->AfterConfigRead(false); $this->processDynamicClones(); $this->retrieveCollections(); } /** * Process clones, that were defined via OnAfterConfigRead event * */ function processDynamicClones() { $new_clones = Array(); foreach ($this->configData as $prefix => $config) { $clones = $this->postProcessConfig($prefix, 'Clones', 'prefix'); if ($clones) { $new_clones = array_merge($new_clones, $clones); } } // call OnAfterConfigRead for cloned configs $new_clones = array_unique($new_clones); foreach ($new_clones as $prefix) { $this->runAfterConfigRead($prefix); } } /** * Process all collectable unit config options here to also catch ones, defined from OnAfterConfigRead events * */ function retrieveCollections() { foreach ($this->configData as $prefix => $config) { // collect replacement templates if (array_key_exists('ReplacementTemplates', $config) && $config['ReplacementTemplates']) { $this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config['ReplacementTemplates']); } // collect rewrite listeners if (array_key_exists('RewriteListener', $config) && $config['RewriteListener']) { $rewrite_listeners = $config['RewriteListener']; if (!is_array($rewrite_listeners)) { // when one method is used to build and parse url $rewrite_listeners = Array ($rewrite_listeners, $rewrite_listeners); } foreach ($rewrite_listeners as $index => $rewrite_listener) { if (strpos($rewrite_listener, ':') === false) { $rewrite_listeners[$index] = $prefix . '_EventHandler:' . $rewrite_listener; } } $rewrite_priority = array_key_exists('RewritePriority', $config) ? $config['RewritePriority'] : false; $this->Application->RewriteListeners[$prefix] = Array ('listener' => $rewrite_listeners, 'priority' => $rewrite_priority); } } } /** * Register nessasary classes * This method should only process the data which is cached! * * @param string $prefix * @access private */ function parseConfig($prefix) { $config =& $this->configData[$prefix]; $event_manager =& $this->Application->recallObject('EventManager'); /* @var $event_manager kEventManager */ $register_classes = getArrayValue($config,'RegisterClasses'); if (!$register_classes) $register_classes = Array(); $class_params=Array('ItemClass','ListClass','EventHandlerClass','TagProcessorClass'); foreach($class_params as $param_name) { if ( !(isset($config[$param_name]) ) ) continue; $config[$param_name]['pseudo'] = $this->getPrefixByParamName($param_name,$prefix); $register_classes[] = $config[$param_name]; } foreach($register_classes as $class_info) { $require_classes = getArrayValue($class_info, 'require_classes'); if ($require_classes) { if (!is_array($require_classes)) { $require_classes = array($require_classes); } if (!isset($config['_Dependencies'][$class_info['class']])) { $config['_Dependencies'][$class_info['class']] = array(); } $config['_Dependencies'][$class_info['class']] = array_merge($config['_Dependencies'][$class_info['class']], $require_classes); } $this->Application->registerClass( $class_info['class'], $config['BasePath'] . DIRECTORY_SEPARATOR . $class_info['file'], $class_info['pseudo']/*, getArrayValue($class_info, 'require_classes')*/ ); if (getArrayValue($class_info, 'build_event')) { $event_manager->registerBuildEvent($class_info['pseudo'],$class_info['build_event']); } } $regular_events = getArrayValue($config, 'RegularEvents'); if ($regular_events) { foreach ($regular_events as $short_name => $regular_event_info) { $event_status = array_key_exists('Status', $regular_event_info) ? $regular_event_info['Status'] : STATUS_ACTIVE; $event_manager->registerRegularEvent( $short_name, $config['Prefix'].':'.$regular_event_info['EventName'], $regular_event_info['RunInterval'], $regular_event_info['Type'], $event_status); } } $hooks = getArrayValue($config, 'Hooks'); if (is_array($hooks) && count($hooks) > 0) { foreach ($hooks as $hook) { if (isset($config['ParentPrefix']) && $hook['HookToPrefix'] == $config['ParentPrefix']) { trigger_error('Depricated Hook Usage [prefix: <b>'.$config['Prefix'].'</b>; do_prefix: <b>'.$hook['DoPrefix'].'</b>] use <b>#PARENT#</b> as <b>HookToPrefix</b> value, where HookToPrefix is same as ParentPrefix', E_USER_NOTICE); } if ($hook['HookToPrefix'] == '') { $hook['HookToPrefix'] = $config['Prefix']; // new: set hooktoprefix to current prefix if not set } if (isset($config['ParentPrefix'])) { // new: allow to set hook to parent prefix what ever it is if ($hook['HookToPrefix'] == '#PARENT#') { $hook['HookToPrefix'] = $config['ParentPrefix']; } if ($hook['DoPrefix'] == '#PARENT#') { $hook['DoPrefix'] = $config['ParentPrefix']; } } elseif ($hook['HookToPrefix'] == '#PARENT#' || $hook['DoPrefix'] == '#PARENT#') { continue; // we need parent prefix but it's not set ! } $do_prefix = $hook['DoPrefix'] == '' ? $config['Prefix'] : $hook['DoPrefix']; if ( !is_array($hook['HookToEvent']) ) { $hook_events = Array( $hook['HookToEvent'] ); } else { $hook_events = $hook['HookToEvent']; } foreach ($hook_events as $hook_event) { $this->Application->registerHook($hook['HookToPrefix'], $hook['HookToSpecial'], $hook_event, $hook['Mode'], $do_prefix, $hook['DoSpecial'], $hook['DoEvent'], $hook['Conditional']); } } } if ( is_array(getArrayValue($config, 'AggregateTags')) ) { foreach ($config['AggregateTags'] as $aggregate_tag) { if (isset($config['ParentPrefix'])) { if ($aggregate_tag['AggregateTo'] == $config['ParentPrefix']) { trigger_error('Depricated Aggregate Tag Usage [prefix: <b>'.$config['Prefix'].'</b>; AggregateTo: <b>'.$aggregate_tag['AggregateTo'].'</b>] use <b>#PARENT#</b> as <b>AggregateTo</b> value, where AggregateTo is same as ParentPrefix', E_USER_NOTICE); } if ($aggregate_tag['AggregateTo'] == '#PARENT#') { $aggregate_tag['AggregateTo'] = $config['ParentPrefix']; } } $aggregate_tag['LocalPrefix'] = $config['Prefix']; $this->Application->registerAggregateTag($aggregate_tag); } } } function ValidateConfig($prefix) { global $debugger; $config =& $this->configData[$prefix]; $tablename = $config['TableName']; $float_types = Array ('float', 'double', 'numeric'); $conn =& $this->Application->GetADODBConnection(); $table_found = $conn->Query('SHOW TABLES LIKE "'.$tablename.'"'); if (!$table_found) { // config present, but table missing, strange $debugger->appendHTML("<b class='debug_error'>Config Warning: </b>Table <strong>$tablename</strong> missing, but prefix <b>".$config['Prefix']."</b> requires it!"); safeDefine('DBG_RAISE_ON_WARNINGS', 1); return ; } $res = $conn->Query('DESCRIBE '.$tablename); $config_link = $debugger->getFileLink(FULL_PATH.$this->prefixFiles[$config['Prefix']], 1, $config['Prefix']); $error_messages = Array ( 'field_not_found' => 'Field <strong>%s</strong> exists in the database, but <strong>is not defined</strong> in config', 'default_missing' => 'Default value for field <strong>%s</strong> not set in config', 'not_null_error1' => 'Field <strong>%s</strong> is NOT NULL in the database, but is not configured as not_null', // or required', 'not_null_error2' => 'Field <strong>%s</strong> is described as NOT NULL in config, but <strong>does not have DEFAULT value</strong>', 'not_null_error3' => 'Field <strong>%s</strong> is described as <strong>NOT NULL in config</strong>, but is <strong>NULL in db</strong>', 'invalid_default' => '<strong>Default value</strong> for field %s<strong>%s</strong> not sync. to db (in config = %s, in db = %s)', 'type_missing' => '<strong>Type definition</strong> for field <strong>%s</strong> missing in config', ); $config_errors = Array (); $tablename = preg_replace('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', '\\1', $tablename); // remove table prefix foreach ($res as $field) { $f_name = $field['Field']; if (getArrayValue($config, 'Fields')) { if (preg_match('/l[\d]+_[\w]/', $f_name)) { // skip multilingual fields continue; } if (!array_key_exists ($f_name, $config['Fields'])) { $config_errors[] = sprintf($error_messages['field_not_found'], $f_name); } else { if (is_numeric($field['Default'])) { $field['Default'] = preg_match('/[\.,]/', $field['Default']) ? (float)$field['Default'] : (int)$field['Default']; } $options = $config['Fields'][$f_name]; $default_missing = false; if (!array_key_exists('default', $options)) { $config_errors[] = sprintf($error_messages['default_missing'], $f_name); $default_missing = true; } if ($field['Null'] != 'YES') { // field is NOT NULL in database (MySQL5 for null returns "NO", but MySQL4 returns "") if ( $f_name != $config['IDField'] && !isset($options['not_null']) /*&& !isset($options['required'])*/ ) { $config_errors[] = sprintf($error_messages['not_null_error1'], $f_name); } if (isset($options['not_null']) && $options['not_null'] && !isset($options['default']) ) { $config_errors[] = sprintf($error_messages['not_null_error2'], $f_name); } } else { if (isset($options['not_null']) && $options['not_null']) { $config_errors[] = sprintf($error_messages['not_null_error3'], $f_name); } } if (!array_key_exists('type', $options)) { $config_errors[] = sprintf($error_messages['type_missing'], $f_name); } if (!$default_missing) { if ($f_name == $config['IDField'] && $options['type'] != 'string' && $options['default'] !== 0) { $config_errors[] = sprintf($error_messages['invalid_default'], '<span class="debug_error">IDField</span> ', $f_name, $this->varDump($options['default']), $this->varDump($field['Default'])); } else if ($options['default'] != '#NOW#' && $field['Default'] !== $options['default'] && !in_array($options['type'], $float_types)) { $config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->varDump($options['default']), $this->varDump($field['Default'])); } } } } } if ($config_errors) { $error_prefix = '<strong class="debug_error">Config Error'.(count($config_errors) > 1 ? 's' : '').': </strong> for prefix <strong>'.$config_link.'</strong> ('.$tablename.') in unit config:<br />'; $config_errors = $error_prefix.' '.implode('<br /> ', $config_errors); $debugger->appendHTML($config_errors); safeDefine('DBG_RAISE_ON_WARNINGS', 1); } } function varDump($value) { return '<strong>'.var_export($value, true).'</strong> of '.gettype($value); } function ProcessDependencies($prefix) { $config =& $this->configData[$prefix]; $deps = getArrayValue($config, '_Dependencies'); if (!$deps) return ; foreach ($deps as $real_class => $requires) { foreach ($requires as $class) { $this->Application->registerDependency($real_class, $class); } } unset($config['_Dependencies']); } function postProcessConfig($prefix, $config_key, $dst_prefix_var) { $main_config =& $this->configData[$prefix]; $sub_configs = isset($main_config[$config_key]) && $main_config[$config_key] ? $main_config[$config_key] : false; // getArrayValue($main_config, $config_key); if (!$sub_configs) { return array(); } unset($main_config[$config_key]); $processed = array(); foreach ($sub_configs as $sub_prefix => $sub_config) { if ($config_key == 'AggregateConfigs' && !isset($this->configData[$sub_prefix])) { $this->loadConfig($sub_prefix); } $sub_config['Prefix'] = $sub_prefix; $this->configData[$sub_prefix] = array_merge_recursive2($this->configData[$$dst_prefix_var], $sub_config); // when merging empty array to non-empty results non-empty array, but empty is required foreach ($sub_config as $sub_key => $sub_value) { if (!$sub_value) { unset($this->configData[$sub_prefix][$sub_key]); } } if ($config_key == 'Clones') { $this->prefixFiles[$sub_prefix] = $this->prefixFiles[$prefix]; } $this->postProcessConfig($sub_prefix, $config_key, $dst_prefix_var); if ($config_key == 'AggregateConfigs') { $processed = array_merge($this->postProcessConfig($sub_prefix, 'Clones', 'prefix'), $processed); } elseif ($this->ProcessAllConfigs) { $this->parseConfig($sub_prefix); } array_push($processed, $sub_prefix); } if (!$prefix) { // configs, that used only for cloning & not used ifself unset($this->configData[$prefix]); } return array_unique($processed); } function PreloadConfigFile($filename) { $config_found = file_exists(FULL_PATH . $filename) && $this->configAllowed($filename); if (defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES) { if ( in_array($filename, get_required_files()) ) { return; } global $debugger; if ($config_found) { $file = FULL_PATH . $filename; $file_crc = crc32($file); $debugger->ProfileStart('inc_' . $file_crc, $file); include_once($file); $debugger->ProfileFinish('inc_' . $file_crc); $debugger->profilerAddTotal('includes', 'inc_' . $file_crc); } } elseif ($config_found) { include_once(FULL_PATH . $filename); } if ($config_found) { if (isset($config) && $config) { // config file is included for 1st time -> save it's content for future processing $prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : ''; preg_match($this->_moduleFolderRegExp, $filename, $rets); - $config['ModuleFolder'] = $rets[1]; + $config['ModuleFolder'] = str_replace(DIRECTORY_SEPARATOR, '/', $rets[1]); $config['BasePath'] = dirname(FULL_PATH . $filename); if (array_key_exists('AdminTemplatePath', $config)) { // append template base folder for admin templates path of this prefix $module_templates = $rets[1] == 'core' ? '' : substr($rets[1], 8) . '/'; $config['AdminTemplatePath'] = $module_templates . $config['AdminTemplatePath']; } if (array_key_exists($prefix, $this->prefixFiles) && ($this->prefixFiles[$prefix] != $filename)) { trigger_error( 'Single unit config prefix "<strong>' . $prefix . '</strong>" ' . 'is used in multiple unit config files: ' . '"<strong>' . $this->prefixFiles[$prefix] . '</strong>", "<strong>' . $filename . '</strong>"', E_USER_WARNING ); } $this->configData[$prefix] = $config; $this->prefixFiles[$prefix] = $filename; return $prefix; } elseif ($prefix = array_search($filename, $this->prefixFiles)) { // attempt is made to include config file twice or more, but include_once prevents that, // but file exists on hdd, then it is already saved to all required arrays, just return it's prefix return $prefix; } } return 'dummy'; } function loadConfig($prefix) { if (!isset($this->prefixFiles[$prefix])) { if ($this->Application->isDebugMode()) $this->Application->Debugger->appendTrace(); trigger_error('Configuration file for prefix <b>'.$prefix.'</b> is unknown', E_USER_ERROR); return ; } $file = $this->prefixFiles[$prefix]; $prefix = $this->PreloadConfigFile($file); if ($this->FinalStage) { // run prefix OnAfterConfigRead so all // hooks to it can define their clonses $this->runAfterConfigRead($prefix); } $clones = $this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix'); $clones = array_merge($this->postProcessConfig($prefix, 'Clones', 'prefix'), $clones); if ($this->FinalStage) { $clones = array_unique($clones); foreach ($clones as $a_prefix) { $this->runAfterConfigRead($a_prefix); } } } function runAfterConfigRead($prefix) { if (in_array($prefix, $this->AfterConfigProcessed)) { return ; } $this->Application->HandleEvent( new kEvent($prefix . ':OnAfterConfigRead') ); if (!(defined('IS_INSTALL') && IS_INSTALL)) { // allow to call OnAfterConfigRead multiple times during install array_push($this->AfterConfigProcessed, $prefix); } } /** * Reads unit (specified by $prefix) * option specified by $option * * @param string $prefix * @param string $name * @param mixed $default * @return string * @access public */ function getUnitOption($prefix, $name, $default = false) { if (preg_match('/(.*)\.(.*)/', $prefix, $rets)) { if (!isset($this->configData[$rets[1]])) { $this->loadConfig($rets[1]); } $ret = isset($this->configData[$rets[1]][$name][$rets[2]]) ? $this->configData[$rets[1]][$name][$rets[2]] : false; // $ret = getArrayValue($this->configData, $rets[1], $name, $rets[2]); } else { if (!isset($this->configData[$prefix])) { $this->loadConfig($prefix); } $ret = isset($this->configData[$prefix][$name]) ? $this->configData[$prefix][$name] : false; // $ret = getArrayValue($this->configData, $prefix, $name); } return $ret === false ? $default : $ret; } /** * Read all unit with $prefix options * * @param string $prefix * @return Array * @access public */ function getUnitOptions($prefix) { if (!isset($this->configData[$prefix])) { $this->loadConfig($prefix); } return $this->configData[$prefix]; } /** * Set's new unit option value * * @param string $prefix * @param string $name * @param string $value * @access public */ function setUnitOption($prefix, $name, $value) { if (preg_match('/(.*)\.(.*)/', $prefix, $rets)) { if (!isset($this->configData[$rets[1]])) { $this->loadConfig($rets[1]); } $this->configData[$rets[1]][$name][$rets[2]] = $value; } else { if (!isset($this->configData[$prefix])) { $this->loadConfig($prefix); } $this->configData[$prefix][$name] = $value; } } function getPrefixByParamName($paramName,$prefix) { $pseudo_class_map=Array( 'ItemClass'=>'%s', 'ListClass'=>'%s_List', 'EventHandlerClass'=>'%s_EventHandler', 'TagProcessorClass'=>'%s_TagProcessor' ); return sprintf($pseudo_class_map[$paramName],$prefix); } /** * Get's config file name based * on folder name supplied * * @param string $folderPath * @return string * @access private */ function getConfigName($folderPath) { return $folderPath . DIRECTORY_SEPARATOR . basename($folderPath) . '_config.php'; } /** * Checks if config file is allowed for includion (if module of config is installed) * * @param string $config_path relative path from in-portal directory */ function configAllowed($config_path) { static $module_paths = null; if (defined('IS_INSTALL') && IS_INSTALL) { // at installation start no modules in db and kernel configs could not be read return true; } if (preg_match('#^' . $this->_directorySeparator . 'core#', $config_path)) { // always allow to include configs from "core" module's folder return true; } if (!$this->Application->ModuleInfo) { return false; } if (!isset($module_paths)) { $module_paths = Array (); foreach ($this->Application->ModuleInfo as $module_name => $module_info) { - $module_paths[] = rtrim($module_info['Path'], '/'); + $module_paths[] = str_replace('/', DIRECTORY_SEPARATOR, rtrim($module_info['Path'], '/')); } $module_paths = array_unique($module_paths); } preg_match($this->_moduleFolderRegExp, $config_path, $rets); // config file path starts with module folder path return in_array($rets[1], $module_paths); } /** * Returns true if config exists and is allowed for reading * * @param string $prefix * @return bool */ function prefixRegistred($prefix) { return isset($this->prefixFiles[$prefix]) ? true : false; } /** * Returns config file for given prefix * * @param string $prefix * @return string */ function getPrefixFile($prefix) { return array_key_exists($prefix, $this->prefixFiles) ? $this->prefixFiles[$prefix] : false; } function iterateConfigs($callback_function, $params) { $this->includeConfigFiles(MODULES_PATH); //make sure to re-read all configs $this->AfterConfigRead(); foreach ($this->configData as $prefix => $config_data) { $callback_function[0]->$callback_function[1]($prefix, $config_data, $params); } } } \ No newline at end of file Index: branches/5.1.x/core/kernel/startup.php =================================================================== --- branches/5.1.x/core/kernel/startup.php (revision 13701) +++ branches/5.1.x/core/kernel/startup.php (revision 13702) @@ -1,172 +1,172 @@ <?php /** * @version $Id$ * @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!'); define('KERNEL_PATH', FULL_PATH . '/core/kernel'); if (!function_exists('getmicrotime')) { function getmicrotime() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } } $globals_start = getmicrotime(); include_once(KERNEL_PATH . '/globals.php'); // non OOP functions used through kernel, e.g. print_pre include_once(KERNEL_PATH . '/utility/multibyte.php'); // emulating multi-byte php extension $globals_end = getmicrotime(); $vars = parse_portal_ini(); $admin_directory = isset($vars['AdminDirectory']) ? $vars['AdminDirectory'] : '/admin'; define('ADMIN_DIRECTORY', $admin_directory); # New path detection method: begin if (defined('REL_PATH')) { // location of index.php relatively to site base folder $relative_path = preg_replace('/^[\/]{0,1}admin(.*)/', $admin_directory . '\\1', REL_PATH); } else { // default index.php relative location is administrative console folder define('REL_PATH', $admin_directory); $relative_path = REL_PATH; } $ps = rtrim(preg_replace('/' . preg_quote(rtrim($relative_path, '/'), '/') . '$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/'); safeDefine('BASE_PATH', $ps); // in case in-portal has defined it before # New path detection method: end safeDefine('SERVER_NAME', $_SERVER['HTTP_HOST'] ? $_SERVER['HTTP_HOST'] : $vars['Domain']); $https_mark = getArrayValue($_SERVER, 'HTTPS'); safeDefine('PROTOCOL', ($https_mark == 'on') || ($https_mark == '1') ? 'https://' : 'http://'); $port = array_key_exists('HTTP_PORT', $_SERVER) ? $_SERVER['HTTP_PORT'] : false; if ($port && ((PROTOCOL == 'http://' && $port != '80') || (PROTOCOL == 'https://' && $port != '443'))) { // if non-standard port is used, then define it define('PORT', $_SERVER['HTTP_PORT']); } safeDefine('APPLICATION_CLASS', isset($vars['ApplicationClass']) ? $vars['ApplicationClass'] : 'kApplication'); safeDefine('APPLICATION_PATH', isset($vars['ApplicationPath']) ? $vars['ApplicationPath'] : '/core/kernel/application.php'); if (isset($vars['WriteablePath'])) { define('WRITEABLE', FULL_PATH . $vars['WriteablePath']); define('WRITEBALE_BASE', $vars['WriteablePath']); } if ($vars === false || count($vars) == 0) { global $rootURL; echo 'In-Portal is probably not installed, or configuration file is missing.<br>'; echo 'Please use the installation script to fix the problem.<br><br>'; echo '<a href="' . PROTOCOL . SERVER_NAME . rtrim(BASE_PATH, '/') . '/core/install.php">Go to installation script</a><br><br>'; flush(); exit; } define('SQL_TYPE', $vars['DBType']); define('SQL_SERVER', $vars['DBHost']); define('SQL_USER', $vars['DBUser']); define('SQL_PASS', $vars['DBUserPassword']); define('SQL_DB', $vars['DBName']); if (isset($vars['DBCollation']) && isset($vars['DBCharset'])) { define('SQL_COLLATION', $vars['DBCollation']); define('SQL_CHARSET', $vars['DBCharset']); } define('TABLE_PREFIX', $vars['TablePrefix']); define('DOMAIN', getArrayValue($vars, 'Domain')); ini_set('memory_limit', '50M'); - define('MODULES_PATH', FULL_PATH . '/modules'); + define('MODULES_PATH', FULL_PATH . DIRECTORY_SEPARATOR . 'modules'); define('EXPORT_BASE_PATH', WRITEBALE_BASE . '/export'); define('EXPORT_PATH', FULL_PATH . EXPORT_BASE_PATH); define('GW_CLASS_PATH', MODULES_PATH . '/in-commerce/units/gateways/gw_classes'); // Payment Gateway Classes Path define('SYNC_CLASS_PATH', FULL_PATH . '/sync'); // path for 3rd party user syncronization scripts safeDefine('ENV_VAR_NAME','env'); define('IMAGES_PATH', WRITEBALE_BASE . '/images/'); define('IMAGES_PENDING_PATH', IMAGES_PATH . 'pending/'); safeDefine('MAX_UPLOAD_SIZE', min(ini_get('upload_max_filesize'), ini_get('post_max_size'))*1024*1024); safeDefine('EDITOR_PATH', isset($vars['EditorPath']) ? $vars['EditorPath'] : '/core/editor/'); // caching types define('CACHING_TYPE_NONE', 0); define('CACHING_TYPE_MEMORY', 1); define('CACHING_TYPE_TEMPORARY', 2); if (ini_get('safe_mode')) { // safe mode will be removed at all in PHP6 define('SAFE_MODE', 1); } if (file_exists(WRITEABLE . '/debug.php')) { include_once(WRITEABLE . '/debug.php'); if (array_key_exists('DEBUG_MODE', $dbg_options) && $dbg_options['DEBUG_MODE']) { $debugger_start = getmicrotime(); include_once(KERNEL_PATH . '/utility/debugger.php'); $debugger_end = getmicrotime(); if (isset($debugger) && constOn('DBG_PROFILE_INCLUDES')) { $debugger->profileStart('inc_globals', KERNEL_PATH . '/globals.php', $globals_start); $debugger->profileFinish('inc_globals', KERNEL_PATH . '/globals.php', $globals_end); $debugger->profilerAddTotal('includes', 'inc_globals'); $debugger->profileStart('inc_debugger', KERNEL_PATH . '/utility/debugger.php', $debugger_start); $debugger->profileFinish('inc_debugger', KERNEL_PATH . '/utility/debugger.php', $debugger_end); $debugger->profilerAddTotal('includes', 'inc_debugger'); } } } safeDefine('SILENT_LOG', 0); $includes = Array( KERNEL_PATH . '/application.php', FULL_PATH . APPLICATION_PATH, KERNEL_PATH . '/db/db_connection.php', KERNEL_PATH . "/kbase.php", KERNEL_PATH . '/utility/event.php', KERNEL_PATH . "/utility/factory.php", KERNEL_PATH . "/languages/phrases_cache.php", KERNEL_PATH . "/db/dblist.php", KERNEL_PATH . "/db/dbitem.php", KERNEL_PATH . "/event_handler.php", KERNEL_PATH . '/db/db_event_handler.php', ); foreach ($includes as $a_file) { k4_include_once($a_file); } if (defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger)) { $debugger->AttachToApplication(); } if( !function_exists('adodb_mktime') ) { include_once(KERNEL_PATH . '/utility/adodb-time.inc.php'); } // system users define('USER_ROOT', -1); define('USER_GUEST', -2); \ No newline at end of file Index: branches/5.1.x/core/units/helpers/minifiers/minify_helper.php =================================================================== --- branches/5.1.x/core/units/helpers/minifiers/minify_helper.php (revision 13701) +++ branches/5.1.x/core/units/helpers/minifiers/minify_helper.php (revision 13702) @@ -1,246 +1,246 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2010 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!'); class MinifyHelper extends kHelper { /** * Debug mode mark * * @var bool */ var $debugMode = false; /** * Path to compress information file * * @var string */ var $infoFile = ''; /** * Compress information * * @var Array */ var $compressInfo = Array (); function MinifyHelper() { parent::kHelper(); $this->debugMode = $this->Application->isDebugMode(false); $this->infoFile = WRITEABLE . '/cache/compress_info.txt'; if ( file_exists($this->infoFile) ) { $this->compressInfo = unserialize( file_get_contents($this->infoFile) ); } } /** * When used as non-block tag, then compress given files and return url to result * * @param Array $files * @return string */ function CompressScriptTag($params) { // put to queue if (array_key_exists('to', $params)) { $files = $this->Application->GetVar($params['to'], ''); $this->Application->SetVar($params['to'], $files . '|' . $params['files']); return ''; } if (array_key_exists('from', $params)) { // get from queue $files = $this->Application->GetVar($params['from']); } else { // get from tag $files = $params['files']; } $files = $this->_getTemplatePaths( array_map('trim', explode('|', $files)) ); $extension = pathinfo($files[0], PATHINFO_EXTENSION); $hash = ($this->debugMode ? 'd' : 'c') . '_' . $this->_getHash($files); $file_mask = 'cache/' . $hash . '_%s.' . $extension; $was_compressed = array_key_exists($hash, $this->compressInfo); if ($was_compressed) { $current_file = WRITEABLE . '/' . sprintf($file_mask, $this->compressInfo[$hash]); if (!file_exists($current_file)) { // when info exists, but file doesn't -> re-compress $was_compressed = false; } if ($this->debugMode) { // check if any of listed files was changed since compressed file was created (only, when in debug mode) foreach ($files as $file) { if (filemtime($file) > $this->compressInfo[$hash]) { $was_compressed = false; break; } } } } if (!$was_compressed) { $string = ''; $path_length = strlen(FULL_PATH) + 1; foreach ($files as $file) { // add filename before for easier debugging if ($this->debugMode) { $string .= '/* === File: ' . substr($file, $path_length) . ' === */' . "\n"; $string .= '/* ' . str_repeat('=', strlen(substr($file, $path_length)) + 14) . ' */' . "\n\n"; } // add file content $string .= file_get_contents($file) . "\n\n"; } // remove previous version of compressed file if (isset($current_file)) { if (file_exists($current_file)) { unlink($current_file); } } // replace templates base $templates_base = $this->Application->ProcessParsedTag('m', 'TemplatesBase', Array ()); $templates_base = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', BASE_PATH . '/', $templates_base); $string = str_replace('@templates_base@', rtrim($templates_base, '/'), $string); // compress collected data $this->compressInfo[$hash] = adodb_mktime(); if (!$this->debugMode) { // don't compress merged js/css file in debug mode to allow js/css debugging $this->compressString($string, $extension); } // save compressed file $fp = fopen(WRITEABLE . '/' . sprintf($file_mask, $this->compressInfo[$hash]), 'w'); fwrite($fp, $string); fclose($fp); $this->_saveInfo(); } // got compressed file -> use it - return $this->Application->BaseURL(WRITEBALE_BASE) . sprintf($file_mask, $this->compressInfo[$hash]); + return $this->Application->BaseURL( str_replace(DIRECTORY_SEPARATOR, '/', WRITEBALE_BASE)) . sprintf($file_mask, $this->compressInfo[$hash]); } /** * Returns hash string based on given files * * @param Array $files * @return int */ function _getHash($files) { $hash = $files; if ($this->Application->isAdmin) { array_unshift($hash, 'A:1'); } else { array_unshift($hash, 'A:0;T:' . $this->Application->GetVar('m_theme')); } return crc32( implode('|', $hash) ); } /** * Saves file with compress information * */ function _saveInfo() { $fp = fopen($this->infoFile, 'w'); fwrite($fp, serialize($this->compressInfo)); fclose($fp); } /** * Deletes compression info file * * @todo also delete all listed there files */ function delete() { if (file_exists($this->infoFile)) { unlink($this->infoFile); } } /** * Compress $string based on $extension * * @param string $string * @param string $extension */ function compressString(&$string, $extension) { if ($extension == 'js') { $minifier =& $this->Application->makeClass('JsMinifyHelper'); /* @var $minifier JsMinifyHelper */ $string = $minifier->minify($string); } elseif ($extension == 'css') { $minifier =& $this->Application->makeClass('CssMinifyHelper'); /* @var $minifier CssMinifyHelper */ $string = $minifier->minify($string); } } /** * Get full paths on disk for each of given templates * * @param Array $templates * @return Array */ function _getTemplatePaths($templates) { $ret = Array (); $reg_exp = '/^' . preg_quote($this->Application->BaseURL(), '/') . '(.*)/'; foreach ($templates as $template) { if (!$template) { continue; } if (preg_match($reg_exp, $template, $regs)) { // full url (from current domain) to a file $path = FULL_PATH . '/' . $regs[1]; } else { list ($path, $module_filename) = $this->Application->TemplatesCache->GetTemplatePaths($template); $path .= DIRECTORY_SEPARATOR . $module_filename; } $ret[] = $path; } return $ret; } } \ No newline at end of file