Index: trunk/core/install.php
===================================================================
diff -u -N -r6707 -r7702
--- trunk/core/install.php (.../install.php) (revision 6707)
+++ trunk/core/install.php (.../install.php) (revision 7702)
@@ -3,9 +3,22 @@
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.sql');
+
+ /**
+ * Format of version identificator in upgrade files
+ *
+ */
+ define('VERSION_MARK', '# ===== v ([\d]+\.[\d]+\.[\d]+) =====');
+
// print_pre($_POST);
$install_engine = new kInstallator();
@@ -72,26 +85,32 @@
'fresh_install' => Array ('check_paths', 'db_config', 'root_password', 'choose_modules', 'finish'),
'already_installed' => Array ('install_setup'),
- 'upgrade' => Array ('install_setup',/* ..., */ 'finish'),
+ '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
- 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');
+ var $writeableFolders = Array ('/system');
/**
* Contains last error message text
@@ -121,46 +140,59 @@
// 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->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) {
- // first step of current preset
- reset($this->steps[$this->stepsPreset]);
- $this->currentStep = current($this->steps[$this->stepsPreset]);
+ $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 ($preset === false) {
- $preset = 'fresh_install'; // default preset
-
- if (file_exists($this->INIFile)) {
- // only at installation first step
- $status = $this->CheckDatabase(false);
- if ($status && $this->AlreadyInstalled()) {
+ 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;
@@ -172,8 +204,17 @@
*/
function InitStep()
{
- $this->InitApplication();
+ $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) {
@@ -214,30 +255,43 @@
}
break;
- case 'install_setup':
- if ($this->stepsPreset == 'already_installed') {
- // if preset was not choosen, then raise error
- $this->errorMessage = 'Please select action to perform';
+ case 'upgrade_modules':
+ // get installed modules from db and compare their versions to upgrade script
+ $modules = $this->GetUpgradableModules();
+ if (!$modules) {
+ $this->currentStep = $this->GetNextStep();
}
- else {
- // if preset was choosen, then check root password entered
- $user_name = $this->GetVar('user_name');
- $user_password = $this->GetVar('user_password');
-
- if ($user_name == 'root') {
- $sql = 'SELECT VariableValue
- FROM '.$this->systemConfig['Database']['TablePrefix'].'
- WHERE VariableName = "RootPass"';
- $root_password = $this->Conn->GetOne($sql);
- $user_password = md5( md5($user_password) . 'b38');
- if ($user_password != $root_password) {
- $this->errorMessage = 'Invalid User Name or Password. If you don\'t know your username or password, contact Intechnic Support';
+ 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 {
- $this->errorMessage = 'By now only login using "root" username is supported';
+ // 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;
}
@@ -314,29 +368,100 @@
// 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');
- $sql = 'UPDATE '.TABLE_PREFIX.'ConfigurationValues
- SET VariableValue = '.$this->Conn->qstr($password).'
- WHERE VariableName = "RootPass"';
- $this->Conn->Query($sql);
+ $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');
- foreach ($modules as $module) {
- $install_file = MODULES_PATH.'/'.$module.'/install.php';
- if (file_exists($install_file)) {
- include_once($install_file);
+ 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
+ $themes_helper =& $this->Application->recallObject('ThemesHelper');
+ /* @var $themes_helper kThemesHelper */
+
+ $themes_helper->refreshThemes();
+
+ $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();
+
+ foreach ($modules as $module_name) {
+ $module_info = $upgrade_data[$module_name];
+ $upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path']);
+
+ $sqls = file_get_contents($upgrades_file);
+ $version_mark = preg_replace('/(\(.*?\))/', $module_info['FromVersion'], VERSION_MARK);
+
+ $start_pos = strpos($sqls, $version_mark);
+ $sqls = substr($sqls, $start_pos);
+ $this->RunSQLText($sqls);
+
+ // 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) {
@@ -351,15 +476,55 @@
if ($this->currentStep == -1) {
// step after last step -> redirect to admin
- $this->Application->Redirect('index', null, '', 'admin/index.php');
+ $this->Application->Redirect('index', null, '', 'index.php');
}
}
- function InitApplication()
+ /**
+ * Sets module version to passed
+ *
+ * @param string $module_name
+ * @param string $version
+ */
+ function SetModuleVersion($module_name, $version = false)
{
- if (!in_array($this->currentStep, $this->skipApplicationSteps) && !isset($this->Application)) {
+ 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 $debugger;
+ global $start, $debugger, $dbg_options;
include_once(FULL_PATH.'/core/kernel/startup.php');
$this->Application =& kApplication::Instance();
@@ -383,21 +548,42 @@
if (isset($this->Application)) {
$this->Application->Done();
- echo 'SID: ['.$this->Application->GetSID().']
';
+// echo 'SID: ['.$this->Application->GetSID().']
';
}
exit;
}
- function GetModuleVersion($module_name)
+ function GetMaxModuleVersion($module_name)
{
- return '0.1.1';
+ $upgrades_file = sprintf(UPGRADES_FILE, strtolower($module_name).'/');
+ 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;
@@ -410,7 +596,8 @@
*/
function AlreadyInstalled()
{
- return $this->TableExists('ConfigurationAdmin'); //,Category,Permissions');
+ $table_prefix = $this->systemConfig['Database']['TablePrefix'];
+ return $this->TableExists('ConfigurationValues') && $this->Conn->GetOne('SELECT VariableValue FROM '.$table_prefix.'ConfigurationValues WHERE VariableName = \'InstallFinished\'');
}
function CheckDatabase($check_installed = true)
@@ -463,6 +650,7 @@
}
else {
// was error while connecting
+ if (!$this->Conn) return false;
$this->errorMessage = 'Connection Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg();
return false;
}
@@ -493,14 +681,32 @@
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);
+ $this->RunSQLText($sqls, $replace_from, $replace_to);
+ }
+ /**
+ * Runs SQLs from string
+ *
+ * @param string $sqls
+ * @param mixed $replace_from
+ * @param mixed $replace_to
+ */
+ function RunSQLText(&$sqls, $replace_from = null, $replace_to = null)
+ {
$table_prefix = $this->systemConfig['Database']['TablePrefix'];
// add prefix to all tables
@@ -516,13 +722,15 @@
// 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);
foreach ($sqls as $sql) {
$sql = trim($sql);
- if (!$sql || substr($sql, 0, 1) == '#') {
- continue; // usually last line || comment
+ if (!$sql) {
+ continue; // usually last line
}
$this->Conn->Query($sql);
if ($this->Conn->getErrorCode() != 0) {
@@ -532,7 +740,7 @@
}
}
}
-
+
function ImportLanguage($lang_file)
{
$lang_file = FULL_PATH.$lang_file.'.lang';
@@ -575,6 +783,69 @@
}
/**
+ * 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']);
+ 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