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