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