Merge pull request #16263 from eileenmcnaughton/ids_3
[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 *
626f739f 39 * Changes and modifications (c) 2007-2017 by CiviCRM LLC
6a488035
TO
40 *
41 */
42
43/**
44 * CiviCRM Installer
45 */
6a488035
TO
46ini_set('max_execution_time', 3000);
47
48if (stristr(PHP_OS, 'WIN')) {
49 define('CIVICRM_DIRECTORY_SEPARATOR', '/');
481a74f4 50 define('CIVICRM_WINDOWS', 1);
6a488035
TO
51}
52else {
53 define('CIVICRM_DIRECTORY_SEPARATOR', DIRECTORY_SEPARATOR);
481a74f4 54 define('CIVICRM_WINDOWS', 0);
6a488035
TO
55}
56
6a488035 57global $installType;
6a488035 58global $crmPath;
97b8e6b2 59global $pkgPath;
6a488035
TO
60global $installDirPath;
61global $installURLPath;
97b8e6b2 62
aa58127b
WM
63// Set the install type
64// this is sent as a query string when the page is first loaded
65// and subsequently posted to the page as a hidden field
e063fc66
SL
66// only permit acceptable installation types to prevent issues;
67$acceptableInstallTypes = ['drupal', 'wordpress', 'backdrop'];
68if (isset($_POST['civicrm_install_type']) && in_array($_POST['civicrm_install_type'], $acceptableInstallTypes)) {
aa58127b
WM
69 $installType = $_POST['civicrm_install_type'];
70}
e063fc66 71elseif (isset($_GET['civicrm_install_type']) && in_array(strtolower($_GET['civicrm_install_type']), $acceptableInstallTypes)) {
aa58127b
WM
72 $installType = strtolower($_GET['civicrm_install_type']);
73}
74else {
e063fc66 75 // default value if not set and not an acceptable install type.
aa58127b
WM
76 $installType = "drupal";
77}
6a9514e1 78
5757adf3 79if ($installType == 'drupal' || $installType == 'backdrop') {
6a488035
TO
80 $crmPath = dirname(dirname($_SERVER['SCRIPT_FILENAME']));
81 $installDirPath = $installURLPath = '';
82}
83elseif ($installType == 'wordpress') {
84 $crmPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR;
85 $installDirPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
6a488035
TO
86 $installURLPath = WP_PLUGIN_URL . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
87}
6a9514e1
ML
88else {
89 $errorTitle = "Oops! Unsupported installation mode";
90 $errorMsg = sprintf('%s: unknown installation mode. Please refer to the online documentation for more information.', $installType);
91 errorDisplayPage($errorTitle, $errorMsg, FALSE);
92}
6a488035 93
1f180621
TO
94$composerJsonPath = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'composer.json';
95if (file_exists($composerJsonPath)) {
96 $composerJson = json_decode(file_get_contents($composerJsonPath), 1);
97 $minPhpVer = preg_replace(';[~^];', '', $composerJson['require']['php']);
98 if (!version_compare(phpversion(), $minPhpVer, '>=')) {
99 errorDisplayPage('PHP Version Requirement', sprintf("CiviCRM requires PHP %s+. The web server is running PHP %s.", $minPhpVer, phpversion()), FALSE);
100 }
101}
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$loadGenerated = 0;
aca6ce6e 109if (isset($_POST['loadGenerated'])) {
6a488035
TO
110 $loadGenerated = 1;
111}
112
113require_once dirname(__FILE__) . CIVICRM_DIRECTORY_SEPARATOR . 'langs.php';
114foreach ($langs as $locale => $_) {
115 if ($locale == 'en_US') {
116 continue;
117 }
4f99ca55
TO
118 if (!file_exists(implode(CIVICRM_DIRECTORY_SEPARATOR, array($crmPath, 'sql', "civicrm_data.$locale.mysql")))) {
119 unset($langs[$locale]);
1f5f3294 120 }
6a488035
TO
121}
122
ece6501c 123// Set the CMS
97b8e6b2 124// This is mostly sympbolic, since nothing we do during the install
125// really requires CIVICRM_UF to be defined.
126$installTypeToUF = array(
127 'wordpress' => 'WordPress',
128 'drupal' => 'Drupal',
5757adf3 129 'backdrop' => 'Backdrop',
97b8e6b2 130);
131
c8ab0a65 132$uf = (isset($installTypeToUF[$installType]) ? $installTypeToUF[$installType] : 'Drupal');
97b8e6b2 133define('CIVICRM_UF', $uf);
134
ece6501c 135// Set the Locale (required by CRM_Core_Config)
97b8e6b2 136global $tsLocale;
137
138$tsLocale = 'en_US';
6a488035 139$seedLanguage = 'en_US';
97b8e6b2 140
5f6b3b5d
ML
141// CRM-16801 This validates that seedLanguage is valid by looking in $langs.
142// NB: the variable is initial a $_REQUEST for the initial page reload,
143// then becomes a $_POST when the installation form is submitted.
144if (isset($_REQUEST['seedLanguage']) and isset($langs[$_REQUEST['seedLanguage']])) {
145 $seedLanguage = $_REQUEST['seedLanguage'];
146 $tsLocale = $_REQUEST['seedLanguage'];
6a488035
TO
147}
148
97b8e6b2 149$config = CRM_Core_Config::singleton(FALSE);
0ad1e2de
ML
150$GLOBALS['civicrm_default_error_scope'] = NULL;
151
97b8e6b2 152// The translation files are in the parent directory (l10n)
97b8e6b2 153$i18n = CRM_Core_I18n::singleton();
154
3a55aa6b
ML
155// Support for Arabic, Hebrew, Farsi, etc.
156// Used in the template.html
157$short_lang_code = CRM_Core_I18n_PseudoConstant::shortForLong($tsLocale);
158$text_direction = (CRM_Core_I18n::isLanguageRTL($tsLocale) ? 'rtl' : 'ltr');
159
6a488035
TO
160global $cmsPath;
161if ($installType == 'drupal') {
162 //CRM-6840 -don't force to install in sites/all/modules/
163 $object = new CRM_Utils_System_Drupal();
164 $cmsPath = $object->cmsRootPath();
165
166 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
167 $alreadyInstalled = file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
168 'sites' . CIVICRM_DIRECTORY_SEPARATOR .
169 $siteDir . CIVICRM_DIRECTORY_SEPARATOR .
170 'civicrm.settings.php'
171 );
172}
5757adf3
HD
173elseif ($installType == 'backdrop') {
174 $object = new CRM_Utils_System_Backdrop();
175 $cmsPath = $object->cmsRootPath();
176 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
683bf891 177 $alreadyInstalled = file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR . 'civicrm.settings.php');
5757adf3 178}
6a488035
TO
179elseif ($installType == 'wordpress') {
180 $cmsPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'civicrm';
7ba2c8ad
KC
181 $upload_dir = wp_upload_dir();
182 $files_dirname = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'civicrm';
895a7985 183 $wp_civi_settings = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm.settings.php';
e7bf24e8
KC
184 $wp_civi_settings_deprectated = CIVICRM_PLUGIN_DIR . 'civicrm.settings.php';
185 if (file_exists($wp_civi_settings_deprectated)) {
186 $alreadyInstalled = $wp_civi_settings_deprectated;
187 }
188 elseif (file_exists($wp_civi_settings)) {
189 $alreadyInstalled = $wp_civi_settings;
190 }
6a488035
TO
191}
192
6a9514e1
ML
193if ($installType == 'drupal') {
194 // Lets check only /modules/.
195 $pattern = '/' . preg_quote(CIVICRM_DIRECTORY_SEPARATOR . 'modules', CIVICRM_DIRECTORY_SEPARATOR) . '/';
196
197 if (!preg_match($pattern, str_replace("\\", "/", $_SERVER['SCRIPT_FILENAME']))) {
198 $directory = implode(CIVICRM_DIRECTORY_SEPARATOR, array('sites', 'all', 'modules'));
199 $errorTitle = ts("Oops! Please correct your install location");
200 $errorMsg = ts("Please untar (uncompress) your downloaded copy of CiviCRM in the <strong>%1</strong> directory below your Drupal root directory.", array(1 => $directory));
201 errorDisplayPage($errorTitle, $errorMsg);
202 }
203}
204
5757adf3
HD
205if ($installType == 'backdrop') {
206 // Lets check only /modules/.
207 $pattern = '/' . preg_quote(CIVICRM_DIRECTORY_SEPARATOR . 'modules', CIVICRM_DIRECTORY_SEPARATOR) . '/';
208
209 if (!preg_match($pattern, str_replace("\\", "/", $_SERVER['SCRIPT_FILENAME']))) {
210 $directory = 'modules';
211 $errorTitle = ts("Oops! Please correct your install location");
212 $errorMsg = ts("Please untar (uncompress) your downloaded copy of CiviCRM in the <strong>%1</strong> directory below your Drupal root directory.", array(1 => $directory));
213 errorDisplayPage($errorTitle, $errorMsg);
214 }
215}
216
6a488035
TO
217// Exit with error if CiviCRM has already been installed.
218if ($alreadyInstalled) {
6a9514e1
ML
219 $errorTitle = ts("Oops! CiviCRM is already installed");
220 $settings_directory = $cmsPath;
6a488035 221
6a9514e1
ML
222 if ($installType == 'drupal') {
223 $settings_directory = implode(CIVICRM_DIRECTORY_SEPARATOR, array(
224 ts('[your Drupal root directory]'),
225 'sites',
226 $siteDir,
227 ));
6a488035 228 }
5757adf3
HD
229 if ($installType == 'backdrop') {
230 $settings_directory = implode(CIVICRM_DIRECTORY_SEPARATOR, array(
231 ts('[your Backdrop root directory]'),
232 $siteDir,
233 ));
234 }
6a9514e1
ML
235
236 $docLink = CRM_Utils_System::docURL2('Installation and Upgrades', FALSE, ts('Installation Guide'), NULL, NULL, "wiki");
ac2b0d29 237 $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>, refer to the online documentation: %2.</li></ul>", array(1 => $settings_directory, 2 => $docLink));
6a9514e1 238 errorDisplayPage($errorTitle, $errorMsg, FALSE);
6a488035
TO
239}
240
241$versionFile = $crmPath . CIVICRM_DIRECTORY_SEPARATOR . 'civicrm-version.php';
242if (file_exists($versionFile)) {
1f5f3294 243 require_once $versionFile;
6a488035
TO
244 $civicrm_version = civicrmVersion();
245}
246else {
247 $civicrm_version = 'unknown';
248}
249
250if ($installType == 'drupal') {
251 // Ensure that they have downloaded the correct version of CiviCRM
6a9514e1
ML
252 if ($civicrm_version['cms'] != 'Drupal' && $civicrm_version['cms'] != 'Drupal6') {
253 $errorTitle = ts("Oops! Incorrect CiviCRM version");
254 $errorMsg = ts("This installer can only be used for the Drupal version of CiviCRM.");
6a488035
TO
255 errorDisplayPage($errorTitle, $errorMsg);
256 }
257
258 define('DRUPAL_ROOT', $cmsPath);
259 $drupalVersionFiles = array(
260 // D6
261 implode(CIVICRM_DIRECTORY_SEPARATOR, array($cmsPath, 'modules', 'system', 'system.module')),
262 // D7
263 implode(CIVICRM_DIRECTORY_SEPARATOR, array($cmsPath, 'includes', 'bootstrap.inc')),
264 );
265 foreach ($drupalVersionFiles as $drupalVersionFile) {
266 if (file_exists($drupalVersionFile)) {
267 require_once $drupalVersionFile;
268 }
269 }
270
bb79149d
WM
271 // Bootstrap Drupal to get settings and user
272 $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
273 $base_root .= '://' . $_SERVER['HTTP_HOST'];
274 $base_url = $base_root;
275 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
276
277 // Check that user is logged in and has administrative permissions
278 // This is necessary because the script exposes the database settings in the form and these could be viewed by unauthorised users
279 if ((!function_exists('user_access')) || (!user_access('administer site configuration'))) {
280 $errorTitle = ts("You don't have permission to access this page");
281 $errorMsg = ts("The installer can only be run by a user with the permission to administer site configuration.");
282 errorDisplayPage($errorTitle, $errorMsg);
283 exit();
284 }
db922230 285
6a488035 286 if (!defined('VERSION') or version_compare(VERSION, '6.0') < 0) {
6a9514e1
ML
287 $errorTitle = ts("Oops! Incorrect Drupal version");
288 $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
289 errorDisplayPage($errorTitle, $errorMsg);
290 }
291}
5757adf3
HD
292elseif ($installType == 'backdrop') {
293 // Ensure that they have downloaded the correct version of CiviCRM
294 if ($civicrm_version['cms'] != 'Backdrop') {
295 $errorTitle = ts("Oops! Incorrect CiviCRM version");
296 $errorMsg = ts("This installer can only be used for the Backdrop version of CiviCRM.");
297 errorDisplayPage($errorTitle, $errorMsg);
298 }
299
300 define('BACKDROP_ROOT', $cmsPath);
301
302 $backdropVersionFiles = array(
303 // Backdrop
304 implode(CIVICRM_DIRECTORY_SEPARATOR, array($cmsPath, 'core', 'includes', 'bootstrap.inc')),
305 );
306 foreach ($backdropVersionFiles as $backdropVersionFile) {
307 if (file_exists($backdropVersionFile)) {
308 require_once $backdropVersionFile;
309 }
310 }
311 if (!defined('BACKDROP_VERSION') or version_compare(BACKDROP_VERSION, '1.0') < 0) {
312 $errorTitle = ts("Oops! Incorrect Backdrop version");
313 $errorMsg = ts("This version of CiviCRM can only be used with Backdrop 1.x. Please ensure that '%1' exists if you are running Backdrop 1.0 and over.", array(1 => implode("' or '", $backdropVersionFiles)));
314 errorDisplayPage($errorTitle, $errorMsg);
315 }
316}
6a488035
TO
317elseif ($installType == 'wordpress') {
318 //HACK for now
319 $civicrm_version['cms'] = 'WordPress';
320
321 // Ensure that they have downloaded the correct version of CiviCRM
322 if ($civicrm_version['cms'] != 'WordPress') {
6a9514e1
ML
323 $errorTitle = ts("Oops! Incorrect CiviCRM version");
324 $errorMsg = ts("This installer can only be used for the WordPress version of CiviCRM.");
6a488035
TO
325 errorDisplayPage($errorTitle, $errorMsg);
326 }
327}
328
9178521b 329// Load CiviCRM database config
09e6ef4a 330if (isset($_POST['mysql'])) {
7f5a1bb1 331 $databaseConfig = $_POST['mysql'];
09e6ef4a 332}
09e6ef4a
WM
333
334if ($installType == 'wordpress') {
7f5a1bb1
WM
335 // Load WP database config
336 if (isset($_POST['mysql'])) {
337 $databaseConfig = $_POST['mysql'];
338 }
339 else {
7862a492 340 $databaseConfig = array(
7f5a1bb1
WM
341 "server" => DB_HOST,
342 "username" => DB_USER,
343 "password" => DB_PASSWORD,
344 "database" => DB_NAME,
7862a492 345 );
7f5a1bb1 346 }
09e6ef4a
WM
347}
348
349if ($installType == 'drupal') {
7f5a1bb1
WM
350 // Load drupal database config
351 if (isset($_POST['drupal'])) {
352 $drupalConfig = $_POST['drupal'];
353 }
354 else {
61ab5109
WM
355 $dbServer = $databases['default']['default']['host'];
356 if (!empty($databases['default']['default']['port'])) {
357 $dbServer .= ':' . $databases['default']['default']['port'];
358 }
7862a492 359 $drupalConfig = array(
61ab5109 360 "server" => $dbServer,
7f5a1bb1
WM
361 "username" => $databases['default']['default']['username'],
362 "password" => $databases['default']['default']['password'],
363 "database" => $databases['default']['default']['database'],
7862a492 364 );
7f5a1bb1 365 }
09e6ef4a
WM
366}
367
368if ($installType == 'backdrop') {
7f5a1bb1
WM
369 // Load backdrop database config
370 if (isset($_POST['backdrop'])) {
371 $backdropConfig = $_POST['backdrop'];
372 }
373 else {
7862a492 374 $backdropConfig = array(
7f5a1bb1
WM
375 "server" => "localhost",
376 "username" => "backdrop",
377 "password" => "",
378 "database" => "backdrop",
7862a492 379 );
7f5a1bb1 380 }
09e6ef4a
WM
381}
382
9178521b
WM
383// By default set CiviCRM database to be same as CMS database
384if (!isset($databaseConfig)) {
385 if (($installType == 'drupal') && (isset($drupalConfig))) {
386 $databaseConfig = $drupalConfig;
387 }
388 if (($installType == 'backdrop') && (isset($backdropConfig))) {
389 $databaseConfig = $backdropConfig;
390 }
391}
392
6a488035
TO
393// Check requirements
394$req = new InstallRequirements();
395$req->check();
396
397if ($req->hasErrors()) {
398 $hasErrorOtherThanDatabase = TRUE;
399}
400
401if ($databaseConfig) {
402 $dbReq = new InstallRequirements();
403 $dbReq->checkdatabase($databaseConfig, 'CiviCRM');
404 if ($installType == 'drupal') {
405 $dbReq->checkdatabase($drupalConfig, 'Drupal');
406 }
5757adf3
HD
407 if ($installType == 'backdrop') {
408 $dbReq->checkdatabase($backdropConfig, 'Backdrop');
409 }
6a488035
TO
410}
411
412// Actual processor
aca6ce6e 413if (isset($_POST['go']) && !$req->hasErrors() && !$dbReq->hasErrors()) {
6a488035 414 // Confirm before reinstalling
aca6ce6e 415 if (!isset($_POST['force_reinstall']) && $alreadyInstalled) {
1f5f3294 416 include $installDirPath . 'template.html';
6a488035
TO
417 }
418 else {
419 $inst = new Installer();
aca6ce6e 420 $inst->install($_POST);
6a488035
TO
421 }
422
423 // Show the config form
424}
425else {
1f5f3294 426 include $installDirPath . 'template.html';
6a488035
TO
427}
428
429/**
430 * This class checks requirements
431 * Each of the requireXXX functions takes an argument which gives a user description of the test. It's an array
432 * of 3 parts:
6a9514e1 433 * $description[0] - The test category
6a488035
TO
434 * $description[1] - The test title
435 * $description[2] - The test error to show, if it goes wrong
436 */
437class InstallRequirements {
683bf891
SL
438 public $errors;
439 public $warnings;
440 public $tests;
441 public $conn;
6a488035
TO
442
443 // @see CRM_Upgrade_Form::MINIMUM_THREAD_STACK
444 const MINIMUM_THREAD_STACK = 192;
445
446 /**
fe482240 447 * Just check that the database configuration is okay.
d7c8cf03
EM
448 * @param $databaseConfig
449 * @param $dbName
6a488035 450 */
971d41b1 451 public function checkdatabase($databaseConfig, $dbName) {
fcf908c6 452 if ($this->requireFunction('mysqli_connect',
56fdfc52 453 array(
97b8e6b2 454 ts("PHP Configuration"),
455 ts("MySQL support"),
456 ts("MySQL support not included in PHP."),
56fdfc52
TO
457 )
458 )
459 ) {
6a488035
TO
460 $this->requireMySQLServer($databaseConfig['server'],
461 array(
2f8082cd
ML
462 ts("MySQL %1 Configuration", array(1 => $dbName)),
463 ts("Does the server exist?"),
464 ts("Can't find the a MySQL server on '%1'.", array(1 => $databaseConfig['server'])),
6a488035
TO
465 $databaseConfig['server'],
466 )
467 );
468 if ($this->requireMysqlConnection($databaseConfig['server'],
56fdfc52
TO
469 $databaseConfig['username'],
470 $databaseConfig['password'],
471 array(
2f8082cd
ML
472 ts("MySQL %1 Configuration", array(1 => $dbName)),
473 ts("Are the access credentials correct?"),
474 ts("That username/password doesn't work"),
56fdfc52
TO
475 )
476 )
477 ) {
6a488035
TO
478 @$this->requireMySQLVersion("5.1",
479 array(
2f8082cd
ML
480 ts("MySQL %1 Configuration", array(1 => $dbName)),
481 ts("MySQL version at least %1", array(1 => '5.1')),
fcf908c6 482 ts("MySQL version %1 or higher is required, you are running MySQL %2.", array(1 => '5.1', 2 => mysqli_get_server_info($this->conn))),
483 ts("MySQL %1", array(1 => mysqli_get_server_info($this->conn))),
6a488035
TO
484 )
485 );
486 $this->requireMySQLAutoIncrementIncrementOne($databaseConfig['server'],
487 $databaseConfig['username'],
488 $databaseConfig['password'],
489 array(
2f8082cd
ML
490 ts("MySQL %1 Configuration", array(1 => $dbName)),
491 ts("Is auto_increment_increment set to 1"),
492 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
493 )
494 );
e5d3a11d
PN
495 $testDetails = array(
496 ts("MySQL %1 Configuration", array(1 => $dbName)),
497 ts("Is the provided database name valid?"),
ee17d64d 498 ts("The database name provided is not valid. Please use only 0-9, a-z, A-Z, _ and - as characters in the name."),
e5d3a11d 499 );
ee17d64d 500 if (!CRM_Core_DAO::requireSafeDBName($databaseConfig['database'])) {
e5d3a11d 501 $this->error($testDetails);
ff3f52f7
PN
502 return FALSE;
503 }
e5d3a11d
PN
504 else {
505 $this->testing($testDetails);
506 }
6a488035
TO
507 $this->requireMySQLThreadStack($databaseConfig['server'],
508 $databaseConfig['username'],
509 $databaseConfig['password'],
510 $databaseConfig['database'],
511 self::MINIMUM_THREAD_STACK,
512 array(
2f8082cd
ML
513 ts("MySQL %1 Configuration", array(1 => $dbName)),
514 ts("Does MySQL thread_stack meet minimum (%1k)", array(1 => self::MINIMUM_THREAD_STACK)),
56fdfc52
TO
515 "",
516 // "The MySQL thread_stack does not meet minimum " . CRM_Upgrade_Form::MINIMUM_THREAD_STACK . "k. Please update thread_stack in my.cnf.",
6a488035
TO
517 )
518 );
519 }
5757adf3 520 $onlyRequire = ($dbName == 'Drupal' || $dbName == 'Backdrop') ? TRUE : FALSE;
6a488035
TO
521 $this->requireDatabaseOrCreatePermissions(
522 $databaseConfig['server'],
523 $databaseConfig['username'],
524 $databaseConfig['password'],
525 $databaseConfig['database'],
526 array(
2f8082cd
ML
527 ts("MySQL %1 Configuration", array(1 => $dbName)),
528 ts("Can I access/create the database?"),
5f40a878 529 ts("I can't create new databases and the database '%1' doesn't exist.", array(1 => $databaseConfig['database'])),
6a488035
TO
530 ),
531 $onlyRequire
532 );
5757adf3 533 if ($dbName != 'Drupal' && $dbName != 'Backdrop') {
1b4a049e
AP
534 $this->requireNoExistingData(
535 $databaseConfig['server'],
536 $databaseConfig['username'],
537 $databaseConfig['password'],
538 $databaseConfig['database'],
539 array(
540 ts("MySQL %1 Configuration", array(1 => $dbName)),
c66b58c9 541 ts("Does the database have data from a previous installation?"),
1b4a049e
AP
542 ts("CiviCRM data from previous installation exists in '%1'.", array(1 => $databaseConfig['database'])),
543 )
544 );
6a488035
TO
545 $this->requireMySQLInnoDB($databaseConfig['server'],
546 $databaseConfig['username'],
547 $databaseConfig['password'],
548 $databaseConfig['database'],
549 array(
2f8082cd
ML
550 ts("MySQL %1 Configuration", array(1 => $dbName)),
551 ts("Can I access/create InnoDB tables in the database?"),
552 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
553 )
554 );
555 $this->requireMySQLTempTables($databaseConfig['server'],
556 $databaseConfig['username'],
557 $databaseConfig['password'],
558 $databaseConfig['database'],
559 array(
2f8082cd
ML
560 ts("MySQL %1 Configuration", array(1 => $dbName)),
561 ts('Can I create temporary tables in the database?'),
562 ts('Unable to create temporary tables. This MySQL user is missing the CREATE TEMPORARY TABLES privilege.'),
6a488035
TO
563 )
564 );
565 $this->requireMySQLLockTables($databaseConfig['server'],
566 $databaseConfig['username'],
567 $databaseConfig['password'],
568 $databaseConfig['database'],
569 array(
2f8082cd
ML
570 ts("MySQL %1 Configuration", array(1 => $dbName)),
571 ts('Can I create lock tables in the database?'),
572 ts('Unable to lock tables. This MySQL user is missing the LOCK TABLES privilege.'),
6a488035
TO
573 )
574 );
575 $this->requireMySQLTrigger($databaseConfig['server'],
576 $databaseConfig['username'],
577 $databaseConfig['password'],
578 $databaseConfig['database'],
579 array(
2f8082cd
ML
580 ts("MySQL %1 Configuration", array(1 => $dbName)),
581 ts('Can I create triggers in the database?'),
582 ts('Unable to create triggers. This MySQL user is missing the CREATE TRIGGERS privilege.'),
6a488035
TO
583 )
584 );
75615982 585 $this->requireMySQLUtf8mb4($databaseConfig['server'],
586 $databaseConfig['username'],
587 $databaseConfig['password'],
588 $databaseConfig['database'],
589 array(
590 ts("MySQL %1 Configuration", array(1 => $dbName)),
591 ts('Is the <code>utf8mb4</code> character set supported?'),
592 ts('This MySQL server does not support the <code>utf8mb4</code> character set.'),
593 )
594 );
6a488035
TO
595 }
596 }
597 }
598
6a511829
TO
599 /**
600 * Connect via mysqli.
601 *
602 * This is exactly the same as mysqli_connect(), except that it accepts
603 * the port as part of the `$host`.
604 *
28d4c718
TO
605 * @param string $host
606 * Ex: 'localhost', 'localhost:3307', '127.0.0.1:3307', '[::1]', '[::1]:3307'.
607 * @param string $username
608 * @param string $password
6a511829
TO
609 * @param string $database
610 * @return \mysqli
611 */
612 protected function connect($host, $username, $password, $database = '') {
613 $hostParts = explode(':', $host);
8e1997f9
TO
614 if (count($hostParts) > 1 && strrpos($host, ']') !== strlen($host) - 1) {
615 $port = array_pop($hostParts);
616 $host = implode(':', $hostParts);
6a511829
TO
617 }
618 else {
5df2c60d 619 $port = NULL;
6a511829
TO
620 }
621 $conn = @mysqli_connect($host, $username, $password, $database, $port);
622 return $conn;
623 }
624
6a488035 625 /**
fe482240 626 * Check everything except the database.
6a488035 627 */
971d41b1 628 public function check() {
6a488035
TO
629 global $crmPath, $installType;
630
631 $this->errors = NULL;
632
cc1f4988 633 $this->requirePHPVersion(array(
2f8082cd 634 ts("PHP Configuration"),
bd17dd0f 635 ts("PHP7 installed"),
56fdfc52 636 ));
6a488035
TO
637
638 // Check that we can identify the root folder successfully
d34d7fa8 639 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR . 'README.md',
6a488035 640 array(
2f8082cd
ML
641 ts("File permissions"),
642 ts("Does the webserver know where files are stored?"),
643 ts("The webserver isn't letting me identify where files are stored."),
6a488035
TO
644 $this->getBaseDir(),
645 ),
646 TRUE
647 );
648
649 // CRM-6485: make sure the path does not contain PATH_SEPARATOR, as we don’t know how to escape it
650 $this->requireNoPathSeparator(
651 array(
2f8082cd
ML
652 ts("File permissions"),
653 ts('Does the CiviCRM path contain PATH_SEPARATOR?'),
654 ts('The path %1 contains PATH_SEPARATOR (the %2 character).', array(1 => $this->getBaseDir(), 2 => PATH_SEPARATOR)),
6a488035
TO
655 $this->getBaseDir(),
656 )
657 );
658
659 $requiredDirectories = array('CRM', 'packages', 'templates', 'js', 'api', 'i', 'sql');
660 foreach ($requiredDirectories as $dir) {
661 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR . $dir,
662 array(
2f8082cd
ML
663 ts("File permissions"),
664 ts("Folder '%1' exists?", array(1 => $dir)),
665 ts("There is no '%1' folder.", array(1 => $dir)),
56fdfc52 666 ), TRUE
6a488035
TO
667 );
668 }
669
670 $configIDSiniDir = NULL;
671 global $cmsPath;
3fdcdfbb 672 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
6a488035 673 if ($installType == 'drupal') {
6a488035
TO
674
675 // make sure that we can write to sites/default and files/
676 $writableDirectories = array(
677 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
678 'sites' . CIVICRM_DIRECTORY_SEPARATOR .
679 $siteDir . CIVICRM_DIRECTORY_SEPARATOR .
680 'files',
681 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
682 'sites' . CIVICRM_DIRECTORY_SEPARATOR .
683 $siteDir,
684 );
685 }
5757adf3
HD
686 elseif ($installType == 'backdrop') {
687
688 // make sure that we can write to sites/default and files/
689 $writableDirectories = array(
690 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
691 'files',
692 $cmsPath,
693 );
694 }
6a488035 695 elseif ($installType == 'wordpress') {
7ba2c8ad
KC
696 // make sure that we can write to uploads/civicrm/
697 $upload_dir = wp_upload_dir();
698 $files_dirname = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'civicrm';
699 if (!file_exists($files_dirname)) {
700 wp_mkdir_p($files_dirname);
701 }
702 $writableDirectories = array($files_dirname);
6a488035
TO
703 }
704
705 foreach ($writableDirectories as $dir) {
706 $dirName = CIVICRM_WINDOWS ? $dir : CIVICRM_DIRECTORY_SEPARATOR . $dir;
2f8082cd
ML
707 $testDetails = array(
708 ts("File permissions"),
709 ts("Is the %1 folder writeable?", array(1 => $dir)),
710 NULL,
6a488035 711 );
2f8082cd 712 $this->requireWriteable($dirName, $testDetails, TRUE);
6a488035
TO
713 }
714
6a488035
TO
715 // Check for rewriting
716 if (isset($_SERVER['SERVER_SOFTWARE'])) {
717 $webserver = strip_tags(trim($_SERVER['SERVER_SOFTWARE']));
718 }
719 elseif (isset($_SERVER['SERVER_SIGNATURE'])) {
720 $webserver = strip_tags(trim($_SERVER['SERVER_SIGNATURE']));
721 }
722
723 if ($webserver == '') {
2f8082cd 724 $webserver = ts("I can't tell what webserver you are running");
6a488035
TO
725 }
726
727 // Check for $_SERVER configuration
56fdfc52 728 $this->requireServerVariables(array('SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME'), array(
2f8082cd
ML
729 ts("Webserver config"),
730 ts("Recognised webserver"),
731 ts("You seem to be using an unsupported webserver. The server variables SCRIPT_NAME, HTTP_HOST, SCRIPT_FILENAME need to be set."),
56fdfc52 732 ));
6a488035
TO
733
734 // Check for MySQL support
fcf908c6 735 $this->requireFunction('mysqli_connect', array(
2f8082cd
ML
736 ts("PHP Configuration"),
737 ts("MySQL support"),
738 ts("MySQL support not included in PHP."),
739 ));
6a488035 740
927a045d
J
741 // Check for XML support
742 $this->requireFunction('simplexml_load_file', array(
743 ts("PHP Configuration"),
8d241549
J
744 ts("SimpleXML support"),
745 ts("SimpleXML support not included in PHP."),
927a045d
J
746 ));
747
6a488035 748 // Check for JSON support
2f8082cd
ML
749 $this->requireFunction('json_encode', array(
750 ts("PHP Configuration"),
751 ts("JSON support"),
752 ts("JSON support not included in PHP."),
753 ));
6a488035 754
cbd60be4
SL
755 // check for Multibyte support such as mb_substr. Required for proper handling of Multilingual setups.
756 $this->requireFunction('mb_substr', array(
757 ts("PHP Configuration"),
758 ts("Multibyte support"),
759 ts("Multibyte support not enabled in PHP."),
760 ));
761
6a488035
TO
762 // Check for xcache_isset and emit warning if exists
763 $this->checkXCache(array(
2f8082cd
ML
764 ts("PHP Configuration"),
765 ts("XCache compatibility"),
766 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 767 ));
6a488035
TO
768
769 // Check memory allocation
770 $this->requireMemory(32 * 1024 * 1024,
771 64 * 1024 * 1024,
772 array(
2f8082cd
ML
773 ts("PHP Configuration"),
774 ts("Memory allocated (PHP config option 'memory_limit')"),
775 ts("CiviCRM needs a minimum of %1 MB allocated to PHP, but recommends %2 MB.", array(1 => 32, 2 => 64)),
6a488035
TO
776 ini_get("memory_limit"),
777 )
778 );
779
780 return $this->errors;
781 }
782
627456b5
EM
783 /**
784 * @param $min
785 * @param $recommended
786 * @param $testDetails
787 */
971d41b1 788 public function requireMemory($min, $recommended, $testDetails) {
6a488035
TO
789 $this->testing($testDetails);
790 $mem = $this->getPHPMemory();
791
792 if ($mem < $min && $mem > 0) {
2f8082cd 793 $testDetails[2] .= " " . ts("You only have %1 allocated", array(1 => ini_get("memory_limit")));
6a488035
TO
794 $this->error($testDetails);
795 }
796 elseif ($mem < $recommended && $mem > 0) {
2f8082cd 797 $testDetails[2] .= " " . ts("You only have %1 allocated", array(1 => ini_get("memory_limit")));
6a488035
TO
798 $this->warning($testDetails);
799 }
800 elseif ($mem == 0) {
2f8082cd 801 $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
802 $this->warning($testDetails);
803 }
804 }
805
627456b5
EM
806 /**
807 * @return float
808 */
971d41b1 809 public function getPHPMemory() {
6a488035
TO
810 $memString = ini_get("memory_limit");
811
812 switch (strtolower(substr($memString, -1))) {
813 case "k":
814 return round(substr($memString, 0, -1) * 1024);
815
816 case "m":
817 return round(substr($memString, 0, -1) * 1024 * 1024);
818
819 case "g":
820 return round(substr($memString, 0, -1) * 1024 * 1024 * 1024);
821
822 default:
823 return round($memString);
824 }
825 }
826
971d41b1 827 public function listErrors() {
6a488035 828 if ($this->errors) {
2f8082cd 829 echo "<p>" . ts("The following problems are preventing me from installing CiviCRM:") . "</p>";
6a488035
TO
830 foreach ($this->errors as $error) {
831 echo "<li>" . htmlentities($error) . "</li>";
832 }
833 }
834 }
835
627456b5
EM
836 /**
837 * @param null $section
838 */
971d41b1 839 public function showTable($section = NULL) {
6a488035
TO
840 if ($section) {
841 $tests = $this->tests[$section];
842 echo "<table class=\"testResults\" width=\"100%\">";
843 foreach ($tests as $test => $result) {
844 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
845 }
846 echo "</table>";
847 }
848 else {
849 foreach ($this->tests as $section => $tests) {
850 echo "<h3>$section</h3>";
851 echo "<table class=\"testResults\" width=\"100%\">";
852
853 foreach ($tests as $test => $result) {
854 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
855 }
856 echo "</table>";
857 }
858 }
859 }
860
627456b5 861 /**
100fef9d 862 * @param string $funcName
627456b5
EM
863 * @param $testDetails
864 *
865 * @return bool
866 */
971d41b1 867 public function requireFunction($funcName, $testDetails) {
6a488035
TO
868 $this->testing($testDetails);
869
870 if (!function_exists($funcName)) {
871 $this->error($testDetails);
872 return FALSE;
873 }
874 else {
875 return TRUE;
876 }
877 }
878
627456b5
EM
879 /**
880 * @param $testDetails
881 */
971d41b1 882 public function checkXCache($testDetails) {
6a488035
TO
883 if (function_exists('xcache_isset') &&
884 ini_get('xcache.size') > 0
885 ) {
886 $this->testing($testDetails);
887 $this->warning($testDetails);
888 }
889 }
890
627456b5 891 /**
cc1f4988
CW
892 * @param array $testDetails
893 * @return bool
627456b5 894 */
cc1f4988 895 public function requirePHPVersion($testDetails) {
6a488035
TO
896
897 $this->testing($testDetails);
898
56fdfc52 899 $phpVersion = phpversion();
cc1f4988 900 $aboveMinVersion = version_compare($phpVersion, CRM_Upgrade_Incremental_General::MIN_INSTALL_PHP_VER) >= 0;
6a488035 901
cc1f4988 902 if ($aboveMinVersion) {
f0c475b0 903 if (version_compare($phpVersion, CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER) < 0) {
cc1f4988 904 $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. The preferred version is %3.', array(
f0c475b0 905 1 => $phpVersion,
ef064e55 906 2 => CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER,
cc1f4988 907 3 => CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER,
ef064e55
CW
908 ));
909 $this->warning($testDetails);
910 }
6a488035
TO
911 return TRUE;
912 }
913
cc1f4988
CW
914 if (empty($testDetails[2])) {
915 $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 => CRM_Upgrade_Incremental_General::MIN_INSTALL_PHP_VER, 2 => $phpVersion));
6a488035
TO
916 }
917
918 $this->error($testDetails);
919 }
920
627456b5 921 /**
100fef9d 922 * @param string $filename
627456b5
EM
923 * @param $testDetails
924 * @param bool $absolute
925 */
971d41b1 926 public function requireFile($filename, $testDetails, $absolute = FALSE) {
6a488035
TO
927 $this->testing($testDetails);
928 if (!$absolute) {
929 $filename = $this->getBaseDir() . $filename;
930 }
931 if (!file_exists($filename)) {
2f8082cd 932 $testDetails[2] .= " (" . ts("file '%1' not found", array(1 => $filename)) . ')';
6a488035
TO
933 $this->error($testDetails);
934 }
935 }
936
627456b5
EM
937 /**
938 * @param $testDetails
939 */
971d41b1 940 public function requireNoPathSeparator($testDetails) {
6a488035
TO
941 $this->testing($testDetails);
942 if (substr_count($this->getBaseDir(), PATH_SEPARATOR)) {
943 $this->error($testDetails);
944 }
945 }
946
627456b5 947 /**
100fef9d 948 * @param string $filename
627456b5
EM
949 * @param $testDetails
950 */
971d41b1 951 public function requireNoFile($filename, $testDetails) {
6a488035
TO
952 $this->testing($testDetails);
953 $filename = $this->getBaseDir() . $filename;
954 if (file_exists($filename)) {
2f8082cd 955 $testDetails[2] .= " (" . ts("file '%1' found", array(1 => $filename)) . ")";
6a488035
TO
956 $this->error($testDetails);
957 }
958 }
959
627456b5 960 /**
100fef9d 961 * @param string $filename
627456b5
EM
962 * @param $testDetails
963 */
971d41b1 964 public function moveFileOutOfTheWay($filename, $testDetails) {
6a488035
TO
965 $this->testing($testDetails);
966 $filename = $this->getBaseDir() . $filename;
967 if (file_exists($filename)) {
968 if (file_exists("$filename.bak")) {
969 rm("$filename.bak");
970 }
971 rename($filename, "$filename.bak");
972 }
973 }
974
627456b5 975 /**
100fef9d 976 * @param string $filename
627456b5
EM
977 * @param $testDetails
978 * @param bool $absolute
979 */
971d41b1 980 public function requireWriteable($filename, $testDetails, $absolute = FALSE) {
6a488035
TO
981 $this->testing($testDetails);
982 if (!$absolute) {
983 $filename = $this->getBaseDir() . $filename;
984 }
985
22e263ad 986 if (!is_writable($filename)) {
6a488035
TO
987 $name = NULL;
988 if (function_exists('posix_getpwuid')) {
989 $user = posix_getpwuid(posix_geteuid());
990 $name = '- ' . $user['name'] . ' -';
991 }
992
993 if (!isset($testDetails[2])) {
994 $testDetails[2] = NULL;
995 }
2f8082cd 996 $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
997 $this->error($testDetails);
998 }
999 }
1000
627456b5 1001 /**
100fef9d 1002 * @param string $moduleName
627456b5
EM
1003 * @param $testDetails
1004 */
971d41b1 1005 public function requireApacheModule($moduleName, $testDetails) {
6a488035
TO
1006 $this->testing($testDetails);
1007 if (!in_array($moduleName, apache_get_modules())) {
1008 $this->error($testDetails);
1009 }
1010 }
1011
627456b5
EM
1012 /**
1013 * @param $server
100fef9d 1014 * @param string $username
627456b5
EM
1015 * @param $password
1016 * @param $testDetails
1017 */
971d41b1 1018 public function requireMysqlConnection($server, $username, $password, $testDetails) {
6a488035 1019 $this->testing($testDetails);
6a511829 1020 $this->conn = $this->connect($server, $username, $password);
6a488035 1021
fcf908c6 1022 if ($this->conn) {
6a488035
TO
1023 return TRUE;
1024 }
1025 else {
fcf908c6 1026 $testDetails[2] .= ": " . mysqli_connect_error();
6a488035
TO
1027 $this->error($testDetails);
1028 }
1029 }
1030
627456b5
EM
1031 /**
1032 * @param $server
1033 * @param $testDetails
1034 */
971d41b1 1035 public function requireMySQLServer($server, $testDetails) {
6a488035 1036 $this->testing($testDetails);
6a511829 1037 $conn = $this->connect($server, NULL, NULL);
6a488035 1038
fcf908c6 1039 if ($conn || mysqli_connect_errno() < 2000) {
6a488035
TO
1040 return TRUE;
1041 }
1042 else {
fcf908c6 1043 $testDetails[2] .= ": " . mysqli_connect_error();
6a488035
TO
1044 $this->error($testDetails);
1045 }
1046 }
1047
627456b5
EM
1048 /**
1049 * @param $version
1050 * @param $testDetails
1051 */
971d41b1 1052 public function requireMySQLVersion($version, $testDetails) {
6a488035
TO
1053 $this->testing($testDetails);
1054
fcf908c6 1055 if (!mysqli_get_server_info($this->conn)) {
2f8082cd 1056 $testDetails[2] = ts('Cannot determine the version of MySQL installed. Please ensure at least version %1 is installed.', array(1 => $version));
6a488035
TO
1057 $this->warning($testDetails);
1058 }
1059 else {
1060 list($majorRequested, $minorRequested) = explode('.', $version);
fcf908c6 1061 list($majorHas, $minorHas) = explode('.', mysqli_get_server_info($this->conn));
6a488035
TO
1062
1063 if (($majorHas > $majorRequested) || ($majorHas == $majorRequested && $minorHas >= $minorRequested)) {
1064 return TRUE;
1065 }
1066 else {
1067 $testDetails[2] .= "{$majorHas}.{$minorHas}.";
1068 $this->error($testDetails);
1069 }
1070 }
1071 }
1072
627456b5
EM
1073 /**
1074 * @param $server
100fef9d 1075 * @param string $username
627456b5
EM
1076 * @param $password
1077 * @param $database
1078 * @param $testDetails
1079 */
971d41b1 1080 public function requireMySQLInnoDB($server, $username, $password, $database, $testDetails) {
6a488035 1081 $this->testing($testDetails);
6a511829 1082 $conn = $this->connect($server, $username, $password);
6a488035 1083 if (!$conn) {
2f8082cd 1084 $testDetails[2] .= ' ' . ts("Could not determine if MySQL has InnoDB support. Assuming no.");
6a488035
TO
1085 $this->error($testDetails);
1086 return;
1087 }
1088
1089 $innodb_support = FALSE;
fcf908c6 1090 $result = mysqli_query($conn, "SHOW ENGINES");
1091 while ($values = mysqli_fetch_array($result)) {
6a488035
TO
1092 if ($values['Engine'] == 'InnoDB') {
1093 if (strtolower($values['Support']) == 'yes' ||
1094 strtolower($values['Support']) == 'default'
1095 ) {
1096 $innodb_support = TRUE;
1097 }
1098 }
1099 }
1100 if ($innodb_support) {
2f8082cd 1101 $testDetails[3] = ts('MySQL server does have InnoDB support');
6a488035
TO
1102 }
1103 else {
2f8082cd 1104 $testDetails[2] .= ' ' . ts('Could not determine if MySQL has InnoDB support. Assuming no');
6a488035
TO
1105 }
1106 }
1107
627456b5
EM
1108 /**
1109 * @param $server
100fef9d 1110 * @param string $username
627456b5
EM
1111 * @param $password
1112 * @param $database
1113 * @param $testDetails
1114 */
971d41b1 1115 public function requireMySQLTempTables($server, $username, $password, $database, $testDetails) {
6a488035 1116 $this->testing($testDetails);
6a511829 1117 $conn = $this->connect($server, $username, $password);
6a488035 1118 if (!$conn) {
2f8082cd 1119 $testDetails[2] = ts('Could not login to the database.');
6a488035
TO
1120 $this->error($testDetails);
1121 return;
1122 }
1123
fcf908c6 1124 if (!@mysqli_select_db($conn, $database)) {
2f8082cd 1125 $testDetails[2] = ts('Could not select the database.');
6a488035
TO
1126 $this->error($testDetails);
1127 return;
1128 }
1129
fcf908c6 1130 $result = mysqli_query($conn, 'CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)');
6a488035 1131 if (!$result) {
2f8082cd 1132 $testDetails[2] = ts('Could not create a temp table.');
6a488035
TO
1133 $this->error($testDetails);
1134 }
fcf908c6 1135 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
6a488035
TO
1136 }
1137
627456b5
EM
1138 /**
1139 * @param $server
100fef9d 1140 * @param string $username
627456b5
EM
1141 * @param $password
1142 * @param $database
1143 * @param $testDetails
1144 */
971d41b1 1145 public function requireMySQLTrigger($server, $username, $password, $database, $testDetails) {
6a488035 1146 $this->testing($testDetails);
6a511829 1147 $conn = $this->connect($server, $username, $password);
6a488035 1148 if (!$conn) {
2f8082cd 1149 $testDetails[2] = ts('Could not login to the database.');
6a488035
TO
1150 $this->error($testDetails);
1151 return;
1152 }
1153
fcf908c6 1154 if (!@mysqli_select_db($conn, $database)) {
2f8082cd 1155 $testDetails[2] = ts('Could not select the database.');
6a488035
TO
1156 $this->error($testDetails);
1157 return;
1158 }
1159
fcf908c6 1160 $result = mysqli_query($conn, 'CREATE TABLE civicrm_install_temp_table_test (test text)');
6a488035 1161 if (!$result) {
2f8082cd 1162 $testDetails[2] = ts('Could not create a table in the database.');
6a488035
TO
1163 $this->error($testDetails);
1164 }
1165
fcf908c6 1166 $result = mysqli_query($conn, 'CREATE TRIGGER civicrm_install_temp_table_test_trigger BEFORE INSERT ON civicrm_install_temp_table_test FOR EACH ROW BEGIN END');
6a488035 1167 if (!$result) {
fcf908c6 1168 mysqli_query($conn, 'DROP TABLE civicrm_install_temp_table_test');
2f8082cd 1169 $testDetails[2] = ts('Could not create a database trigger.');
6a488035
TO
1170 $this->error($testDetails);
1171 }
1172
fcf908c6 1173 mysqli_query($conn, 'DROP TRIGGER civicrm_install_temp_table_test_trigger');
1174 mysqli_query($conn, 'DROP TABLE civicrm_install_temp_table_test');
6a488035
TO
1175 }
1176
627456b5
EM
1177 /**
1178 * @param $server
100fef9d 1179 * @param string $username
627456b5
EM
1180 * @param $password
1181 * @param $database
1182 * @param $testDetails
1183 */
971d41b1 1184 public function requireMySQLLockTables($server, $username, $password, $database, $testDetails) {
6a488035 1185 $this->testing($testDetails);
6a511829 1186 $conn = $this->connect($server, $username, $password);
6a488035 1187 if (!$conn) {
2f8082cd 1188 $testDetails[2] = ts('Could not connect to the database server.');
6a488035
TO
1189 $this->error($testDetails);
1190 return;
1191 }
1192
fcf908c6 1193 if (!@mysqli_select_db($conn, $database)) {
2f8082cd 1194 $testDetails[2] = ts('Could not select the database.');
6a488035
TO
1195 $this->error($testDetails);
1196 return;
1197 }
1198
fcf908c6 1199 $result = mysqli_query($conn, 'CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)');
6a488035 1200 if (!$result) {
2f8082cd 1201 $testDetails[2] = ts('Could not create a table in the database.');
6a488035
TO
1202 $this->error($testDetails);
1203 return;
1204 }
1205
fcf908c6 1206 $result = mysqli_query($conn, 'LOCK TABLES civicrm_install_temp_table_test WRITE');
6a488035 1207 if (!$result) {
2f8082cd 1208 $testDetails[2] = ts('Could not obtain a write lock for the database table.');
6a488035 1209 $this->error($testDetails);
fcf908c6 1210 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
6a488035
TO
1211 return;
1212 }
1213
fcf908c6 1214 $result = mysqli_query($conn, 'UNLOCK TABLES');
6a488035 1215 if (!$result) {
2f8082cd 1216 $testDetails[2] = ts('Could not release the lock for the database table.');
6a488035 1217 $this->error($testDetails);
fcf908c6 1218 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
6a488035
TO
1219 return;
1220 }
1221
fcf908c6 1222 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
6a488035
TO
1223 }
1224
627456b5
EM
1225 /**
1226 * @param $server
100fef9d 1227 * @param string $username
627456b5
EM
1228 * @param $password
1229 * @param $testDetails
1230 */
971d41b1 1231 public function requireMySQLAutoIncrementIncrementOne($server, $username, $password, $testDetails) {
6a488035 1232 $this->testing($testDetails);
6a511829 1233 $conn = $this->connect($server, $username, $password);
6a488035 1234 if (!$conn) {
2f8082cd 1235 $testDetails[2] = ts('Could not connect to the database server.');
6a488035
TO
1236 $this->error($testDetails);
1237 return;
1238 }
1239
fcf908c6 1240 $result = mysqli_query($conn, "SHOW variables like 'auto_increment_increment'");
6a488035 1241 if (!$result) {
2f8082cd 1242 $testDetails[2] = ts('Could not query database server variables.');
6a488035
TO
1243 $this->error($testDetails);
1244 return;
1245 }
1246 else {
fcf908c6 1247 $values = mysqli_fetch_row($result);
6a488035 1248 if ($values[1] == 1) {
2f8082cd 1249 $testDetails[3] = ts('MySQL server auto_increment_increment is 1');
6a488035
TO
1250 }
1251 else {
1252 $this->error($testDetails);
1253 }
1254 }
1255 }
1256
627456b5
EM
1257 /**
1258 * @param $server
100fef9d 1259 * @param string $username
627456b5
EM
1260 * @param $password
1261 * @param $database
1262 * @param $minValueKB
1263 * @param $testDetails
1264 */
971d41b1 1265 public function requireMySQLThreadStack($server, $username, $password, $database, $minValueKB, $testDetails) {
6a488035 1266 $this->testing($testDetails);
6a511829 1267 $conn = $this->connect($server, $username, $password);
6a488035 1268 if (!$conn) {
2f8082cd 1269 $testDetails[2] = ts('Could not connect to the database server.');
6a488035
TO
1270 $this->error($testDetails);
1271 return;
1272 }
1273
fcf908c6 1274 if (!@mysqli_select_db($conn, $database)) {
2f8082cd 1275 $testDetails[2] = ts('Could not select the database.');
6a488035
TO
1276 $this->error($testDetails);
1277 return;
1278 }
1279
683bf891
SL
1280 // bytes => kb
1281 $result = mysqli_query($conn, "SHOW VARIABLES LIKE 'thread_stack'");
6a488035 1282 if (!$result) {
2f8082cd 1283 $testDetails[2] = ts('Could not get information about the thread_stack of the database.');
6a488035 1284 $this->error($testDetails);
0db6c3e1
TO
1285 }
1286 else {
fcf908c6 1287 $values = mysqli_fetch_row($result);
1f5f3294 1288 if ($values[1] < (1024 * $minValueKB)) {
2f8082cd 1289 $testDetails[2] = ts('MySQL "thread_stack" is %1 kb', array(1 => ($values[1] / 1024)));
6a488035
TO
1290 $this->error($testDetails);
1291 }
1292 }
1293 }
1294
1b4a049e
AP
1295 /**
1296 * @param $server
1297 * @param $username
1298 * @param $password
1299 * @param $database
1300 * @param $testDetails
1301 */
1302 public function requireNoExistingData(
1303 $server,
1304 $username,
1305 $password,
1306 $database,
1307 $testDetails
1308 ) {
1309 $this->testing($testDetails);
1310 $conn = $this->connect($server, $username, $password);
1311
1312 @mysqli_select_db($conn, $database);
1313 $contactRecords = mysqli_query($conn, "SELECT count(*) as contactscount FROM civicrm_contact");
1314 if ($contactRecords) {
1315 $contactRecords = mysqli_fetch_object($contactRecords);
1316 if ($contactRecords->contactscount > 0) {
1317 $this->error($testDetails);
1318 return;
1319 }
1320 }
1321
c66b58c9 1322 $testDetails[3] = ts('CiviCRM data from previous installation does not exist in %1.', array(1 => $database));
1b4a049e
AP
1323 $this->testing($testDetails);
1324 }
1325
627456b5
EM
1326 /**
1327 * @param $server
100fef9d 1328 * @param string $username
627456b5
EM
1329 * @param $password
1330 * @param $database
1331 * @param $testDetails
1332 * @param bool $onlyRequire
1333 */
971d41b1 1334 public function requireDatabaseOrCreatePermissions(
56fdfc52 1335 $server,
6a488035
TO
1336 $username,
1337 $password,
1338 $database,
1339 $testDetails,
1340 $onlyRequire = FALSE
1341 ) {
1342 $this->testing($testDetails);
6a511829 1343 $conn = $this->connect($server, $username, $password);
6a488035
TO
1344
1345 $okay = NULL;
fcf908c6 1346 if (@mysqli_select_db($conn, $database)) {
6a488035
TO
1347 $okay = "Database '$database' exists";
1348 }
1349 elseif ($onlyRequire) {
2f8082cd 1350 $testDetails[2] = ts("The database: '%1' does not exist.", array(1 => $database));
6a488035
TO
1351 $this->error($testDetails);
1352 return;
1353 }
1354 else {
fcf908c6 1355 $query = sprintf("CREATE DATABASE %s", mysqli_real_escape_string($conn, $database));
1356 if (@mysqli_query($conn, $query)) {
2f8082cd 1357 $okay = ts("Able to create a new database.");
6a488035
TO
1358 }
1359 else {
2f8082cd 1360 $testDetails[2] .= " (" . ts("user '%1' doesn't have CREATE DATABASE permissions.", array(1 => $username)) . ")";
6a488035
TO
1361 $this->error($testDetails);
1362 return;
1363 }
1364 }
1365
1366 if ($okay) {
1367 $testDetails[3] = $okay;
1368 $this->testing($testDetails);
1369 }
1370 }
1371
627456b5
EM
1372 /**
1373 * @param $varNames
1374 * @param $errorMessage
1375 */
971d41b1 1376 public function requireServerVariables($varNames, $errorMessage) {
6a488035
TO
1377 //$this->testing($testDetails);
1378 foreach ($varNames as $varName) {
1379 if (!$_SERVER[$varName]) {
1380 $missing[] = '$_SERVER[' . $varName . ']';
1381 }
1382 }
1383 if (!isset($missing)) {
1384 return TRUE;
1385 }
1386 else {
2f8082cd 1387 $testDetails[2] = " (" . ts('the following PHP variables are missing: %1', array(1 => implode(", ", $missing))) . ")";
6a488035
TO
1388 $this->error($testDetails);
1389 }
1390 }
1391
75615982 1392 /**
1393 * @param $server
1394 * @param string $username
1395 * @param $password
1396 * @param $database
1397 * @param $testDetails
1398 */
1399 public function requireMysqlUtf8mb4($server, $username, $password, $database, $testDetails) {
1400 $this->testing($testDetails);
1401 $conn = $this->connect($server, $username, $password);
1402 if (!$conn) {
1403 $testDetails[2] = ts('Could not connect to the database server.');
1404 $this->error($testDetails);
1405 return;
1406 }
1407
1408 if (!@mysqli_select_db($conn, $database)) {
1409 $testDetails[2] = ts('Could not select the database.');
1410 $this->error($testDetails);
1411 return;
1412 }
1413
1414 $result = mysqli_query($conn, 'CREATE TABLE civicrm_utf8mb4_test (id VARCHAR(255), PRIMARY KEY(id(255))) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC ENGINE=INNODB');
1415 if (!$result) {
1416 $testDetails[2] = ts('It is recommended, though not yet required, to configure your MySQL server for utf8mb4 support. You will need the following MySQL server configuration: innodb_large_prefix=true innodb_file_format=barracuda innodb_file_per_table=true');
1417 $this->warning($testDetails);
1418 return;
1419 }
1420 $result = mysqli_query($conn, 'DROP TABLE civicrm_utf8mb4_test');
1421
1422 // Ensure that the MySQL driver supports utf8mb4 encoding.
e479ac61 1423 $version = mysqli_get_client_info();
75615982 1424 if (strpos($version, 'mysqlnd') !== FALSE) {
1425 // The mysqlnd driver supports utf8mb4 starting at version 5.0.9.
1426 $version = preg_replace('/^\D+([\d.]+).*/', '$1', $version);
1427 if (version_compare($version, '5.0.9', '<')) {
1428 $testDetails[2] = 'It is recommended, though not yet required, to upgrade your PHP MySQL driver (mysqlnd) to >= 5.0.9 for utf8mb4 support.';
1429 $this->warning($testDetails);
1430 return;
1431 }
1432 }
1433 else {
1434 // The libmysqlclient driver supports utf8mb4 starting at version 5.5.3.
1435 if (version_compare($version, '5.5.3', '<')) {
1436 $testDetails[2] = 'It is recommended, though not yet required, to upgrade your PHP MySQL driver (libmysqlclient) to >= 5.5.3 for utf8mb4 support.';
1437 $this->warning($testDetails);
1438 return;
1439 }
1440 }
1441 }
1442
627456b5
EM
1443 /**
1444 * @param $testDetails
1445 *
1446 * @return bool
1447 */
971d41b1 1448 public function isRunningApache($testDetails) {
6a488035
TO
1449 $this->testing($testDetails);
1450 if (function_exists('apache_get_modules') || stristr($_SERVER['SERVER_SIGNATURE'], 'Apache')) {
1451 return TRUE;
1452 }
1453
1454 $this->warning($testDetails);
1455 return FALSE;
1456 }
1457
627456b5
EM
1458 /**
1459 * @return string
1460 */
971d41b1 1461 public function getBaseDir() {
6a488035
TO
1462 return dirname($_SERVER['SCRIPT_FILENAME']) . CIVICRM_DIRECTORY_SEPARATOR;
1463 }
1464
627456b5
EM
1465 /**
1466 * @param $testDetails
1467 */
971d41b1 1468 public function testing($testDetails) {
6a488035
TO
1469 if (!$testDetails) {
1470 return;
1471 }
1472
1473 $section = $testDetails[0];
1474 $test = $testDetails[1];
1475
2f8082cd 1476 $message = ts("OK");
6a488035
TO
1477 if (isset($testDetails[3])) {
1478 $message .= " ($testDetails[3])";
1479 }
1480
1481 $this->tests[$section][$test] = array("good", $message);
1482 }
1483
627456b5
EM
1484 /**
1485 * @param $testDetails
1486 */
971d41b1 1487 public function error($testDetails) {
6a488035
TO
1488 $section = $testDetails[0];
1489 $test = $testDetails[1];
1490
1491 $this->tests[$section][$test] = array("error", $testDetails[2]);
1492 $this->errors[] = $testDetails;
1493 }
1494
627456b5
EM
1495 /**
1496 * @param $testDetails
1497 */
971d41b1 1498 public function warning($testDetails) {
6a488035
TO
1499 $section = $testDetails[0];
1500 $test = $testDetails[1];
1501
6a488035
TO
1502 $this->tests[$section][$test] = array("warning", $testDetails[2]);
1503 $this->warnings[] = $testDetails;
1504 }
1505
627456b5
EM
1506 /**
1507 * @return int
1508 */
971d41b1 1509 public function hasErrors() {
f0879544 1510 return !empty($this->errors);
6a488035
TO
1511 }
1512
627456b5
EM
1513 /**
1514 * @return int
1515 */
971d41b1 1516 public function hasWarnings() {
f0879544 1517 return !empty($this->warnings);
6a488035 1518 }
96025800 1519
6a488035
TO
1520}
1521
627456b5
EM
1522/**
1523 * Class Installer
1524 */
6a488035 1525class Installer extends InstallRequirements {
683bf891 1526
627456b5
EM
1527 /**
1528 * @param $server
1529 * @param $username
1530 * @param $password
1531 * @param $database
1532 */
971d41b1 1533 public function createDatabaseIfNotExists($server, $username, $password, $database) {
6a511829 1534 $conn = $this->connect($server, $username, $password);
6a488035 1535
fcf908c6 1536 if (@mysqli_select_db($conn, $database)) {
6a488035
TO
1537 // skip if database already present
1538 return;
1539 }
fcf908c6 1540 $query = sprintf("CREATE DATABASE %s", mysqli_real_escape_string($conn, $database));
1541 if (@mysqli_query($conn, $query)) {
56fdfc52 1542 }
6a488035 1543 else {
6a9514e1
ML
1544 $errorTitle = ts("Oops! Could not create database %1", array(1 => $database));
1545 $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
1546 errorDisplayPage($errorTitle, $errorMsg);
1547 }
1548 }
1549
627456b5
EM
1550 /**
1551 * @param $config
1552 *
1553 * @return mixed
1554 */
971d41b1 1555 public function install($config) {
6a488035
TO
1556 global $installDirPath;
1557
1558 // create database if does not exists
1559 $this->createDatabaseIfNotExists($config['mysql']['server'],
1560 $config['mysql']['username'],
1561 $config['mysql']['password'],
1562 $config['mysql']['database']
1563 );
1564
1565 global $installDirPath;
1566
1567 // Build database
1568 require_once $installDirPath . 'civicrm.php';
1569 civicrm_main($config);
1570
1571 if (!$this->errors) {
1572 global $installType, $installURLPath;
1573
bb216f68 1574 $registerSiteURL = "https://civicrm.org/register-site";
56de7273
ML
1575 $commonOutputMessage
1576 = "<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>"
1577 . "<li>" . ts("We have integrated KCFinder with CKEditor and TinyMCE. This allows a user to upload images. All uploaded images are public.") . "</li>";
41547a19 1578
6a488035 1579 $output = NULL;
97b8e6b2 1580
6a488035
TO
1581 if (
1582 $installType == 'drupal' &&
1583 version_compare(VERSION, '7.0-rc1') >= 0
1584 ) {
1585
1586 // clean output
1587 @ob_clean();
1588
1589 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1590 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1591 $output .= '<head>';
2f8082cd 1592 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
6a9514e1 1593 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
6a488035
TO
1594 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1595 $output .= '</head>';
1596 $output .= '<body>';
2f8082cd 1597 $output .= '<div style="padding: 1em;"><p class="good">' . ts('CiviCRM has been successfully installed') . '</p>';
6a488035 1598 $output .= '<ul>';
6a9514e1 1599
6a488035
TO
1600 $drupalURL = civicrm_cms_base();
1601 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/people/permissions";
1602 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
6a488035 1603
6a9514e1
ML
1604 $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>";
1605 $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>";
1606 $output .= $commonOutputMessage;
6a488035
TO
1607
1608 // automatically enable CiviCRM module once it is installed successfully.
1609 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1610 global $cmsPath, $crmPath;
1611
1612 // relative / abosolute paths are not working for drupal, hence using chdir()
1613 chdir($cmsPath);
1614
97b8e6b2 1615 // Force the re-initialisation of the config singleton on the next call
1616 // since so far, we had used the Config object without loading the DB.
1617 $c = CRM_Core_Config::singleton(FALSE);
1618 $c->free();
1619
6a488035
TO
1620 include_once "./includes/bootstrap.inc";
1621 include_once "./includes/unicode.inc";
1622
1623 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
1624
1625 // prevent session information from being saved.
1626 drupal_save_session(FALSE);
1627
1628 // Force the current user to anonymous.
1629 $original_user = $GLOBALS['user'];
1630 $GLOBALS['user'] = drupal_anonymous_user();
1631
1632 // explicitly setting error reporting, since we cannot handle drupal related notices
1633 error_reporting(1);
1634
1635 // rebuild modules, so that civicrm is added
1636 system_rebuild_module_data();
1637
1638 // now enable civicrm module.
1639 module_enable(array('civicrm', 'civicrmtheme'));
1640
ece6501c
ML
1641 // SystemInstallEvent will be called from here with the first call of CRM_Core_Config,
1642 // which calls Core_BAO_ConfigSetting::applyLocale(), who will default to calling
1643 // Civi::settings()->get('lcMessages');
1644 // Therefore, we need to pass the seedLanguage before that.
1645 global $civicrm_setting;
1646 $civicrm_setting['domain']['lcMessages'] = $config['seedLanguage'];
1647
d8a4acc0
C
1648 // clear block, page, theme, and hook caches
1649 drupal_flush_all_caches();
6a488035
TO
1650
1651 //add basic drupal permissions
1652 civicrm_install_set_drupal_perms();
1653
1654 // restore the user.
1655 $GLOBALS['user'] = $original_user;
1656 drupal_save_session(TRUE);
1657
1658 $output .= '</ul>';
1659 $output .= '</div>';
1660 $output .= '</body>';
1661 $output .= '</html>';
1662 echo $output;
1663 }
5757adf3
HD
1664 elseif (
1665 $installType == 'backdrop'
1666 ) {
1667
1668 // clean output
1669 @ob_clean();
1670
1671 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1672 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1673 $output .= '<head>';
1674 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1675 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
1676 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1677 $output .= '</head>';
1678 $output .= '<body>';
1679 $output .= '<div style="padding: 1em;"><p class="good">' . ts('CiviCRM has been successfully installed') . '</p>';
1680 $output .= '<ul>';
1681
1682 $backdropURL = civicrm_cms_base();
1683 $backdropPermissionsURL = "{$backdropURL}index.php?q=admin/config/people/permissions";
1684 $backdropURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1685
1686 $output .= "<li>" . ts("Backdrop 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='{$backdropPermissionsURL}'", 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'")) . "</li>";
1687 $output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$backdropURL'")) . "</li>";
1688 $output .= $commonOutputMessage;
1689
1690 // automatically enable CiviCRM module once it is installed successfully.
1691 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1692 global $cmsPath, $crmPath;
1693
1694 // relative / abosolute paths are not working for drupal, hence using chdir()
1695 chdir($cmsPath);
1696
1697 // Force the re-initialisation of the config singleton on the next call
1698 // since so far, we had used the Config object without loading the DB.
1699 $c = CRM_Core_Config::singleton(FALSE);
1700 $c->free();
1701
1702 include_once "./core/includes/bootstrap.inc";
1703 include_once "./core/includes/unicode.inc";
228391b3 1704 include_once "./core/includes/config.inc";
5757adf3
HD
1705
1706 backdrop_bootstrap(BACKDROP_BOOTSTRAP_FULL);
1707
1708 // prevent session information from being saved.
1709 backdrop_save_session(FALSE);
1710
1711 // Force the current user to anonymous.
1712 $original_user = $GLOBALS['user'];
1713 $GLOBALS['user'] = backdrop_anonymous_user();
1714
1715 // explicitly setting error reporting, since we cannot handle drupal related notices
1716 error_reporting(1);
1717
1718 // rebuild modules, so that civicrm is added
1719 system_rebuild_module_data();
1720
1721 // now enable civicrm module.
1722 module_enable(array('civicrm', 'civicrmtheme'));
1723
1724 // clear block, page, theme, and hook caches
1725 backdrop_flush_all_caches();
1726
1727 //add basic backdrop permissions
1728 civicrm_install_set_backdrop_perms();
1729
1730 // restore the user.
1731 $GLOBALS['user'] = $original_user;
1732 backdrop_save_session(TRUE);
1733
1734 //change the default language to one chosen
1735 if (isset($config['seedLanguage']) && $config['seedLanguage'] != 'en_US') {
1736 civicrm_api3('Setting', 'create', array(
683bf891
SL
1737 'domain_id' => 'current_domain',
1738 'lcMessages' => $config['seedLanguage'],
1739 ));
5757adf3
HD
1740 }
1741
1742 $output .= '</ul>';
1743 $output .= '</div>';
1744 $output .= '</body>';
1745 $output .= '</html>';
1746 echo $output;
1747 }
6a488035
TO
1748 elseif ($installType == 'drupal' && version_compare(VERSION, '6.0') >= 0) {
1749 // clean output
1750 @ob_clean();
1751
1752 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1753 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1754 $output .= '<head>';
6a9514e1
ML
1755 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1756 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
6a488035
TO
1757 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1758 $output .= '</head>';
1759 $output .= '<body>';
97b8e6b2 1760 $output .= '<div style="padding: 1em;"><p class="good">' . ts("CiviCRM has been successfully installed") . '</p>';
6a488035 1761 $output .= '<ul>';
6a9514e1 1762
6a488035
TO
1763 $drupalURL = civicrm_cms_base();
1764 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/user/permissions";
1765 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
6a488035 1766
6a9514e1
ML
1767 $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>";
1768 $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>";
1769 $output .= $commonOutputMessage;
6a488035
TO
1770
1771 // explicitly setting error reporting, since we cannot handle drupal related notices
1772 error_reporting(1);
1773
1774 // automatically enable CiviCRM module once it is installed successfully.
1775 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1776 global $cmsPath, $crmPath;
1777
1778 // relative / abosolute paths are not working for drupal, hence using chdir()
1779 chdir($cmsPath);
1780
2f8082cd
ML
1781 // Force the re-initialisation of the config singleton on the next call
1782 // since so far, we had used the Config object without loading the DB.
1783 $c = CRM_Core_Config::singleton(FALSE);
1784 $c->free();
1785
6a488035
TO
1786 include_once "./includes/bootstrap.inc";
1787 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
1788
1789 // rebuild modules, so that civicrm is added
1790 module_rebuild_cache();
1791
1792 // now enable civicrm module.
1793 module_enable(array('civicrm'));
1794
d8a4acc0
C
1795 // clear block, page, theme, and hook caches
1796 drupal_flush_all_caches();
6a488035
TO
1797
1798 //add basic drupal permissions
1799 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)');
1800
1801 echo $output;
1802 }
1803 elseif ($installType == 'wordpress') {
6a9514e1 1804 echo '<h1>' . ts('CiviCRM Installed') . '</h1>';
97b8e6b2 1805 echo '<div style="padding: 1em;"><p style="background-color: #0C0; border: 1px #070 solid; color: white;">' . ts("CiviCRM has been successfully installed") . '</p>';
6a488035 1806 echo '<ul>';
6a488035
TO
1807
1808 $cmsURL = civicrm_cms_base();
1809 $cmsURL .= "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/configtask&reset=1";
41547a19
DL
1810 $wpPermissionsURL = "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/access/wp-permissions&reset=1";
1811
6a9514e1
ML
1812 $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>";
1813 $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>";
1814 $output .= $commonOutputMessage;
41547a19 1815
138a7961
KC
1816 $output .= '</ul>';
1817 $output .= '</div>';
1818 echo $output;
b232c132 1819
1820 $c = CRM_Core_Config::singleton(FALSE);
1821 $c->free();
138a7961 1822 $wpInstallRedirect = admin_url('admin.php?page=CiviCRM&q=civicrm&reset=1');
7ba2c8ad
KC
1823 echo "<script>
1824 window.location = '$wpInstallRedirect';
1825 </script>";
1f5f3294
TO
1826 }
1827 }
6a488035
TO
1828
1829 return $this->errors;
1830 }
96025800 1831
6a488035
TO
1832}
1833
1834function civicrm_install_set_drupal_perms() {
1835 if (!function_exists('db_select')) {
1836 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)');
1837 }
1838 else {
1839 $perms = array(
1840 'access all custom data',
1841 'access uploaded files',
1842 'make online contributions',
1843 'profile create',
1844 'profile edit',
1845 'profile view',
1846 'register for events',
1847 'view event info',
1848 'view event participants',
1849 'access CiviMail subscribe/unsubscribe pages',
1850 );
1851
1852 // Adding a permission that has not yet been assigned to a module by
1853 // a hook_permission implementation results in a database error.
1854 // CRM-9042
1855 $allPerms = array_keys(module_invoke_all('permission'));
1856 foreach (array_diff($perms, $allPerms) as $perm) {
1857 watchdog('civicrm',
1858 'Cannot grant the %perm permission because it does not yet exist.',
971d41b1
CW
1859 array('%perm' => $perm),
1860 WATCHDOG_ERROR
6a488035
TO
1861 );
1862 }
1863 $perms = array_intersect($perms, $allPerms);
1864 user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, $perms);
1865 user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, $perms);
1866 }
1867}
1868
5757adf3
HD
1869function civicrm_install_set_backdrop_perms() {
1870 $perms = array(
1871 'access all custom data',
1872 'access uploaded files',
1873 'make online contributions',
1874 'profile create',
1875 'profile edit',
1876 'profile view',
1877 'register for events',
1878 'view event info',
1879 'view event participants',
1880 'access CiviMail subscribe/unsubscribe pages',
1881 );
1882
1883 // Adding a permission that has not yet been assigned to a module by
1884 // a hook_permission implementation results in a database error.
1885 // CRM-9042
1886 $allPerms = array_keys(module_invoke_all('permission'));
1887 foreach (array_diff($perms, $allPerms) as $perm) {
1888 watchdog('civicrm',
1889 'Cannot grant the %perm permission because it does not yet exist.',
1890 array('%perm' => $perm),
1891 WATCHDOG_ERROR
1892 );
1893 }
1894 $perms = array_intersect($perms, $allPerms);
1895 user_role_grant_permissions(BACKDROP_AUTHENTICATED_ROLE, $perms);
1896 user_role_grant_permissions(BACKDROP_ANONYMOUS_ROLE, $perms);
1897}
1898
627456b5
EM
1899/**
1900 * @param $cmsPath
1901 * @param $str
1902 *
1903 * @return string
1904 */
6a488035
TO
1905function getSiteDir($cmsPath, $str) {
1906 static $siteDir = '';
1907
1908 if ($siteDir) {
1909 return $siteDir;
1910 }
1911
1912 $sites = CIVICRM_DIRECTORY_SEPARATOR . 'sites' . CIVICRM_DIRECTORY_SEPARATOR;
1913 $modules = CIVICRM_DIRECTORY_SEPARATOR . 'modules' . CIVICRM_DIRECTORY_SEPARATOR;
1914 preg_match("/" . preg_quote($sites, CIVICRM_DIRECTORY_SEPARATOR) .
1915 "([\-a-zA-Z0-9_.]+)" .
1916 preg_quote($modules, CIVICRM_DIRECTORY_SEPARATOR) . "/",
1917 $_SERVER['SCRIPT_FILENAME'], $matches
1918 );
1919 $siteDir = isset($matches[1]) ? $matches[1] : 'default';
1920
1921 if (strtolower($siteDir) == 'all') {
1922 // For this case - use drupal's way of finding out multi-site directory
1923 $uri = explode(CIVICRM_DIRECTORY_SEPARATOR, $_SERVER['SCRIPT_FILENAME']);
1924 $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
1925 for ($i = count($uri) - 1; $i > 0; $i--) {
1926 for ($j = count($server); $j > 0; $j--) {
1927 $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
1928 if (file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
56fdfc52
TO
1929 'sites' . CIVICRM_DIRECTORY_SEPARATOR . $dir
1930 )) {
6a488035
TO
1931 $siteDir = $dir;
1932 return $siteDir;
1933 }
1934 }
1935 }
1936 $siteDir = 'default';
1937 }
1938
1939 return $siteDir;
1940}
1941
627456b5
EM
1942/**
1943 * @param $errorTitle
1944 * @param $errorMsg
6a9514e1 1945 * @param $showRefer
627456b5 1946 */
6a9514e1 1947function errorDisplayPage($errorTitle, $errorMsg, $showRefer = TRUE) {
e0b29e26
WM
1948
1949 // Add a link to the documentation
6a9514e1 1950 if ($showRefer) {
e0b29e26
WM
1951 if (is_callable(array('CRM_Utils_System', 'docURL2'))) {
1952 $docLink = CRM_Utils_System::docURL2('Installation and Upgrades', FALSE, 'Installation Guide', NULL, NULL, "wiki");
1953 }
2c99dd75
WM
1954 else {
1955 $docLink = '';
1956 }
e0b29e26
WM
1957
1958 if (function_exists('ts')) {
1959 $errorMsg .= '<p>' . ts("Refer to the online documentation for more information: ") . $docLink . '</p>';
1960 }
1961 else {
1962 $errorMsg .= '<p>' . 'Refer to the online documentation for more information: ' . $docLink . '</p>';
1963 }
6a9514e1
ML
1964 }
1965
1f5f3294 1966 include 'error.html';
6a488035
TO
1967 exit();
1968}