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