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