4 * Note that this installer has been based of the SilverStripe installer.
5 * You can get more information from the SilverStripe Website at
6 * http://www.silverstripe.com/. Please check
7 * http://www.silverstripe.com/licensing for licensing details.
9 * Copyright (c) 2006-7, SilverStripe Limited - www.silverstripe.com
10 * All rights reserved.
12 * Changes and modifications (c) 2007-8 by CiviCRM LLC
20 ini_set('max_execution_time', 3000);
22 if (stristr(PHP_OS
, 'WIN')) {
23 define('CIVICRM_DIRECTORY_SEPARATOR', '/');
24 define('CIVICRM_WINDOWS', 1);
27 define('CIVICRM_DIRECTORY_SEPARATOR', DIRECTORY_SEPARATOR
);
28 define('CIVICRM_WINDOWS', 0);
31 // set installation type - drupal
36 // unset civicrm session if any
37 if (array_key_exists('CiviCRM', $_SESSION)) {
38 unset($_SESSION['CiviCRM']);
41 if (isset($_GET['civicrm_install_type'])) {
42 $_SESSION['civicrm_install_type'] = $_GET['civicrm_install_type'];
45 if (!isset($_SESSION['civicrm_install_type'])) {
46 $_SESSION['civicrm_install_type'] = "drupal";
51 $installType = strtolower($_SESSION['civicrm_install_type']);
53 if (!in_array($installType, array('drupal', 'wordpress'))) {
54 $errorTitle = "Oops! Unsupported installation mode";
56 errorDisplayPage($errorTitle, $errorMsg);
60 global $installDirPath;
61 global $installURLPath;
62 if ($installType == 'drupal') {
63 $crmPath = dirname(dirname($_SERVER['SCRIPT_FILENAME']));
64 $installDirPath = $installURLPath = '';
66 elseif ($installType == 'wordpress') {
67 $crmPath = WP_PLUGIN_DIR
. DIRECTORY_SEPARATOR
. 'civicrm' . DIRECTORY_SEPARATOR
. 'civicrm' . DIRECTORY_SEPARATOR
;
68 $installDirPath = WP_PLUGIN_DIR
. DIRECTORY_SEPARATOR
. 'civicrm' . DIRECTORY_SEPARATOR
. 'civicrm' . DIRECTORY_SEPARATOR
. 'install' . DIRECTORY_SEPARATOR
;
70 $installURLPath = WP_PLUGIN_URL
. DIRECTORY_SEPARATOR
. 'civicrm' . DIRECTORY_SEPARATOR
. 'civicrm' . DIRECTORY_SEPARATOR
. 'install' . DIRECTORY_SEPARATOR
;
73 set_include_path(get_include_path() . PATH_SEPARATOR
. $crmPath);
75 require_once $crmPath . '/CRM/Core/ClassLoader.php';
76 CRM_Core_ClassLoader
::singleton()->register();
78 $docLink = CRM_Utils_System
::docURL2('Installation and Upgrades', FALSE, 'Installation Guide', NULL, NULL, "wiki");
80 if ($installType == 'drupal') {
81 //lets check only /modules/.
82 $pattern = '/' . preg_quote(CIVICRM_DIRECTORY_SEPARATOR
. 'modules', CIVICRM_DIRECTORY_SEPARATOR
) . '/';
84 if (!preg_match($pattern,
85 str_replace("\\", "/", $_SERVER['SCRIPT_FILENAME'])
88 $errorTitle = "Oops! Please Correct Your Install Location";
89 $errorMsg = "Please untar (uncompress) your downloaded copy of CiviCRM in the <strong>" . implode(CIVICRM_DIRECTORY_SEPARATOR
, array(
93 )) . "</strong> directory below your Drupal root directory. Refer to the online " . $docLink . " for more information.";
94 errorDisplayPage($errorTitle, $errorMsg);
98 // Load civicrm database config
99 if (isset($_REQUEST['mysql'])) {
100 $databaseConfig = $_REQUEST['mysql'];
103 $databaseConfig = array(
104 "server" => "localhost",
105 "username" => "civicrm",
107 "database" => "civicrm",
111 if ($installType == 'drupal') {
112 // Load drupal database config
113 if (isset($_REQUEST['drupal'])) {
114 $drupalConfig = $_REQUEST['drupal'];
117 $drupalConfig = array(
118 "server" => "localhost",
119 "username" => "drupal",
121 "database" => "drupal",
127 if (isset($_REQUEST['loadGenerated'])) {
131 require_once dirname(__FILE__
) . CIVICRM_DIRECTORY_SEPARATOR
. 'langs.php';
132 foreach ($langs as $locale => $_) {
133 if ($locale == 'en_US') {
136 if (!file_exists(implode(CIVICRM_DIRECTORY_SEPARATOR
, array($crmPath, 'sql', "civicrm_data.$locale.mysql")))) {
137 unset($langs[$locale]);
141 $seedLanguage = 'en_US';
142 if (isset($_REQUEST['seedLanguage']) and isset($langs[$_REQUEST['seedLanguage']])) {
143 $seedLanguage = $_REQUEST['seedLanguage'];
147 if ($installType == 'drupal') {
148 //CRM-6840 -don't force to install in sites/all/modules/
149 $object = new CRM_Utils_System_Drupal();
150 $cmsPath = $object->cmsRootPath();
152 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
153 $alreadyInstalled = file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
154 'sites' . CIVICRM_DIRECTORY_SEPARATOR
.
155 $siteDir . CIVICRM_DIRECTORY_SEPARATOR
.
156 'civicrm.settings.php'
159 elseif ($installType == 'wordpress') {
160 $cmsPath = WP_PLUGIN_DIR
. DIRECTORY_SEPARATOR
. 'civicrm';
161 $alreadyInstalled = file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
162 'civicrm.settings.php'
166 // Exit with error if CiviCRM has already been installed.
167 if ($alreadyInstalled) {
168 $errorTitle = "Oops! CiviCRM is Already Installed";
169 if ($installType == 'drupal') {
171 $errorMsg = "CiviCRM has already been installed in this Drupal site. <ul><li>To <strong>start over</strong>, you must delete or rename the existing CiviCRM settings file - <strong>civicrm.settings.php</strong> - from <strong>" . implode(CIVICRM_DIRECTORY_SEPARATOR
, array(
172 '[your Drupal root directory]',
175 )) . "</strong>.</li><li>To <strong>upgrade an existing installation</strong>, refer to the online " . $docLink . ".</li></ul>";
177 elseif ($installType == 'wordpress') {
178 $errorMsg = "CiviCRM has already been installed in this WordPress site. <ul><li>To <strong>start over</strong>, you must delete or rename the existing CiviCRM settings file - <strong>civicrm.settings.php</strong> - from <strong>" . $cmsPath . "</strong>.</li><li>To <strong>upgrade an existing installation</strong>, refer to the online " . $docLink . ".</li></ul>";
180 errorDisplayPage($errorTitle, $errorMsg);
183 $versionFile = $crmPath . CIVICRM_DIRECTORY_SEPARATOR
. 'civicrm-version.php';
184 if (file_exists($versionFile)) {
185 require_once $versionFile;
186 $civicrm_version = civicrmVersion();
189 $civicrm_version = 'unknown';
192 if ($installType == 'drupal') {
193 // Ensure that they have downloaded the correct version of CiviCRM
194 if ($civicrm_version['cms'] != 'Drupal' &&
195 $civicrm_version['cms'] != 'Drupal6'
197 $errorTitle = "Oops! Incorrect CiviCRM Version";
198 $errorMsg = "This installer can only be used for the Drupal version of CiviCRM. Refer to the online " . $docLink . " for information about installing CiviCRM on PHP4 servers OR installing CiviCRM for Joomla!";
199 errorDisplayPage($errorTitle, $errorMsg);
202 define('DRUPAL_ROOT', $cmsPath);
203 $drupalVersionFiles = array(
205 implode(CIVICRM_DIRECTORY_SEPARATOR
, array($cmsPath, 'modules', 'system', 'system.module')),
207 implode(CIVICRM_DIRECTORY_SEPARATOR
, array($cmsPath, 'includes', 'bootstrap.inc')),
209 foreach ($drupalVersionFiles as $drupalVersionFile) {
210 if (file_exists($drupalVersionFile)) {
211 require_once $drupalVersionFile;
215 if (!defined('VERSION') or version_compare(VERSION
, '6.0') < 0) {
216 $errorTitle = "Oops! Incorrect Drupal Version";
217 $errorMsg = "This version of CiviCRM can only be used with Drupal 6.x or 7.x. Please ensure that '" . implode("' or '", $drupalVersionFiles) . "' exists if you are running Drupal 7.0 and over. Refer to the online " . $docLink . " for information about installing CiviCRM.";
218 errorDisplayPage($errorTitle, $errorMsg);
221 elseif ($installType == 'wordpress') {
223 $civicrm_version['cms'] = 'WordPress';
225 // Ensure that they have downloaded the correct version of CiviCRM
226 if ($civicrm_version['cms'] != 'WordPress') {
227 $errorTitle = "Oops! Incorrect CiviCRM Version";
228 $errorMsg = "This installer can only be used for the WordPress version of CiviCRM. Refer to the online " . $docLink . " for information about installing CiviCRM for Drupal or Joomla!";
229 errorDisplayPage($errorTitle, $errorMsg);
233 // Check requirements
234 $req = new InstallRequirements();
237 if ($req->hasErrors()) {
238 $hasErrorOtherThanDatabase = TRUE;
241 if ($databaseConfig) {
242 $dbReq = new InstallRequirements();
243 $dbReq->checkdatabase($databaseConfig, 'CiviCRM');
244 if ($installType == 'drupal') {
245 $dbReq->checkdatabase($drupalConfig, 'Drupal');
250 if (isset($_REQUEST['go']) && !$req->hasErrors() && !$dbReq->hasErrors()) {
251 // Confirm before reinstalling
252 if (!isset($_REQUEST['force_reinstall']) && $alreadyInstalled) {
253 include $installDirPath . 'template.html';
256 $inst = new Installer();
257 $inst->install($_REQUEST);
260 // Show the config form
263 include $installDirPath . 'template.html';
267 * This class checks requirements
268 * Each of the requireXXX functions takes an argument which gives a user description of the test. It's an array
270 * $description[0] - The test catetgory
271 * $description[1] - The test title
272 * $description[2] - The test error to show, if it goes wrong
274 class InstallRequirements
{
275 var $errors, $warnings, $tests;
277 // @see CRM_Upgrade_Form::MINIMUM_THREAD_STACK
278 const MINIMUM_THREAD_STACK
= 192;
281 * Just check that the database configuration is okay
282 * @param $databaseConfig
285 public function checkdatabase($databaseConfig, $dbName) {
286 if ($this->requireFunction('mysql_connect',
290 "MySQL support not included in PHP.",
294 $this->requireMySQLServer($databaseConfig['server'],
296 "MySQL $dbName Configuration",
297 "Does the server exist",
298 "Can't find the a MySQL server on '$databaseConfig[server]'",
299 $databaseConfig['server'],
302 if ($this->requireMysqlConnection($databaseConfig['server'],
303 $databaseConfig['username'],
304 $databaseConfig['password'],
306 "MySQL $dbName Configuration",
307 "Are the access credentials correct",
308 "That username/password doesn't work",
312 @$this->requireMySQLVersion("5.1",
314 "MySQL $dbName Configuration",
315 "MySQL version at least 5.1",
316 "MySQL version 5.1 or higher is required, you only have ",
317 "MySQL " . mysql_get_server_info(),
320 $this->requireMySQLAutoIncrementIncrementOne($databaseConfig['server'],
321 $databaseConfig['username'],
322 $databaseConfig['password'],
324 "MySQL $dbName Configuration",
325 "Is auto_increment_increment set to 1",
326 "An auto_increment_increment value greater than 1 is not currently supported. Please see issue CRM-7923 for further details and potential workaround.",
329 $this->requireMySQLThreadStack($databaseConfig['server'],
330 $databaseConfig['username'],
331 $databaseConfig['password'],
332 $databaseConfig['database'],
333 self
::MINIMUM_THREAD_STACK
,
335 "MySQL $dbName Configuration",
336 "Does MySQL thread_stack meet minimum (" . self
::MINIMUM_THREAD_STACK
. "k)",
338 // "The MySQL thread_stack does not meet minimum " . CRM_Upgrade_Form::MINIMUM_THREAD_STACK . "k. Please update thread_stack in my.cnf.",
342 $onlyRequire = ($dbName == 'Drupal') ?
TRUE : FALSE;
343 $this->requireDatabaseOrCreatePermissions(
344 $databaseConfig['server'],
345 $databaseConfig['username'],
346 $databaseConfig['password'],
347 $databaseConfig['database'],
349 "MySQL $dbName Configuration",
350 "Can I access/create the database",
351 "I can't create new databases and the database '$databaseConfig[database]' doesn't exist",
355 if ($dbName != 'Drupal') {
356 $this->requireMySQLInnoDB($databaseConfig['server'],
357 $databaseConfig['username'],
358 $databaseConfig['password'],
359 $databaseConfig['database'],
361 "MySQL $dbName Configuration",
362 "Can I access/create InnoDB tables in the database",
363 "Unable to create InnoDB tables. MySQL InnoDB support is required for CiviCRM but is either not available or not enabled in this MySQL database server.",
366 $this->requireMySQLTempTables($databaseConfig['server'],
367 $databaseConfig['username'],
368 $databaseConfig['password'],
369 $databaseConfig['database'],
371 "MySQL $dbName Configuration",
372 'Can I create temporary tables in the database',
373 'Unable to create temporary tables. This MySQL user is missing the CREATE TEMPORARY TABLES privilege.',
376 $this->requireMySQLLockTables($databaseConfig['server'],
377 $databaseConfig['username'],
378 $databaseConfig['password'],
379 $databaseConfig['database'],
381 "MySQL $dbName Configuration",
382 'Can I create lock tables in the database',
383 'Unable to lock tables. This MySQL user is missing the LOCK TABLES privilege.',
386 $this->requireMySQLTrigger($databaseConfig['server'],
387 $databaseConfig['username'],
388 $databaseConfig['password'],
389 $databaseConfig['database'],
391 "MySQL $dbName Configuration",
392 'Can I create triggers in the database',
393 'Unable to create triggers. This MySQL user is missing the CREATE TRIGGERS privilege.',
401 * Check everything except the database
403 public function check() {
404 global $crmPath, $installType;
406 $this->errors
= NULL;
408 $this->requirePHPVersion('5.3.3', array(
412 "PHP version " . phpversion(),
415 // Check that we can identify the root folder successfully
416 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR
. 'README.txt',
419 "Does the webserver know where files are stored?",
420 "The webserver isn't letting me identify where files are stored.",
426 // CRM-6485: make sure the path does not contain PATH_SEPARATOR, as we don’t know how to escape it
427 $this->requireNoPathSeparator(
430 'does the CiviCRM path contain PATH_SEPARATOR?',
431 'the ' . $this->getBaseDir() . ' path contains PATH_SEPARATOR (the ' . PATH_SEPARATOR
. ' character)',
436 $requiredDirectories = array('CRM', 'packages', 'templates', 'js', 'api', 'i', 'sql');
437 foreach ($requiredDirectories as $dir) {
438 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR
. $dir,
441 "$dir folder exists",
442 "There is no $dir folder",
447 $configIDSiniDir = NULL;
449 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
450 if ($installType == 'drupal') {
452 // make sure that we can write to sites/default and files/
453 $writableDirectories = array(
454 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
455 'sites' . CIVICRM_DIRECTORY_SEPARATOR
.
456 $siteDir . CIVICRM_DIRECTORY_SEPARATOR
.
458 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
459 'sites' . CIVICRM_DIRECTORY_SEPARATOR
.
463 elseif ($installType == 'wordpress') {
464 // make sure that we can write to plugins/civicrm and plugins/files/
465 $writableDirectories = array(WP_PLUGIN_DIR
. DIRECTORY_SEPARATOR
. 'files', $cmsPath);
468 foreach ($writableDirectories as $dir) {
469 $dirName = CIVICRM_WINDOWS ?
$dir : CIVICRM_DIRECTORY_SEPARATOR
. $dir;
470 $this->requireWriteable($dirName,
471 array("File permissions", "Is the $dir folder writeable?", NULL),
476 //check for Config.IDS.ini, file may exist in re-install
477 $configIDSiniDir = array($cmsPath, 'sites', $siteDir, 'files', 'civicrm', 'upload', 'Config.IDS.ini');
479 if (is_array($configIDSiniDir) && !empty($configIDSiniDir)) {
480 $configIDSiniFile = implode(CIVICRM_DIRECTORY_SEPARATOR
, $configIDSiniDir);
481 if (file_exists($configIDSiniFile)) {
482 unlink($configIDSiniFile);
486 // Check for rewriting
487 if (isset($_SERVER['SERVER_SOFTWARE'])) {
488 $webserver = strip_tags(trim($_SERVER['SERVER_SOFTWARE']));
490 elseif (isset($_SERVER['SERVER_SIGNATURE'])) {
491 $webserver = strip_tags(trim($_SERVER['SERVER_SIGNATURE']));
494 if ($webserver == '') {
495 $webserver = "I can't tell what webserver you are running";
498 // Check for $_SERVER configuration
499 $this->requireServerVariables(array('SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME'), array(
501 "Recognised webserver",
502 "You seem to be using an unsupported webserver. The server variables SCRIPT_NAME, HTTP_HOST, SCRIPT_FILENAME need to be set.",
505 // Check for MySQL support
506 $this->requireFunction('mysql_connect',
507 array("PHP Configuration", "MySQL support", "MySQL support not included in PHP.")
510 // Check for JSON support
511 $this->requireFunction('json_encode',
512 array("PHP Configuration", "JSON support", "JSON support not included in PHP.")
515 // Check for xcache_isset and emit warning if exists
516 $this->checkXCache(array(
518 "XCache compatibility",
519 "XCache is installed and there are known compatibility issues between XCache and CiviCRM. Consider using an alternative PHP caching mechanism or disable PHP caching altogether.",
522 // Check memory allocation
523 $this->requireMemory(32 * 1024 * 1024,
527 "Memory allocated (PHP config option 'memory_limit')",
528 "CiviCRM needs a minimum of 32M allocated to PHP, but recommends 64M.",
529 ini_get("memory_limit"),
533 return $this->errors
;
538 * @param $recommended
539 * @param $testDetails
541 public function requireMemory($min, $recommended, $testDetails) {
542 $this->testing($testDetails);
543 $mem = $this->getPHPMemory();
545 if ($mem < $min && $mem > 0) {
546 $testDetails[2] .= " You only have " . ini_get("memory_limit") . " allocated";
547 $this->error($testDetails);
549 elseif ($mem < $recommended && $mem > 0) {
550 $testDetails[2] .= " You only have " . ini_get("memory_limit") . " allocated";
551 $this->warning($testDetails);
554 $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.";
555 $this->warning($testDetails);
562 public function getPHPMemory() {
563 $memString = ini_get("memory_limit");
565 switch (strtolower(substr($memString, -1))) {
567 return round(substr($memString, 0, -1) * 1024);
570 return round(substr($memString, 0, -1) * 1024 * 1024);
573 return round(substr($memString, 0, -1) * 1024 * 1024 * 1024);
576 return round($memString);
580 public function listErrors() {
582 echo "<p>The following problems are preventing me from installing CiviCRM:</p>";
583 foreach ($this->errors
as $error) {
584 echo "<li>" . htmlentities($error) . "</li>";
590 * @param null $section
592 public function showTable($section = NULL) {
594 $tests = $this->tests
[$section];
595 echo "<table class=\"testResults\" width=\"100%\">";
596 foreach ($tests as $test => $result) {
597 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
602 foreach ($this->tests
as $section => $tests) {
603 echo "<h3>$section</h3>";
604 echo "<table class=\"testResults\" width=\"100%\">";
606 foreach ($tests as $test => $result) {
607 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
615 * @param string $funcName
616 * @param $testDetails
620 public function requireFunction($funcName, $testDetails) {
621 $this->testing($testDetails);
623 if (!function_exists($funcName)) {
624 $this->error($testDetails);
633 * @param $testDetails
635 public function checkXCache($testDetails) {
636 if (function_exists('xcache_isset') &&
637 ini_get('xcache.size') > 0
639 $this->testing($testDetails);
640 $this->warning($testDetails);
646 * @param $testDetails
647 * @param null $maxVersion
649 public function requirePHPVersion($minVersion, $testDetails, $maxVersion = NULL) {
651 $this->testing($testDetails);
653 $phpVersion = phpversion();
654 $aboveMinVersion = version_compare($phpVersion, $minVersion) >= 0;
655 $belowMaxVersion = $maxVersion ?
version_compare($phpVersion, $maxVersion) < 0 : TRUE;
657 if ($maxVersion && $aboveMinVersion && $belowMaxVersion) {
660 elseif (!$maxVersion && $aboveMinVersion) {
664 if (!$testDetails[2]) {
665 if (!$aboveMinVersion) {
666 $testDetails[2] = "You need PHP version $minVersion or later, only {$phpVersion} is installed. Please upgrade your server, or ask your web-host to do so.";
669 $testDetails[2] = "PHP version {$phpVersion} is not supported. PHP version earlier than $maxVersion is required. You might want to downgrade your server, or ask your web-host to do so.";
673 $this->error($testDetails);
677 * @param string $filename
678 * @param $testDetails
679 * @param bool $absolute
681 public function requireFile($filename, $testDetails, $absolute = FALSE) {
682 $this->testing($testDetails);
684 $filename = $this->getBaseDir() . $filename;
686 if (!file_exists($filename)) {
687 $testDetails[2] .= " (file '$filename' not found)";
688 $this->error($testDetails);
693 * @param $testDetails
695 public function requireNoPathSeparator($testDetails) {
696 $this->testing($testDetails);
697 if (substr_count($this->getBaseDir(), PATH_SEPARATOR
)) {
698 $this->error($testDetails);
703 * @param string $filename
704 * @param $testDetails
706 public function requireNoFile($filename, $testDetails) {
707 $this->testing($testDetails);
708 $filename = $this->getBaseDir() . $filename;
709 if (file_exists($filename)) {
710 $testDetails[2] .= " (file '$filename' found)";
711 $this->error($testDetails);
716 * @param string $filename
717 * @param $testDetails
719 public function moveFileOutOfTheWay($filename, $testDetails) {
720 $this->testing($testDetails);
721 $filename = $this->getBaseDir() . $filename;
722 if (file_exists($filename)) {
723 if (file_exists("$filename.bak")) {
726 rename($filename, "$filename.bak");
731 * @param string $filename
732 * @param $testDetails
733 * @param bool $absolute
735 public function requireWriteable($filename, $testDetails, $absolute = FALSE) {
736 $this->testing($testDetails);
738 $filename = $this->getBaseDir() . $filename;
741 if (!is_writable($filename)) {
743 if (function_exists('posix_getpwuid')) {
744 $user = posix_getpwuid(posix_geteuid());
745 $name = '- ' . $user['name'] . ' -';
748 if (!isset($testDetails[2])) {
749 $testDetails[2] = NULL;
751 $testDetails[2] .= "The user account used by your web-server $name needs to be granted write access to the following directory in order to configure the CiviCRM settings file:\n$filename";
752 $this->error($testDetails);
757 * @param string $moduleName
758 * @param $testDetails
760 public function requireApacheModule($moduleName, $testDetails) {
761 $this->testing($testDetails);
762 if (!in_array($moduleName, apache_get_modules())) {
763 $this->error($testDetails);
769 * @param string $username
771 * @param $testDetails
773 public function requireMysqlConnection($server, $username, $password, $testDetails) {
774 $this->testing($testDetails);
775 $conn = @mysql_connect
($server, $username, $password);
781 $testDetails[2] .= ": " . mysql_error();
782 $this->error($testDetails);
788 * @param $testDetails
790 public function requireMySQLServer($server, $testDetails) {
791 $this->testing($testDetails);
792 $conn = @mysql_connect
($server, NULL, NULL);
794 if ($conn ||
mysql_errno() < 2000) {
798 $testDetails[2] .= ": " . mysql_error();
799 $this->error($testDetails);
805 * @param $testDetails
807 public function requireMySQLVersion($version, $testDetails) {
808 $this->testing($testDetails);
810 if (!mysql_get_server_info()) {
811 $testDetails[2] = 'Cannot determine the version of MySQL installed. Please ensure at least version 4.1 is installed.';
812 $this->warning($testDetails);
815 list($majorRequested, $minorRequested) = explode('.', $version);
816 list($majorHas, $minorHas) = explode('.', mysql_get_server_info());
818 if (($majorHas > $majorRequested) ||
($majorHas == $majorRequested && $minorHas >= $minorRequested)) {
822 $testDetails[2] .= "{$majorHas}.{$minorHas}.";
823 $this->error($testDetails);
830 * @param string $username
833 * @param $testDetails
835 public function requireMySQLInnoDB($server, $username, $password, $database, $testDetails) {
836 $this->testing($testDetails);
837 $conn = @mysql_connect
($server, $username, $password);
839 $testDetails[2] .= ' Could not determine if mysql has innodb support. Assuming no';
840 $this->error($testDetails);
844 $innodb_support = FALSE;
845 $result = mysql_query("SHOW ENGINES", $conn);
846 while ($values = mysql_fetch_array($result)) {
847 if ($values['Engine'] == 'InnoDB') {
848 if (strtolower($values['Support']) == 'yes' ||
849 strtolower($values['Support']) == 'default'
851 $innodb_support = TRUE;
855 if ($innodb_support) {
856 $testDetails[3] = 'MySQL server does have innodb support';
859 $testDetails[2] .= ' Could not determine if mysql has innodb support. Assuming no';
865 * @param string $username
868 * @param $testDetails
870 public function requireMySQLTempTables($server, $username, $password, $database, $testDetails) {
871 $this->testing($testDetails);
872 $conn = @mysql_connect
($server, $username, $password);
874 $testDetails[2] = 'Could not login to the database.';
875 $this->error($testDetails);
879 if (!@mysql_select_db
($database, $conn)) {
880 $testDetails[2] = 'Could not select the database.';
881 $this->error($testDetails);
885 $result = mysql_query('CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)', $conn);
887 $testDetails[2] = 'Could not create a temp table.';
888 $this->error($testDetails);
890 $result = mysql_query('DROP TEMPORARY TABLE civicrm_install_temp_table_test');
895 * @param string $username
898 * @param $testDetails
900 public function requireMySQLTrigger($server, $username, $password, $database, $testDetails) {
901 $this->testing($testDetails);
902 $conn = @mysql_connect
($server, $username, $password);
904 $testDetails[2] = 'Could not login to the database.';
905 $this->error($testDetails);
909 if (!@mysql_select_db
($database, $conn)) {
910 $testDetails[2] = 'Could not select the database.';
911 $this->error($testDetails);
915 $result = mysql_query('CREATE TABLE civicrm_install_temp_table_test (test text)', $conn);
917 $testDetails[2] = 'Could not create a table.';
918 $this->error($testDetails);
921 $result = mysql_query('CREATE TRIGGER civicrm_install_temp_table_test_trigger BEFORE INSERT ON civicrm_install_temp_table_test FOR EACH ROW BEGIN END');
923 mysql_query('DROP TABLE civicrm_install_temp_table_test');
924 $testDetails[2] = 'Could not create a trigger.';
925 $this->error($testDetails);
928 mysql_query('DROP TRIGGER civicrm_install_temp_table_test_trigger');
929 mysql_query('DROP TABLE civicrm_install_temp_table_test');
935 * @param string $username
938 * @param $testDetails
940 public function requireMySQLLockTables($server, $username, $password, $database, $testDetails) {
941 $this->testing($testDetails);
942 $conn = @mysql_connect
($server, $username, $password);
944 $testDetails[2] = 'Could not login to the database.';
945 $this->error($testDetails);
949 if (!@mysql_select_db
($database, $conn)) {
950 $testDetails[2] = 'Could not select the database.';
951 $this->error($testDetails);
955 $result = mysql_query('CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)', $conn);
957 $testDetails[2] = 'Could not create a table.';
958 $this->error($testDetails);
962 $result = mysql_query('LOCK TABLES civicrm_install_temp_table_test WRITE', $conn);
964 $testDetails[2] = 'Could not obtain a write lock for the table.';
965 $this->error($testDetails);
966 $result = mysql_query('DROP TEMPORARY TABLE civicrm_install_temp_table_test');
970 $result = mysql_query('UNLOCK TABLES', $conn);
972 $testDetails[2] = 'Could not release the lock for the table.';
973 $this->error($testDetails);
974 $result = mysql_query('DROP TEMPORARY TABLE civicrm_install_temp_table_test');
978 $result = mysql_query('DROP TEMPORARY TABLE civicrm_install_temp_table_test');
983 * @param string $username
985 * @param $testDetails
987 public function requireMySQLAutoIncrementIncrementOne($server, $username, $password, $testDetails) {
988 $this->testing($testDetails);
989 $conn = @mysql_connect
($server, $username, $password);
991 $testDetails[2] = 'Could not connect to the database server.';
992 $this->error($testDetails);
996 $result = mysql_query("SHOW variables like 'auto_increment_increment'", $conn);
998 $testDetails[2] = 'Could not query database server variables.';
999 $this->error($testDetails);
1003 $values = mysql_fetch_row($result);
1004 if ($values[1] == 1) {
1005 $testDetails[3] = 'MySQL server auto_increment_increment is 1';
1008 $this->error($testDetails);
1015 * @param string $username
1018 * @param $minValueKB
1019 * @param $testDetails
1021 public function requireMySQLThreadStack($server, $username, $password, $database, $minValueKB, $testDetails) {
1022 $this->testing($testDetails);
1023 $conn = @mysql_connect
($server, $username, $password);
1025 $testDetails[2] = 'Could not login to the database.';
1026 $this->error($testDetails);
1030 if (!@mysql_select_db
($database, $conn)) {
1031 $testDetails[2] = 'Could not select the database.';
1032 $this->error($testDetails);
1036 $result = mysql_query("SHOW VARIABLES LIKE 'thread_stack'", $conn); // bytes => kb
1038 $testDetails[2] = 'Could not query thread_stack.';
1039 $this->error($testDetails);
1042 $values = mysql_fetch_row($result);
1043 if ($values[1] < (1024 * $minValueKB)) {
1044 $testDetails[2] = 'MySQL "thread_stack" is ' . ($values[1] / 1024) . 'k';
1045 $this->error($testDetails);
1052 * @param string $username
1055 * @param $testDetails
1056 * @param bool $onlyRequire
1058 public function requireDatabaseOrCreatePermissions(
1064 $onlyRequire = FALSE
1066 $this->testing($testDetails);
1067 $conn = @mysql_connect
($server, $username, $password);
1070 if (@mysql_select_db
($database)) {
1071 $okay = "Database '$database' exists";
1073 elseif ($onlyRequire) {
1074 $testDetails[2] = "The database: '$database' does not exist";
1075 $this->error($testDetails);
1079 if (@mysql_query
("CREATE DATABASE $database")) {
1080 $okay = "Able to create a new database";
1083 $testDetails[2] .= " (user '$username' doesn't have CREATE DATABASE permissions.)";
1084 $this->error($testDetails);
1090 $testDetails[3] = $okay;
1091 $this->testing($testDetails);
1097 * @param $errorMessage
1099 public function requireServerVariables($varNames, $errorMessage) {
1100 //$this->testing($testDetails);
1101 foreach ($varNames as $varName) {
1102 if (!$_SERVER[$varName]) {
1103 $missing[] = '$_SERVER[' . $varName . ']';
1106 if (!isset($missing)) {
1110 $testDetails[2] = " (the following PHP variables are missing: " . implode(", ", $missing) . ")";
1111 $this->error($testDetails);
1116 * @param $testDetails
1120 public function isRunningApache($testDetails) {
1121 $this->testing($testDetails);
1122 if (function_exists('apache_get_modules') ||
stristr($_SERVER['SERVER_SIGNATURE'], 'Apache')) {
1126 $this->warning($testDetails);
1133 public function getBaseDir() {
1134 return dirname($_SERVER['SCRIPT_FILENAME']) . CIVICRM_DIRECTORY_SEPARATOR
;
1138 * @param $testDetails
1140 public function testing($testDetails) {
1141 if (!$testDetails) {
1145 $section = $testDetails[0];
1146 $test = $testDetails[1];
1149 if (isset($testDetails[3])) {
1150 $message .= " ($testDetails[3])";
1153 $this->tests
[$section][$test] = array("good", $message);
1157 * @param $testDetails
1159 public function error($testDetails) {
1160 $section = $testDetails[0];
1161 $test = $testDetails[1];
1163 $this->tests
[$section][$test] = array("error", $testDetails[2]);
1164 $this->errors
[] = $testDetails;
1168 * @param $testDetails
1170 public function warning($testDetails) {
1171 $section = $testDetails[0];
1172 $test = $testDetails[1];
1174 $this->tests
[$section][$test] = array("warning", $testDetails[2]);
1175 $this->warnings
[] = $testDetails;
1181 public function hasErrors() {
1182 return count($this->errors
);
1188 public function hasWarnings() {
1189 return count($this->warnings
);
1197 class Installer
extends InstallRequirements
{
1204 public function createDatabaseIfNotExists($server, $username, $password, $database) {
1205 $conn = @mysql_connect
($server, $username, $password);
1207 if (@mysql_select_db
($database)) {
1208 // skip if database already present
1212 if (@mysql_query
("CREATE DATABASE $database")) {
1215 $errorTitle = "Oops! Could not create Database $database";
1216 $errorMsg = "We encountered an error when attempting to create the database. Please check your mysql server permissions and the database name and try again.";
1217 errorDisplayPage($errorTitle, $errorMsg);
1226 public function install($config) {
1227 global $installDirPath;
1229 // create database if does not exists
1230 $this->createDatabaseIfNotExists($config['mysql']['server'],
1231 $config['mysql']['username'],
1232 $config['mysql']['password'],
1233 $config['mysql']['database']
1236 global $installDirPath;
1239 require_once $installDirPath . 'civicrm.php';
1240 civicrm_main($config);
1242 if (!$this->errors
) {
1243 global $installType, $installURLPath;
1245 $registerSiteURL = "https://civicrm.org/register-site";
1246 $commonOutputMessage = "
1247 <li>Have you registered this site at CiviCRM.org? If not, please help strengthen the CiviCRM ecosystem by taking a few minutes to <a href='$registerSiteURL' target='_blank'>fill out the site registration form</a>. The information collected will help us prioritize improvements, target our communications and build the community. If you have a technical role for this site, be sure to check Keep in Touch to receive technical updates (a low volume mailing list).</li>
1248 <li>We have integrated KCFinder with CKEditor and TinyMCE. This allows a user to upload images. All uploaded images are public.</li>
1253 $installType == 'drupal' &&
1254 version_compare(VERSION
, '7.0-rc1') >= 0
1260 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1261 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1262 $output .= '<head>';
1263 $output .= '<title>CiviCRM Installed</title>';
1264 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1265 $output .= '</head>';
1266 $output .= '<body>';
1267 $output .= '<div style="padding: 1em;"><p class="good">CiviCRM has been successfully installed</p>';
1269 $docLinkConfig = CRM_Utils_System
::docURL2('Configuring a New Site', FALSE, 'here', NULL, NULL, "wiki");
1270 if (!function_exists('ts')) {
1271 $docLinkConfig = "<a href=\"{$docLinkConfig}\">here</a>";
1273 $drupalURL = civicrm_cms_base();
1274 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/people/permissions";
1275 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1277 $output .= "<li>Drupal user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a target='_blank' href={$drupalPermissionsURL}>review these permissions</a> to ensure that they are appropriate for your requirements (<a target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'>learn more...</a>)</li>
1278 <li>Use the <a target='_blank' href=\"$drupalURL\">Configuration Checklist</a> to review and configure settings for your new site</li>
1279 {$commonOutputMessage}";
1281 // automatically enable CiviCRM module once it is installed successfully.
1282 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1283 global $cmsPath, $crmPath;
1285 // relative / abosolute paths are not working for drupal, hence using chdir()
1288 include_once "./includes/bootstrap.inc";
1289 include_once "./includes/unicode.inc";
1291 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL
);
1293 // prevent session information from being saved.
1294 drupal_save_session(FALSE);
1296 // Force the current user to anonymous.
1297 $original_user = $GLOBALS['user'];
1298 $GLOBALS['user'] = drupal_anonymous_user();
1300 // explicitly setting error reporting, since we cannot handle drupal related notices
1303 // rebuild modules, so that civicrm is added
1304 system_rebuild_module_data();
1306 // now enable civicrm module.
1307 module_enable(array('civicrm', 'civicrmtheme'));
1309 // clear block, page, theme, and hook caches
1310 drupal_flush_all_caches();
1312 //add basic drupal permissions
1313 civicrm_install_set_drupal_perms();
1315 // restore the user.
1316 $GLOBALS['user'] = $original_user;
1317 drupal_save_session(TRUE);
1320 $output .= '</div>';
1321 $output .= '</body>';
1322 $output .= '</html>';
1325 elseif ($installType == 'drupal' && version_compare(VERSION
, '6.0') >= 0) {
1329 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1330 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1331 $output .= '<head>';
1332 $output .= '<title>CiviCRM Installed</title>';
1333 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1334 $output .= '</head>';
1335 $output .= '<body>';
1336 $output .= '<div style="padding: 1em;"><p class="good">CiviCRM has been successfully installed</p>';
1338 $docLinkConfig = CRM_Utils_System
::docURL2('Configuring a New Site', FALSE, 'here', NULL, NULL, "wiki");
1339 if (!function_exists('ts')) {
1340 $docLinkConfig = "<a href=\"{$docLinkConfig}\">here</a>";
1342 $drupalURL = civicrm_cms_base();
1343 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/user/permissions";
1344 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1346 $output .= "<li>Drupal user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a target='_blank' href={$drupalPermissionsURL}>review these permissions</a> to ensure that they are appropriate for your requirements (<a target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'>learn more...</a>)</li>
1347 <li>Use the <a target='_blank' href=\"$drupalURL\">Configuration Checklist</a> to review and configure settings for your new site</li>
1348 {$commonOutputMessage}";
1350 // explicitly setting error reporting, since we cannot handle drupal related notices
1353 // automatically enable CiviCRM module once it is installed successfully.
1354 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1355 global $cmsPath, $crmPath;
1357 // relative / abosolute paths are not working for drupal, hence using chdir()
1360 include_once "./includes/bootstrap.inc";
1361 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL
);
1363 // rebuild modules, so that civicrm is added
1364 module_rebuild_cache();
1366 // now enable civicrm module.
1367 module_enable(array('civicrm'));
1369 // clear block, page, theme, and hook caches
1370 drupal_flush_all_caches();
1372 //add basic drupal permissions
1373 db_query('UPDATE {permission} SET perm = CONCAT( perm, \', access CiviMail subscribe/unsubscribe pages, access all custom data, access uploaded files, make online contributions, profile create, profile edit, profile view, register for events, view event info\') WHERE rid IN (1, 2)');
1377 elseif ($installType == 'wordpress') {
1378 echo '<h1>CiviCRM Installed</h1>';
1379 echo '<div style="padding: 1em;"><p style="background-color: #0C0; border: 1px #070 solid; color: white;">CiviCRM has been successfully installed</p>';
1381 $docLinkConfig = CRM_Utils_System
::docURL2('Configuring a New Site', FALSE, 'here', NULL, NULL, "wiki");
1382 if (!function_exists('ts')) {
1383 $docLinkConfig = "<a href=\"{$docLinkConfig}\">here</a>";
1386 $cmsURL = civicrm_cms_base();
1387 $cmsURL .= "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/configtask&reset=1";
1388 $wpPermissionsURL = "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/access/wp-permissions&reset=1";
1391 <li>WordPress user permissions have been automatically set - giving Anonymous and Subscribers access to public CiviCRM forms and features. We recommend that you <a target='_blank' href={$wpPermissionsURL}>review these permissions</a> to ensure that they are appropriate for your requirements (<a target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'>learn more...</a>)</li>
1392 <li>Use the <a target='_blank' href=\"$cmsURL\">Configuration Checklist</a> to review and configure settings for your new site</li>
1393 {$commonOutputMessage}
1401 return $this->errors
;
1406 function civicrm_install_set_drupal_perms() {
1407 if (!function_exists('db_select')) {
1408 db_query('UPDATE {permission} SET perm = CONCAT( perm, \', access CiviMail subscribe/unsubscribe pages, access all custom data, access uploaded files, make online contributions, profile listings and forms, register for events, view event info, view event participants\') WHERE rid IN (1, 2)');
1412 'access all custom data',
1413 'access uploaded files',
1414 'make online contributions',
1418 'register for events',
1420 'view event participants',
1421 'access CiviMail subscribe/unsubscribe pages',
1424 // Adding a permission that has not yet been assigned to a module by
1425 // a hook_permission implementation results in a database error.
1427 $allPerms = array_keys(module_invoke_all('permission'));
1428 foreach (array_diff($perms, $allPerms) as $perm) {
1430 'Cannot grant the %perm permission because it does not yet exist.',
1431 array('%perm' => $perm),
1435 $perms = array_intersect($perms, $allPerms);
1436 user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID
, $perms);
1437 user_role_grant_permissions(DRUPAL_ANONYMOUS_RID
, $perms);
1447 function getSiteDir($cmsPath, $str) {
1448 static $siteDir = '';
1454 $sites = CIVICRM_DIRECTORY_SEPARATOR
. 'sites' . CIVICRM_DIRECTORY_SEPARATOR
;
1455 $modules = CIVICRM_DIRECTORY_SEPARATOR
. 'modules' . CIVICRM_DIRECTORY_SEPARATOR
;
1456 preg_match("/" . preg_quote($sites, CIVICRM_DIRECTORY_SEPARATOR
) .
1457 "([\-a-zA-Z0-9_.]+)" .
1458 preg_quote($modules, CIVICRM_DIRECTORY_SEPARATOR
) . "/",
1459 $_SERVER['SCRIPT_FILENAME'], $matches
1461 $siteDir = isset($matches[1]) ?
$matches[1] : 'default';
1463 if (strtolower($siteDir) == 'all') {
1464 // For this case - use drupal's way of finding out multi-site directory
1465 $uri = explode(CIVICRM_DIRECTORY_SEPARATOR
, $_SERVER['SCRIPT_FILENAME']);
1466 $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
1467 for ($i = count($uri) - 1; $i > 0; $i--) {
1468 for ($j = count($server); $j > 0; $j--) {
1469 $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
1470 if (file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
1471 'sites' . CIVICRM_DIRECTORY_SEPARATOR
. $dir
1478 $siteDir = 'default';
1485 * @param $errorTitle
1488 function errorDisplayPage($errorTitle, $errorMsg) {
1489 include 'error.html';