array( 'dir' => 'mssql/code', 'class' => 'MSSQLDatabase', 'title' => 'SQL Server 2008' ), 'postgresql' => array( 'dir' => 'postgresql/code', 'class' => 'PostgreSQLDatabase', 'title' => 'PostgreSQL' ), 'sqlite3' => array( 'dir' => 'sqlite3/code', 'class' => 'SQLite3Database', 'title' => 'SQLite 3' ), 'sqlitepdo' => array( 'dir' => 'sqlite3/code', 'class' => 'SQLitePDODatabase', 'title' => 'SQLite PDO' ) ); // MySQL support comes out of the box with sapphire $foundDatabaseClasses = array('MySQLDatabase' => 'MySQL'); include_once('sapphire/dev/install/DatabaseConfigurationHelper.php'); include_once('sapphire/dev/install/MySQLDatabaseConfigurationHelper.php'); // Determine which external database modules are installed foreach($otherDatabaseLocations as $details) { $helperPath = $details['dir'] . '/' . $details['class'] . 'ConfigurationHelper.php'; if(file_exists($helperPath)) { $foundDatabaseClasses[$details['class']] = $details['title']; include_once($helperPath); } } // Load database config if(isset($_REQUEST['db'])) { // Disabled inputs don't submit anything - we need to use the environment (except the database name) if($usingEnv) { $_REQUEST['db'] = $databaseConfig = array( "type" => defined('SS_DATABASE_CLASS') ? SS_DATABASE_CLASS : "MySQLDatabase", "server" => defined('SS_DATABASE_SERVER') ? SS_DATABASE_SERVER : "localhost", "username" => defined('SS_DATABASE_USERNAME') ? SS_DATABASE_USERNAME : "root", "password" => defined('SS_DATABASE_PASSWORD') ? SS_DATABASE_PASSWORD : "", "database" => $_REQUEST['db']['database'], ); } else { // Normal behaviour without the environment $databaseConfig = $_REQUEST['db']; } } else { $_REQUEST['db'] = $databaseConfig = array( "type" => defined('SS_DATABASE_CLASS') ? SS_DATABASE_CLASS : "MySQLDatabase", "server" => defined('SS_DATABASE_SERVER') ? SS_DATABASE_SERVER : "localhost", "username" => defined('SS_DATABASE_USERNAME') ? SS_DATABASE_USERNAME : "root", "password" => defined('SS_DATABASE_PASSWORD') ? SS_DATABASE_PASSWORD : "", "database" => isset($_SERVER['argv'][2]) ? $_SERVER['argv'][2] : "SS_mysite", ); } if(isset($_REQUEST['admin'])) { // Disabled inputs don't submit anything - we need to use the environment (except the database name) if($usingEnv) { $_REQUEST['admin'] = $adminConfig = array( 'username' => defined('SS_DEFAULT_ADMIN_USERNAME') ? SS_DEFAULT_ADMIN_USERNAME : 'admin', 'password' => defined('SS_DEFAULT_ADMIN_PASSWORD') ? SS_DEFAULT_ADMIN_PASSWORD : '', 'firstname' => $_REQUEST['admin']['firstname'], 'surname' => $_REQUEST['admin']['surname'] ); } else { $adminConfig = $_REQUEST['admin']; } } else { $_REQUEST['admin'] = $adminConfig = array( 'username' => defined('SS_DEFAULT_ADMIN_USERNAME') ? SS_DEFAULT_ADMIN_USERNAME : 'admin', 'password' => defined('SS_DEFAULT_ADMIN_PASSWORD') ? SS_DEFAULT_ADMIN_PASSWORD : '', 'firstname' => '', 'surname' => '' ); } $alreadyInstalled = false; if(file_exists('mysite/_config.php')) { // Find the $database variable in the relevant config file without having to execute the config file if(preg_match("/\\\$database\s*=\s*[^\n\r]+[\n\r]/", file_get_contents("mysite/_config.php"), $parts)) { eval($parts[0]); if($database) $alreadyInstalled = true; // Assume that if $databaseConfig is defined in mysite/_config.php, then a non-environment-based installation has // already gone ahead } else if(preg_match("/\\\$databaseConfig\s*=\s*[^\n\r]+[\n\r]/", file_get_contents("mysite/_config.php"), $parts)) { $alreadyInstalled = true; } } if(file_exists('sapphire/silverstripe_version')) { $sapphireVersionFile = file_get_contents('sapphire/silverstripe_version'); if(strstr($sapphireVersionFile, "/sapphire/trunk")) { $silverstripe_version = "trunk"; } else { preg_match("/sapphire\/(?:(?:branches)|(?:tags))(?:\/rc)?\/([A-Za-z0-9._-]+)\/silverstripe_version/", $sapphireVersionFile, $matches); $silverstripe_version = $matches[1]; } } else { $silverstripe_version = "unknown"; } // Check requirements $req = new InstallRequirements(); $req->check(); if($req->hasErrors()) { $hasErrorOtherThanDatabase = true; } if($databaseConfig) { $dbReq = new InstallRequirements(); $dbReq->checkdatabase($databaseConfig); } if($adminConfig) { $adminReq = new InstallRequirements(); $adminReq->checkAdminConfig($adminConfig); } // Actual processor $installFromCli = (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == 'install'); // CLI-install error message. exit(1) will halt any makefile. if($installFromCli && ($req->hasErrors() || $dbReq->hasErrors())) { echo "Cannot install due to errors:\n"; $req->listErrors(); $dbReq->listErrors(); exit(1); } if((isset($_REQUEST['go']) || $installFromCli) && !$req->hasErrors() && !$dbReq->hasErrors() && $adminConfig['username'] && $adminConfig['password']) { // Confirm before reinstalling if(!isset($_REQUEST['force_reinstall']) && !$installFromCli && $alreadyInstalled) { include('sapphire/dev/install/config-form.html'); } else { $inst = new Installer(); if($_REQUEST) $inst->install($_REQUEST); else $inst->install(array( 'db' => $databaseConfig, 'admin' => $adminConfig, )); } // Show the config form } else { include('sapphire/dev/install/config-form.html'); } /** * This class checks requirements * Each of the requireXXX functions takes an argument which gives a user description of the test. It's an array * of 3 parts: * $description[0] - The test catetgory * $description[1] - The test title * $description[2] - The test error to show, if it goes wrong */ class InstallRequirements { var $errors, $warnings, $tests; /** * Check the database configuration. These are done one after another * starting with checking the database function exists in PHP, and * continuing onto more difficult checks like database permissions. */ function checkdatabase($databaseConfig) { if($this->requireDatabaseFunctions( $databaseConfig, array( "Database Configuration", "Database support", "Database support not included in PHP.") ) ) { if($this->requireDatabaseServer( $databaseConfig, array( "Database Configuration", "Database server exists", "I couldn't find a database server on '$databaseConfig[server]'", $databaseConfig['server'] ) )) { if($this->requireDatabaseConnection( $databaseConfig, array( "Database Configuration", "Database access credentials correct", "That username/password doesn't work" ) )) { $this->requireDatabaseOrCreatePermissions( $databaseConfig, array( "Database Configuration", "Can I access/create the database", "I can't create new databases and the database '$databaseConfig[database]' doesn't exist" ) ); } } } } function checkAdminConfig($adminConfig) { if(!$adminConfig['username']) { $this->error(array('', 'Please enter a username!')); } if(!$adminConfig['password']) { $this->error(array('', 'Please enter a password!')); } } /** * Check everything except the database */ function check() { $this->errors = null; $this->requirePHPVersion('5.2.0', '5.0.4', array("PHP Configuration", "PHP5 installed", null, "PHP version " . phpversion())); // Check that we can identify the root folder successfully $this->requireFile('sapphire/dev/install/config-form.html', array("File permissions", "Does the webserver know where files are stored?", "The webserver isn't letting me identify where files are stored.", $this->getBaseDir() )); $this->requireFile('mysite', array("File permissions", "mysite/ folder exists", "There's no mysite folder.")); $this->requireFile('sapphire', array("File permissions", "sapphire/ folder exists", "There's no sapphire folder.")); $this->requireFile('cms', array("File permissions", "cms/ folder exists", "There's no cms folder.")); $this->requireWriteable('.htaccess', array("File permissions", "Is the .htaccess file writeable?", null)); $this->requireWriteable('mysite/_config.php', array("File permissions", "Is the mysite/_config.php file writeable?", null)); $this->requireWriteable('assets', array("File permissions", "Is the assets/ folder writeable?", null)); $this->requireTempFolder(array('File permissions', 'Is the temporary folder writeable?', null)); // Check for web server, unless we're calling the installer from the command-line if(!isset($_SERVER['argv']) || !$_SERVER['argv']) { $webserver = strip_tags(trim($_SERVER['SERVER_SIGNATURE'])); if(!$webserver) { if(isset($_SERVER['SERVER_SOFTWARE'])) { if(strpos($_SERVER['SERVER_SOFTWARE'], 'IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false) { $webserver = $_SERVER['SERVER_SOFTWARE']; } } else { $webserver = "I can't tell what webserver you are running"; } } $this->isRunningWebServer(array("Webserver Configuration", "Server software", "$webserver. Without Apache I can't tell if mod_rewrite is enabled.", $webserver)); if(function_exists('apache_get_modules')) { $this->requireApacheModule('mod_rewrite', array("Webserver Configuration", "mod_rewrite enabled", "You need mod_rewrite to run SilverStripe CMS, but it is not enabled.")); } elseif(strpos($webserver, 'IIS/7') !== false) { $this->requireIISRewriteModule('IIS_UrlRewriteModule', array("Webserver Configuration", "IIS URL Rewrite Module enabled", "You need to enable the IIS URL Rewrite Module, but it is not installed or enabled. Download it for IIS 7 from http://www.iis.net/expand/URLRewrite")); } else { $this->warning(array("Webserver Configuration", "URL rewrite enabled", "I can't tell whether any rewriting module is running. You may need to configure a rewriting rule yourself.")); } $this->requireServerVariables(array('SCRIPT_NAME','HTTP_HOST','SCRIPT_FILENAME'), array("Webserver config", "Recognised webserver", "You seem to be using an unsupported webserver. The server variables SCRIPT_NAME, HTTP_HOST, SCRIPT_FILENAME need to be set.")); } // Check for GD support if(!$this->requireFunction("imagecreatetruecolor", array("PHP Configuration", "GD2 support", "PHP must have GD version 2."))) { $this->requireFunction("imagecreate", array("PHP Configuration", "GD2 support", "GD support for PHP not included.")); } // Check for XML support $this->requireFunction('xml_set_object', array("PHP Configuration", "XML support", "XML support not included in PHP.")); // Check for token_get_all $this->requireFunction('token_get_all', array("PHP Configuration", "PHP Tokenizer", "PHP tokenizer support not included in PHP.")); // Check memory allocation $this->requireMemory(32*1024*1024, 64*1024*1024, array("PHP Configuration", "Memory allocated (PHP config option 'memory_limit')", "SilverStripe needs a minimum of 32M allocated to PHP, but recommends 64M.", ini_get("memory_limit"))); return $this->errors; } function suggestPHPSetting($settingName, $settingValues, $testDetails) { $this->testing($testDetails); $val = ini_get($settingName); if(!in_array($val, $settingValues) && $val != $settingValues) { $testDetails[2] = "$settingName is set to '$val' in php.ini. $testDetails[2]"; $this->warning($testDetails); } } function requireMemory($min, $recommended, $testDetails) { $_SESSION['forcemem'] = false; $mem = $this->getPHPMemory(); if($mem < (64 * 1024 * 1024)) { ini_set('memory_limit', '64M'); $mem = $this->getPHPMemory(); $testDetails[3] = ini_get("memory_limit"); } $this->testing($testDetails); if($mem < $min && $mem > 0) { $testDetails[2] .= " You only have " . ini_get("memory_limit") . " allocated"; $this->error($testDetails); } else if($mem < $recommended && $mem > 0) { $testDetails[2] .= " You only have " . ini_get("memory_limit") . " allocated"; $this->warning($testDetails); } elseif($mem == 0) { $testDetails[2] .= " We can't determine how much memory you have allocated. Install only if you're sure you've allocated at least 20 MB."; $this->warning($testDetails); } } function getPHPMemory() { $memString = ini_get("memory_limit"); switch(strtolower(substr($memString,-1))) { case "k": return round(substr($memString,0,-1)*1024); case "m": return round(substr($memString,0,-1)*1024*1024); case "g": return round(substr($memString,0,-1)*1024*1024*1024); default: return round($memString); } } function listErrors() { if($this->errors) { echo "

