0byt3m1n1
Path:
/
data
/
applications
/
aps
/
gallery
/
2.2-08
/
htdocs
/
install
/
steps
/
[
Home
]
File: DatabaseSetupStep.class
<?php /* * Gallery - a web based photo album viewer and editor * Copyright (C) 2000-2007 Bharat Mediratta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /** * Setup Database * @package Install */ class DatabaseSetupStep extends InstallStep { function stepName() { return _('Database Setup'); } function loadTemplateData(&$templateData) { global $galleryStub; $firstConfig = false; if (empty($this->_config)) { $this->_config = $galleryStub->getConfig('storage.config'); if (empty($this->_config)) { $this->_config = array(); $this->_config['type'] = 'mysqlt'; $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; } } $templateData['password'] = strlen($this->_config['password']) ? '******' : ''; if (!empty($_POST['action']) && $_POST['action'] == 'save') { foreach (array('type', 'hostname', 'username', 'database', 'tablePrefix', 'columnPrefix') as $key) { $this->_config[$key] = $this->sanitize($_POST[$key]); } if (!preg_match('/^\*+$/', trim($_POST['password']))){ $this->_config['password'] = $this->sanitize($_POST['password']); } } $dbPlatformType = null; /* Autoselect mysqli instead of mysqlt if possible */ $mysqltType = 'mysqlt'; if ($this->_config['type'] == 'mysqlt' && function_exists('mysqli_real_connect')) { $this->_config['type'] = 'mysqli'; $mysqltType = 'mysqli'; } 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 'mysqli': if (!$firstConfig && !function_exists('mysqli_real_connect')) { $templateData['error']['phpDbMissing'] = _('You must have the MySQL Improved PHP module installed'); } $dbPlatformType = 'mysql'; break; case 'db2': if (!$firstConfig && !function_exists('db2_connect')) { $templateData['error']['phpDbMissing'] = _('You must have the ibm_db2 PHP module installed'); } $dbPlatformType = 'db2'; break; case 'postgres7': 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; case 'ado_mssql': if (!$firstConfig && !class_exists('COM')) { $templateData['error']['phpDbMissing'] = _('You must have the Component Object Model(COM) PHP module installed'); } $dbPlatformType = 'mssql'; 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(); if ($dbPlatformType != 'mssql') { $result = $this->_db->NConnect($this->_config['hostname'], $this->_config['username'], $this->_config['password'], $this->_config['database']); } else { $result = $this->_db->NConnect( 'PROVIDER=MSDASQL;DRIVER={SQL Server};SERVER=' . $this->_config['hostname'] . ';DATABASE=' . $this->_config['database'], $this->_config['username'], $this->_config['password'], 'MSDASQL'); } $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) { $ret = $ret; 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'; } else if ((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($mysqltType => _('MySQL (v3.23.34a and newer)'), 'mysql' => _('MySQL (versions before v3.23.34a)'), 'postgres7' => _('PostgreSQL v7.x and newer'), 'oci8po' => _('Oracle (9i and newer)'), 'db2' => _('IBM DB2 (v9.x and newer)'), 'ado_mssql' => _('Microsoft SQL Server 2005 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 $dbType platform name (i.e. not mysqlt, but mysql) * @param array $metatables (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/GalleryStorageExtras.class'); /* GalleryPlatform is not available at this point */ $schemaTpl = dirname(__FILE__) . '/../../modules/core/classes/GalleryStorage/schema.tpl'; if (!($sqlData = file($schemaTpl))) { return array(false, sprintf(_('Could not open schema file: "%s".'), $schemaTpl)); } $moduleSql = GalleryStorageExtras::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 string $buffer the SQL statements * @param string $tablePrefix prefix for table names * @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', '', $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 $metaTables 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 && !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 int $level (optional) 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'); if (!defined('GALLERY_FORM_VARIABLE_PREFIX')) { define('GALLERY_FORM_VARIABLE_PREFIX', 'g2_'); } $ret = GalleryInitFirstPass(array('noDatabase' => true)); $success = !$ret; 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'], true); } 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(); $gallery->setDebug('immediate'); $ret = $storage->cleanStore(); if ($ret) { return $ret; } $ret = $storage->commitTransaction(); if ($ret) { return $ret; } return null; } /** * 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 * * PostgreSQL in windows/linux, Mysql on Windows both return a all lower-case list of existing * tables. DB2 on Windows returns a all upper-case list of tablenames. * Probably, only MySQL on linux returns a case-sensitive list of tablenames. Generalize to * compare tablenames case-insensitively. * * @param string $strItem the search argument * @param array $arItems 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; } } ?>