Merge pull request #5618 from sfe-ev/fix-upgrade-option_value-4.6
[civicrm-core.git] / install / index.php
CommitLineData
6a488035
TO
1<?php
2
3/**
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.
8 *
9 * Copyright (c) 2006-7, SilverStripe Limited - www.silverstripe.com
10 * All rights reserved.
11 *
6a9514e1 12 * Changes and modifications (c) 2007-2015 by CiviCRM LLC
6a488035
TO
13 *
14 */
15
16/**
17 * CiviCRM Installer
18 */
19
20ini_set('max_execution_time', 3000);
21
22if (stristr(PHP_OS, 'WIN')) {
23 define('CIVICRM_DIRECTORY_SEPARATOR', '/');
481a74f4 24 define('CIVICRM_WINDOWS', 1);
6a488035
TO
25}
26else {
27 define('CIVICRM_DIRECTORY_SEPARATOR', DIRECTORY_SEPARATOR);
481a74f4 28 define('CIVICRM_WINDOWS', 0);
6a488035
TO
29}
30
31// set installation type - drupal
32if (!session_id()) {
33 session_start();
34}
35
36// unset civicrm session if any
37if (array_key_exists('CiviCRM', $_SESSION)) {
38 unset($_SESSION['CiviCRM']);
39}
40
41if (isset($_GET['civicrm_install_type'])) {
42 $_SESSION['civicrm_install_type'] = $_GET['civicrm_install_type'];
43}
44else {
45 if (!isset($_SESSION['civicrm_install_type'])) {
46 $_SESSION['civicrm_install_type'] = "drupal";
47 }
48}
49
50global $installType;
6a488035 51global $crmPath;
97b8e6b2 52global $pkgPath;
6a488035
TO
53global $installDirPath;
54global $installURLPath;
97b8e6b2 55
6a9514e1
ML
56$installType = strtolower($_SESSION['civicrm_install_type']);
57
6a488035
TO
58if ($installType == 'drupal') {
59 $crmPath = dirname(dirname($_SERVER['SCRIPT_FILENAME']));
60 $installDirPath = $installURLPath = '';
61}
62elseif ($installType == 'wordpress') {
63 $crmPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR;
64 $installDirPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
6a488035
TO
65 $installURLPath = WP_PLUGIN_URL . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
66}
6a9514e1
ML
67else {
68 $errorTitle = "Oops! Unsupported installation mode";
69 $errorMsg = sprintf('%s: unknown installation mode. Please refer to the online documentation for more information.', $installType);
70 errorDisplayPage($errorTitle, $errorMsg, FALSE);
71}
6a488035 72
97b8e6b2 73$pkgPath = $crmPath . DIRECTORY_SEPARATOR . 'packages';
74
6a488035
TO
75require_once $crmPath . '/CRM/Core/ClassLoader.php';
76CRM_Core_ClassLoader::singleton()->register();
77
6a488035
TO
78// Load civicrm database config
79if (isset($_REQUEST['mysql'])) {
80 $databaseConfig = $_REQUEST['mysql'];
81}
82else {
83 $databaseConfig = array(
84 "server" => "localhost",
85 "username" => "civicrm",
86 "password" => "",
87 "database" => "civicrm",
88 );
89}
90
91if ($installType == 'drupal') {
92 // Load drupal database config
93 if (isset($_REQUEST['drupal'])) {
94 $drupalConfig = $_REQUEST['drupal'];
95 }
96 else {
97 $drupalConfig = array(
98 "server" => "localhost",
99 "username" => "drupal",
100 "password" => "",
101 "database" => "drupal",
102 );
103 }
104}
105
106$loadGenerated = 0;
107if (isset($_REQUEST['loadGenerated'])) {
108 $loadGenerated = 1;
109}
110
111require_once dirname(__FILE__) . CIVICRM_DIRECTORY_SEPARATOR . 'langs.php';
112foreach ($langs as $locale => $_) {
113 if ($locale == 'en_US') {
114 continue;
115 }
4f99ca55
TO
116 if (!file_exists(implode(CIVICRM_DIRECTORY_SEPARATOR, array($crmPath, 'sql', "civicrm_data.$locale.mysql")))) {
117 unset($langs[$locale]);
1f5f3294 118 }
6a488035
TO
119}
120
97b8e6b2 121// Set the locale (required by CRM_Core_Config)
122// This is mostly sympbolic, since nothing we do during the install
123// really requires CIVICRM_UF to be defined.
124$installTypeToUF = array(
125 'wordpress' => 'WordPress',
126 'drupal' => 'Drupal',
127);
128
c8ab0a65 129$uf = (isset($installTypeToUF[$installType]) ? $installTypeToUF[$installType] : 'Drupal');
97b8e6b2 130define('CIVICRM_UF', $uf);
131
132global $tsLocale;
133
134$tsLocale = 'en_US';
6a488035 135$seedLanguage = 'en_US';
97b8e6b2 136
6a488035
TO
137if (isset($_REQUEST['seedLanguage']) and isset($langs[$_REQUEST['seedLanguage']])) {
138 $seedLanguage = $_REQUEST['seedLanguage'];
97b8e6b2 139 $tsLocale = $_REQUEST['seedLanguage'];
6a488035
TO
140}
141
97b8e6b2 142$config = CRM_Core_Config::singleton(FALSE);
0ad1e2de
ML
143$GLOBALS['civicrm_default_error_scope'] = NULL;
144
97b8e6b2 145// The translation files are in the parent directory (l10n)
146$config->gettextResourceDir = '..' . DIRECTORY_SEPARATOR . $config->gettextResourceDir;
147$i18n = CRM_Core_I18n::singleton();
148
6a488035
TO
149global $cmsPath;
150if ($installType == 'drupal') {
151 //CRM-6840 -don't force to install in sites/all/modules/
152 $object = new CRM_Utils_System_Drupal();
153 $cmsPath = $object->cmsRootPath();
154
155 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
156 $alreadyInstalled = file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
157 'sites' . CIVICRM_DIRECTORY_SEPARATOR .
158 $siteDir . CIVICRM_DIRECTORY_SEPARATOR .
159 'civicrm.settings.php'
160 );
161}
162elseif ($installType == 'wordpress') {
163 $cmsPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'civicrm';
164 $alreadyInstalled = file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
165 'civicrm.settings.php'
166 );
167}
168
6a9514e1
ML
169if ($installType == 'drupal') {
170 // Lets check only /modules/.
171 $pattern = '/' . preg_quote(CIVICRM_DIRECTORY_SEPARATOR . 'modules', CIVICRM_DIRECTORY_SEPARATOR) . '/';
172
173 if (!preg_match($pattern, str_replace("\\", "/", $_SERVER['SCRIPT_FILENAME']))) {
174 $directory = implode(CIVICRM_DIRECTORY_SEPARATOR, array('sites', 'all', 'modules'));
175 $errorTitle = ts("Oops! Please correct your install location");
176 $errorMsg = ts("Please untar (uncompress) your downloaded copy of CiviCRM in the <strong>%1</strong> directory below your Drupal root directory.", array(1 => $directory));
177 errorDisplayPage($errorTitle, $errorMsg);
178 }
179}
180
6a488035
TO
181// Exit with error if CiviCRM has already been installed.
182if ($alreadyInstalled) {
6a9514e1
ML
183 $errorTitle = ts("Oops! CiviCRM is already installed");
184 $settings_directory = $cmsPath;
6a488035 185
6a9514e1
ML
186 if ($installType == 'drupal') {
187 $settings_directory = implode(CIVICRM_DIRECTORY_SEPARATOR, array(
188 ts('[your Drupal root directory]'),
189 'sites',
190 $siteDir,
191 ));
6a488035 192 }
6a9514e1
ML
193
194 $docLink = CRM_Utils_System::docURL2('Installation and Upgrades', FALSE, ts('Installation Guide'), NULL, NULL, "wiki");
195 $errorMsg = ts("CiviCRM has already been installed. <ul><li>To <strong>start over</strong>, you must delete or rename the existing CiviCRM settings file - <strong>civicrm.settings.php</strong> - from <strong>%1</strong>.</li><li>To <strong>upgrade an existing installation</strong>, <a href='%2'>refer to the online documentation</a>.</li></ul>", array(1 => $settings_directory, 2 => $docLink));
196 errorDisplayPage($errorTitle, $errorMsg, FALSE);
6a488035
TO
197}
198
199$versionFile = $crmPath . CIVICRM_DIRECTORY_SEPARATOR . 'civicrm-version.php';
200if (file_exists($versionFile)) {
1f5f3294 201 require_once $versionFile;
6a488035
TO
202 $civicrm_version = civicrmVersion();
203}
204else {
205 $civicrm_version = 'unknown';
206}
207
208if ($installType == 'drupal') {
209 // Ensure that they have downloaded the correct version of CiviCRM
6a9514e1
ML
210 if ($civicrm_version['cms'] != 'Drupal' && $civicrm_version['cms'] != 'Drupal6') {
211 $errorTitle = ts("Oops! Incorrect CiviCRM version");
212 $errorMsg = ts("This installer can only be used for the Drupal version of CiviCRM.");
6a488035
TO
213 errorDisplayPage($errorTitle, $errorMsg);
214 }
215
216 define('DRUPAL_ROOT', $cmsPath);
217 $drupalVersionFiles = array(
218 // D6
219 implode(CIVICRM_DIRECTORY_SEPARATOR, array($cmsPath, 'modules', 'system', 'system.module')),
220 // D7
221 implode(CIVICRM_DIRECTORY_SEPARATOR, array($cmsPath, 'includes', 'bootstrap.inc')),
222 );
223 foreach ($drupalVersionFiles as $drupalVersionFile) {
224 if (file_exists($drupalVersionFile)) {
225 require_once $drupalVersionFile;
226 }
227 }
228
229 if (!defined('VERSION') or version_compare(VERSION, '6.0') < 0) {
6a9514e1
ML
230 $errorTitle = ts("Oops! Incorrect Drupal version");
231 $errorMsg = ts("This version of CiviCRM can only be used with Drupal 6.x or 7.x. Please ensure that '%1' exists if you are running Drupal 7.0 and over.", array(1 => implode("' or '", $drupalVersionFiles)));
6a488035
TO
232 errorDisplayPage($errorTitle, $errorMsg);
233 }
234}
235elseif ($installType == 'wordpress') {
236 //HACK for now
237 $civicrm_version['cms'] = 'WordPress';
238
239 // Ensure that they have downloaded the correct version of CiviCRM
240 if ($civicrm_version['cms'] != 'WordPress') {
6a9514e1
ML
241 $errorTitle = ts("Oops! Incorrect CiviCRM version");
242 $errorMsg = ts("This installer can only be used for the WordPress version of CiviCRM.");
6a488035
TO
243 errorDisplayPage($errorTitle, $errorMsg);
244 }
245}
246
247// Check requirements
248$req = new InstallRequirements();
249$req->check();
250
251if ($req->hasErrors()) {
252 $hasErrorOtherThanDatabase = TRUE;
253}
254
255if ($databaseConfig) {
256 $dbReq = new InstallRequirements();
257 $dbReq->checkdatabase($databaseConfig, 'CiviCRM');
258 if ($installType == 'drupal') {
259 $dbReq->checkdatabase($drupalConfig, 'Drupal');
260 }
261}
262
263// Actual processor
264if (isset($_REQUEST['go']) && !$req->hasErrors() && !$dbReq->hasErrors()) {
265 // Confirm before reinstalling
266 if (!isset($_REQUEST['force_reinstall']) && $alreadyInstalled) {
1f5f3294 267 include $installDirPath . 'template.html';
6a488035
TO
268 }
269 else {
270 $inst = new Installer();
271 $inst->install($_REQUEST);
272 }
273
274 // Show the config form
275}
276else {
1f5f3294 277 include $installDirPath . 'template.html';
6a488035
TO
278}
279
280/**
281 * This class checks requirements
282 * Each of the requireXXX functions takes an argument which gives a user description of the test. It's an array
283 * of 3 parts:
6a9514e1 284 * $description[0] - The test category
6a488035
TO
285 * $description[1] - The test title
286 * $description[2] - The test error to show, if it goes wrong
287 */
288class InstallRequirements {
289 var $errors, $warnings, $tests;
290
291 // @see CRM_Upgrade_Form::MINIMUM_THREAD_STACK
292 const MINIMUM_THREAD_STACK = 192;
293
294 /**
fe482240 295 * Just check that the database configuration is okay.
d7c8cf03
EM
296 * @param $databaseConfig
297 * @param $dbName
6a488035 298 */
971d41b1 299 public function checkdatabase($databaseConfig, $dbName) {
6a488035 300 if ($this->requireFunction('mysql_connect',
56fdfc52 301 array(
97b8e6b2 302 ts("PHP Configuration"),
303 ts("MySQL support"),
304 ts("MySQL support not included in PHP."),
56fdfc52
TO
305 )
306 )
307 ) {
6a488035
TO
308 $this->requireMySQLServer($databaseConfig['server'],
309 array(
2f8082cd
ML
310 ts("MySQL %1 Configuration", array(1 => $dbName)),
311 ts("Does the server exist?"),
312 ts("Can't find the a MySQL server on '%1'.", array(1 => $databaseConfig['server'])),
6a488035
TO
313 $databaseConfig['server'],
314 )
315 );
316 if ($this->requireMysqlConnection($databaseConfig['server'],
56fdfc52
TO
317 $databaseConfig['username'],
318 $databaseConfig['password'],
319 array(
2f8082cd
ML
320 ts("MySQL %1 Configuration", array(1 => $dbName)),
321 ts("Are the access credentials correct?"),
322 ts("That username/password doesn't work"),
56fdfc52
TO
323 )
324 )
325 ) {
6a488035
TO
326 @$this->requireMySQLVersion("5.1",
327 array(
2f8082cd
ML
328 ts("MySQL %1 Configuration", array(1 => $dbName)),
329 ts("MySQL version at least %1", array(1 => '5.1')),
330 ts("MySQL version %1 or higher is required, you are running MySQL %2.", array(1 => '5.1', 2 => mysql_get_server_info())),
331 ts("MySQL %1", array(1 => mysql_get_server_info())),
6a488035
TO
332 )
333 );
334 $this->requireMySQLAutoIncrementIncrementOne($databaseConfig['server'],
335 $databaseConfig['username'],
336 $databaseConfig['password'],
337 array(
2f8082cd
ML
338 ts("MySQL %1 Configuration", array(1 => $dbName)),
339 ts("Is auto_increment_increment set to 1"),
340 ts("An auto_increment_increment value greater than 1 is not currently supported. Please see issue CRM-7923 for further details and potential workaround."),
6a488035
TO
341 )
342 );
343 $this->requireMySQLThreadStack($databaseConfig['server'],
344 $databaseConfig['username'],
345 $databaseConfig['password'],
346 $databaseConfig['database'],
347 self::MINIMUM_THREAD_STACK,
348 array(
2f8082cd
ML
349 ts("MySQL %1 Configuration", array(1 => $dbName)),
350 ts("Does MySQL thread_stack meet minimum (%1k)", array(1 => self::MINIMUM_THREAD_STACK)),
56fdfc52
TO
351 "",
352 // "The MySQL thread_stack does not meet minimum " . CRM_Upgrade_Form::MINIMUM_THREAD_STACK . "k. Please update thread_stack in my.cnf.",
6a488035
TO
353 )
354 );
355 }
356 $onlyRequire = ($dbName == 'Drupal') ? TRUE : FALSE;
357 $this->requireDatabaseOrCreatePermissions(
358 $databaseConfig['server'],
359 $databaseConfig['username'],
360 $databaseConfig['password'],
361 $databaseConfig['database'],
362 array(
2f8082cd
ML
363 ts("MySQL %1 Configuration", array(1 => $dbName)),
364 ts("Can I access/create the database?"),
5f40a878 365 ts("I can't create new databases and the database '%1' doesn't exist.", array(1 => $databaseConfig['database'])),
6a488035
TO
366 ),
367 $onlyRequire
368 );
369 if ($dbName != 'Drupal') {
370 $this->requireMySQLInnoDB($databaseConfig['server'],
371 $databaseConfig['username'],
372 $databaseConfig['password'],
373 $databaseConfig['database'],
374 array(
2f8082cd
ML
375 ts("MySQL %1 Configuration", array(1 => $dbName)),
376 ts("Can I access/create InnoDB tables in the database?"),
377 ts("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."),
6a488035
TO
378 )
379 );
380 $this->requireMySQLTempTables($databaseConfig['server'],
381 $databaseConfig['username'],
382 $databaseConfig['password'],
383 $databaseConfig['database'],
384 array(
2f8082cd
ML
385 ts("MySQL %1 Configuration", array(1 => $dbName)),
386 ts('Can I create temporary tables in the database?'),
387 ts('Unable to create temporary tables. This MySQL user is missing the CREATE TEMPORARY TABLES privilege.'),
6a488035
TO
388 )
389 );
390 $this->requireMySQLLockTables($databaseConfig['server'],
391 $databaseConfig['username'],
392 $databaseConfig['password'],
393 $databaseConfig['database'],
394 array(
2f8082cd
ML
395 ts("MySQL %1 Configuration", array(1 => $dbName)),
396 ts('Can I create lock tables in the database?'),
397 ts('Unable to lock tables. This MySQL user is missing the LOCK TABLES privilege.'),
6a488035
TO
398 )
399 );
400 $this->requireMySQLTrigger($databaseConfig['server'],
401 $databaseConfig['username'],
402 $databaseConfig['password'],
403 $databaseConfig['database'],
404 array(
2f8082cd
ML
405 ts("MySQL %1 Configuration", array(1 => $dbName)),
406 ts('Can I create triggers in the database?'),
407 ts('Unable to create triggers. This MySQL user is missing the CREATE TRIGGERS privilege.'),
6a488035
TO
408 )
409 );
410 }
411 }
412 }
413
414 /**
fe482240 415 * Check everything except the database.
6a488035 416 */
971d41b1 417 public function check() {
6a488035
TO
418 global $crmPath, $installType;
419
420 $this->errors = NULL;
421
56fdfc52 422 $this->requirePHPVersion('5.3.3', array(
2f8082cd
ML
423 ts("PHP Configuration"),
424 ts("PHP5 installed"),
56fdfc52 425 NULL,
2f8082cd 426 ts("PHP version %1", array(1 => phpversion())),
56fdfc52 427 ));
6a488035
TO
428
429 // Check that we can identify the root folder successfully
430 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR . 'README.txt',
431 array(
2f8082cd
ML
432 ts("File permissions"),
433 ts("Does the webserver know where files are stored?"),
434 ts("The webserver isn't letting me identify where files are stored."),
6a488035
TO
435 $this->getBaseDir(),
436 ),
437 TRUE
438 );
439
440 // CRM-6485: make sure the path does not contain PATH_SEPARATOR, as we don’t know how to escape it
441 $this->requireNoPathSeparator(
442 array(
2f8082cd
ML
443 ts("File permissions"),
444 ts('Does the CiviCRM path contain PATH_SEPARATOR?'),
445 ts('The path %1 contains PATH_SEPARATOR (the %2 character).', array(1 => $this->getBaseDir(), 2 => PATH_SEPARATOR)),
6a488035
TO
446 $this->getBaseDir(),
447 )
448 );
449
450 $requiredDirectories = array('CRM', 'packages', 'templates', 'js', 'api', 'i', 'sql');
451 foreach ($requiredDirectories as $dir) {
452 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR . $dir,
453 array(
2f8082cd
ML
454 ts("File permissions"),
455 ts("Folder '%1' exists?", array(1 => $dir)),
456 ts("There is no '%1' folder.", array(1 => $dir)),
56fdfc52 457 ), TRUE
6a488035
TO
458 );
459 }
460
461 $configIDSiniDir = NULL;
462 global $cmsPath;
3fdcdfbb 463 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
6a488035 464 if ($installType == 'drupal') {
6a488035
TO
465
466 // make sure that we can write to sites/default and files/
467 $writableDirectories = array(
468 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
469 'sites' . CIVICRM_DIRECTORY_SEPARATOR .
470 $siteDir . CIVICRM_DIRECTORY_SEPARATOR .
471 'files',
472 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
473 'sites' . CIVICRM_DIRECTORY_SEPARATOR .
474 $siteDir,
475 );
476 }
477 elseif ($installType == 'wordpress') {
478 // make sure that we can write to plugins/civicrm and plugins/files/
479 $writableDirectories = array(WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'files', $cmsPath);
480 }
481
482 foreach ($writableDirectories as $dir) {
483 $dirName = CIVICRM_WINDOWS ? $dir : CIVICRM_DIRECTORY_SEPARATOR . $dir;
2f8082cd
ML
484 $testDetails = array(
485 ts("File permissions"),
486 ts("Is the %1 folder writeable?", array(1 => $dir)),
487 NULL,
6a488035 488 );
2f8082cd 489 $this->requireWriteable($dirName, $testDetails, TRUE);
6a488035
TO
490 }
491
492 //check for Config.IDS.ini, file may exist in re-install
493 $configIDSiniDir = array($cmsPath, 'sites', $siteDir, 'files', 'civicrm', 'upload', 'Config.IDS.ini');
494
495 if (is_array($configIDSiniDir) && !empty($configIDSiniDir)) {
496 $configIDSiniFile = implode(CIVICRM_DIRECTORY_SEPARATOR, $configIDSiniDir);
497 if (file_exists($configIDSiniFile)) {
498 unlink($configIDSiniFile);
499 }
500 }
501
502 // Check for rewriting
503 if (isset($_SERVER['SERVER_SOFTWARE'])) {
504 $webserver = strip_tags(trim($_SERVER['SERVER_SOFTWARE']));
505 }
506 elseif (isset($_SERVER['SERVER_SIGNATURE'])) {
507 $webserver = strip_tags(trim($_SERVER['SERVER_SIGNATURE']));
508 }
509
510 if ($webserver == '') {
2f8082cd 511 $webserver = ts("I can't tell what webserver you are running");
6a488035
TO
512 }
513
514 // Check for $_SERVER configuration
56fdfc52 515 $this->requireServerVariables(array('SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME'), array(
2f8082cd
ML
516 ts("Webserver config"),
517 ts("Recognised webserver"),
518 ts("You seem to be using an unsupported webserver. The server variables SCRIPT_NAME, HTTP_HOST, SCRIPT_FILENAME need to be set."),
56fdfc52 519 ));
6a488035
TO
520
521 // Check for MySQL support
2f8082cd
ML
522 $this->requireFunction('mysql_connect', array(
523 ts("PHP Configuration"),
524 ts("MySQL support"),
525 ts("MySQL support not included in PHP."),
526 ));
6a488035
TO
527
528 // Check for JSON support
2f8082cd
ML
529 $this->requireFunction('json_encode', array(
530 ts("PHP Configuration"),
531 ts("JSON support"),
532 ts("JSON support not included in PHP."),
533 ));
6a488035
TO
534
535 // Check for xcache_isset and emit warning if exists
536 $this->checkXCache(array(
2f8082cd
ML
537 ts("PHP Configuration"),
538 ts("XCache compatibility"),
539 ts("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."),
56fdfc52 540 ));
6a488035
TO
541
542 // Check memory allocation
543 $this->requireMemory(32 * 1024 * 1024,
544 64 * 1024 * 1024,
545 array(
2f8082cd
ML
546 ts("PHP Configuration"),
547 ts("Memory allocated (PHP config option 'memory_limit')"),
548 ts("CiviCRM needs a minimum of %1 MB allocated to PHP, but recommends %2 MB.", array(1 => 32, 2 => 64)),
6a488035
TO
549 ini_get("memory_limit"),
550 )
551 );
552
553 return $this->errors;
554 }
555
627456b5
EM
556 /**
557 * @param $min
558 * @param $recommended
559 * @param $testDetails
560 */
971d41b1 561 public function requireMemory($min, $recommended, $testDetails) {
6a488035
TO
562 $this->testing($testDetails);
563 $mem = $this->getPHPMemory();
564
565 if ($mem < $min && $mem > 0) {
2f8082cd 566 $testDetails[2] .= " " . ts("You only have %1 allocated", array(1 => ini_get("memory_limit")));
6a488035
TO
567 $this->error($testDetails);
568 }
569 elseif ($mem < $recommended && $mem > 0) {
2f8082cd 570 $testDetails[2] .= " " . ts("You only have %1 allocated", array(1 => ini_get("memory_limit")));
6a488035
TO
571 $this->warning($testDetails);
572 }
573 elseif ($mem == 0) {
2f8082cd 574 $testDetails[2] .= " " . ts("We can't determine how much memory you have allocated. Install only if you're sure you've allocated at least %1 MB.", array(1 => 32));
6a488035
TO
575 $this->warning($testDetails);
576 }
577 }
578
627456b5
EM
579 /**
580 * @return float
581 */
971d41b1 582 public function getPHPMemory() {
6a488035
TO
583 $memString = ini_get("memory_limit");
584
585 switch (strtolower(substr($memString, -1))) {
586 case "k":
587 return round(substr($memString, 0, -1) * 1024);
588
589 case "m":
590 return round(substr($memString, 0, -1) * 1024 * 1024);
591
592 case "g":
593 return round(substr($memString, 0, -1) * 1024 * 1024 * 1024);
594
595 default:
596 return round($memString);
597 }
598 }
599
971d41b1 600 public function listErrors() {
6a488035 601 if ($this->errors) {
2f8082cd 602 echo "<p>" . ts("The following problems are preventing me from installing CiviCRM:") . "</p>";
6a488035
TO
603 foreach ($this->errors as $error) {
604 echo "<li>" . htmlentities($error) . "</li>";
605 }
606 }
607 }
608
627456b5
EM
609 /**
610 * @param null $section
611 */
971d41b1 612 public function showTable($section = NULL) {
6a488035
TO
613 if ($section) {
614 $tests = $this->tests[$section];
615 echo "<table class=\"testResults\" width=\"100%\">";
616 foreach ($tests as $test => $result) {
617 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
618 }
619 echo "</table>";
620 }
621 else {
622 foreach ($this->tests as $section => $tests) {
623 echo "<h3>$section</h3>";
624 echo "<table class=\"testResults\" width=\"100%\">";
625
626 foreach ($tests as $test => $result) {
627 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
628 }
629 echo "</table>";
630 }
631 }
632 }
633
627456b5 634 /**
100fef9d 635 * @param string $funcName
627456b5
EM
636 * @param $testDetails
637 *
638 * @return bool
639 */
971d41b1 640 public function requireFunction($funcName, $testDetails) {
6a488035
TO
641 $this->testing($testDetails);
642
643 if (!function_exists($funcName)) {
644 $this->error($testDetails);
645 return FALSE;
646 }
647 else {
648 return TRUE;
649 }
650 }
651
627456b5
EM
652 /**
653 * @param $testDetails
654 */
971d41b1 655 public function checkXCache($testDetails) {
6a488035
TO
656 if (function_exists('xcache_isset') &&
657 ini_get('xcache.size') > 0
658 ) {
659 $this->testing($testDetails);
660 $this->warning($testDetails);
661 }
662 }
663
627456b5
EM
664 /**
665 * @param $minVersion
666 * @param $testDetails
667 * @param null $maxVersion
668 */
971d41b1 669 public function requirePHPVersion($minVersion, $testDetails, $maxVersion = NULL) {
6a488035
TO
670
671 $this->testing($testDetails);
672
56fdfc52 673 $phpVersion = phpversion();
6a488035
TO
674 $aboveMinVersion = version_compare($phpVersion, $minVersion) >= 0;
675 $belowMaxVersion = $maxVersion ? version_compare($phpVersion, $maxVersion) < 0 : TRUE;
676
677 if ($maxVersion && $aboveMinVersion && $belowMaxVersion) {
678 return TRUE;
679 }
680 elseif (!$maxVersion && $aboveMinVersion) {
681 return TRUE;
682 }
683
684 if (!$testDetails[2]) {
685 if (!$aboveMinVersion) {
2f8082cd 686 $testDetails[2] = ts("You need PHP version %1 or later, only %2 is installed. Please upgrade your server, or ask your web-host to do so.", array(1 => $minVersion, 2 => $phpVersion));
6a488035
TO
687 }
688 else {
2f8082cd 689 $testDetails[2] = ts("PHP version %1 is not supported. PHP version earlier than %2 is required. You might want to downgrade your server, or ask your web-host to do so.", array(1 => $maxVersion, 2 => $phpVersion));
6a488035
TO
690 }
691 }
692
693 $this->error($testDetails);
694 }
695
627456b5 696 /**
100fef9d 697 * @param string $filename
627456b5
EM
698 * @param $testDetails
699 * @param bool $absolute
700 */
971d41b1 701 public function requireFile($filename, $testDetails, $absolute = FALSE) {
6a488035
TO
702 $this->testing($testDetails);
703 if (!$absolute) {
704 $filename = $this->getBaseDir() . $filename;
705 }
706 if (!file_exists($filename)) {
2f8082cd 707 $testDetails[2] .= " (" . ts("file '%1' not found", array(1 => $filename)) . ')';
6a488035
TO
708 $this->error($testDetails);
709 }
710 }
711
627456b5
EM
712 /**
713 * @param $testDetails
714 */
971d41b1 715 public function requireNoPathSeparator($testDetails) {
6a488035
TO
716 $this->testing($testDetails);
717 if (substr_count($this->getBaseDir(), PATH_SEPARATOR)) {
718 $this->error($testDetails);
719 }
720 }
721
627456b5 722 /**
100fef9d 723 * @param string $filename
627456b5
EM
724 * @param $testDetails
725 */
971d41b1 726 public function requireNoFile($filename, $testDetails) {
6a488035
TO
727 $this->testing($testDetails);
728 $filename = $this->getBaseDir() . $filename;
729 if (file_exists($filename)) {
2f8082cd 730 $testDetails[2] .= " (" . ts("file '%1' found", array(1 => $filename)) . ")";
6a488035
TO
731 $this->error($testDetails);
732 }
733 }
734
627456b5 735 /**
100fef9d 736 * @param string $filename
627456b5
EM
737 * @param $testDetails
738 */
971d41b1 739 public function moveFileOutOfTheWay($filename, $testDetails) {
6a488035
TO
740 $this->testing($testDetails);
741 $filename = $this->getBaseDir() . $filename;
742 if (file_exists($filename)) {
743 if (file_exists("$filename.bak")) {
744 rm("$filename.bak");
745 }
746 rename($filename, "$filename.bak");
747 }
748 }
749
627456b5 750 /**
100fef9d 751 * @param string $filename
627456b5
EM
752 * @param $testDetails
753 * @param bool $absolute
754 */
971d41b1 755 public function requireWriteable($filename, $testDetails, $absolute = FALSE) {
6a488035
TO
756 $this->testing($testDetails);
757 if (!$absolute) {
758 $filename = $this->getBaseDir() . $filename;
759 }
760
22e263ad 761 if (!is_writable($filename)) {
6a488035
TO
762 $name = NULL;
763 if (function_exists('posix_getpwuid')) {
764 $user = posix_getpwuid(posix_geteuid());
765 $name = '- ' . $user['name'] . ' -';
766 }
767
768 if (!isset($testDetails[2])) {
769 $testDetails[2] = NULL;
770 }
2f8082cd 771 $testDetails[2] .= ts("The user account used by your web-server %1 needs to be granted write access to the following directory in order to configure the CiviCRM settings file:", array(1 => $name)) . "\n$filename";
6a488035
TO
772 $this->error($testDetails);
773 }
774 }
775
627456b5 776 /**
100fef9d 777 * @param string $moduleName
627456b5
EM
778 * @param $testDetails
779 */
971d41b1 780 public function requireApacheModule($moduleName, $testDetails) {
6a488035
TO
781 $this->testing($testDetails);
782 if (!in_array($moduleName, apache_get_modules())) {
783 $this->error($testDetails);
784 }
785 }
786
627456b5
EM
787 /**
788 * @param $server
100fef9d 789 * @param string $username
627456b5
EM
790 * @param $password
791 * @param $testDetails
792 */
971d41b1 793 public function requireMysqlConnection($server, $username, $password, $testDetails) {
6a488035
TO
794 $this->testing($testDetails);
795 $conn = @mysql_connect($server, $username, $password);
796
797 if ($conn) {
798 return TRUE;
799 }
800 else {
801 $testDetails[2] .= ": " . mysql_error();
802 $this->error($testDetails);
803 }
804 }
805
627456b5
EM
806 /**
807 * @param $server
808 * @param $testDetails
809 */
971d41b1 810 public function requireMySQLServer($server, $testDetails) {
6a488035
TO
811 $this->testing($testDetails);
812 $conn = @mysql_connect($server, NULL, NULL);
813
814 if ($conn || mysql_errno() < 2000) {
815 return TRUE;
816 }
817 else {
818 $testDetails[2] .= ": " . mysql_error();
819 $this->error($testDetails);
820 }
821 }
822
627456b5
EM
823 /**
824 * @param $version
825 * @param $testDetails
826 */
971d41b1 827 public function requireMySQLVersion($version, $testDetails) {
6a488035
TO
828 $this->testing($testDetails);
829
830 if (!mysql_get_server_info()) {
2f8082cd 831 $testDetails[2] = ts('Cannot determine the version of MySQL installed. Please ensure at least version %1 is installed.', array(1 => $version));
6a488035
TO
832 $this->warning($testDetails);
833 }
834 else {
835 list($majorRequested, $minorRequested) = explode('.', $version);
836 list($majorHas, $minorHas) = explode('.', mysql_get_server_info());
837
838 if (($majorHas > $majorRequested) || ($majorHas == $majorRequested && $minorHas >= $minorRequested)) {
839 return TRUE;
840 }
841 else {
842 $testDetails[2] .= "{$majorHas}.{$minorHas}.";
843 $this->error($testDetails);
844 }
845 }
846 }
847
627456b5
EM
848 /**
849 * @param $server
100fef9d 850 * @param string $username
627456b5
EM
851 * @param $password
852 * @param $database
853 * @param $testDetails
854 */
971d41b1 855 public function requireMySQLInnoDB($server, $username, $password, $database, $testDetails) {
6a488035
TO
856 $this->testing($testDetails);
857 $conn = @mysql_connect($server, $username, $password);
858 if (!$conn) {
2f8082cd 859 $testDetails[2] .= ' ' . ts("Could not determine if MySQL has InnoDB support. Assuming no.");
6a488035
TO
860 $this->error($testDetails);
861 return;
862 }
863
864 $innodb_support = FALSE;
865 $result = mysql_query("SHOW ENGINES", $conn);
866 while ($values = mysql_fetch_array($result)) {
867 if ($values['Engine'] == 'InnoDB') {
868 if (strtolower($values['Support']) == 'yes' ||
869 strtolower($values['Support']) == 'default'
870 ) {
871 $innodb_support = TRUE;
872 }
873 }
874 }
875 if ($innodb_support) {
2f8082cd 876 $testDetails[3] = ts('MySQL server does have InnoDB support');
6a488035
TO
877 }
878 else {
2f8082cd 879 $testDetails[2] .= ' ' . ts('Could not determine if MySQL has InnoDB support. Assuming no');
6a488035
TO
880 }
881 }
882
627456b5
EM
883 /**
884 * @param $server
100fef9d 885 * @param string $username
627456b5
EM
886 * @param $password
887 * @param $database
888 * @param $testDetails
889 */
971d41b1 890 public function requireMySQLTempTables($server, $username, $password, $database, $testDetails) {
6a488035
TO
891 $this->testing($testDetails);
892 $conn = @mysql_connect($server, $username, $password);
893 if (!$conn) {
2f8082cd 894 $testDetails[2] = ts('Could not login to the database.');
6a488035
TO
895 $this->error($testDetails);
896 return;
897 }
898
899 if (!@mysql_select_db($database, $conn)) {
2f8082cd 900 $testDetails[2] = ts('Could not select the database.');
6a488035
TO
901 $this->error($testDetails);
902 return;
903 }
904
905 $result = mysql_query('CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)', $conn);
906 if (!$result) {
2f8082cd 907 $testDetails[2] = ts('Could not create a temp table.');
6a488035
TO
908 $this->error($testDetails);
909 }
910 $result = mysql_query('DROP TEMPORARY TABLE civicrm_install_temp_table_test');
911 }
912
627456b5
EM
913 /**
914 * @param $server
100fef9d 915 * @param string $username
627456b5
EM
916 * @param $password
917 * @param $database
918 * @param $testDetails
919 */
971d41b1 920 public function requireMySQLTrigger($server, $username, $password, $database, $testDetails) {
6a488035
TO
921 $this->testing($testDetails);
922 $conn = @mysql_connect($server, $username, $password);
923 if (!$conn) {
2f8082cd 924 $testDetails[2] = ts('Could not login to the database.');
6a488035
TO
925 $this->error($testDetails);
926 return;
927 }
928
929 if (!@mysql_select_db($database, $conn)) {
2f8082cd 930 $testDetails[2] = ts('Could not select the database.');
6a488035
TO
931 $this->error($testDetails);
932 return;
933 }
934
935 $result = mysql_query('CREATE TABLE civicrm_install_temp_table_test (test text)', $conn);
936 if (!$result) {
2f8082cd 937 $testDetails[2] = ts('Could not create a table in the database.');
6a488035
TO
938 $this->error($testDetails);
939 }
940
941 $result = mysql_query('CREATE TRIGGER civicrm_install_temp_table_test_trigger BEFORE INSERT ON civicrm_install_temp_table_test FOR EACH ROW BEGIN END');
942 if (!$result) {
943 mysql_query('DROP TABLE civicrm_install_temp_table_test');
2f8082cd 944 $testDetails[2] = ts('Could not create a database trigger.');
6a488035
TO
945 $this->error($testDetails);
946 }
947
6a488035
TO
948 mysql_query('DROP TRIGGER civicrm_install_temp_table_test_trigger');
949 mysql_query('DROP TABLE civicrm_install_temp_table_test');
950 }
951
952
627456b5
EM
953 /**
954 * @param $server
100fef9d 955 * @param string $username
627456b5
EM
956 * @param $password
957 * @param $database
958 * @param $testDetails
959 */
971d41b1 960 public function requireMySQLLockTables($server, $username, $password, $database, $testDetails) {
6a488035
TO
961 $this->testing($testDetails);
962 $conn = @mysql_connect($server, $username, $password);
963 if (!$conn) {
2f8082cd 964 $testDetails[2] = ts('Could not connect to the database server.');
6a488035
TO
965 $this->error($testDetails);
966 return;
967 }
968
969 if (!@mysql_select_db($database, $conn)) {
2f8082cd 970 $testDetails[2] = ts('Could not select the database.');
6a488035
TO
971 $this->error($testDetails);
972 return;
973 }
974
975 $result = mysql_query('CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)', $conn);
976 if (!$result) {
2f8082cd 977 $testDetails[2] = ts('Could not create a table in the database.');
6a488035
TO
978 $this->error($testDetails);
979 return;
980 }
981
982 $result = mysql_query('LOCK TABLES civicrm_install_temp_table_test WRITE', $conn);
983 if (!$result) {
2f8082cd 984 $testDetails[2] = ts('Could not obtain a write lock for the database table.');
6a488035
TO
985 $this->error($testDetails);
986 $result = mysql_query('DROP TEMPORARY TABLE civicrm_install_temp_table_test');
987 return;
988 }
989
990 $result = mysql_query('UNLOCK TABLES', $conn);
991 if (!$result) {
2f8082cd 992 $testDetails[2] = ts('Could not release the lock for the database table.');
6a488035
TO
993 $this->error($testDetails);
994 $result = mysql_query('DROP TEMPORARY TABLE civicrm_install_temp_table_test');
995 return;
996 }
997
998 $result = mysql_query('DROP TEMPORARY TABLE civicrm_install_temp_table_test');
6a488035
TO
999 }
1000
627456b5
EM
1001 /**
1002 * @param $server
100fef9d 1003 * @param string $username
627456b5
EM
1004 * @param $password
1005 * @param $testDetails
1006 */
971d41b1 1007 public function requireMySQLAutoIncrementIncrementOne($server, $username, $password, $testDetails) {
6a488035
TO
1008 $this->testing($testDetails);
1009 $conn = @mysql_connect($server, $username, $password);
1010 if (!$conn) {
2f8082cd 1011 $testDetails[2] = ts('Could not connect to the database server.');
6a488035
TO
1012 $this->error($testDetails);
1013 return;
1014 }
1015
1016 $result = mysql_query("SHOW variables like 'auto_increment_increment'", $conn);
1017 if (!$result) {
2f8082cd 1018 $testDetails[2] = ts('Could not query database server variables.');
6a488035
TO
1019 $this->error($testDetails);
1020 return;
1021 }
1022 else {
1023 $values = mysql_fetch_row($result);
1024 if ($values[1] == 1) {
2f8082cd 1025 $testDetails[3] = ts('MySQL server auto_increment_increment is 1');
6a488035
TO
1026 }
1027 else {
1028 $this->error($testDetails);
1029 }
1030 }
1031 }
1032
627456b5
EM
1033 /**
1034 * @param $server
100fef9d 1035 * @param string $username
627456b5
EM
1036 * @param $password
1037 * @param $database
1038 * @param $minValueKB
1039 * @param $testDetails
1040 */
971d41b1 1041 public function requireMySQLThreadStack($server, $username, $password, $database, $minValueKB, $testDetails) {
6a488035
TO
1042 $this->testing($testDetails);
1043 $conn = @mysql_connect($server, $username, $password);
1044 if (!$conn) {
2f8082cd 1045 $testDetails[2] = ts('Could not connect to the database server.');
6a488035
TO
1046 $this->error($testDetails);
1047 return;
1048 }
1049
1050 if (!@mysql_select_db($database, $conn)) {
2f8082cd 1051 $testDetails[2] = ts('Could not select the database.');
6a488035
TO
1052 $this->error($testDetails);
1053 return;
1054 }
1055
032c9d10 1056 $result = mysql_query("SHOW VARIABLES LIKE 'thread_stack'", $conn); // bytes => kb
6a488035 1057 if (!$result) {
2f8082cd 1058 $testDetails[2] = ts('Could not get information about the thread_stack of the database.');
6a488035 1059 $this->error($testDetails);
0db6c3e1
TO
1060 }
1061 else {
6a488035 1062 $values = mysql_fetch_row($result);
1f5f3294 1063 if ($values[1] < (1024 * $minValueKB)) {
2f8082cd 1064 $testDetails[2] = ts('MySQL "thread_stack" is %1 kb', array(1 => ($values[1] / 1024)));
6a488035
TO
1065 $this->error($testDetails);
1066 }
1067 }
1068 }
1069
627456b5
EM
1070 /**
1071 * @param $server
100fef9d 1072 * @param string $username
627456b5
EM
1073 * @param $password
1074 * @param $database
1075 * @param $testDetails
1076 * @param bool $onlyRequire
1077 */
971d41b1 1078 public function requireDatabaseOrCreatePermissions(
56fdfc52 1079 $server,
6a488035
TO
1080 $username,
1081 $password,
1082 $database,
1083 $testDetails,
1084 $onlyRequire = FALSE
1085 ) {
1086 $this->testing($testDetails);
1087 $conn = @mysql_connect($server, $username, $password);
1088
1089 $okay = NULL;
1090 if (@mysql_select_db($database)) {
1091 $okay = "Database '$database' exists";
1092 }
1093 elseif ($onlyRequire) {
2f8082cd 1094 $testDetails[2] = ts("The database: '%1' does not exist.", array(1 => $database));
6a488035
TO
1095 $this->error($testDetails);
1096 return;
1097 }
1098 else {
1099 if (@mysql_query("CREATE DATABASE $database")) {
2f8082cd 1100 $okay = ts("Able to create a new database.");
6a488035
TO
1101 }
1102 else {
2f8082cd 1103 $testDetails[2] .= " (" . ts("user '%1' doesn't have CREATE DATABASE permissions.", array(1 => $username)) . ")";
6a488035
TO
1104 $this->error($testDetails);
1105 return;
1106 }
1107 }
1108
1109 if ($okay) {
1110 $testDetails[3] = $okay;
1111 $this->testing($testDetails);
1112 }
1113 }
1114
627456b5
EM
1115 /**
1116 * @param $varNames
1117 * @param $errorMessage
1118 */
971d41b1 1119 public function requireServerVariables($varNames, $errorMessage) {
6a488035
TO
1120 //$this->testing($testDetails);
1121 foreach ($varNames as $varName) {
1122 if (!$_SERVER[$varName]) {
1123 $missing[] = '$_SERVER[' . $varName . ']';
1124 }
1125 }
1126 if (!isset($missing)) {
1127 return TRUE;
1128 }
1129 else {
2f8082cd 1130 $testDetails[2] = " (" . ts('the following PHP variables are missing: %1', array(1 => implode(", ", $missing))) . ")";
6a488035
TO
1131 $this->error($testDetails);
1132 }
1133 }
1134
627456b5
EM
1135 /**
1136 * @param $testDetails
1137 *
1138 * @return bool
1139 */
971d41b1 1140 public function isRunningApache($testDetails) {
6a488035
TO
1141 $this->testing($testDetails);
1142 if (function_exists('apache_get_modules') || stristr($_SERVER['SERVER_SIGNATURE'], 'Apache')) {
1143 return TRUE;
1144 }
1145
1146 $this->warning($testDetails);
1147 return FALSE;
1148 }
1149
627456b5
EM
1150 /**
1151 * @return string
1152 */
971d41b1 1153 public function getBaseDir() {
6a488035
TO
1154 return dirname($_SERVER['SCRIPT_FILENAME']) . CIVICRM_DIRECTORY_SEPARATOR;
1155 }
1156
627456b5
EM
1157 /**
1158 * @param $testDetails
1159 */
971d41b1 1160 public function testing($testDetails) {
6a488035
TO
1161 if (!$testDetails) {
1162 return;
1163 }
1164
1165 $section = $testDetails[0];
1166 $test = $testDetails[1];
1167
2f8082cd 1168 $message = ts("OK");
6a488035
TO
1169 if (isset($testDetails[3])) {
1170 $message .= " ($testDetails[3])";
1171 }
1172
1173 $this->tests[$section][$test] = array("good", $message);
1174 }
1175
627456b5
EM
1176 /**
1177 * @param $testDetails
1178 */
971d41b1 1179 public function error($testDetails) {
6a488035
TO
1180 $section = $testDetails[0];
1181 $test = $testDetails[1];
1182
1183 $this->tests[$section][$test] = array("error", $testDetails[2]);
1184 $this->errors[] = $testDetails;
1185 }
1186
627456b5
EM
1187 /**
1188 * @param $testDetails
1189 */
971d41b1 1190 public function warning($testDetails) {
6a488035
TO
1191 $section = $testDetails[0];
1192 $test = $testDetails[1];
1193
6a488035
TO
1194 $this->tests[$section][$test] = array("warning", $testDetails[2]);
1195 $this->warnings[] = $testDetails;
1196 }
1197
627456b5
EM
1198 /**
1199 * @return int
1200 */
971d41b1
CW
1201 public function hasErrors() {
1202 return count($this->errors);
6a488035
TO
1203 }
1204
627456b5
EM
1205 /**
1206 * @return int
1207 */
971d41b1
CW
1208 public function hasWarnings() {
1209 return count($this->warnings);
6a488035 1210 }
96025800 1211
6a488035
TO
1212}
1213
627456b5
EM
1214/**
1215 * Class Installer
1216 */
6a488035 1217class Installer extends InstallRequirements {
627456b5
EM
1218 /**
1219 * @param $server
1220 * @param $username
1221 * @param $password
1222 * @param $database
1223 */
971d41b1 1224 public function createDatabaseIfNotExists($server, $username, $password, $database) {
6a488035
TO
1225 $conn = @mysql_connect($server, $username, $password);
1226
1227 if (@mysql_select_db($database)) {
1228 // skip if database already present
1229 return;
1230 }
1231
56fdfc52
TO
1232 if (@mysql_query("CREATE DATABASE $database")) {
1233 }
6a488035 1234 else {
6a9514e1
ML
1235 $errorTitle = ts("Oops! Could not create database %1", array(1 => $database));
1236 $errorMsg = ts("We encountered an error when attempting to create the database. Please check your MySQL server permissions and the database name and try again.");
6a488035
TO
1237 errorDisplayPage($errorTitle, $errorMsg);
1238 }
1239 }
1240
627456b5
EM
1241 /**
1242 * @param $config
1243 *
1244 * @return mixed
1245 */
971d41b1 1246 public function install($config) {
6a488035
TO
1247 global $installDirPath;
1248
1249 // create database if does not exists
1250 $this->createDatabaseIfNotExists($config['mysql']['server'],
1251 $config['mysql']['username'],
1252 $config['mysql']['password'],
1253 $config['mysql']['database']
1254 );
1255
1256 global $installDirPath;
1257
1258 // Build database
1259 require_once $installDirPath . 'civicrm.php';
1260 civicrm_main($config);
1261
1262 if (!$this->errors) {
1263 global $installType, $installURLPath;
1264
bb216f68 1265 $registerSiteURL = "https://civicrm.org/register-site";
56de7273
ML
1266 $commonOutputMessage
1267 = "<li>" . ts("Have you registered this site at CiviCRM.org? If not, please help strengthen the CiviCRM ecosystem by taking a few minutes to <a %1>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).", array(1 => "href='$registerSiteURL' target='_blank'")) . "</li>"
1268 . "<li>" . ts("We have integrated KCFinder with CKEditor and TinyMCE. This allows a user to upload images. All uploaded images are public.") . "</li>";
41547a19 1269
6a488035 1270 $output = NULL;
97b8e6b2 1271
6a488035
TO
1272 if (
1273 $installType == 'drupal' &&
1274 version_compare(VERSION, '7.0-rc1') >= 0
1275 ) {
1276
1277 // clean output
1278 @ob_clean();
1279
1280 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1281 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1282 $output .= '<head>';
2f8082cd 1283 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
6a9514e1 1284 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
6a488035
TO
1285 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1286 $output .= '</head>';
1287 $output .= '<body>';
2f8082cd 1288 $output .= '<div style="padding: 1em;"><p class="good">' . ts('CiviCRM has been successfully installed') . '</p>';
6a488035 1289 $output .= '<ul>';
6a9514e1 1290
6a488035
TO
1291 $drupalURL = civicrm_cms_base();
1292 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/people/permissions";
1293 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
6a488035 1294
6a9514e1
ML
1295 $output .= "<li>" . ts("Drupal user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(1 => "target='_blank' href='{$drupalPermissionsURL}'", 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'")) . "</li>";
1296 $output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$drupalURL'")) . "</li>";
1297 $output .= $commonOutputMessage;
6a488035
TO
1298
1299 // automatically enable CiviCRM module once it is installed successfully.
1300 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1301 global $cmsPath, $crmPath;
1302
1303 // relative / abosolute paths are not working for drupal, hence using chdir()
1304 chdir($cmsPath);
1305
97b8e6b2 1306 // Force the re-initialisation of the config singleton on the next call
1307 // since so far, we had used the Config object without loading the DB.
1308 $c = CRM_Core_Config::singleton(FALSE);
1309 $c->free();
1310
6a488035
TO
1311 include_once "./includes/bootstrap.inc";
1312 include_once "./includes/unicode.inc";
1313
1314 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
1315
1316 // prevent session information from being saved.
1317 drupal_save_session(FALSE);
1318
1319 // Force the current user to anonymous.
1320 $original_user = $GLOBALS['user'];
1321 $GLOBALS['user'] = drupal_anonymous_user();
1322
1323 // explicitly setting error reporting, since we cannot handle drupal related notices
1324 error_reporting(1);
1325
1326 // rebuild modules, so that civicrm is added
1327 system_rebuild_module_data();
1328
1329 // now enable civicrm module.
1330 module_enable(array('civicrm', 'civicrmtheme'));
1331
d8a4acc0
C
1332 // clear block, page, theme, and hook caches
1333 drupal_flush_all_caches();
6a488035
TO
1334
1335 //add basic drupal permissions
1336 civicrm_install_set_drupal_perms();
1337
1338 // restore the user.
1339 $GLOBALS['user'] = $original_user;
1340 drupal_save_session(TRUE);
1341
e181e386 1342 //change the default language to one chosen
1343 if (isset($config['seedLanguage']) && $config['seedLanguage'] != 'en_US') {
d2860c54
ML
1344 // This ensures that defaults get set, otherwise the user will login
1345 // and most configurations will be empty, not set to en_US defaults.
1346 civicrm_api3('Setting', 'revert');
1347
e181e386 1348 civicrm_api3('Setting', 'create', array(
1349 'domain_id' => 'current_domain',
1350 'lcMessages' => $config['seedLanguage'],
1351 )
1352 );
1353 }
1354
6a488035
TO
1355 $output .= '</ul>';
1356 $output .= '</div>';
1357 $output .= '</body>';
1358 $output .= '</html>';
1359 echo $output;
1360 }
1361 elseif ($installType == 'drupal' && version_compare(VERSION, '6.0') >= 0) {
1362 // clean output
1363 @ob_clean();
1364
1365 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1366 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1367 $output .= '<head>';
6a9514e1
ML
1368 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1369 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
6a488035
TO
1370 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1371 $output .= '</head>';
1372 $output .= '<body>';
97b8e6b2 1373 $output .= '<div style="padding: 1em;"><p class="good">' . ts("CiviCRM has been successfully installed") . '</p>';
6a488035 1374 $output .= '<ul>';
6a9514e1 1375
6a488035
TO
1376 $drupalURL = civicrm_cms_base();
1377 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/user/permissions";
1378 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
6a488035 1379
6a9514e1
ML
1380 $output .= "<li>" . ts("Drupal user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(1 => "target='_blank' href='{$drupalPermissionsURL}'", 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'")) . "</li>";
1381 $output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$drupalURL'")) . "</li>";
1382 $output .= $commonOutputMessage;
6a488035
TO
1383
1384 // explicitly setting error reporting, since we cannot handle drupal related notices
1385 error_reporting(1);
1386
1387 // automatically enable CiviCRM module once it is installed successfully.
1388 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1389 global $cmsPath, $crmPath;
1390
1391 // relative / abosolute paths are not working for drupal, hence using chdir()
1392 chdir($cmsPath);
1393
2f8082cd
ML
1394 // Force the re-initialisation of the config singleton on the next call
1395 // since so far, we had used the Config object without loading the DB.
1396 $c = CRM_Core_Config::singleton(FALSE);
1397 $c->free();
1398
6a488035
TO
1399 include_once "./includes/bootstrap.inc";
1400 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
1401
1402 // rebuild modules, so that civicrm is added
1403 module_rebuild_cache();
1404
1405 // now enable civicrm module.
1406 module_enable(array('civicrm'));
1407
d8a4acc0
C
1408 // clear block, page, theme, and hook caches
1409 drupal_flush_all_caches();
6a488035
TO
1410
1411 //add basic drupal permissions
1412 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)');
1413
1414 echo $output;
1415 }
1416 elseif ($installType == 'wordpress') {
6a9514e1 1417 echo '<h1>' . ts('CiviCRM Installed') . '</h1>';
97b8e6b2 1418 echo '<div style="padding: 1em;"><p style="background-color: #0C0; border: 1px #070 solid; color: white;">' . ts("CiviCRM has been successfully installed") . '</p>';
6a488035 1419 echo '<ul>';
6a488035
TO
1420
1421 $cmsURL = civicrm_cms_base();
1422 $cmsURL .= "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/configtask&reset=1";
41547a19
DL
1423 $wpPermissionsURL = "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/access/wp-permissions&reset=1";
1424
6a9514e1
ML
1425 $output .= "<li>" . ts("WordPress user permissions have been automatically set - giving Anonymous and Subscribers access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(1 => "target='_blank' href='{$wpPermissionsURL}'", 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'")) . "</li>";
1426 $output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$cmsURL'")) . "</li>";
1427 $output .= $commonOutputMessage;
41547a19 1428
1f5f3294
TO
1429 echo '</ul>';
1430 echo '</div>';
b232c132 1431
1432 $c = CRM_Core_Config::singleton(FALSE);
1433 $c->free();
1f5f3294
TO
1434 }
1435 }
6a488035
TO
1436
1437 return $this->errors;
1438 }
96025800 1439
6a488035
TO
1440}
1441
1442function civicrm_install_set_drupal_perms() {
1443 if (!function_exists('db_select')) {
1444 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)');
1445 }
1446 else {
1447 $perms = array(
1448 'access all custom data',
1449 'access uploaded files',
1450 'make online contributions',
1451 'profile create',
1452 'profile edit',
1453 'profile view',
1454 'register for events',
1455 'view event info',
1456 'view event participants',
1457 'access CiviMail subscribe/unsubscribe pages',
1458 );
1459
1460 // Adding a permission that has not yet been assigned to a module by
1461 // a hook_permission implementation results in a database error.
1462 // CRM-9042
1463 $allPerms = array_keys(module_invoke_all('permission'));
1464 foreach (array_diff($perms, $allPerms) as $perm) {
1465 watchdog('civicrm',
1466 'Cannot grant the %perm permission because it does not yet exist.',
971d41b1
CW
1467 array('%perm' => $perm),
1468 WATCHDOG_ERROR
6a488035
TO
1469 );
1470 }
1471 $perms = array_intersect($perms, $allPerms);
1472 user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, $perms);
1473 user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, $perms);
1474 }
1475}
1476
627456b5
EM
1477/**
1478 * @param $cmsPath
1479 * @param $str
1480 *
1481 * @return string
1482 */
6a488035
TO
1483function getSiteDir($cmsPath, $str) {
1484 static $siteDir = '';
1485
1486 if ($siteDir) {
1487 return $siteDir;
1488 }
1489
1490 $sites = CIVICRM_DIRECTORY_SEPARATOR . 'sites' . CIVICRM_DIRECTORY_SEPARATOR;
1491 $modules = CIVICRM_DIRECTORY_SEPARATOR . 'modules' . CIVICRM_DIRECTORY_SEPARATOR;
1492 preg_match("/" . preg_quote($sites, CIVICRM_DIRECTORY_SEPARATOR) .
1493 "([\-a-zA-Z0-9_.]+)" .
1494 preg_quote($modules, CIVICRM_DIRECTORY_SEPARATOR) . "/",
1495 $_SERVER['SCRIPT_FILENAME'], $matches
1496 );
1497 $siteDir = isset($matches[1]) ? $matches[1] : 'default';
1498
1499 if (strtolower($siteDir) == 'all') {
1500 // For this case - use drupal's way of finding out multi-site directory
1501 $uri = explode(CIVICRM_DIRECTORY_SEPARATOR, $_SERVER['SCRIPT_FILENAME']);
1502 $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
1503 for ($i = count($uri) - 1; $i > 0; $i--) {
1504 for ($j = count($server); $j > 0; $j--) {
1505 $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
1506 if (file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
56fdfc52
TO
1507 'sites' . CIVICRM_DIRECTORY_SEPARATOR . $dir
1508 )) {
6a488035
TO
1509 $siteDir = $dir;
1510 return $siteDir;
1511 }
1512 }
1513 }
1514 $siteDir = 'default';
1515 }
1516
1517 return $siteDir;
1518}
1519
627456b5
EM
1520/**
1521 * @param $errorTitle
1522 * @param $errorMsg
6a9514e1 1523 * @param $showRefer
627456b5 1524 */
6a9514e1
ML
1525function errorDisplayPage($errorTitle, $errorMsg, $showRefer = TRUE) {
1526 if ($showRefer) {
1527 $docLink = CRM_Utils_System::docURL2('Installation and Upgrades', FALSE, 'Installation Guide', NULL, NULL, "wiki");
1528
1529 if (function_exists('ts')) {
1530 $errorMsg .= '<p>' . ts("<a %1>Refer to the online documentation for more information</a>", array(1 => "href='$docLink'")) . '</p>';
1531 }
1532 else {
1533 $errorMsg .= '<p>' . sprintf("<a %s>Refer to the online documentation for more information</a>", "href='$docLink'") . '</p>';
1534 }
1535 }
1536
1f5f3294 1537 include 'error.html';
6a488035
TO
1538 exit();
1539}