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