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