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