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