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