_config)) { $this->_config = $galleryStub->getConfig('storage.config'); if (empty($this->_config)) { $this->_config = array(); $this->_config['type'] = 'mysql'; $this->_config['hostname'] = 'localhost'; $this->_config['username'] = 'root'; $this->_config['password'] = ''; $this->_config['database'] = 'gallery2'; $this->_config['tablePrefix'] = 'g2_'; $this->_config['columnPrefix'] = 'g_'; $firstConfig = true; } } if (!empty($_POST['action']) && $_POST['action'] == 'save') { foreach (array('type', 'hostname', 'username', 'password', 'database', 'tablePrefix', 'columnPrefix') as $key) { $this->_config[$key] = $this->sanitize($_POST[$key]); } } $dbPlatformType = null; switch ($this->_config['type']) { case 'mysql': case 'mysqlt': if (!$firstConfig && !function_exists('mysql_connect')) { $templateData['error']['phpDbMissing'] = _('You must have the MySQL PHP module installed'); } $dbPlatformType = 'mysql'; break; case 'db2': if (!$firstConfig && !function_exists('odbc_connect')) { $templateData['error']['phpDbMissing'] = _('You must have the ODBC module installed'); } $dbPlatformType = 'db2'; break; case 'postgres7': case 'postgres': if (!$firstConfig && !function_exists('pg_connect')) { $templateData['error']['phpDbMissing'] = _('You must have the PostgreSQL PHP module installed'); } $dbPlatformType = 'postgres'; break; case 'oci8po': if (!$firstConfig && !function_exists('OCIPLogon')) { $templateData['error']['phpDbMissing'] = _('You must have the Oracle OCI8 PHP module installed'); } $dbPlatformType = 'oracle'; break; } if (!empty($_POST['action']) && $_POST['action'] == 'save') { if (empty($this->_config['columnPrefix'])) { $templateData['error']['columnPrefix'] = sprintf(_('You must specify a column prefix (we recommend %s)'), 'g_'); } else if (preg_match('{[^A-Za-z0-9_]}', $this->_config['columnPrefix'])) { $templateData['error']['columnPrefix'] = _('Use only letters, numbers and underscore in the column prefix'); } if (empty($this->_config['tablePrefix'])) { $templateData['error']['tablePrefix'] = sprintf(_('You must specify a table prefix (we recommend %s)'), 'g2_'); } else if (preg_match('{[^A-Za-z0-9_]}', $this->_config['tablePrefix'])) { $templateData['error']['tablePrefix'] = _('Use only letters, numbers and underscore in the table prefix'); } if (empty($templateData['errors']) && empty($templateData['error'])) { /* Load up ADOdb */ require_once(dirname(__FILE__) . '/../../lib/adodb/adodb.inc.php'); $this->_captureStart(); $this->_db =& ADONewConnection($this->_config['type']); $this->_db->debug = true; $this->_captureEnd(); if (empty($this->_db)) { $templateData['errors'][] = sprintf( _('Unable to create a database connection of type %s'), $this->_config['type']); } if (empty($templateData['errors'])) { $this->_captureStart(); $result = $this->_db->NConnect($this->_config['hostname'], $this->_config['username'], $this->_config['password'], $this->_config['database']); $this->_captureEnd(); if ($result === false) { $templateData['errors'][] = _('Unable to connect to database with the information provided.'); } } } if (empty($templateData['errors']) && empty($templateData['error'])) { $this->_captureStart(); $result = $this->_db->MetaTables(); if ($result === false) { $templateData['errors'][] = _('The database you specified does not exist. Please create it.'); $dbVersion = ''; } else { /* * Check if the db user has (all?) required db privileges to finish the * installer steps. */ list ($ret, $error) = $this->_testPrivileges($dbPlatformType, $result); if ($ret === false) { $templateData['errors'][] = _('The database privileges test did not complete successfully.'); if (!empty($error)) { $templateData['errors'][] = $error; } } /* Check the status and get the version of the Gallery database */ $dbVersion = $this->_getDbVersion($result); } $this->_captureEnd(); /* Check the status of the disk storage (g2data directory) */ $versions = $this->_getVersions(); $datVersion = $versions['installed']; $codebaseVersion = $versions['codebase']; $galleryStub->setConfig('codebase.version', $codebaseVersion); } $templateData['databaseErrors'] = $this->_getCaptured(); if (empty($_POST['confirmReuseTables'])) { if (empty($dbVersion) && empty($datVersion)) { /* Fresh, clean install. Good. */ /* Advance to next step */ $confirmRequired = 0; } else if (!empty($dbVersion) && empty($datVersion)) { /* DB tables exist, but g2data seems to be not ok. */ /* Explain and offer clean install. */ $confirmRequired = 1; $templateData['showConfirmCleanInstall'] = 1; $templateData['warnings'][] = _('Gallery tables already exist in this database! But there is no \'versions.dat\' file in your G2 storage directory which we interpret as a broken state of G2. Either create a versions.dat file with the correct format if you think your G2 should still work or select a clean install, which will erase all data in the database and in the storage directory.'); } else if (empty($dbVersion) && !empty($datVersion)) { /* DB tables don't exist, but g2data has versions.dat */ /* Explain and offer clean install. */ $confirmRequired = 1; $templateData['showConfirmCleanInstall'] = 1; $templateData['warnings'][] = _('The G2 storage directory has a versions.dat file of an old install. But the Gallery database tables don\'t exist. Select a clean install to erase all data in the Gallery storage directory and advance to the next step.'); } else if ($dbVersion != $datVersion) { /* Installed version is not ok, mismatch between g2data and the database */ /* Explain and offer Clean Install. */ $confirmRequired = 1; $templateData['showConfirmCleanInstall'] = 1; $templateData['warnings'][] = _('Gallery tables already exist in the database and there is a versions.dat file in the Gallery storage directory. But the version of the installed Gallery database tables does not match the version of the installed data in the Gallery storage directory. Select a clean install to erase all data in the database and in the storage directory and to advance to the next step.'); } else { /* Installed version is ok, offer Reuse Tables and Clean Install. */ $confirmRequired = 1; $templateData['showConfirmCleanInstall'] = 1; $templateData['showConfirmReuseTables'] = 1; $templateData['warnings'][] = _('Gallery tables already exist in the database and the Gallery storage directory seems to be intact. Either choose to reuse the existing database tables and storage directory data or select a clean install to erase all existing data in the database and the storage directory.'); } } if (empty($templateData['errors']) && empty($templateData['error']) && (empty($confirmRequired) || !empty($_POST['confirmReuseTables']))) { $this->setComplete(true); } } elseif (!empty($_GET['action']) && $_GET['action'] == 'clean') { /* Do a clean install, erase the data, initiate the G2 API on db level */ if ($this->_loadG2Api(3)) { $storageCleaned = $dbCleaned = false; /* Reset the storage directory */ if (!$this->_emptyGalleryStorageDirectory()) { $templateData['errors'][] = _('Could not execute the required API to erase the storage directory. Please erase the Gallery storage directory manually.'); } else { $storageCleaned = true; } /* Drop all Gallery database tables listed in the schema table */ $this->_captureStart(); $ret = $this->_cleanDatabase(); if ($ret->isError()) { $ret = $ret->wrap(__FILE__, __LINE__); global $gallery; $templateData['errors'][] = _('Could not execute the required API to drop the Gallery database tables. Please clean the Gallery database manually.'); $templateData['databaseErrors'] = $gallery->getDebugBuffer(); $templateData['stackTrace'] = $ret->getAsHtml(); $gallery->clearDebugBuffer(); } else { $dbCleaned = true; } $this->_captureEnd(); $templateData['databaseErrors'] = $this->_getCaptured(); if ($storageCleaned && $dbCleaned) { $this->setComplete(true); } } else { $templateData['errors'][] = _('Could not load the G2 API. Please erase the Gallery database tables and the storage directory manually.'); } } $templateData['isMultisite'] = $galleryStub->getConfig('isMultisite'); if ($this->isComplete()) { $galleryStub->setConfig('storage.config', $this->_config); if (empty($_POST['confirmReuseTables'])) { /* Remember that this is a fresh/clean install for later steps */ $galleryStub->setConfig('freshInstall', true); } else { $galleryStub->setConfig('freshInstall', false); /* Remember the installed version to compare it later to the codebase version */ $galleryStub->setConfig('installed.version', $datVersion); } $templateData['bodyFile'] = 'DatabaseSetupSuccess.html'; } elseif ((empty($templateData['errors']) && empty($templateData['error']) || !empty($_POST['action']) && $_POST['action'] == 'clean') && !empty($_POST['confirmCleanInstall'])) { $galleryStub->setConfig('storage.config', $this->_config); $templateData['bodyFile'] = 'CleanInstallRequest.html'; } else { $templateData['config'] = $this->_config; foreach (array('mysql' => _('MySQL (all versions)'), 'mysqlt' => _('MySQL with Transactions (v3.23.34a and newer)'), 'postgres7' => _('PostgreSQL v7.x'), 'postgres' => _('PostgreSQL v6.x (not well tested)'), 'oci8po' => _('Oracle (9i and newer)'), 'db2' => _('(EXPERIMENTAL!) IBM DB2 for Unix/Windows, v8.2.2 and newer')) as $key => $value) { $templateData['dbList'][$key] = $value; } $templateData['dbSelected'][$this->_config['type']] = 1; $templateData['bodyFile'] = 'DatabaseSetupRequest.html'; } } function _captureStart() { ob_start(); } function _captureEnd() { if (!isset($this->_debugContents)) { $this->_debugContents = ''; } $this->_debugContents .= ob_get_contents(); ob_end_clean(); } function _getCaptured() { if (empty($this->_debugContents)) { return ""; } $contents = $this->_debugContents; unset($this->_debugContents); return $contents; } function isRedoable() { return true; } /** * Check if the user has the most basic database privileges required to finish the install * steps successfully. Check: * - CREATE TABLE, ALTER TABLE, DROP TABLE * - CREATE INDEX, DROP INDEX * - CREATE SEQUENCE, DROP SEQUENCE * * @param: string db type platform name (i.e. not mysqlt, but mysql) * @param: array (string tableName) * @return array (boolean success, string errors) */ function _testPrivileges($dbType, $metatables) { global $gallery; if (!is_array($metatables) || empty($dbType)) { return array(false, _('Unknown DB type or no known tables information.')); } /* * Execute T_InstallerTest_1.sql through T_InstallerTest_4.sql. These create, alter and * drop a table, and create and drop index. Because our .xml transforms (MySQL.xsl,...) * always updates the Schema table for all table create, alter, drops, we use here a test * table which also has the name and the structure of the Schema table, just with another * tablePrefix. * * Set an unused tablePrefix such that we can play with create/drop table in an * unused database "namespace". Try a few prefices, don't try to drop! */ $ok = false; for ($i = 0; $i < 10; $i++) { $tablePrefix = 'gtst' . $i; if (!$this->in_array_cin($tablePrefix . 'Schema', $metatables)) { $ok = true; break; } } if (!$ok) { return array(false, sprintf(_('Could not find an unused table prefix similar to "%s".'), $tablePrefix)); } /* Load the Test SQL code */ require_once(dirname(__FILE__) . '/../../modules/core/classes/GalleryStorage/DatabaseStorageExtras.class'); /* GalleryPlatform is not available at this point */ $schemaTpl = dirname(__FILE__) . '/../../modules/core/classes/GalleryStorage/' . 'DatabaseStorage/schema/schema.tpl'; if (!($fd = fopen($schemaTpl, 'rb'))) { return array(false, sprintf(_('Could not open schema file: "%s".'), $schemaTpl)); } $fileSize = filesize($schemaTpl); $sqlData = $fileSize == 0 ? '' : fread($fd, $fileSize); fclose($fd); $sqlData = explode("\n", $sqlData); $moduleSql = DatabaseStorageExtras::parseSqlTemplate($sqlData, $dbType); for ($i = 1; $i <= 4; $i++) { list ($success, $errorMessage) = $this->_executeSql($moduleSql['test']['InstallerTest'][$i], $tablePrefix); if (!$success) { return array(false, $errorMessage); } } /* Check CREATE and DROP SEQUENCE privileges */ $sequenceId = 'g2privtestseq'; $result = $this->_db->CreateSequence($tablePrefix . $sequenceId); if (empty($result)) { return array(false, _('Failed to create a DB test sequence.' . 'Check the returned error message and README.html ' . 'for missing privileges and clean up the database.')); } $result = $this->_db->DropSequence($tablePrefix . $sequenceId); if (empty($result)) { return array(false, _('Test failed to drop a DB test sequence.' . 'Check the returned error message and README.html ' . 'for missing privileges and clean up the database.')); } return array(true, null); } /** * Execute a series of SQL statements * * @param the path to the file * @param the table prefix to use * @return array(boolean success, string error message) */ function _executeSql($buffer, $tablePrefix) { if (empty($buffer)) { return array(false, _('Missing SQL statements')); } /* * Split the file where semicolons are followed by a blank line.. * PL/SQL blocks will have other semicolons, so we can't split on every one. * But first, remove that last semicolon such that all statements have no semicolon * (required for oracle) */ if ($pos = strrpos($buffer, ';')) { $buffer = substr($buffer, 0, $pos); } $statements = preg_split('/; *\r?\n *\r?\n/s', $buffer); foreach ($statements as $query) { $query = trim($query); if (!empty($query)) { $query = str_replace('DB_TABLE_PREFIX', $tablePrefix, $query); $query = str_replace('DB_COLUMN_PREFIX', $this->_config['columnPrefix'], $query); /* For mysql, another replacement is required */ $query = str_replace('DB_TABLE_TYPE', 'MyISAM', $query); $result = $this->_db->Execute($query); if (empty($result)) { return array(false, _('Check the returned error message and README.html ' . 'for missing privileges and clean up the database.')); } } } return array(true, null); } /** * Get the installed version from versions.dat in the Storage directory * And the codebase version from modules/core/module.inc * * Avoid using GalleryInitFirstPass (as this is called even for fresh installs) * * @return array ('installed' => string the storage versions.dat version, * 'codebase' => string the codebase version) */ function _getVersions() { global $galleryStub; $versions['installed'] = $versions['codebase'] = ''; /* Load platform level to use the CoreModule and read data from versions.dat */ $this->_loadG2Api(2); require_once(dirname(__FILE__) . '/../../modules/core/module.inc'); $coreModule = new CoreModule(); $instVersions = $coreModule->getInstalledVersions(); if (isset($instVersions) && isset($instVersions['core']) && !empty($instVersions['core'])) { $versions['installed'] = $instVersions['core']; } /* Get the codebase version for the CreateConfigFileStep */ $versions['codebase'] = $coreModule->getVersion(); $this->resetL10Domain(); return $versions; } /** * Get the state and the version of the Gallery database * * @param array the meta tables info array from the database * @return string the db version */ function _getDbVersion($metaTables) { $dbVersion = ''; if ($this->in_array_cin($this->_config['tablePrefix'] . 'Schema', $metaTables)) { /* * Get core module version from the database. But first verify that we * have plugin params db table. Default to dummy version */ $dbVersion = 'corrupt db install'; if ($this->in_array_cin($this->_config['tablePrefix'] . 'PluginParameterMap', $metaTables)) { /* Initiate the G2 API to use its db abstraction layer */ if ($this->_loadG2Api(3)) { list ($ret, $version) = GalleryCoreApi::getPluginParameter('module', 'core', '_version'); if ($ret->isSuccess() && !empty($version)) { $dbVersion = $version; } } } else { /* * Not all db tables are present, a clean install is definitely * required */ } } return $dbVersion; } /** * Load minimal G2 API * * Used to check the version in the db and to clean the db / storage dir * * @param [optional integer which level to load] * @return boolean success */ function _loadG2Api($level=1) { global $galleryStub; /* * levels: 0 = off, 1 = basic, 2 = platform, 3 = initfirstpass / db * firstPass includes platform level */ static $initiated; $platformLevel = 2; $firstPassLevel = 3; $success = true; if (!isset($initiated)) { $initiated = 1; /* Include basic G2 classes to use G2 API to get the core version etc. */ require_once(dirname(__FILE__) . '/../../modules/core/classes/Gallery.class'); require_once(dirname(__FILE__) .'/../../modules/core/classes/GalleryDataCache.class'); $GLOBALS['gallery'] =& new Gallery(); /* * GalleryInitFirstPass adds a trailing slash if necessary, but we don't call it in * all cases */ $configPath = $galleryStub->getConfig('data.gallery.base'); if ($configPath{strlen($configPath)-1} != DIRECTORY_SEPARATOR) { $configPath .= DIRECTORY_SEPARATOR; } $GLOBALS['gallery']->setConfig('data.gallery.base', $configPath); $GLOBALS['gallery']->setConfig('plugins.dirname', $galleryStub->getConfig('plugins.dirname')); } global $gallery; if ($level == $platformLevel && $initiated < $platformLevel) { /* Platform level requested, platform level not already loaded */ $initiated = $platformLevel; require_once(dirname(__FILE__) . '/../../modules/core/classes/GalleryCoreApi.class'); require_once(dirname(__FILE__) . '/../../modules/core/classes/GalleryCapabilities.class'); require_once(dirname(__FILE__) . '/../../modules/core/classes/GalleryPlatform.class'); $gallery->setPlatform(new GalleryPlatform()); /* Configure G2 such that getInstalledVersions() can be called */ $gallery->initTranslator(true); require_once(dirname(__FILE__) . '/../../modules/core/classes/GalleryModule.class'); } if ($level == $firstPassLevel && $initiated < $firstPassLevel) { $initiated = $firstPassLevel; /* Configure the G2 db abstraction layer */ $this->_config['usePersistentConnections'] = false; $gallery->setConfig('storage.config', $this->_config); $gallery->setDebug(false); $gallery->setProfile(false); /* Init paths etc., used for the clean DB function */ require_once(dirname(__FILE__) . '/../../init.inc'); $ret = GalleryInitFirstPass(array('noDatabase' => true)); $success = $ret->isSuccess(); GalleryDataCache::setFileCachingEnabled(false); GalleryDataCache::reset(); } if ($level >= $platformLevel) { /* Gallery init selects language from browser; reset to language currently in use */ $translator =& $gallery->getTranslator(); $translator->init($_SESSION['language']); } return $success; } /** * Empty the Gallery storage directory and reset it to its original state * * Deletes everything in the storage directory and then rebuilds the initial folder structure * @return boolean success */ function _emptyGalleryStorageDirectory() { global $gallery; /* Make sure that all the required paths exist. */ $baseDir = $gallery->getConfig('data.gallery.base'); $platform = $gallery->getPlatform(); if ($baseDir{strlen($baseDir)-1} != $platform->getDirectorySeparator()) { $baseDir .= $platform->getDirectorySeparator(); } /* Scrub the contents of the gallery data directory */ $dir = $platform->opendir($baseDir); while (($filename = $platform->readdir($dir)) !== false) { if (!strcmp($filename, '.') || !strcmp($filename, '..')) { continue; } $path = $baseDir . $filename; if ($platform->is_dir($path)) { $ret = $platform->recursiveRmdir($path); } else { $ret = $platform->unlink($path); } if ($ret == false) { return false; } } $platform->closedir($dir); /* Recreate the gallery data directory */ return populateDataDirectory($baseDir); } /** * Drop all Gallery database tables * * Drop all Gallery database tables that are listed in the Gallery schema table * @return array GalleryStatus a status code */ function _cleanDatabase() { global $gallery; $storage =& $gallery->getStorage(); $storage->_db->debug = true; $gallery->setDebug('immediate'); $ret = $storage->cleanStore(); if ($ret->isError()) { return $ret->wrap(__FILE__, __LINE__); } $ret = $storage->commitTransaction(); if ($ret->isError()) { return $ret->wrap(__FILE__, __LINE__); } return GalleryStatus::success(); } /** * in_array_cin: case-insensitive in_array * * case-insensitive version of php function in_array() * returns true if param 1 is in array param 2 * * @param var the search argument * @param array of vars to search in * @return bool success status */ function in_array_cin($strItem, $arItems) { foreach ($arItems as $strValue) { if (strtoupper($strItem) == strtoupper($strValue)) { return true; } } return false; } } ?>