The following problems are preventing me from installing SilverStripe CMS:

\n\n"; foreach($this->errors as $error) { echo "
  • " . htmlentities(implode(", ", $error)) . "
  • \n"; } } } function showTable($section = null) { if($section) { $tests = $this->tests[$section]; $id = strtolower(str_replace(' ', '_', $section)); echo ""; foreach($tests as $test => $result) { echo ""; } echo "
    $test" . nl2br(htmlentities($result[1])) . "
    "; } else { foreach($this->tests as $section => $tests) { echo "
    $section
    "; echo ""; foreach($tests as $test => $result) { echo ""; } echo "
    $test" . nl2br(htmlentities($result[1])) . "
    "; } } } function requireFunction($funcName, $testDetails) { $this->testing($testDetails); if(!function_exists($funcName)) $this->error($testDetails); else return true; } /** * Require that the given class doesn't exist */ function requireNoClasses($classNames, $testDetails) { $this->testing($testDetails); $badClasses = array(); foreach($classNames as $className) { if(class_exists($className)) $badClasses[] = $className; } if($badClasses) { $testDetails[2] .= ". The following classes are at fault: " . implode(', ', $badClasses); $this->error($testDetails); } else return true; } function requirePHPVersion($recommendedVersion, $requiredVersion, $testDetails) { $this->testing($testDetails); $installedVersion = phpversion(); if(version_compare($installedVersion, $requiredVersion, '<')) { $testDetails[2] = "SilverStripe requires PHP version $requiredVersion or later.\n PHP version $installedVersion is currently installed.\n While SilverStripe requires at least PHP version $requiredVersion, upgrading to $recommendedVersion or later is recommended.\n If you are installing SilverStripe on a shared web server, please ask your web hosting provider to upgrade PHP for you."; $this->error($testDetails); return; } if(version_compare($installedVersion, $recommendedVersion, '<')) { $testDetails[2] = "PHP version $installedVersion is currently installed.\n Upgrading to at least PHP version $recommendedVersion is recommended.\n SilverStripe should run, but you may run into issues. Future releases may require a later version of PHP.\n"; $this->warning($testDetails); return; } return true; } function requireFile($filename, $testDetails) { $this->testing($testDetails); $filename = $this->getBaseDir() . $filename; if(!file_exists($filename)) { $testDetails[2] .= " (file '$filename' not found)"; $this->error($testDetails); } } function requireNoFile($filename, $testDetails) { $this->testing($testDetails); $filename = $this->getBaseDir() . $filename; if(file_exists($filename)) { $testDetails[2] .= " (file '$filename' found)"; $this->error($testDetails); } } function moveFileOutOfTheWay($filename, $testDetails) { $this->testing($testDetails); $filename = $this->getBaseDir() . $filename; if(file_exists($filename)) { if(file_exists("$filename.bak")) rm("$filename.bak"); rename($filename, "$filename.bak"); } } function requireWriteable($filename, $testDetails) { $this->testing($testDetails); $filename = $this->getBaseDir() . str_replace("/", DIRECTORY_SEPARATOR,$filename); if(!is_writeable($filename)) { if(function_exists('posix_getgroups')) { $userID = posix_geteuid(); $user = posix_getpwuid($userID); $currentOwnerID = fileowner($filename); $currentOwner = posix_getpwuid($currentOwnerID); $testDetails[2] .= "User '$user[name]' needs to be able to write to this file:\n$filename\n\nThe file is currently owned by '$currentOwner[name]'. "; if($user['name'] == $currentOwner['name']) { $testDetails[2] .= "We recommend that you make the file writeable."; } else { $groups = posix_getgroups(); foreach($groups as $group) { $groupInfo = posix_getgrgid($group); if(in_array($currentOwner['name'], $groupInfo['members'])) $groupList[] = $groupInfo['name']; } if($groupList) { $testDetails[2] .= " We recommend that you make the file group-writeable and change the group to one of these groups:\n - ". implode("\n - ", $groupList) . "\n\nFor example:\nchmod g+w $filename\nchgrp " . $groupList[0] . " $filename"; } else { $testDetails[2] .= " There is no user-group that contains both the web-server user and the owner of this file. Change the ownership of the file, create a new group, or temporarily make the file writeable by everyone during the install process."; } } } else { $testDetails[2] .= "The webserver user needs to be able to write to this file:\n$filename"; } $this->error($testDetails); } } function requireTempFolder($testDetails) { $this->testing($testDetails); if(function_exists('sys_get_temp_dir')) { $sysTmp = sys_get_temp_dir(); } elseif(isset($_ENV['TMP'])) { $sysTmp = $_ENV['TMP']; } else { @$tmpFile = tempnam('adfadsfdas',''); @unlink($tmpFile); $sysTmp = dirname($tmpFile); } $worked = true; $ssTmp = "$sysTmp/silverstripe-cache"; if(!@file_exists($ssTmp)) { @$worked = mkdir($ssTmp); if(!$worked) { $ssTmp = dirname($_SERVER['SCRIPT_FILENAME']) . "/silverstripe-cache"; $worked = true; if(!@file_exists($ssTmp)) { @$worked = mkdir($ssTmp); } if(!$worked) { $testDetails[2] = "Permission problem gaining access to a temp folder. " . "Please create a folder named silverstripe-cache in the base folder " . "of the installation and ensure it has the adequate permissions"; $this->error($testDetails); } } } } function requireApacheModule($moduleName, $testDetails) { $this->testing($testDetails); if(!in_array($moduleName, apache_get_modules())) $this->error($testDetails); } function requireIISRewriteModule($moduleName, $testDetails) { $this->testing($testDetails); if(isset($_SERVER[$moduleName]) && $_SERVER[$moduleName]) { return true; } else { $this->error($testDetails); return false; } } /** * Get an instance of a helper class for the specific database. * @param string $databaseClass e.g. MySQLDatabase or MSSQLDatabase */ function getDatabaseConfigurationHelper($databaseClass) { $class = $databaseClass . 'ConfigurationHelper'; return (class_exists($class)) ? new $class() : new MySQLDatabaseConfigurationHelper(); return new $class(); } function requireDatabaseFunctions($databaseConfig, $testDetails) { $this->testing($testDetails); $helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']); $result = $helper->requireDatabaseFunctions($databaseConfig); if($result) { return true; } else { $this->error($testDetails); return false; } } function requireDatabaseConnection($databaseConfig, $testDetails) { $this->testing($testDetails); $helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']); $result = $helper->requireDatabaseConnection($databaseConfig); if($result['success']) { return true; } else { $testDetails[2] .= ": " . $result['error']; $this->error($testDetails); return false; } } function requireDatabaseServer($databaseConfig, $testDetails) { $this->testing($testDetails); $helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']); $result = $helper->requireDatabaseServer($databaseConfig); if($result['success']) { return true; } else { $testDetails[2] .= ": " . $result['error']; $this->error($testDetails); return false; } } function requireDatabaseOrCreatePermissions($databaseConfig, $testDetails) { $this->testing($testDetails); $helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']); $result = $helper->requireDatabaseOrCreatePermissions($databaseConfig); if($result['success']) { if($result['alreadyExists']) $testDetails[3] = "Database $databaseConfig[database] exists"; else $testDetails[3] = "Able to create a new database"; $this->testing($testDetails); return true; } else { if(!@$result['cannotCreate']) { $testDetails[2] .= ". Please create the database manually."; } else { $testDetails[2] .= " (user '$databaseConfig[username]' doesn't have CREATE DATABASE permissions.)"; } $this->error($testDetails); return false; } } function requireServerVariables($varNames, $errorMessage) { //$this->testing($testDetails); foreach($varNames as $varName) { if(!$_SERVER[$varName]) $missing[] = '$_SERVER[' . $varName . ']'; } if(!isset($missing)) { return true; } else { $testDetails[2] .= " (the following PHP variables are missing: " . implode(", ", $missing) . ")"; $this->error($testDetails); } } function isRunningWebServer($testDetails) { $this->testing($testDetails); if(function_exists('apache_get_modules') || stristr($_SERVER['SERVER_SIGNATURE'], 'Apache')) { return true; } elseif(strpos($_SERVER['SERVER_SOFTWARE'], 'IIS') !== false) { return true; } else { $this->warning($testDetails); return false; } } // Must be PHP4 compatible var $baseDir; function getBaseDir() { // Cache the value so that when the installer mucks with SCRIPT_FILENAME half way through, this method // still returns the correct value. if(!$this->baseDir) $this->baseDir = realpath(dirname($_SERVER['SCRIPT_FILENAME'])) . DIRECTORY_SEPARATOR; return $this->baseDir; } function testing($testDetails) { if(!$testDetails) return; $section = $testDetails[0]; $test = $testDetails[1]; $message = "OK"; if(isset($testDetails[3])) $message .= " ($testDetails[3])"; $this->tests[$section][$test] = array("good", $message); } function error($testDetails) { $section = $testDetails[0]; $test = $testDetails[1]; $this->tests[$section][$test] = array("error", $testDetails[2]); $this->errors[] = $testDetails; } function warning($testDetails) { $section = $testDetails[0]; $test = $testDetails[1]; $this->tests[$section][$test] = array("warning", $testDetails[2]); $this->warnings[] = $testDetails; } function hasErrors() { return sizeof($this->errors); } function hasWarnings() { return sizeof($this->warnings); } } class Installer extends InstallRequirements { function __construct() { // Cache the baseDir value $this->getBaseDir(); } function install($config) { if(isset($_SERVER['HTTP_HOST'])) { ?> PHP 5 is required

    Installing SilverStripe...

    I am now running through the installation steps (this should take about 30 seconds)

    If you receive a fatal error, refresh this page to continue the installation