Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Tue, Sep 16, 6:59 AM

in-portal

Index: branches/RC/core/kernel/db/db_connection.php
===================================================================
--- branches/RC/core/kernel/db/db_connection.php (revision 10508)
+++ branches/RC/core/kernel/db/db_connection.php (revision 10509)
@@ -1,675 +1,678 @@
<?php
/**
* Multi database connection class
*
*/
class kDBConnection {
/**
* Holds reference to global KernelApplication instance
* @access public
* @var kApplication
*/
var $Application;
/**
* Current database type
*
* @var string
* @access private
*/
var $dbType = 'mysql';
/**
* Created connection handle
*
* @var resource
* @access private
*/
var $connectionID = null;
/**
* Handle of currenty processed recordset
*
* @var resource
* @access private
*/
var $queryID = null;
/**
* DB type specific function mappings
*
* @var Array
* @access private
*/
var $metaFunctions = Array();
/**
* Function to handle sql errors
*
* @var string
* @access private
*/
var $errorHandler = '';
/**
* Error code
*
* @var int
* @access private
*/
var $errorCode = 0;
/**
* Error message
*
* @var string
* @access private
*/
var $errorMessage = '';
/**
* Defines if database connection
* operations should generate debug
* information
*
* @var bool
*/
var $debugMode = false;
/**
* Save query execution statistics
*
* @var bool
*/
var $_captureStatistics = false;
/**
* Last query to database
*
* @var string
*/
var $lastQuery = '';
/**
* Total processed queries count
*
* @var int
*/
var $_queryCount = 0;
/**
* Total time, used for serving queries
*
* @var Array
*/
var $_queryTime = 0;
/**
* Initializes connection class with
* db type to used in future
*
* @param string $dbType
* @return DBConnection
* @access public
*/
function kDBConnection($dbType, $errorHandler = '')
{
- $this->Application =& kApplication::Instance();
-
$this->dbType = $dbType;
// $this->initMetaFunctions();
if (!$errorHandler) {
$this->errorHandler = Array(&$this, 'handleError');
}
else {
$this->errorHandler = $errorHandler;
}
$this->_captureStatistics = defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !(defined('ADMIN') && ADMIN);
+
+ if (class_exists('kApplication')) {
+ // prevents "Fatal Error" on 2nd installation step (when database is empty)
+ $this->Application =& kApplication::Instance();
+ }
}
/**
* Set's custom error
*
* @param int $code
* @param string $msg
* @access public
*/
function setError($code, $msg)
{
$this->errorCode = $code;
$this->errorMessage = $msg;
}
/**
* Checks if previous query execution
* raised an error.
*
* @return bool
* @access public
*/
function hasError()
{
return !($this->errorCode == 0);
}
/**
* Caches function specific to requested
* db type
*
* @access private
*/
function initMetaFunctions()
{
$ret = Array();
switch ($this->dbType)
{
case 'mysql':
$ret = Array(); // only define functions, that name differs from "dbType_<meta_name>"
break;
}
$this->metaFunctions = $ret;
}
/**
* Get's function for specific db type
* based on it's meta name
*
* @param string $name
* @return string
* @access private
*/
function getMetaFunction($name)
{
/*if (!isset($this->metaFunctions[$name])) {
$this->metaFunctions[$name] = $name;
}*/
return $this->dbType.'_'.$name;
}
/**
* Try to connect to database server
* using specified parameters and set
* database to $db if connection made
*
* @param string $host
* @param string $user
* @param string $pass
* @param string $db
* @access public
*/
function Connect($host, $user, $pass, $db, $force_new = false)
{
$func = $this->getMetaFunction('connect');
$this->connectionID = $func($host, $user, $pass, $force_new) or trigger_error("Database connection failed, please check your connection settings", E_USER_ERROR);
if ($this->connectionID) {
if (defined('DBG_SQL_MODE')) {
$this->Query('SET sql_mode = \''.DBG_SQL_MODE.'\'');
}
if (defined('SQL_COLLATION') && defined('SQL_CHARSET')) {
$this->Query('SET NAMES \''.SQL_CHARSET.'\' COLLATE \''.SQL_COLLATION.'\'');
}
$this->setDB($db);
$this->showError();
}
}
function ReConnect($host, $user, $pass, $db, $force_new = false)
{
$func = $this->getMetaFunction('close');
$func($this->connectionID);
$this->Connect($host, $user, $pass, $db, $force_new);
}
/**
* Shows error message from previous operation
* if it failed
*
* @access private
*/
function showError($sql = '')
{
$this->setError(0, ''); // reset error
if ($this->connectionID) {
$func = $this->getMetaFunction('errno'); $this->errorCode = $func($this->connectionID);
if ($this->hasError()) {
$func = $this->getMetaFunction('error'); $this->errorMessage = $func($this->connectionID);
if (is_array($this->errorHandler)) {
$func = $this->errorHandler[1];
$ret = $this->errorHandler[0]->$func($this->errorCode, $this->errorMessage, $sql);
}
else {
$func = $this->errorHandler;
$ret = $func($this->errorCode,$this->errorMessage,$sql);
}
if (!$ret) exit;
}
}
}
/**
* Default error handler for sql errors
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access private
*/
function handleError($code, $msg, $sql)
{
echo '<b>Processing SQL</b>: '.$sql.'<br>';
echo '<b>Error ('.$code.'):</b> '.$msg.'<br>';
return false;
}
/**
* Set's database name for connection
* to $new_name
*
* @param string $new_name
* @return bool
* @access public
*/
function setDB($new_name)
{
if (!$this->connectionID) return false;
$func = $this->getMetaFunction('select_db');
return $func($new_name, $this->connectionID);
}
/**
* Returns first field of first line
* of recordset if query ok or false
* otherwise
*
* @param string $sql
* @param int $offset
* @return string
* @access public
*/
function GetOne($sql, $offset = 0)
{
$row = $this->GetRow($sql, $offset);
if(!$row) return false;
return array_shift($row);
}
/**
* Returns first row of recordset
* if query ok, false otherwise
*
* @param stirng $sql
* @param int $offset
* @return Array
* @access public
*/
function GetRow($sql, $offset = 0)
{
$sql .= ' '.$this->getLimitClause($offset, 1);
$ret = $this->Query($sql);
if(!$ret) return false;
return array_shift($ret);
}
/**
* Returns 1st column of recordset as
* one-dimensional array or false otherwise
* Optional parameter $key_field can be used
* to set field name to be used as resulting
* array key
*
* @param string $sql
* @param string $key_field
* @return Array
* @access public
*/
function GetCol($sql, $key_field = null)
{
$rows = $this->Query($sql);
if (!$rows) return $rows;
$i = 0; $row_count = count($rows);
$ret = Array();
if (isset($key_field)) {
while ($i < $row_count) {
$ret[$rows[$i][$key_field]] = array_shift($rows[$i]);
$i++;
}
}
else {
while ($i < $row_count) {
$ret[] = array_shift($rows[$i]);
$i++;
}
}
return $ret;
}
/**
* Queries db with $sql query supplied
* and returns rows selected if any, false
* otherwise. Optional parameter $key_field
* allows to set one of the query fields
* value as key in string array.
*
* @param string $sql
* @param string $key_field
* @return Array
*/
function Query($sql, $key_field = null, $no_debug = false)
{
$this->lastQuery = $sql;
if (!$no_debug) {
$this->_queryCount++;
}
if ($this->debugMode && !$no_debug) {
return $this->debugQuery($sql,$key_field);
}
$query_func = $this->getMetaFunction('query');
// set 1st checkpoint: begin
if ($this->_captureStatistics) {
$start_time = getmicrotime();
}
// set 1st checkpoint: end
$this->queryID = $query_func($sql,$this->connectionID);
if (is_resource($this->queryID)) {
$ret = Array();
$fetch_func = $this->getMetaFunction('fetch_assoc');
if (isset($key_field)) {
while (($row = $fetch_func($this->queryID))) {
$ret[$row[$key_field]] = $row;
}
}
else {
while (($row = $fetch_func($this->queryID))) {
$ret[] = $row;
}
}
// set 2nd checkpoint: begin
if ($this->_captureStatistics) {
$query_time = getmicrotime() - $start_time;
if ($query_time > DBG_MAX_SQL_TIME && !$no_debug) {
$this->Application->logSlowQuery($sql, $query_time);
}
$this->_queryTime += $query_time;
}
// set 2nd checkpoint: end
$this->Destroy();
return $ret;
}
else {
// set 2nd checkpoint: begin
if ($this->_captureStatistics) {
$this->_queryTime += getmicrotime() - $start_time;
}
// set 2nd checkpoint: end
}
$this->showError($sql);
return false;
}
function ChangeQuery($sql)
{
$this->Query($sql);
return $this->errorCode == 0 ? true : false;
}
function debugQuery($sql, $key_field = null)
{
global $debugger;
$query_func = $this->getMetaFunction('query');
// set 1st checkpoint: begin
$profileSQLs = defined('DBG_SQL_PROFILE') && DBG_SQL_PROFILE;
if ($profileSQLs) {
$queryID = $debugger->generateID();
$debugger->profileStart('sql_'.$queryID, $debugger->formatSQL($sql));
}
// set 1st checkpoint: end
$this->queryID = $query_func($sql, $this->connectionID);
if( is_resource($this->queryID) )
{
$ret = Array();
$fetch_func = $this->getMetaFunction('fetch_assoc');
if( isset($key_field) )
{
while( ($row = $fetch_func($this->queryID)) )
{
$ret[$row[$key_field]] = $row;
}
}
else
{
while( ($row = $fetch_func($this->queryID)) )
{
$ret[] = $row;
}
}
// set 2nd checkpoint: begin
$first_cell = count($ret) == 1 && count(current($ret)) == 1 ? current(current($ret)) : null;
if ($profileSQLs) {
$debugger->profileFinish('sql_'.$queryID, null, null, $this->getAffectedRows(), $first_cell, $this->_queryCount);
$debugger->profilerAddTotal('sql', 'sql_'.$queryID);
}
// set 2nd checkpoint: end
$this->Destroy();
return $ret;
}
else {
// set 2nd checkpoint: begin
if ($profileSQLs) {
$debugger->profileFinish('sql_'.$queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount);
$debugger->profilerAddTotal('sql', 'sql_'.$queryID);
}
// set 2nd checkpoint: end
}
$this->showError($sql);
return false;
}
/**
* Free memory used to hold recordset handle
*
* @access private
*/
function Destroy()
{
if($this->queryID)
{
$free_func = $this->getMetaFunction('free_result');
$free_func($this->queryID);
$this->queryID = null;
}
}
/**
* Returns auto increment field value from
* insert like operation if any, zero otherwise
*
* @return int
* @access public
*/
function getInsertID()
{
$func = $this->getMetaFunction('insert_id');
return $func($this->connectionID);
}
/**
* Returns row count affected by last query
*
* @return int
* @access public
*/
function getAffectedRows()
{
$func = $this->getMetaFunction('affected_rows');
return $func($this->connectionID);
}
/**
* Returns LIMIT sql clause part for specific db
*
* @param int $offset
* @param int $rows
* @return string
* @access private
*/
function getLimitClause($offset, $rows)
{
if(!($rows > 0)) return '';
switch ($this->dbType) {
default:
return 'LIMIT '.$offset.','.$rows;
break;
}
}
/**
* Correctly quotes a string so that all strings are escaped. We prefix and append
* to the string single-quotes.
* An example is $db->qstr("Don't bother",magic_quotes_runtime());
*
* @param s the string to quote
* @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
* This undoes the stupidity of magic quotes for GPC.
*
* @return quoted string to be sent back to database
*/
function qstr($s,$magic_quotes=false)
{
$replaceQuote = "\\'";
if (!$magic_quotes)
{
if ($replaceQuote[0] == '\\')
{
// only since php 4.0.5
$s = str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
}
return "'".str_replace("'",$replaceQuote,$s)."'";
}
// undo magic quotes for "
$s = str_replace('\\"','"',$s);
if($replaceQuote == "\\'") // ' already quoted, no need to change anything
{
return "'$s'";
}
else // change \' to '' for sybase/mssql
{
$s = str_replace('\\\\','\\',$s);
return "'".str_replace("\\'",$replaceQuote,$s)."'";
}
}
/**
* Returns last error code occured
*
* @return int
*/
function getErrorCode()
{
return $this->errorCode;
}
/**
* Returns last error message
*
* @return string
* @access public
*/
function getErrorMsg()
{
return $this->errorMessage;
}
function doInsert($fields_hash, $table, $type = 'INSERT')
{
$fields_sql = '';
$values_sql = '';
foreach ($fields_hash as $field_name => $field_value) {
$fields_sql .= '`'.$field_name.'`,';
$values_sql .= $this->qstr($field_value).',';
}
$fields_sql = preg_replace('/(.*),$/', '\\1', $fields_sql);
$values_sql = preg_replace('/(.*),$/', '\\1', $values_sql);
$sql = strtoupper($type).' INTO `'.$table.'` ('.$fields_sql.') VALUES ('.$values_sql.')';
return $this->ChangeQuery($sql);
}
function doUpdate($fields_hash, $table, $key_clause)
{
if (!$fields_hash) return true;
$fields_sql = '';
foreach ($fields_hash as $field_name => $field_value) {
$fields_sql .= '`'.$field_name.'` = '.$this->qstr($field_value).',';
}
$fields_sql = preg_replace('/(.*),$/', '\\1', $fields_sql);
$sql = 'UPDATE `'.$table.'` SET '.$fields_sql.' WHERE '.$key_clause;
return $this->ChangeQuery($sql);
}
/**
* Allows to detect table's presense in database
*
* @param string $table_name
* @return bool
*/
function TableFound($table_name)
{
static $table_found = Array();
if (!preg_match('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', $table_name)) {
$table_name = TABLE_PREFIX.$table_name;
}
if (!isset($table_found[$table_name])) {
$table_found[$table_name] = $this->Query('SHOW TABLES LIKE "'.$table_name.'"');
}
return $table_found[$table_name];
}
/**
* Returns query processing statistics
*
* @return Array
*/
function getQueryStatistics()
{
return Array ('time' => $this->_queryTime, 'count' => $this->_queryCount);
}
}
?>
\ No newline at end of file
Property changes on: branches/RC/core/kernel/db/db_connection.php
___________________________________________________________________
Modified: cvs2svn:cvs-rev
## -1 +1 ##
-1.25.2.5
\ No newline at end of property
+1.25.2.6
\ No newline at end of property
Index: branches/RC/core/install.php
===================================================================
--- branches/RC/core/install.php (revision 10508)
+++ branches/RC/core/install.php (revision 10509)
@@ -1,1207 +1,1207 @@
<?php
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');
/**
* Upgrade sqls are located using this mask
*
*/
define('UPGRADES_FILE', FULL_PATH.'/%sinstall/upgrades.%s');
/**
* Format of version identificator in upgrade files
*
*/
define('VERSION_MARK', '# ===== v ([\d]+\.[\d]+\.[\d]+) =====');
// print_pre($_POST);
$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;
/**
* Path to config.php
*
* @var string
*/
var $INIFile = '';
/**
* XML file containing steps information
*
* @var string
*/
var $StepDBFile = '';
/**
* Parsed data from config.php
*
* @var Array
*/
var $systemConfig = Array ();
/**
* 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', 'root_password', 'choose_modules', 'finish'),
'already_installed' => Array ('install_setup'),
'upgrade' => Array ('install_setup', 'upgrade_modules', /* ..., */ 'finish'),
'db_reconfig' => Array ('install_setup',/* ..., */ 'finish'),
'fix_paths' => Array ('install_setup',/* ..., */ 'finish'),
);
/**
* Steps, that doesn't required admin to be logged-in to proceed
*
* @var Array
*/
var $skipLoginSteps = Array ('root_password', 'choose_modules', 'finish', -1);
/**
* Steps, on which kApplication should not be initialized, because of missing correct db table structure
*
* @var Array
*/
var $skipApplicationSteps = Array ('check_paths', 'db_config'/*, 'install_setup'*/); // remove install_setup when application will work separately from install
/**
* Folders that should be writeable to continue installation
*
* @var Array
*/
var $writeableFolders = Array ('/system');
/**
* 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 unknown_type
*/
var $LastQueryNum = 0;
function Init()
{
$this->INIFile = FULL_PATH.'/config.php';
$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->INIFile)) {
// if config.php found, then check his write permission too
$this->writeableFolders[] = '/config.php';
}
else {
$this->writeableFolders[] = '/';
}
$this->systemConfig = $this->ParseConfig(true);
$this->systemConfig['Misc']['WriteablePath'] = '/system'; // for development purposes
$this->systemConfig['Misc']['Domain'] = $_SERVER['HTTP_HOST']; // for redirects from SSL mode
$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 (file_exists($this->INIFile) && $this->systemConfig) {
// 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 (-1);
if (!$preset) {
$preset = 'already_installed';
$this->currentStep = '';
}
}
}
if ($preset === false) {
$preset = 'fresh_install'; // default preset
}
$this->stepsPreset = $preset;
}
function GetVar($name)
{
return isset($_REQUEST[$name]) ? $_REQUEST[$name] : false;
}
/**
* 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->SetFirstStep();
}
}
switch ($this->currentStep) {
case 'check_paths':
foreach ($this->writeableFolders as $folder_path) {
$file_path = FULL_PATH.$folder_path;
if (!is_writable($file_path)) {
$this->errorMessage = 'Install cannot write to specified folder in the root directory of your installation';
break;
}
}
break;
case 'db_config':
$section_name = 'Database';
$fields = Array ('DBType', 'DBHost', 'DBName', 'DBUser', 'DBUserPassword', 'DBCollation', 'TablePrefix');
if (!isset($this->systemConfig[$section_name])) {
$this->systemConfig[$section_name] = Array ();
}
// set fields
foreach ($fields as $field_name) {
$submit_value = $this->GetVar($field_name);
if ($submit_value !== false) {
$this->systemConfig[$section_name][$field_name] = $submit_value;
}
elseif (!isset($this->systemConfig[$section_name][$field_name])) {
$this->systemConfig[$section_name][$field_name] = '';
}
}
break;
case 'choose_modules':
// if no modules found, then proceed to next step
$modules = $this->ScanModules();
if (!$modules) {
$this->currentStep = $this->GetNextStep();
}
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 && $this->Application->GetVar('login') == 'root') {
// option was choosen, then verify password & login user
$login_event = new kEvent('u.current:OnLogin');
$this->Application->HandleEvent($login_event);
if ($login_event->status == erSUCCESS) {
// login succeeded
if (!isset($this->steps[$next_preset])) {
$this->errorMessage = 'Preset "'.$next_preset.'" not yet implemented';
}
else {
$this->stepsPreset = $next_preset;
}
}
else {
// login failed
$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 {
// if preset was not choosen, then raise error
$this->errorMessage = 'Please select action to perform';
}
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':
// 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->systemConfig[$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();
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;
}
return $status;
}
/**
* Perform installation step actions
*
*/
function Run()
{
if ($this->errorMessage) {
// was error during data validation stage
return ;
}
switch ($this->currentStep) {
case 'db_config':
// store db configuration
$sql = 'SHOW COLLATION
LIKE \''.$this->systemConfig['Database']['DBCollation'].'\'';
$collation_info = $this->Conn->Query($sql);
if ($collation_info) {
$this->systemConfig['Database']['DBCharset'] = $collation_info[0]['Charset'];
// database is already connected, that's why set collation on the fly
$this->Conn->Query('SET NAMES \''.$this->systemConfig['Database']['DBCharset'].'\' COLLATE \''.$this->systemConfig['Database']['DBCollation'].'\'');
}
$this->SaveConfig();
// import base data into database
$this->RunSQL('/core/install/install_schema.sql');
$this->RunSQL('/core/install/install_data.sql');
// set module "Core" version after install (based on upgrade scripts)
$this->SetModuleVersion('Core');
break;
case 'root_password':
// update root password in database
$password = md5( md5($this->Application->GetVar('root_password')) . 'b38');
$this->SetConfigValue('RootPass', $password);
// set Site_Path (for SSL & old in-portal code)
$this->SetConfigValue('Site_Path', BASE_PATH.'/');
// import base language for core (english)
$this->ImportLanguage('/core/install/english');
// 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();
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->SetModuleVersion($module);
}
}
}
// scan themes
$this->Application->HandleEvent($themes_event, 'adm:OnRebuildThemes');
$this->Conn->Query('UPDATE '.TABLE_PREFIX.'Theme SET Enabled=1, PrimaryTheme =1 LIMIT 1');
// update categories cache
$updater =& $this->Application->recallObject('kPermCacheUpdater');
/* @var $updater kPermCacheUpdater */
$updater->OneStepRun();
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->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->RunUpgrades($module_info['Path'], $regs[1], 'after');
// after upgrade sqls are executed update version
$this->SetModuleVersion($module_name, $module_info['ToVersion']);
}
}
else {
$this->errorMessage = 'Please select module(-s) to upgrade';
}
break;
case 'finish':
// delete cache
$sql = 'DELETE FROM '.TABLE_PREFIX.'Cache
WHERE VarName IN ("config_files","configs_parsed","sections_parsed")';
$this->Conn->Query($sql);
// 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, 'setInstallator')) {
$upgrade_object->setInstallator($this);
}
foreach ($versions as $version) {
$upgrade_method = 'Upgrade_'.str_replace('.', '_', $version);
if (method_exists($upgrade_object, $upgrade_method)) {
$upgrade_object->$upgrade_method($mode);
}
}
}
/**
* Sets module version to passed
*
* @param string $module_name
* @param string $version
*/
function SetModuleVersion($module_name, $version = false)
{
if ($version === false) {
$version = $this->GetMaxModuleVersion($module_name);
}
$table_prefix = $this->systemConfig['Database']['TablePrefix'];
$sql = 'UPDATE '.$table_prefix.'Modules
SET Version = "'.$version.'"
WHERE Name = "'.$module_name.'"';
$this->Conn->Query($sql);
}
/**
* Sets new configuration variable value
*
* @param string $name
* @param mixed $value
*/
function SetConfigValue($name, $value)
{
$sql = 'UPDATE '.TABLE_PREFIX.'ConfigurationValues
SET VariableValue = '.$this->Conn->qstr($value).'
WHERE VariableName = '.$this->Conn->qstr($name);
$this->Conn->Query($sql);
}
/**
* 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;
include_once(FULL_PATH.'/core/kernel/startup.php');
$this->Application =& kApplication::Instance();
$this->Application->Init();
$this->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();
// echo 'SID: ['.$this->Application->GetSID().']<br />';
}
exit;
}
function GetMaxModuleVersion($module_name)
{
$upgrades_file = sprintf(UPGRADES_FILE, mb_strtolower($module_name).'/', 'sql');
if (!file_exists($upgrades_file)) {
// no upgrade file
return '4.0.1';
}
$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 '4.0.1';
}
return end($regs[1]);
}
function ConnectToDatabase()
{
include_once FULL_PATH.'/core/kernel/db/db_connection.php';
if (!isset($this->systemConfig['Database']['DBType']) ||
!isset($this->systemConfig['Database']['DBUser']) ||
!isset($this->systemConfig['Database']['DBName'])
) {
return false;
}
$this->Conn = new kDBConnection($this->systemConfig['Database']['DBType'], Array(&$this, 'DBErrorHandler'));
$this->Conn->Connect($this->systemConfig['Database']['DBHost'], $this->systemConfig['Database']['DBUser'], $this->systemConfig['Database']['DBUserPassword'], $this->systemConfig['Database']['DBName']);
return $this->Conn->errorCode == 0;
}
/**
* Checks if core is already installed
*
* @return bool
*/
function AlreadyInstalled()
{
$table_prefix = $this->systemConfig['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->systemConfig['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
$db_error = '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->systemConfig['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;
}
/**
* 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)) {
$this->Done();
}
}
/**
* 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->systemConfig['Database']['TablePrefix'];
// add prefix to all tables
if (strlen($table_prefix) > 0) {
$replacements = Array ('CREATE TABLE ', 'INSERT INTO ', 'UPDATE ', 'ALTER TABLE ', 'DELETE FROM ', 'REPLACE INTO ');
foreach ($replacements as $replacement) {
$sqls = str_replace($replacement, $replacement.$table_prefix, $sqls);
}
$sqls = str_replace('DROP TABLE ', 'DROP TABLE IF EXISTS '.$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
$sqls = preg_replace("/#([^;]*?)\n/", '', $sqls); // remove all comments
- $sqls = explode(";\n", $sqls);
+ $sqls = explode(";\n", $sqls . "\n"); // ensures that last sql won't have ";" in it
for ($i=$start_from; $i<count($sqls); $i++) {
$sql = trim($sqls[$i]);
if (!$sql) {
continue; // usually last line
}
if (substr($sql, 0, 13) == 'CREATE TABLE ' && $this->systemConfig['Database']['DBCollation']) {
// it is CREATE TABLE statement -> add collation
$sql .= ' COLLATE \''.$this->systemConfig['Database']['DBCollation'].'\'';
}
$this->Conn->Query($sql);
if ($this->Conn->getErrorCode() != 0) {
- $this->errorMessage = 'Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg().'<br /><br />Database Query:<pre>'.htmlspecialchars($sql).'</pre>';
- $this->LastQueryNum = $i+1;
- return false;
- }
+ $this->errorMessage = 'Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg().'<br /><br />Database Query:<pre>'.htmlspecialchars($sql).'</pre>';
+ $this->LastQueryNum = $i+1;
+ return false;
+ }
}
return true;
}
function ImportLanguage($lang_file)
{
$lang_file = FULL_PATH.$lang_file.'.lang';
if (!file_exists($lang_file)) {
return ;
}
$lang_xml =& $this->Application->recallObjectP('LangXML', null, Array(), false); // false - don't use temp tables
$lang_xml->Parse($lang_file, '|0|1|2|', '');
}
/**
* 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)) {
if ($sub_folder == 'core') {
// skip modules here
continue;
}
// this is folder in MODULES_PATH directory
if (file_exists($folder_path.'/install.php') && file_exists($folder_path.'/install/install_schema.sql')) {
$modules[] = $sub_folder;
}
}
}
}
return $modules;
}
/**
* Converts module version in format X.Y.Z to signle integer
*
* @param string $version
* @return int
*/
function ConvertModuleVersion($version)
{
$parts = explode('.', $version);
$bin = '';
foreach ($parts as $part) {
$bin .= str_pad(decbin($part), 8, '0', STR_PAD_LEFT);
}
return bindec($bin);
}
/**
* Returns list of modules, that can be upgraded
*
*/
function GetUpgradableModules()
{
$ret = Array ();
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
$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->ConvertModuleVersion($module_info['Version']);
if ($this->ConvertModuleVersion($to_version) > $this_version) {
// destination version is greather then current
foreach ($regs[1] as $version) {
if ($this->ConvertModuleVersion($version) > $this_version) {
$from_version = $version;
break;
}
}
$version_info = Array (
'FromVersion' => $from_version,
'ToVersion' => $to_version,
);
$ret[$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;
}
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 = eregi_replace(';[.]*','',$line);
if (strlen($line) > 0) {
//echo $line . " - ";
if(eregi('^[[a-z]+]$',str_replace(' ', '', $line))) {
//echo 'section';
$section = mb_substr($line, 1, (mb_strlen($line) - 2));
if ($parse_section) {
$retval[$section] = array();
}
continue;
} elseif (eregi('=',$line)) {
//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()
{
$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);
}
/**
* 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);
}
}
}
/*function print_pre($s)
{
echo '<pre>', print_r($s, true). '</pre>';
}*/
?>
\ No newline at end of file
Property changes on: branches/RC/core/install.php
___________________________________________________________________
Modified: cvs2svn:cvs-rev
## -1 +1 ##
-1.12.2.5
\ No newline at end of property
+1.12.2.6
\ No newline at end of property

Event Timeline