Index: branches/5.1.x/core/units/modules/modules_tag_processor.php =================================================================== --- branches/5.1.x/core/units/modules/modules_tag_processor.php (revision 13188) +++ branches/5.1.x/core/units/modules/modules_tag_processor.php (revision 13189) @@ -1,117 +1,117 @@ <?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 ModulesTagProcessor extends kDBTagProcessor { function ModuleInstalled($params) { return $this->Application->isModuleEnabled($params['name']); } function _hasPrivileges() { return $this->Application->RecallVar('user_id') == -1; } function AlreadyInstalled($params) { if (!$this->_hasPrivileges()) { // don't show licenses status for non-privileged users return true; } $object =& $this->getObject($params); /* @var $object kDBList */ $modules_helper =& $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ return $modules_helper->moduleInstalled( $object->GetDBField('Name') ); } function ModuleLicensed($params) { if (!$this->_hasPrivileges()) { // don't show licenses status for non-privileged users return true; } $object =& $this->getObject($params); /* @var $object kDBList */ $modules_helper =& $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ $licensed_modules = array_map('strtolower', $modules_helper->_GetModules()); return in_array(strtolower($object->GetDBField('Name')), $licensed_modules); } function PrerequisitesMet($params) { return !$this->_getPrerequisitesErrors($params); } function _getPrerequisitesErrors($params) { static $errors = Array (); $object =& $this->getObject($params); /* @var $object kDBList */ - $module_path = strtolower( $object->GetDBField('Name') ); + $module_path = $object->GetDBField('Path'); if (!array_key_exists($module_path, $errors)) { require_once FULL_PATH . '/core/install/install_toolkit.php'; $toolkit = new kInstallToolkit(); $module_version = $toolkit->GetMaxModuleVersion($module_path); - $errors[$module_path] = $toolkit->CheckPrerequisites($module_path . '/', Array ($module_version), 'standalone'); + $errors[$module_path] = $toolkit->CheckPrerequisites($module_path, Array ($module_version), 'standalone'); } return $errors[$module_path]; } function ListPrerequisites($params) { $errors = $this->_getPrerequisitesErrors($params); $ret = ''; $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; foreach ($errors as $error) { $block_params['error'] = $error; $ret .= $this->Application->ParseBlock($block_params); } return $ret; } function InstallLink($params) { $object =& $this->getObject($params); /* @var $object kDBList */ $module_path = strtolower( $object->GetDBField('Path') ); $url_params = Array ('redirect' => 1, 'admin' => 1); return $this->Application->HREF('dummy', '_FRONT_END_', $url_params, $module_path . 'install.php'); } } \ No newline at end of file Index: branches/5.1.x/core/units/modules/modules_event_handler.php =================================================================== --- branches/5.1.x/core/units/modules/modules_event_handler.php (revision 13188) +++ branches/5.1.x/core/units/modules/modules_event_handler.php (revision 13189) @@ -1,187 +1,187 @@ <?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 ModulesEventHandler extends kDBEventHandler { /** * Builds item * * @param kEvent $event * @access protected */ function OnItemBuild(&$event) { $this->Application->SetVar($event->getPrefixSpecial(true).'_id', $event->Special); parent::OnItemBuild($event); } /** * List with one record if special passed * * @param kEvent $event */ function SetCustomQuery(&$event) { $object =& $event->getObject(); if ($event->Special) { $object->addFilter('current_module', 'Name = '.$event->Special); } $object->addFilter('not_core', '%1$s.Name <> "Core"'); } function mapEvents() { parent::mapEvents(); $this->eventMethods['OnMassApprove'] = 'moduleAction'; $this->eventMethods['OnMassDecline'] = 'moduleAction'; } /** * Disabled modules, but not In-Portal * * @param kEvent $event */ function moduleAction(&$event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = erFAIL; return ; } $object =& $event->getObject( Array('skip_autoload' => true) ); $ids = $this->StoreSelectedIDs($event); if (!$ids) { return true; } $status_field = array_shift( $this->Application->getUnitOption($event->Prefix, 'StatusField') ); foreach ($ids as $id) { if (($event->Name == 'OnMassDecline') && ($id == 'In-Portal')) { // don't allow to disable in-portal continue; } if ($id == 'Core') { // don't allow any kind of manupulations with kernel continue; } $object->Load($id); $object->SetDBField($status_field, $event->Name == 'OnMassApprove' ? 1 : 0); if ($object->Update()) { $sql = 'UPDATE ' . TABLE_PREFIX . 'ImportScripts SET Status = ' . ($event->Name == 'OnMassApprove' ? STATUS_ACTIVE : STATUS_DISABLED) . ' WHERE Module = "' . $object->GetDBField('Name') . '"'; $this->Conn->Query($sql); $event->status = erSUCCESS; $event->redirect_params = Array('opener' => 's'); //stay! } else { $event->status = erFAIL; $event->redirect = false; break; } } $this->Application->UnitConfigReader->ResetParsedData(true); //true to reset sections cache also $event->SetRedirectParam('RefreshTree', 1); } /** * Occures after list is queried * * @param kEvent $event */ function OnAfterListQuery(&$event) { parent::OnAfterListQuery($event); $new_modules = $this->_getNewModules(); if (!$new_modules || $this->Application->RecallVar('user_id') != -1) { return ; } require_once FULL_PATH . '/core/install/install_toolkit.php'; $toolkit = new kInstallToolkit(); $object =& $event->getObject(); /* @var $object kDBList */ foreach ($new_modules as $module) { $module_record = Array ( 'Name' => $toolkit->getModuleName($module), 'Path' => 'modules/' . $module . '/', - 'Version' => $toolkit->GetMaxModuleVersion($module), + 'Version' => $toolkit->GetMaxModuleVersion('modules/' . $module . '/'), 'Loaded' => 0, 'BuildDate' => null, ); $object->addRecord($module_record); } } /** * Returns list of modules, that are not installed, but available in file system * * @return Array */ function _getNewModules() { $modules_helper =& $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ $modules = Array (); if ($dir = @opendir(MODULES_PATH)) { while (($file = readdir($dir)) !== false) { if ($file != '.' && $file != '..') { $module_folder = MODULES_PATH . '/' . $file; if (is_dir($module_folder) && $this->_isModule($module_folder)) { // this is module -> check if it's installed already if (!$modules_helper->moduleInstalled($file)) { $install_order = trim( file_get_contents($module_folder . '/install/install_order.txt') ); $modules[$install_order] = $file; } } } } closedir($dir); } // allows to control module install order ksort($modules, SORT_NUMERIC); return $modules; } /** * Checks, that given folder is module root folder * * @param string $folder * @return bool */ function _isModule($folder) { return file_exists($folder . '/install.php') && file_exists($folder . '/install/install_schema.sql'); } } \ No newline at end of file Index: branches/5.1.x/core/install/install_toolkit.php =================================================================== --- branches/5.1.x/core/install/install_toolkit.php (revision 13188) +++ branches/5.1.x/core/install/install_toolkit.php (revision 13189) @@ -1,858 +1,866 @@ <?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!'); /** * Upgrade sqls are located using this mask * */ define('UPGRADES_FILE', FULL_PATH.'/%sinstall/upgrades.%s'); /** * Prerequisit check classes are located using this mask * */ define('PREREQUISITE_FILE', FULL_PATH.'/%sinstall/prerequisites.php'); /** * Format of version identificator in upgrade files (normal, beta, release candidate) * */ define('VERSION_MARK', '# ===== v ([\d]+\.[\d]+\.[\d]+|[\d]+\.[\d]+\.[\d]+-B[\d]+|[\d]+\.[\d]+\.[\d]+-RC[\d]+) ====='); if (!defined('GET_LICENSE_URL')) { /** * Url used for retrieving user licenses from Intechnic licensing server * */ define('GET_LICENSE_URL', 'http://www.intechnic.com/myaccount/license.php'); } /** * Misc functions, that are required during installation, when * */ class kInstallToolkit { /** * Reference to kApplication class object * * @var kApplication */ var $Application = null; /** * Connection to database * * @var kDBConnection */ var $Conn = null; /** * Path to config.php * * @var string */ var $INIFile = ''; /** * Parsed data from config.php * * @var Array */ var $systemConfig = Array (); /** * Installator instance * * @var kInstallator */ var $_installator = null; function kInstallToolkit() { if (class_exists('kApplication')) { // auto-setup in case of separate module install $this->Application =& kApplication::Instance(); $this->Conn =& $this->Application->GetADODBConnection(); } $this->INIFile = FULL_PATH . DIRECTORY_SEPARATOR . 'config.php'; $this->systemConfig = $this->ParseConfig(true); } /** * Sets installator * * @param kInstallator $instance */ function setInstallator(&$instance) { $this->_installator =& $instance; } /** * Checks prerequisities before module install or upgrade * * @param string $module_path * @param string $versions * @param string $mode upgrade mode = {install, standalone, upgrade} */ function CheckPrerequisites($module_path, $versions, $mode) { static $prerequisit_classes = Array (); $prerequisites_file = sprintf(PREREQUISITE_FILE, $module_path); if (!file_exists($prerequisites_file) || !$versions) { return Array (); } if (!isset($prerequisit_classes[$module_path])) { // save class name, because 2nd time // (in after call $prerequisite_class variable will not be present) include_once $prerequisites_file; $prerequisit_classes[$module_path] = $prerequisite_class; } $prerequisite_object = new $prerequisit_classes[$module_path](); if (method_exists($prerequisite_object, 'setToolkit')) { $prerequisite_object->setToolkit($this); } // some errors possible return $prerequisite_object->CheckPrerequisites($versions, $mode); } /** * Processes one license, received from server * * @param string $file_data */ function processLicense($file_data) { $modules_helper =& $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ $file_data = explode('Code==:', $file_data); $file_data[0] = str_replace('In-Portal License File - do not edit!' . "\n", '', $file_data[0]); $file_data = array_map('trim', $file_data); if ($modules_helper->verifyLicense($file_data[0])) { $this->setSystemConfig('Intechnic', 'License', $file_data[0]); if (array_key_exists(1, $file_data)) { $this->setSystemConfig('Intechnic', 'LicenseCode', $file_data[1]); } else { $this->setSystemConfig('Intechnic', 'LicenseCode'); } $this->SaveConfig(); } else { // invalid license received from licensing server $this->_installator->errorMessage = 'Invalid License File'; } } /** * Saves given configuration values to database * * @param Array $config */ function saveConfigValues($config) { foreach ($config as $config_var => $value) { $sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationValues SET VariableValue = ' . $this->Conn->qstr($value) . ' WHERE VariableName = ' . $this->Conn->qstr($config_var); $this->Conn->Query($sql); } } /** * Sets module version to passed * * @param string $module_name + * @param string $module_path * @param string $version */ - function SetModuleVersion($module_name, $version = false) + function SetModuleVersion($module_name, $module_path = false, $version = false) { if ($version === false) { - $version = $this->GetMaxModuleVersion($module_name); + if (!$module_path) { + trigger_error('Module path must be given to "SetModuleVersion" method to auto-detect version', E_USER_ERROR); + return ; + } + + $version = $this->GetMaxModuleVersion($module_path); } // get table prefix from config, because application may not be available here $table_prefix = $this->getSystemConfig('Database', 'TablePrefix'); if ($module_name == 'kernel') { $module_name = 'in-portal'; } // don't use "adodb_mktime" here, because it's not yet included $sql = 'UPDATE ' . $table_prefix . 'Modules SET Version = "' . $version . '", BuildDate = ' . time() . ' WHERE LOWER(Name) = "' . strtolower($module_name) . '"'; $this->Conn->Query($sql); } /** * Sets module root category to passed * * @param string $module_name * @param string $category_id */ function SetModuleRootCategory($module_name, $category_id = 0) { // get table prefix from config, because application may not be available here $table_prefix = $this->getSystemConfig('Database', 'TablePrefix'); if ($module_name == 'kernel') { $module_name = 'in-portal'; } $sql = 'UPDATE ' . $table_prefix . 'Modules SET RootCat = ' . $category_id . ' WHERE LOWER(Name) = "' . strtolower($module_name) . '"'; $this->Conn->Query($sql); } /** * Returns maximal version of given module by scanning it's upgrade scripts * * @param string $module_name * @return string */ - function GetMaxModuleVersion($module_name) + function GetMaxModuleVersion($module_path) { - $upgrades_file = sprintf(UPGRADES_FILE, mb_strtolower($module_name).'/', 'sql'); + $module_path = rtrim(mb_strtolower($module_path), '/'); + $upgrades_file = sprintf(UPGRADES_FILE, $module_path . '/', 'sql'); + if (!file_exists($upgrades_file)) { // no upgrade file return '5.0.0'; } $sqls = file_get_contents($upgrades_file); $versions_found = preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs); if (!$versions_found) { // upgrades file doesn't contain version definitions return '5.0.0'; } return end($regs[1]); } /** * Runs SQLs from file * * @param string $filename * @param mixed $replace_from * @param mixed $replace_to */ function RunSQL($filename, $replace_from = null, $replace_to = null) { if (!file_exists(FULL_PATH.$filename)) { return ; } $sqls = file_get_contents(FULL_PATH.$filename); if (!$this->RunSQLText($sqls, $replace_from, $replace_to)) { if (is_object($this->_installator)) { $this->_installator->Done(); } else { if (isset($this->Application)) { $this->Application->Done(); } exit; } } } /** * Runs SQLs from string * * @param string $sqls * @param mixed $replace_from * @param mixed $replace_to */ function RunSQLText(&$sqls, $replace_from = null, $replace_to = null, $start_from=0) { $table_prefix = $this->getSystemConfig('Database', 'TablePrefix'); // add prefix to all tables if (strlen($table_prefix) > 0) { $replacements = Array ('INSERT INTO ', 'UPDATE ', 'ALTER TABLE ', 'DELETE FROM ', 'REPLACE INTO '); foreach ($replacements as $replacement) { $sqls = str_replace($replacement, $replacement . $table_prefix, $sqls); } } $sqls = str_replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS ' . $table_prefix, $sqls); $sqls = str_replace('DROP TABLE ', 'DROP TABLE IF EXISTS ' . $table_prefix, $sqls); $sqls = str_replace('<%TABLE_PREFIX%>', $table_prefix, $sqls); if (isset($replace_from) && isset($replace_to)) { // replace something additionally, e.g. module root category $sqls = str_replace($replace_from, $replace_to, $sqls); } $sqls = str_replace("\r\n", "\n", $sqls); // convert to linux line endings $no_comment_sqls = preg_replace("/#\s([^;]*?)\n/is", '', $sqls); // remove all comments "#" on new lines if ($no_comment_sqls === null) { // "ini.pcre.backtrack-limit" reached and error happened $sqls = explode(";\n", $sqls . "\n"); // ensures that last sql won't have ";" in it $sqls = array_map('trim', $sqls); // remove all comments "#" on new lines (takes about 2 seconds for 53000 sqls) $sqls = preg_replace("/#\s([^;]*?)/", '', $sqls); } else { $sqls = explode(";\n", $no_comment_sqls . "\n"); // ensures that last sql won't have ";" in it $sqls = array_map('trim', $sqls); } $sql_count = count($sqls); $db_collation = $this->getSystemConfig('Database', 'DBCollation'); for ($i = $start_from; $i < $sql_count; $i++) { $sql = $sqls[$i]; if (!$sql || (substr($sql, 0, 1) == '#')) { continue; // usually last line } if (substr($sql, 0, 13) == 'CREATE TABLE ' && $db_collation) { // it is CREATE TABLE statement -> add collation $sql .= ' COLLATE \'' . $db_collation . '\''; } $this->Conn->Query($sql); if ($this->Conn->getErrorCode() != 0) { if (is_object($this->_installator)) { $this->_installator->errorMessage = 'Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg().'<br /><br />Last Database Query:<br /><textarea cols="70" rows="10" readonly>'.htmlspecialchars($sql).'</textarea>'; $this->_installator->LastQueryNum = $i + 1; } return false; } } return true; } /** * Performs clean language import from given xml file * * @param string $lang_file * @param bool $upgrade * @todo Import for "core/install/english.lang" (322KB) takes 18 seconds to work on Windows */ function ImportLanguage($lang_file, $upgrade = false) { $lang_file = FULL_PATH.$lang_file.'.lang'; if (!file_exists($lang_file)) { return ; } $language_import_helper =& $this->Application->recallObject('LanguageImportHelper'); /* @var $language_import_helper LanguageImportHelper */ $language_import_helper->performImport($lang_file, '|0|1|2|', '', $upgrade ? LANG_SKIP_EXISTING : LANG_OVERWRITE_EXISTING); } /** * Converts module version in format X.Y.Z[-BN/-RCM] to signle integer * * @param string $version * @return int */ function ConvertModuleVersion($version) { if (preg_match('/(.*)-(B|RC)([\d]+)/', $version, $regs)) { // -B<M> or RC-<N> $parts = explode('.', $regs[1]); $parts[] = $regs[2] == 'B' ? 1 : 2; // B reliases goes before RC releases $parts[] = $regs[3]; } else { // releases without B/RC marks go after any B/RC releases $parts = explode('.', $version . '.3.100'); } $bin = ''; foreach ($parts as $part_index => $part) { if ($part_index == 3) { // version type only can be 1/2/3 (11 in binary form), so don't use padding at all $pad_count = 2; } else { $pad_count = 8; } $bin .= str_pad(decbin($part), $pad_count, '0', STR_PAD_LEFT); } return bindec($bin); } /** * Returns themes, found in system * * @param bool $rebuild * @return int */ function getThemes($rebuild = false) { if ($rebuild) { $this->rebuildThemes(); } $id_field = $this->Application->getUnitOption('theme', 'IDField'); $table_name = $this->Application->getUnitOption('theme', 'TableName'); $sql = 'SELECT Name, ' . $id_field . ' FROM ' . $table_name . ' ORDER BY Name ASC'; return $this->Conn->GetCol($sql, $id_field); } function ParseConfig($parse_section = false) { if (!file_exists($this->INIFile)) { return Array(); } if( file_exists($this->INIFile) && !is_readable($this->INIFile) ) { die('Could Not Open Ini File'); } $contents = file($this->INIFile); $retval = Array(); $section = ''; $ln = 1; $resave = false; foreach ($contents as $line) { if ($ln == 1 && $line != '<'.'?'.'php die() ?'.">\n") { $resave = true; } $ln++; $line = trim($line); $line = preg_replace('/;[.]*/', '', $line); if (strlen($line) > 0) { //echo $line . " - "; if (preg_match('/^\[[a-z]+\]$/i', str_replace(' ', '', $line))) { //echo 'section'; $section = mb_substr($line, 1, (mb_strlen($line) - 2)); if ($parse_section) { $retval[$section] = array(); } continue; } elseif (strpos($line, '=') !== false) { //echo 'main element'; list ($key, $val) = explode(' = ', $line); if (!$parse_section) { $retval[trim($key)] = str_replace('"', '', $val); } else { $retval[$section][trim($key)] = str_replace('"', '', $val); } } } } if ($resave) { $fp = fopen($this->INIFile, 'w'); reset($contents); fwrite($fp,'<'.'?'.'php die() ?'.">\n\n"); foreach ($contents as $line) { fwrite($fp,"$line"); } fclose($fp); } return $retval; } function SaveConfig($silent = false) { if (!is_writable($this->INIFile) && !is_writable(dirname($this->INIFile))) { trigger_error('Cannot write to "' . $this->INIFile . '" file.', $silent ? E_USER_NOTICE : E_USER_ERROR); return ; } $fp = fopen($this->INIFile, 'w'); fwrite($fp,'<'.'?'.'php die() ?'.">\n\n"); foreach ($this->systemConfig as $section_name => $section_data) { fwrite($fp, '['.$section_name."]\n"); foreach ($section_data as $key => $value) { fwrite($fp, $key.' = "'.$value.'"'."\n"); } fwrite($fp, "\n"); } fclose($fp); } /** * Sets value to system config (yet SaveConfig must be called to write it to file) * * @param string $section * @param string $key * @param string $value */ function setSystemConfig($section, $key, $value = null) { if (isset($value)) { if (!array_key_exists($section, $this->systemConfig)) { // create section, when missing $this->systemConfig[$section] = Array (); } // create key in section $this->systemConfig[$section][$key] = $value; return ; } unset($this->systemConfig[$section][$key]); } /** * Returns information from system config * * @return string */ function getSystemConfig($section, $key) { if (!array_key_exists($section, $this->systemConfig)) { return false; } if (!array_key_exists($key, $this->systemConfig[$section])) { return false; } return $this->systemConfig[$section][$key] ? $this->systemConfig[$section][$key] : false; } /** * Checks if system config is present and is not empty * * @return bool */ function systemConfigFound() { return file_exists($this->INIFile) && $this->systemConfig; } /** * Checks if given section is present in config * * @param string $section * @return bool */ function sectionFound($section) { return array_key_exists($section, $this->systemConfig); } /** * Returns formatted module name based on it's root folder * * @param string $module_folder * @return string */ function getModuleName($module_folder) { return implode('-', array_map('ucfirst', explode('-', $module_folder))); } /** * Returns information about module (based on "install/module_info.xml" file) * * @param string $module_folder * @return Array */ function getModuleInfo($module_folder) { $info_file = MODULES_PATH . '/' . $module_folder . '/install/module_info.xml'; if (!file_exists($info_file)) { return Array (); } $xml_helper =& $this->Application->recallObject('kXMLHelper'); /* @var $xml_helper kXMLHelper */ $root_node =& $xml_helper->Parse( file_get_contents($info_file) ); if (!is_object($root_node) || !preg_match('/^kxmlnode/i', get_class($root_node)) || ($root_node->Name == 'ERROR')) { // non-valid xml file return Array (); } $ret = Array (); $current_node =& $root_node->firstChild; do { $ret[ strtolower($current_node->Name) ] = trim($current_node->Data); } while (($current_node =& $current_node->NextSibling())); return $ret; } /** * Returns nice module string to be used on install/upgrade screens * * @param string $module_folder * @param string $version_string * @return string */ function getModuleString($module_folder, $version_string) { // image (if exists) <description> (<name> <version>) $ret = Array (); $module_info = $this->getModuleInfo($module_folder); if (array_key_exists('name', $module_info) && $module_info['name']) { $module_name = $module_info['name']; } else { $module_name = $this->getModuleName($module_folder); } if (array_key_exists('image', $module_info) && $module_info['image']) { $image_src = $module_info['image']; if (!preg_match('/^(http|https):\/\//', $image_src)) { // local image -> make absolute url $image_src = $this->Application->BaseURL() . $image_src; } $ret[] = '<img src="' . $image_src . '" alt="' . htmlspecialchars($module_name) . '" title="' . htmlspecialchars($module_name) . '" style="vertical-align:middle; margin: 3px 0 3px 5px"/>'; } if (array_key_exists('description', $module_info) && $module_info['description']) { $ret[] = $module_info['description']; } else { $ret[] = $module_name; } $ret[] = '(' . $module_name . ' ' . $version_string . ')'; return implode(' ', $ret); } /** * Creates module root category in "Home" category using given data and returns it * * @param string $name * @param string $description * @param string $category_template * @param string $category_icon * @return kDBItem */ function &createModuleCategory($name, $description, $category_template = null, $category_icon = null) { static $fields = null; if (!isset($fields)) { $ml_formatter =& $this->Application->recallObject('kMultiLanguage'); $fields['name'] = $ml_formatter->LangFieldName('Name'); $fields['description'] = $ml_formatter->LangFieldName('Description'); } $category =& $this->Application->recallObject('c', null, Array ('skip_autoload' => true)); /* @var $category kDBItem */ $category_fields = Array ( $fields['name'] => $name, 'Filename' => $name, 'AutomaticFilename' => 1, $fields['description'] => $description, 'Status' => STATUS_ACTIVE, 'Priority' => -9999, ); $category_fields['ParentId'] = $this->Application->findModule('Name', 'Core', 'RootCat'); if (isset($category_template)) { $category_fields['Template'] = $category_template; $category_fields['CachedTemplate'] = $category_template; } if (isset($category_icon)) { $category_fields['UseMenuIconUrl'] = 1; $category_fields['MenuIconUrl'] = $category_icon; } $category->Clear(); $category->SetDBFieldsFromHash($category_fields); $category->Create(); $priority_helper =& $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ $event = new kEvent('c:OnListBuild'); // ensure, that newly created category has proper value in Priority field $priority_helper->recalculatePriorities($event, 'ParentId = ' . $category_fields['ParentId']); // update Priority field in object, becase "CategoriesItem::Update" method will be called // from "kInstallToolkit::setModuleItemTemplate" and otherwise will set 0 to Priority field $sql = 'SELECT Priority FROM ' . $category->TableName . ' WHERE ' . $category->IDField . ' = ' . $category->GetID(); $category->SetDBField('Priority', $this->Conn->GetOne($sql)); return $category; } /** * Sets category item template into custom field for given prefix * * @param kDBItem $category * @param string $prefix * @param string $item_template */ function setModuleItemTemplate(&$category, $prefix, $item_template) { $this->Application->removeObject('c-cdata'); // recreate all fields, because custom fields are added during install script $category->defineFields(); $category->prepareConfigOptions(); // creates ml fields $category->SetDBField('cust_' . $prefix .'_ItemTemplate', $item_template); $category->Update(); } /** * Link custom field records with search config records + create custom field columns * * @param string $module_folder * @param int $item_type */ function linkCustomFields($module_folder, $prefix, $item_type) { $module_folder = strtolower($module_folder); $module_name = $module_folder; if ($module_folder == 'kernel') { $module_name = 'in-portal'; $module_folder = 'core'; } $db =& $this->Application->GetADODBConnection(); $sql = 'SELECT FieldName, CustomFieldId FROM ' . TABLE_PREFIX . 'CustomField WHERE Type = ' . $item_type . ' AND IsSystem = 0'; // config is not read here yet :( $this->Application->getUnitOption('p', 'ItemType'); $custom_fields = $db->GetCol($sql, 'CustomFieldId'); foreach ($custom_fields as $cf_id => $cf_name) { $sql = 'UPDATE ' . TABLE_PREFIX . 'SearchConfig SET CustomFieldId = ' . $cf_id . ' WHERE (TableName = "CustomField") AND (LOWER(ModuleName) = "' . $module_name . '") AND (FieldName = ' . $db->qstr($cf_name) . ')'; $db->Query($sql); } $this->Application->refreshModuleInfo(); // this module configs are now processed // because of configs was read only from installed before modules (in-portal), then reread configs $unit_config_reader =& $this->Application->recallObject('kUnitConfigReader'); /* @var $unit_config_reader kUnitConfigReader */ $unit_config_reader->scanModules(MODULES_PATH . DIRECTORY_SEPARATOR . $module_folder); // create correct columns in CustomData table $ml_helper =& $this->Application->recallObject('kMultiLanguageHelper'); $ml_helper->createFields($prefix . '-cdata', true); } /** * Deletes cache, useful after separate module install and installator last step * */ function deleteCache($refresh_permissions = false) { $this->Application->HandleEvent($event, 'adm:OnResetConfigsCache'); $this->Application->HandleEvent($event, 'c:OnResetCMSMenuCache'); if ($refresh_permissions) { if ($this->Application->ConfigValue('QuickCategoryPermissionRebuild')) { // refresh permission without progress bar $updater =& $this->Application->recallObject('kPermCacheUpdater'); /* @var $updater kPermCacheUpdater */ $updater->OneStepRun(); } else { // refresh permissions with ajax progress bar (when available) $this->Application->setDBCache('ForcePermCacheUpdate', 1); } } } /** * Perform redirect after separate module install * * @param string $module_folder * @param bool $refresh_permissions */ function finalizeModuleInstall($module_folder, $refresh_permissions = false) { + $this->SetModuleVersion(basename($module_folder), $module_folder); + if (!$this->Application->GetVar('redirect')) { return ; } - $this->SetModuleVersion($module_folder); - $this->deleteCache($refresh_permissions); $url_params = Array ( 'pass' => 'm', 'admin' => 1, 'RefreshTree' => 1, 'index_file' => 'index.php', ); $this->Application->Redirect('modules/modules_list', $url_params); } /** * Performs rebuild of themes * */ function rebuildThemes() { $this->Application->HandleEvent($themes_event, 'adm:OnRebuildThemes'); } /** * Checks that file is writable by group or others * * @param string $file * @return boolean */ function checkWritePermissions($file) { if (DIRECTORY_SEPARATOR == '\\') { // windows doen't allow to check permissions (always returns null) return null; } $permissions = fileperms($file); return $permissions & 0x0010 || $permissions & 0x0002; } } \ No newline at end of file Index: branches/5.1.x/core/install/step_templates/choose_modules.tpl =================================================================== --- branches/5.1.x/core/install/step_templates/choose_modules.tpl (revision 13188) +++ branches/5.1.x/core/install/step_templates/choose_modules.tpl (revision 13189) @@ -1,61 +1,61 @@ <?php ob_start(); ?> <tr class="table-color2"> <td class="text" colspan="2" valign="middle"> <table cellpadding="0" cellspacing="0"> <tr> <td valign="top"> <input type="checkbox" %3$s name="modules[]" id="module_%1$s" value="%1$s" style="margin-top: 8px;"/> </td> <td valign="top"> <label for="module_%1$s" style="">%2$s</label> <div style="font-weight: bold; color: red;">%4$s</div> </td> </tr> </table> </td> </tr> <?php $module_tpl = ob_get_clean(); echo '<br/><p> <strong>Please select Modules you would like to install:</strong></p>'; $first_time = $this->GetVar('step') != $this->currentStep; // data from this step was not submitted yet $selected = $this->GetVar('modules'); if (!$selected) { // preselect interface modules $selected = Array ('core'); } $modules_helper =& $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ $licensed_modules = array_map('strtolower', $modules_helper->_GetModules()); $modules = $this->ScanModules(); foreach ($modules as $module) { - $module_version = $this->toolkit->GetMaxModuleVersion($module); - $prerequisites_errors = $this->toolkit->CheckPrerequisites($module . '/', Array ($module_version), 'install'); + $module_version = $this->toolkit->GetMaxModuleVersion('modules/' . $module . '/'); + $prerequisites_errors = $this->toolkit->CheckPrerequisites('modules/' . $module . '/', Array ($module_version), 'install'); $license_module = $module; if (!in_array(strtolower($license_module), $licensed_modules)) { // when module isn't licensed user can't install it continue; // option #1: don't show non-licensed modules array_unshift($prerequisites_errors, 'Module not licensed'); // option #2: show warning under module name } if ($prerequisites_errors) { // disable checkbox, when some of prerequisites not passed $checked = 'disabled'; } else { // preserve user selected checked status $checked = in_array($module, $selected) || $first_time ? 'checked="checked"' : ''; } $error_msg = $prerequisites_errors ? implode('<br />', $prerequisites_errors) : ''; $module_string = $this->toolkit->getModuleString($module, $module_version); echo sprintf($module_tpl, $module, $module_string, $checked, $error_msg); } ?> \ No newline at end of file Index: branches/5.1.x/core/install.php =================================================================== --- branches/5.1.x/core/install.php (revision 13188) +++ branches/5.1.x/core/install.php (revision 13189) @@ -1,1451 +1,1448 @@ <?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. */ ini_set('display_errors', 1); error_reporting(E_ALL); define('IS_INSTALL', 1); define('ADMIN', 1); define('FULL_PATH', realpath(dirname(__FILE__).'/..') ); define('REL_PATH', '/core'); // run installator $install_engine = new kInstallator(); $install_engine->Init(); $install_engine->Run(); $install_engine->Done(); class kInstallator { /** * Reference to kApplication class object * * @var kApplication */ var $Application = null; /** * Connection to database * * @var kDBConnection */ var $Conn = null; /** * XML file containing steps information * * @var string */ var $StepDBFile = ''; /** * Step name, that currently being processed * * @var string */ var $currentStep = ''; /** * Steps list (preset) to use for current installation * * @var string */ var $stepsPreset = ''; /** * Installtion steps to be done * * @var Array */ var $steps = Array ( 'fresh_install' => Array ('check_paths', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish'), 'clean_reinstall' => Array ('check_paths', 'clean_db', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish'), 'already_installed' => Array ('check_paths', 'install_setup'), 'upgrade' => Array ('check_paths', 'install_setup', 'upgrade_modules', 'security', 'finish'), 'update_license' => Array ('check_paths', 'install_setup', 'select_license', /*'download_license',*/ 'select_domain', 'security', 'finish'), 'db_reconfig' => Array ('check_paths', 'install_setup', 'db_reconfig', 'security', 'finish'), 'fix_paths' => Array ('check_paths', 'install_setup', 'fix_paths', 'security', 'finish'), ); /** * Steps, that doesn't required admin to be logged-in to proceed * * @var Array */ var $skipLoginSteps = Array ('check_paths', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish', -1); /** * Steps, on which kApplication should not be initialized, because of missing correct db table structure * * @var Array */ var $skipApplicationSteps = Array ('check_paths', 'clean_db', 'db_config', 'db_reconfig' /*, 'install_setup'*/); // remove install_setup when application will work separately from install /** * Folders that should be writeable to continue installation. $1 - main writeable folder from config.php ("/system" by default) * * @var Array */ var $writeableFolders = Array ( '$1', '$1/images', '$1/images/resized', '$1/images/pending', '$1/images/pending/resized', '$1/images/emoticons', // for "In-Bulletin" '$1/images/manufacturers', // for "In-Commerce" '$1/images/manufacturers/resized', // for "In-Commerce" '$1/images/polls', // for "In-Bulletin" '$1/images/polls/resized', // for "In-Bulletin" '$1/backupdata', '$1/export', '$1/stylesheets', '$1/user_files', '$1/cache', '/themes', ); /** * Contains last error message text * * @var string */ var $errorMessage = ''; /** * Base path for includes in templates * * @var string */ var $baseURL = ''; /** * Holds number of last executed query in the SQL * * @var int */ var $LastQueryNum = 0; /** * Common tools required for installation process * * @var kInstallToolkit */ var $toolkit = null; function Init() { include_once(FULL_PATH . REL_PATH . '/kernel/utility/multibyte.php'); // emulating multi-byte php extension require_once(FULL_PATH . REL_PATH . '/install/install_toolkit.php'); // toolkit required for module installations to installator $this->toolkit = new kInstallToolkit(); $this->toolkit->setInstallator($this); $this->StepDBFile = FULL_PATH.'/'.REL_PATH.'/install/steps_db.xml'; $base_path = rtrim(preg_replace('/'.preg_quote(rtrim(REL_PATH, '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/'); $this->baseURL = 'http://'.$_SERVER['HTTP_HOST'].$base_path.'/core/install/'; set_error_handler( Array(&$this, 'ErrorHandler') ); if (file_exists($this->toolkit->INIFile)) { // if config.php found, then check his write permission too $this->writeableFolders[] = '/config.php'; } else { $this->writeableFolders[] = '/'; } if (!$this->toolkit->getSystemConfig('Misc', 'WriteablePath')) { // set global writable folder when such setting is missing $this->toolkit->setSystemConfig('Misc', 'WriteablePath', DIRECTORY_SEPARATOR . 'system'); $this->toolkit->SaveConfig(true); // immediately save, because this path will be used in Application later } $this->currentStep = $this->GetVar('step'); // can't check login on steps where no application present anyways :) $this->skipLoginSteps = array_unique(array_merge($this->skipLoginSteps, $this->skipApplicationSteps)); $this->SelectPreset(); if (!$this->currentStep) { $this->SetFirstStep(); // sets first step of current preset } $this->InitStep(); } function SetFirstStep() { reset($this->steps[$this->stepsPreset]); $this->currentStep = current($this->steps[$this->stepsPreset]); } /** * Selects preset to proceed based on various criteria * */ function SelectPreset() { $preset = $this->GetVar('preset'); if ($this->toolkit->systemConfigFound()) { // only at installation first step $status = $this->CheckDatabase(false); if ($status && $this->AlreadyInstalled()) { // if already installed, then all future actions need login to work $this->skipLoginSteps = Array ('check_paths', -1); if (!$preset) { $preset = 'already_installed'; $this->currentStep = ''; } } } if ($preset === false) { $preset = 'fresh_install'; // default preset } $this->stepsPreset = $preset; } function GetVar($name) { return array_key_exists($name, $_REQUEST) ? $_REQUEST[$name] : false; } function SetVar($name, $value) { $_REQUEST[$name] = $value; } /** * Performs needed intialization of data, that step requires * */ function InitStep() { $require_login = !in_array($this->currentStep, $this->skipLoginSteps); $this->InitApplication($require_login); if ($require_login) { // step require login to proceed if (!$this->Application->LoggedIn()) { $this->stepsPreset = 'already_installed'; $this->currentStep = 'install_setup'; // manually set 2nd step, because 'check_paths' step doesn't contain login form // $this->SetFirstStep(); } } switch ($this->currentStep) { case 'check_paths': $writeable_base = $this->toolkit->getSystemConfig('Misc', 'WriteablePath'); foreach ($this->writeableFolders as $folder_path) { $file_path = FULL_PATH . str_replace('$1', $writeable_base, $folder_path); if (file_exists($file_path) && !is_writable($file_path)) { $this->errorMessage = '<br/>Installation can not continue until all required permissions are set correctly'; break; } } break; case 'clean_db': // don't use Application, because all tables will be erased and it will crash $sql = 'SELECT Path FROM ' . TABLE_PREFIX . 'Modules'; $modules = $this->Conn->GetCol($sql); foreach ($modules as $module_folder) { $remove_file = '/' . $module_folder . 'install/remove_schema.sql'; if (file_exists(FULL_PATH . $remove_file)) { $this->toolkit->RunSQL($remove_file); } } $this->currentStep = $this->GetNextStep(); break; case 'db_config': case 'db_reconfig': $fields = Array ( 'DBType', 'DBHost', 'DBName', 'DBUser', 'DBUserPassword', 'DBCollation', 'TablePrefix' ); // set fields foreach ($fields as $field_name) { $submit_value = $this->GetVar($field_name); if ($submit_value !== false) { $this->toolkit->setSystemConfig('Database', $field_name, $submit_value); } /*else { $this->toolkit->setSystemConfig('Database', $field_name, ''); }*/ } break; case 'download_license': $license_source = $this->GetVar('license_source'); if ($license_source !== false && $license_source != 1) { // previous step was "Select License" and not "Download from Intechnic" option was selected $this->currentStep = $this->GetNextStep(); } break; case 'choose_modules': // if no modules found, then proceed to next step $modules = $this->ScanModules(); if (!$modules) { $this->currentStep = $this->GetNextStep(); } break; case 'select_theme': // put available theme list in database $this->toolkit->rebuildThemes(); break; case 'upgrade_modules': // get installed modules from db and compare their versions to upgrade script $modules = $this->GetUpgradableModules(); if (!$modules) { $this->currentStep = $this->GetNextStep(); } break; case 'install_setup': $next_preset = $this->Application->GetVar('next_preset'); if ($next_preset !== false) { if ($this->Application->GetVar('login') == 'root') { // verify "root" user using configuration settings $login_event = new kEvent('u.current:OnLogin'); $this->Application->HandleEvent($login_event); if ($login_event->status != erSUCCESS) { $user =& $this->Application->recallObject('u.current'); /* @var $user UsersItem */ $this->errorMessage = $user->GetErrorMsg('ValidateLogin') . '. If you don\'t know your username or password, contact Intechnic Support'; } } else { // non "root" user -> verify using licensing server $url_params = Array ( 'login=' . md5( $this->GetVar('login') ), 'password=' . md5( $this->GetVar('password') ), 'action=check', 'license_code=' . base64_encode( $this->toolkit->getSystemConfig('Intechnic', 'LicenseCode') ), - 'version=' . '4.3.0',//$this->toolkit->GetMaxModuleVersion('In-Portal'), + 'version=' . '4.3.0',//$this->toolkit->GetMaxModuleVersion('core/'), 'domain=' . base64_encode($_SERVER['HTTP_HOST']), ); $license_url = GET_LICENSE_URL . '?' . implode('&', $url_params); $file_data = curl_post($license_url, '', null, 'GET'); if (substr($file_data, 0, 5) == 'Error') { $this->errorMessage = substr($file_data, 6) . ' If you don\'t know your username or password, contact Intechnic Support'; } if ($this->errorMessage == '') { $user_id = -1; $session =& $this->Application->recallObject('Session'); $session->SetField('PortalUserId', $user_id); $this->Application->SetVar('u.current_id', $user_id); $this->Application->StoreVar('user_id', $user_id); } } if ($this->errorMessage == '') { // processed with redirect to selected step preset if (!isset($this->steps[$next_preset])) { $this->errorMessage = 'Preset "'.$next_preset.'" not yet implemented'; } else { $this->stepsPreset = $next_preset; } } } else { // if preset was not choosen, then raise error $this->errorMessage = 'Please select action to perform'; } break; case 'security': // perform write check if ($this->Application->GetVar('skip_security_check')) { // administrator intensionally skips security checks break; } $write_check = true; $check_paths = Array ('/', '/index.php', '/config.php', ADMIN_DIRECTORY . '/index.php'); foreach ($check_paths as $check_path) { $path_check_status = $this->toolkit->checkWritePermissions(FULL_PATH . $check_path); if (is_bool($path_check_status) && $path_check_status) { $write_check = false; break; } } // script execute check if (file_exists(WRITEABLE . '/install_check.php')) { unlink(WRITEABLE . '/install_check.php'); } $fp = fopen(WRITEABLE . '/install_check.php', 'w'); fwrite($fp, "<?php\n\techo 'OK';\n"); fclose($fp); $curl_helper =& $this->Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ $output = $curl_helper->Send($this->Application->BaseURL(WRITEBALE_BASE) . 'install_check.php'); unlink(WRITEABLE . '/install_check.php'); $execute_check = ($output !== 'OK'); $directive_check = true; $ini_vars = Array ('register_globals' => false, 'open_basedir' => true, 'allow_url_fopen' => false); foreach ($ini_vars as $var_name => $var_value) { $current_value = ini_get($var_name); if (($var_value && !$current_value) || (!$var_value && $current_value)) { $directive_check = false; break; } } if (!$write_check || !$execute_check || !$directive_check) { $this->errorMessage = true; } /*else { $this->currentStep = $this->GetNextStep(); }*/ break; } $this->PerformValidation(); // returns validation status (just in case) } /** * Validates data entered by user * * @return bool */ function PerformValidation() { if ($this->GetVar('step') != $this->currentStep) { // just redirect from previous step, don't validate return true; } $status = true; switch ($this->currentStep) { case 'db_config': case 'db_reconfig': // 1. check if required fields are filled $section_name = 'Database'; $required_fields = Array ('DBType', 'DBHost', 'DBName', 'DBUser', 'DBCollation'); foreach ($required_fields as $required_field) { if (!$this->toolkit->getSystemConfig($section_name, $required_field)) { $status = false; $this->errorMessage = 'Please fill all required fields'; break; } } if (!$status) break; // 2. check permissions, that use have in this database $status = $this->CheckDatabase(($this->currentStep == 'db_config') && !$this->GetVar('UseExistingSetup')); break; case 'select_license': $license_source = $this->GetVar('license_source'); if ($license_source == 2) { // license from file -> file must be uploaded $upload_error = $_FILES['license_file']['error']; if ($upload_error != UPLOAD_ERR_OK) { $this->errorMessage = 'Missing License File'; } } elseif (!is_numeric($license_source)) { $this->errorMessage = 'Please select license'; } $status = $this->errorMessage == ''; break; case 'root_password': // check, that password & verify password match $password = $this->Application->GetVar('root_password'); $password_verify = $this->Application->GetVar('root_password_verify'); if ($password != $password_verify) { $this->errorMessage = 'Passwords does not match'; } elseif (mb_strlen($password) < 4) { $this->errorMessage = 'Root Password must be at least 4 characters'; } $status = $this->errorMessage == ''; break; case 'choose_modules': break; case 'upgrade_modules': $modules = $this->Application->GetVar('modules'); if (!$modules) { $modules = Array (); $this->errorMessage = 'Please select module(-s) to ' . ($this->currentStep == 'choose_modules' ? 'install' : 'upgrade'); } // check interface module $upgrade_data = $this->GetUpgradableModules(); if (array_key_exists('core', $upgrade_data) && !in_array('core', $modules)) { // core can be upgraded, but isn't selected $this->errorMessage = 'Please select "Core" as interface module'; } $status = $this->errorMessage == ''; break; } return $status; } /** * Perform installation step actions * */ function Run() { if ($this->errorMessage) { // was error during data validation stage return ; } switch ($this->currentStep) { case 'db_config': case 'db_reconfig': // store db configuration $sql = 'SHOW COLLATION LIKE \''.$this->toolkit->getSystemConfig('Database', 'DBCollation').'\''; $collation_info = $this->Conn->Query($sql); if ($collation_info) { $this->toolkit->setSystemConfig('Database', 'DBCharset', $collation_info[0]['Charset']); // database is already connected, that's why set collation on the fly $this->Conn->Query('SET NAMES \''.$this->toolkit->getSystemConfig('Database', 'DBCharset').'\' COLLATE \''.$this->toolkit->getSystemConfig('Database', 'DBCollation').'\''); } $this->toolkit->SaveConfig(); if ($this->currentStep == 'db_config') { if ($this->GetVar('UseExistingSetup')) { // abort clean install and redirect to already_installed $this->stepsPreset = 'already_installed'; break; } // import base data into new database, not for db_reconfig $this->toolkit->RunSQL('/core/install/install_schema.sql'); $this->toolkit->RunSQL('/core/install/install_data.sql'); // create category using sql, because Application is not available here $table_name = $this->toolkit->getSystemConfig('Database', 'TablePrefix') . 'IdGenerator'; $this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1'); $resource_id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name); if ($resource_id === false) { $this->Conn->Query('INSERT INTO '.$table_name.' (lastid) VALUES (2)'); $resource_id = 2; } $fields_hash = Array ( 'l1_Name' => 'Content', 'Filename' => 'Content', 'AutomaticFilename' => 0, 'CreatedById' => -1, 'CreatedOn' => time(), 'ResourceId' => $resource_id - 1, 'l1_Description' => 'Content', 'Status' => 4, ); $this->Conn->doInsert($fields_hash, $this->toolkit->getSystemConfig('Database', 'TablePrefix') . 'Category'); $this->toolkit->SetModuleRootCategory('Core', $this->Conn->getInsertID()); // set module "Core" version after install (based on upgrade scripts) - $this->toolkit->SetModuleVersion('Core'); + $this->toolkit->SetModuleVersion('Core', 'core/'); // for now we set "In-Portal" module version to "Core" module version (during clean install) - $this->toolkit->SetModuleVersion('In-Portal', $this->toolkit->GetMaxModuleVersion('Core')); + $this->toolkit->SetModuleVersion('In-Portal', 'core/'); } break; case 'select_license': $license_source = $this->GetVar('license_source'); switch ($license_source) { case 1: // Download from Intechnic break; case 2: // Upload License File $file_data = array_map('trim', file($_FILES['license_file']['tmp_name'])); if ((count($file_data) == 3) && $file_data[1]) { $modules_helper =& $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ if ($modules_helper->verifyLicense($file_data[1])) { $this->toolkit->setSystemConfig('Intechnic', 'License', $file_data[1]); $this->toolkit->setSystemConfig('Intechnic', 'LicenseCode', $file_data[2]); $this->toolkit->SaveConfig(); } else { $this->errorMessage = 'Invalid License File'; } } else { $this->errorMessage = 'Invalid License File'; } break; case 3: // Use Existing License $license_hash = $this->toolkit->getSystemConfig('Intechnic', 'License'); if ($license_hash) { $modules_helper =& $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ if (!$modules_helper->verifyLicense($license_hash)) { $this->errorMessage = 'Invalid or corrupt license detected'; } } else { // happens, when browser's "Back" button is used $this->errorMessage = 'Missing License File'; } break; case 4: // Skip License (Local Domain Installation) if ($this->toolkit->sectionFound('Intechnic')) { // remove any previous license information $this->toolkit->setSystemConfig('Intechnic', 'License'); $this->toolkit->setSystemConfig('Intechnic', 'LicenseCode'); $this->toolkit->SaveConfig(); } break; } break; case 'download_license': $license_login = $this->GetVar('login'); $license_password = $this->GetVar('password'); $license_id = $this->GetVar('licenses'); if (strlen($license_login) && strlen($license_password) && !$license_id) { // Here we determine weather login is ok & check available licenses $url_params = Array ( 'login=' . md5($license_login), 'password=' . md5($license_password), - 'version=' . $this->toolkit->GetMaxModuleVersion('In-Portal'), + 'version=' . $this->toolkit->GetMaxModuleVersion('core/'), 'domain=' . base64_encode($_SERVER['HTTP_HOST']), ); $license_url = GET_LICENSE_URL . '?' . implode('&', $url_params); $file_data = curl_post($license_url, '', null, 'GET'); if (!$file_data) { // error connecting to licensing server $this->errorMessage = 'Unable to connect to the Intechnic server! Please try again later!'; } else { if (substr($file_data, 0, 5) == 'Error') { // after processing data server returned error $this->errorMessage = substr($file_data, 6); } else { // license received if (substr($file_data, 0, 3) == 'SEL') { // we have more, then one license -> let user choose $this->SetVar('license_selection', base64_encode( substr($file_data, 4) )); // we received html with radio buttons with names "licenses" $this->errorMessage = 'Please select which license to use'; } else { // we have one license $this->toolkit->processLicense($file_data); } } } } else if (!$license_id) { // licenses were not queried AND user/password missing $this->errorMessage = 'Incorrect Username or Password. If you don\'t know your username or password, contact Intechnic Support'; } else { // Here we download license $url_params = Array ( 'license_id=' . md5($license_id), 'dlog=' . md5($license_login), 'dpass=' . md5($license_password), - 'version=' . $this->toolkit->GetMaxModuleVersion('In-Portal'), + 'version=' . $this->toolkit->GetMaxModuleVersion('core/'), 'domain=' . base64_encode($_SERVER['HTTP_HOST']), ); $license_url = GET_LICENSE_URL . '?' . implode('&', $url_params); $file_data = curl_post($license_url, '', null, 'GET'); if (!$file_data) { // error connecting to licensing server $this->errorMessage = 'Unable to connect to the Intechnic server! Please try again later!'; } else { if (substr($file_data, 0, 5) == 'Error') { // after processing data server returned error $this->errorMessage = substr($file_data, 6); } else { $this->toolkit->processLicense($file_data); } } } break; case 'select_domain': $modules_helper =& $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ $license_hash = $this->toolkit->getSystemConfig('Intechnic', 'License'); if ($license_hash) { // when license present, then extract domain from it $license_hash = base64_decode($license_hash); list ( , , $license_keys) = $modules_helper->_ParseLicense($license_hash); $license_domain = $license_keys[0]['domain']; } else { // when license missing, then use current domain $license_domain = $_SERVER['HTTP_HOST']; } $domain = $this->GetVar('domain') == 1 ? $_SERVER['HTTP_HOST'] : str_replace(' ', '', $this->GetVar('other')); if ($domain != '') { if (strstr($domain, $license_domain) || $modules_helper->_IsLocalSite($domain)) { $this->toolkit->setSystemConfig('Misc', 'Domain', $domain); $this->toolkit->SaveConfig(); } else { $this->errorMessage = 'Domain name entered does not match domain name in the license!'; } } else { $this->errorMessage = 'Please enter valid domain!'; } break; case 'root_password': // update root password in database $password = md5( md5($this->Application->GetVar('root_password')) . 'b38'); $config_values = Array ( 'RootPass' => $password, 'Site_Path' => BASE_PATH.'/', // set Site_Path (for SSL & old in-portal code) 'Backup_Path' => FULL_PATH . $this->toolkit->getSystemConfig('Misc', 'WriteablePath') . DIRECTORY_SEPARATOR . 'backupdata', 'Smtp_AdminMailFrom' => 'portal@' . $this->toolkit->getSystemConfig('Misc', 'Domain') ); $this->toolkit->saveConfigValues($config_values); // login as "root", when no errors on password screen $this->Application->SetVar('login', 'root'); $this->Application->SetVar('password', $this->Application->GetVar('root_password')); $login_event = new kEvent('u.current:OnLogin'); $this->Application->HandleEvent($login_event); // import base language for core (english) $this->toolkit->ImportLanguage('/core/install/english'); // make sure imported language is set as active in session, created during installation $this->Application->Session->SetField('Language', 1); // set imported language as primary $lang =& $this->Application->recallObject('lang.-item', null, Array('skip_autoload' => true)); /* @var $lang LanguagesItem */ $lang->Load(1); // fresh install => ID=1 $lang->setPrimary(true); // for Front-End break; case 'choose_modules': // run module install scripts $modules = $this->Application->GetVar('modules'); if ($modules) { foreach ($modules as $module) { $install_file = MODULES_PATH.'/'.$module.'/install.php'; if (file_exists($install_file)) { include_once($install_file); - - // set module version after install (based on upgrade scripts) - $this->toolkit->SetModuleVersion($module); } } } // update category cache $updater =& $this->Application->recallObject('kPermCacheUpdater'); /* @var $updater kPermCacheUpdater */ $updater->OneStepRun(); break; case 'post_config': $this->toolkit->saveConfigValues( $this->GetVar('config') ); break; case 'select_theme': // 1. mark theme, that user is selected $theme_id = $this->GetVar('theme'); $theme_table = $this->Application->getUnitOption('theme', 'TableName'); $theme_idfield = $this->Application->getUnitOption('theme', 'IDField'); $sql = 'UPDATE ' . $theme_table . ' SET Enabled = 1, PrimaryTheme = 1 WHERE ' . $theme_idfield . ' = ' . $theme_id; $this->Conn->Query($sql); $this->toolkit->rebuildThemes(); // rescan theme to create structure after theme is enabled !!! if ($this->Application->isModuleEnabled('In-Portal')) { // 2. compile theme stylesheets (only In-Portal uses them) $css_table = $this->Application->getUnitOption('css', 'TableName'); $css_idfield = $this->Application->getUnitOption('css', 'IDField'); $sql = 'SELECT LOWER(Name) AS Name, ' . $css_idfield . ' FROM ' . $css_table; $css_hash = $this->Conn->GetCol($sql, $css_idfield); $css_item =& $this->Application->recallObject('css', null, Array('skip_autoload' => true)); /* @var $css_item StyleshetsItem */ foreach ($css_hash as $stylesheet_id => $theme_name) { $css_item->Load($stylesheet_id); $css_item->Compile(); $sql = 'UPDATE ' . $theme_table . ' SET StylesheetId = ' . $stylesheet_id . ' WHERE LOWER(Name) = ' . $this->Conn->qstr($theme_name); $this->Conn->Query($sql); } } // install theme dependent demo data if ($this->Application->GetVar('install_demo_data')) { $sql = 'SELECT Name FROM ' . $theme_table . ' WHERE ' . $theme_idfield . ' = ' . $theme_id; $theme_name = $this->Conn->GetOne($sql); foreach ($this->Application->ModuleInfo as $module_name => $module_info) { if ($module_name == 'In-Portal') { continue; } $this->toolkit->RunSQL('/themes' . '/' . $theme_name . '/' . $module_info['TemplatePath'] . '_install/install_data.sql', '{ThemeId}', $theme_id); } } break; case 'upgrade_modules': // get installed modules from db and compare their versions to upgrade script $modules = $this->Application->GetVar('modules'); if ($modules) { $upgrade_data = $this->GetUpgradableModules(); $start_from_module = $this->GetVar('continue_from_module'); $start_from_query = $this->GetVar('continue_from_query'); if (!$start_from_query) $start_from_query = 0; foreach ($modules as $module_name) { if ($start_from_module && $module_name != $start_from_module) { continue; } else { $start_from_module = false; //otherwise it will skip all modules after the one we start with! } $module_info = $upgrade_data[$module_name]; $upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'sql'); $sqls = file_get_contents($upgrades_file); $version_mark = preg_replace('/(\(.*?\))/', $module_info['FromVersion'], VERSION_MARK); // get only sqls from next (relative to current) version to end of file $start_pos = strpos($sqls, $version_mark); $sqls = substr($sqls, $start_pos); preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs); if (!$start_from_module) { $this->RunUpgrades($module_info['Path'], $regs[1], 'before'); } if (!$this->toolkit->RunSQLText($sqls, null, null, $start_from_query)) { $this->errorMessage .= '<input type="hidden" name="continue_from_module" value="'.$module_name.'">'; $this->errorMessage .= '<input type="hidden" name="continue_from_query" value="'.$this->LastQueryNum.'">'; $this->errorMessage .= '<br/>Click Continue button below to skip this query and go further<br/>'; $this->Done(); } $start_from_query = 0; // so that next module start from the beggining $this->toolkit->ImportLanguage('/' . $module_info['Path'] . 'install/english', true); $this->RunUpgrades($module_info['Path'], $regs[1], 'after'); // upgrade script could operate resulting language pack // after upgrade sqls are executed update version and upgrade language pack $this->toolkit->SetModuleVersion($module_name, $module_info['ToVersion']); } // for now we set "In-Portal" module version to "Core" module version (during upgrade) if (in_array('core', $modules)) { $this->toolkit->SetModuleVersion('In-Portal', $upgrade_data['core']['ToVersion']); } } break; case 'fix_paths': $this->toolkit->saveConfigValues( $this->Application->GetVar('config') ); break; case 'finish': // delete cache $this->toolkit->deleteCache(); // set installation finished mark if ($this->Application->ConfigValue('InstallFinished') === false) { $fields_hash = Array ( 'VariableName' => 'InstallFinished', 'VariableValue' => 1, ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'ConfigurationValues'); } break; } if ($this->errorMessage) { // was error during run stage return ; } $this->currentStep = $this->GetNextStep(); $this->InitStep(); // init next step (that will be shown now) $this->InitApplication(); if ($this->currentStep == -1) { // step after last step -> redirect to admin $this->Application->Redirect('index', null, '', 'index.php'); } } /** * Run upgrade PHP scripts for module with specified path * * @param string $module_path * @param Array $versions * @param string $mode upgrade mode = {before,after} */ function RunUpgrades($module_path, $versions, $mode) { static $upgrade_classes = Array (); $upgrades_file = sprintf(UPGRADES_FILE, $module_path, 'php'); if (!file_exists($upgrades_file) || !$versions) { return ; } if (!isset($upgrade_classes[$module_path])) { // save class name, because 2nd time // (in after call $upgrade_class variable will not be present) include_once $upgrades_file; $upgrade_classes[$module_path] = $upgrade_class; } $upgrade_object = new $upgrade_classes[$module_path](); if (method_exists($upgrade_object, 'setToolkit')) { $upgrade_object->setToolkit($this->toolkit); } foreach ($versions as $version) { $upgrade_method = 'Upgrade_'.str_replace(Array ('.', '-'), '_', $version); if (method_exists($upgrade_object, $upgrade_method)) { $upgrade_object->$upgrade_method($mode); } } } /** * Initialize kApplication * * @param bool $force initialize in any case */ function InitApplication($force = false) { if (($force || !in_array($this->currentStep, $this->skipApplicationSteps)) && !isset($this->Application)) { // step is allowed for application usage & it was not initialized in previous step global $start, $debugger, $dbg_options, $vars; include_once(FULL_PATH.'/core/kernel/startup.php'); $this->Application =& kApplication::Instance(); $this->toolkit->Application =& kApplication::Instance(); $this->Application->Init(); $this->Conn =& $this->Application->GetADODBConnection(); $this->toolkit->Conn =& $this->Application->GetADODBConnection(); } } /** * Show next step screen * */ function Done($error_message = null) { if (isset($error_message)) { $this->errorMessage = $error_message; } include_once (FULL_PATH.'/'.REL_PATH.'/install/incs/install.tpl'); if (isset($this->Application)) { $this->Application->Done(); } exit; } function ConnectToDatabase() { include_once FULL_PATH . '/core/kernel/db/db_connection.php'; $required_keys = Array ('DBType', 'DBUser', 'DBName'); foreach ($required_keys as $required_key) { if (!$this->toolkit->getSystemConfig('Database', $required_key)) { // one of required db connection settings missing -> abort connection return false; } } $this->Conn = new kDBConnection($this->toolkit->getSystemConfig('Database', 'DBType'), Array(&$this, 'DBErrorHandler')); $this->Conn->Connect( $this->toolkit->getSystemConfig('Database', 'DBHost'), $this->toolkit->getSystemConfig('Database', 'DBUser'), $this->toolkit->getSystemConfig('Database', 'DBUserPassword'), $this->toolkit->getSystemConfig('Database', 'DBName') ); // setup toolkit too $this->toolkit->Conn =& $this->Conn; return $this->Conn->errorCode == 0; } /** * Checks if core is already installed * * @return bool */ function AlreadyInstalled() { $table_prefix = $this->toolkit->getSystemConfig('Database', 'TablePrefix'); $sql = 'SELECT VariableValue FROM ' . $table_prefix . 'ConfigurationValues WHERE VariableName = "InstallFinished"'; return $this->TableExists('ConfigurationValues') && $this->Conn->GetOne($sql); } function CheckDatabase($check_installed = true) { // perform various check type to database specified // 1. user is allowed to connect to database // 2. user has all types of permissions in database if (mb_strlen($this->toolkit->getSystemConfig('Database', 'TablePrefix')) > 7) { $this->errorMessage = 'Table prefix should not be longer than 7 characters'; return false; } // connect to database $status = $this->ConnectToDatabase(); if ($status) { // if connected, then check if all sql statements work $sql_tests[] = 'DROP TABLE IF EXISTS test_table'; $sql_tests[] = 'CREATE TABLE test_table(test_col mediumint(6))'; $sql_tests[] = 'LOCK TABLES test_table WRITE'; $sql_tests[] = 'INSERT INTO test_table(test_col) VALUES (5)'; $sql_tests[] = 'UPDATE test_table SET test_col = 12'; $sql_tests[] = 'UNLOCK TABLES'; $sql_tests[] = 'ALTER TABLE test_table ADD COLUMN new_col varchar(10)'; $sql_tests[] = 'SELECT * FROM test_table'; $sql_tests[] = 'DELETE FROM test_table'; $sql_tests[] = 'DROP TABLE IF EXISTS test_table'; foreach ($sql_tests as $sql_test) { $this->Conn->Query($sql_test); if ($this->Conn->getErrorCode() != 0) { $status = false; break; } } if ($status) { // if statements work & connection made, then check table existance if ($check_installed && $this->AlreadyInstalled()) { $this->errorMessage = 'An In-Portal Database already exists at this location'; return false; } } else { // user has insufficient permissions in database specified $this->errorMessage = 'Permission Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg(); return false; } } else { // was error while connecting if (!$this->Conn) return false; $this->errorMessage = 'Connection Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg(); return false; } return true; } /** * Checks if all passed tables exists * * @param string $tables comma separated tables list * @return bool */ function TableExists($tables) { $prefix = $this->toolkit->getSystemConfig('Database', 'TablePrefix'); $all_found = true; $tables = explode(',', $tables); foreach ($tables as $table_name) { $sql = 'SHOW TABLES LIKE "'.$prefix.$table_name.'"'; if (count($this->Conn->Query($sql)) == 0) { $all_found = false; break; } } return $all_found; } /** * Returns modules list found in modules folder * * @return Array */ function ScanModules() { static $modules = null; if (!isset($modules)) { $modules = Array(); $fh = opendir(MODULES_PATH); while ( ($sub_folder = readdir($fh)) ) { $folder_path = MODULES_PATH . '/'.$sub_folder; if ($sub_folder != '.' && $sub_folder != '..' && is_dir($folder_path)) { // this is folder in MODULES_PATH directory if (file_exists($folder_path.'/install.php') && file_exists($folder_path.'/install/install_schema.sql')) { $install_order = trim( file_get_contents($folder_path . '/install/install_order.txt') ); $modules[$install_order] = $sub_folder; } } } } // allows to control module install order ksort($modules, SORT_NUMERIC); return $modules; } /** * Returns list of modules, that can be upgraded * */ function GetUpgradableModules() { $ret = Array (); foreach ($this->Application->ModuleInfo as $module_name => $module_info) { if ($module_name == 'In-Portal') { // don't show In-Portal, because it shares upgrade scripts with Core module continue; } $upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'sql'); if (!file_exists($upgrades_file)) { // no upgrade file continue; } $sqls = file_get_contents($upgrades_file); $versions_found = preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs); if (!$versions_found) { // upgrades file doesn't contain version definitions continue; } $to_version = end($regs[1]); $this_version = $this->toolkit->ConvertModuleVersion($module_info['Version']); if ($this->toolkit->ConvertModuleVersion($to_version) > $this_version) { // destination version is greather then current foreach ($regs[1] as $version) { if ($this->toolkit->ConvertModuleVersion($version) > $this_version) { $from_version = $version; break; } } $version_info = Array ( 'FromVersion' => $from_version, 'ToVersion' => $to_version, ); $ret[ strtolower($module_name) ] = array_merge_recursive2($module_info, $version_info); } } return $ret; } /** * Returns content to show for current step * * @return string */ function GetStepBody() { $step_template = FULL_PATH.'/core/install/step_templates/'.$this->currentStep.'.tpl'; if (file_exists($step_template)) { ob_start(); include_once ($step_template); return ob_get_clean(); } return '{step template "'.$this->currentStep.'" missing}'; } /** * Parses step information file, cache result for current step ONLY & return it * * @return Array */ function &_getStepInfo() { static $info = Array('help_title' => null, 'step_title' => null, 'help_body' => null, 'queried' => false); if (!$info['queried']) { $fdata = file_get_contents($this->StepDBFile); $parser = xml_parser_create(); xml_parse_into_struct($parser, $fdata, $values, $index); xml_parser_free($parser); foreach ($index['STEP'] as $section_index) { $step_data =& $values[$section_index]; if ($step_data['attributes']['NAME'] == $this->currentStep) { $info['step_title'] = $step_data['attributes']['TITLE']; if (isset($step_data['attributes']['HELP_TITLE'])) { $info['help_title'] = $step_data['attributes']['HELP_TITLE']; } else { // if help title not set, then use step title $info['help_title'] = $step_data['attributes']['TITLE']; } $info['help_body'] = trim($step_data['value']); break; } } $info['queried'] = true; } return $info; } /** * Returns particular information abou current step * * @param string $info_type * @return string */ function GetStepInfo($info_type) { $step_info =& $this->_getStepInfo(); if (isset($step_info[$info_type])) { return $step_info[$info_type]; } return '{step "'.$this->currentStep.'"; param "'.$info_type.'" missing}'; } /** * Returns passed steps titles * * @param Array $steps * @return Array * @see kInstaller:PrintSteps */ function _getStepTitles($steps) { $fdata = file_get_contents($this->StepDBFile); $parser = xml_parser_create(); xml_parse_into_struct($parser, $fdata, $values, $index); xml_parser_free($parser); $ret = Array (); foreach ($index['STEP'] as $section_index) { $step_data =& $values[$section_index]; if (in_array($step_data['attributes']['NAME'], $steps)) { $ret[ $step_data['attributes']['NAME'] ] = $step_data['attributes']['TITLE']; } } return $ret; } /** * Returns current step number in active steps_preset. * Value can't be cached, because same step can have different number in different presets * * @return int */ function GetStepNumber() { return array_search($this->currentStep, $this->steps[$this->stepsPreset]) + 1; } /** * Returns step name to process next * * @return string */ function GetNextStep() { $next_index = $this->GetStepNumber(); if ($next_index > count($this->steps[$this->stepsPreset]) - 1) { return -1; } return $this->steps[$this->stepsPreset][$next_index]; } /** * Returns step name, that was processed before this step * * @return string */ function GetPreviousStep() { $next_index = $this->GetStepNumber() - 1; if ($next_index < 0) { $next_index = 0; } return $this->steps[$this->stepsPreset][$next_index]; } /** * Prints all steps from active steps preset and highlights current step * * @param string $active_tpl * @param string $passive_tpl * @return string */ function PrintSteps($active_tpl, $passive_tpl) { $ret = ''; $step_titles = $this->_getStepTitles($this->steps[$this->stepsPreset]); foreach ($this->steps[$this->stepsPreset] as $step_name) { $template = $step_name == $this->currentStep ? $active_tpl : $passive_tpl; $ret .= sprintf($template, $step_titles[$step_name]); } return $ret; } /** * Installation error handler for sql errors * * @param int $code * @param string $msg * @param string $sql * @return bool * @access private */ function DBErrorHandler($code, $msg, $sql) { $this->errorMessage = 'Query: <br />'.htmlspecialchars($sql).'<br />execution result is error:<br />['.$code.'] '.$msg; return true; } /** * Installation error handler * * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @param Array $errcontext */ function ErrorHandler($errno, $errstr, $errfile = '', $errline = '', $errcontext = '') { if ($errno == E_USER_ERROR) { // only react on user fatal errors $this->Done($errstr); } } /** * Checks, that given button should be visible on current installation step * * @param string $name * @return bool */ function buttonVisible($name) { $button_visibility = Array ( 'continue' => $this->GetNextStep() != -1 || ($this->stepsPreset == 'already_installed'), 'refresh' => in_array($this->currentStep, Array ('check_paths', 'security')), 'back' => in_array($this->currentStep, Array (/*'select_license',*/ 'download_license', 'select_domain')), ); if ($name == 'any') { foreach ($button_visibility as $button_name => $button_visible) { if ($button_visible) { return true; } } return false; } return array_key_exists($name, $button_visibility) ? $button_visibility[$name] : true; } }