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