Merge pull request #11396 from mukeshcompucorp/fix-page-structure
[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 // See also: CRM_Upgrade_Incremental_General::MIN_INSTALL_PHP_VER
599 $this->requirePHPVersion('5.3.4', array(
600 ts("PHP Configuration"),
601 ts("PHP5 installed"),
602 NULL,
603 ts("PHP version %1", array(1 => phpversion())),
604 ));
605
606 // Check that we can identify the root folder successfully
607 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR . 'README.md',
608 array(
609 ts("File permissions"),
610 ts("Does the webserver know where files are stored?"),
611 ts("The webserver isn't letting me identify where files are stored."),
612 $this->getBaseDir(),
613 ),
614 TRUE
615 );
616
617 // CRM-6485: make sure the path does not contain PATH_SEPARATOR, as we don’t know how to escape it
618 $this->requireNoPathSeparator(
619 array(
620 ts("File permissions"),
621 ts('Does the CiviCRM path contain PATH_SEPARATOR?'),
622 ts('The path %1 contains PATH_SEPARATOR (the %2 character).', array(1 => $this->getBaseDir(), 2 => PATH_SEPARATOR)),
623 $this->getBaseDir(),
624 )
625 );
626
627 $requiredDirectories = array('CRM', 'packages', 'templates', 'js', 'api', 'i', 'sql');
628 foreach ($requiredDirectories as $dir) {
629 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR . $dir,
630 array(
631 ts("File permissions"),
632 ts("Folder '%1' exists?", array(1 => $dir)),
633 ts("There is no '%1' folder.", array(1 => $dir)),
634 ), TRUE
635 );
636 }
637
638 $configIDSiniDir = NULL;
639 global $cmsPath;
640 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
641 if ($installType == 'drupal') {
642
643 // make sure that we can write to sites/default and files/
644 $writableDirectories = array(
645 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
646 'sites' . CIVICRM_DIRECTORY_SEPARATOR .
647 $siteDir . CIVICRM_DIRECTORY_SEPARATOR .
648 'files',
649 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
650 'sites' . CIVICRM_DIRECTORY_SEPARATOR .
651 $siteDir,
652 );
653 }
654 elseif ($installType == 'backdrop') {
655
656 // make sure that we can write to sites/default and files/
657 $writableDirectories = array(
658 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
659 'files',
660 $cmsPath,
661 );
662 }
663 elseif ($installType == 'wordpress') {
664 // make sure that we can write to uploads/civicrm/
665 $upload_dir = wp_upload_dir();
666 $files_dirname = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'civicrm';
667 if (!file_exists($files_dirname)) {
668 wp_mkdir_p($files_dirname);
669 }
670 $writableDirectories = array($files_dirname);
671 }
672
673 foreach ($writableDirectories as $dir) {
674 $dirName = CIVICRM_WINDOWS ? $dir : CIVICRM_DIRECTORY_SEPARATOR . $dir;
675 $testDetails = array(
676 ts("File permissions"),
677 ts("Is the %1 folder writeable?", array(1 => $dir)),
678 NULL,
679 );
680 $this->requireWriteable($dirName, $testDetails, TRUE);
681 }
682
683 //check for Config.IDS.ini, file may exist in re-install
684 $configIDSiniDir = array($cmsPath, 'sites', $siteDir, 'files', 'civicrm', 'upload', 'Config.IDS.ini');
685
686 if (is_array($configIDSiniDir) && !empty($configIDSiniDir)) {
687 $configIDSiniFile = implode(CIVICRM_DIRECTORY_SEPARATOR, $configIDSiniDir);
688 if (file_exists($configIDSiniFile)) {
689 unlink($configIDSiniFile);
690 }
691 }
692
693 // Check for rewriting
694 if (isset($_SERVER['SERVER_SOFTWARE'])) {
695 $webserver = strip_tags(trim($_SERVER['SERVER_SOFTWARE']));
696 }
697 elseif (isset($_SERVER['SERVER_SIGNATURE'])) {
698 $webserver = strip_tags(trim($_SERVER['SERVER_SIGNATURE']));
699 }
700
701 if ($webserver == '') {
702 $webserver = ts("I can't tell what webserver you are running");
703 }
704
705 // Check for $_SERVER configuration
706 $this->requireServerVariables(array('SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME'), array(
707 ts("Webserver config"),
708 ts("Recognised webserver"),
709 ts("You seem to be using an unsupported webserver. The server variables SCRIPT_NAME, HTTP_HOST, SCRIPT_FILENAME need to be set."),
710 ));
711
712 // Check for MySQL support
713 $this->requireFunction('mysqli_connect', array(
714 ts("PHP Configuration"),
715 ts("MySQL support"),
716 ts("MySQL support not included in PHP."),
717 ));
718
719 // Check for XML support
720 $this->requireFunction('simplexml_load_file', array(
721 ts("PHP Configuration"),
722 ts("SimpleXML support"),
723 ts("SimpleXML support not included in PHP."),
724 ));
725
726 // Check for JSON support
727 $this->requireFunction('json_encode', array(
728 ts("PHP Configuration"),
729 ts("JSON support"),
730 ts("JSON support not included in PHP."),
731 ));
732
733 // Check for xcache_isset and emit warning if exists
734 $this->checkXCache(array(
735 ts("PHP Configuration"),
736 ts("XCache compatibility"),
737 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."),
738 ));
739
740 // Check memory allocation
741 $this->requireMemory(32 * 1024 * 1024,
742 64 * 1024 * 1024,
743 array(
744 ts("PHP Configuration"),
745 ts("Memory allocated (PHP config option 'memory_limit')"),
746 ts("CiviCRM needs a minimum of %1 MB allocated to PHP, but recommends %2 MB.", array(1 => 32, 2 => 64)),
747 ini_get("memory_limit"),
748 )
749 );
750
751 return $this->errors;
752 }
753
754 /**
755 * @param $min
756 * @param $recommended
757 * @param $testDetails
758 */
759 public function requireMemory($min, $recommended, $testDetails) {
760 $this->testing($testDetails);
761 $mem = $this->getPHPMemory();
762
763 if ($mem < $min && $mem > 0) {
764 $testDetails[2] .= " " . ts("You only have %1 allocated", array(1 => ini_get("memory_limit")));
765 $this->error($testDetails);
766 }
767 elseif ($mem < $recommended && $mem > 0) {
768 $testDetails[2] .= " " . ts("You only have %1 allocated", array(1 => ini_get("memory_limit")));
769 $this->warning($testDetails);
770 }
771 elseif ($mem == 0) {
772 $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));
773 $this->warning($testDetails);
774 }
775 }
776
777 /**
778 * @return float
779 */
780 public function getPHPMemory() {
781 $memString = ini_get("memory_limit");
782
783 switch (strtolower(substr($memString, -1))) {
784 case "k":
785 return round(substr($memString, 0, -1) * 1024);
786
787 case "m":
788 return round(substr($memString, 0, -1) * 1024 * 1024);
789
790 case "g":
791 return round(substr($memString, 0, -1) * 1024 * 1024 * 1024);
792
793 default:
794 return round($memString);
795 }
796 }
797
798 public function listErrors() {
799 if ($this->errors) {
800 echo "<p>" . ts("The following problems are preventing me from installing CiviCRM:") . "</p>";
801 foreach ($this->errors as $error) {
802 echo "<li>" . htmlentities($error) . "</li>";
803 }
804 }
805 }
806
807 /**
808 * @param null $section
809 */
810 public function showTable($section = NULL) {
811 if ($section) {
812 $tests = $this->tests[$section];
813 echo "<table class=\"testResults\" width=\"100%\">";
814 foreach ($tests as $test => $result) {
815 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
816 }
817 echo "</table>";
818 }
819 else {
820 foreach ($this->tests as $section => $tests) {
821 echo "<h3>$section</h3>";
822 echo "<table class=\"testResults\" width=\"100%\">";
823
824 foreach ($tests as $test => $result) {
825 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
826 }
827 echo "</table>";
828 }
829 }
830 }
831
832 /**
833 * @param string $funcName
834 * @param $testDetails
835 *
836 * @return bool
837 */
838 public function requireFunction($funcName, $testDetails) {
839 $this->testing($testDetails);
840
841 if (!function_exists($funcName)) {
842 $this->error($testDetails);
843 return FALSE;
844 }
845 else {
846 return TRUE;
847 }
848 }
849
850 /**
851 * @param $testDetails
852 */
853 public function checkXCache($testDetails) {
854 if (function_exists('xcache_isset') &&
855 ini_get('xcache.size') > 0
856 ) {
857 $this->testing($testDetails);
858 $this->warning($testDetails);
859 }
860 }
861
862 /**
863 * @param $minVersion
864 * @param $testDetails
865 * @param null $maxVersion
866 */
867 public function requirePHPVersion($minVersion, $testDetails, $maxVersion = NULL) {
868
869 $this->testing($testDetails);
870
871 $phpVersion = phpversion();
872 $aboveMinVersion = version_compare($phpVersion, $minVersion) >= 0;
873 $belowMaxVersion = $maxVersion ? version_compare($phpVersion, $maxVersion) < 0 : TRUE;
874
875 if ($aboveMinVersion && $belowMaxVersion) {
876 if (version_compare(phpversion(), CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER) < 0) {
877 $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.', array(
878 1 => phpversion(),
879 2 => CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER,
880 ));
881 $this->warning($testDetails);
882 }
883 return TRUE;
884 }
885
886 if (!$testDetails[2]) {
887 if (!$aboveMinVersion) {
888 $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 => $minVersion, 2 => $phpVersion));
889 }
890 else {
891 $testDetails[2] = ts("PHP version %1 is not supported. PHP version earlier than %2 is required. You might want to downgrade your server, or ask your web-host to do so.", array(1 => $maxVersion, 2 => $phpVersion));
892 }
893 }
894
895 $this->error($testDetails);
896 }
897
898 /**
899 * @param string $filename
900 * @param $testDetails
901 * @param bool $absolute
902 */
903 public function requireFile($filename, $testDetails, $absolute = FALSE) {
904 $this->testing($testDetails);
905 if (!$absolute) {
906 $filename = $this->getBaseDir() . $filename;
907 }
908 if (!file_exists($filename)) {
909 $testDetails[2] .= " (" . ts("file '%1' not found", array(1 => $filename)) . ')';
910 $this->error($testDetails);
911 }
912 }
913
914 /**
915 * @param $testDetails
916 */
917 public function requireNoPathSeparator($testDetails) {
918 $this->testing($testDetails);
919 if (substr_count($this->getBaseDir(), PATH_SEPARATOR)) {
920 $this->error($testDetails);
921 }
922 }
923
924 /**
925 * @param string $filename
926 * @param $testDetails
927 */
928 public function requireNoFile($filename, $testDetails) {
929 $this->testing($testDetails);
930 $filename = $this->getBaseDir() . $filename;
931 if (file_exists($filename)) {
932 $testDetails[2] .= " (" . ts("file '%1' found", array(1 => $filename)) . ")";
933 $this->error($testDetails);
934 }
935 }
936
937 /**
938 * @param string $filename
939 * @param $testDetails
940 */
941 public function moveFileOutOfTheWay($filename, $testDetails) {
942 $this->testing($testDetails);
943 $filename = $this->getBaseDir() . $filename;
944 if (file_exists($filename)) {
945 if (file_exists("$filename.bak")) {
946 rm("$filename.bak");
947 }
948 rename($filename, "$filename.bak");
949 }
950 }
951
952 /**
953 * @param string $filename
954 * @param $testDetails
955 * @param bool $absolute
956 */
957 public function requireWriteable($filename, $testDetails, $absolute = FALSE) {
958 $this->testing($testDetails);
959 if (!$absolute) {
960 $filename = $this->getBaseDir() . $filename;
961 }
962
963 if (!is_writable($filename)) {
964 $name = NULL;
965 if (function_exists('posix_getpwuid')) {
966 $user = posix_getpwuid(posix_geteuid());
967 $name = '- ' . $user['name'] . ' -';
968 }
969
970 if (!isset($testDetails[2])) {
971 $testDetails[2] = NULL;
972 }
973 $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";
974 $this->error($testDetails);
975 }
976 }
977
978 /**
979 * @param string $moduleName
980 * @param $testDetails
981 */
982 public function requireApacheModule($moduleName, $testDetails) {
983 $this->testing($testDetails);
984 if (!in_array($moduleName, apache_get_modules())) {
985 $this->error($testDetails);
986 }
987 }
988
989 /**
990 * @param $server
991 * @param string $username
992 * @param $password
993 * @param $testDetails
994 */
995 public function requireMysqlConnection($server, $username, $password, $testDetails) {
996 $this->testing($testDetails);
997 $this->conn = $this->connect($server, $username, $password);
998
999 if ($this->conn) {
1000 return TRUE;
1001 }
1002 else {
1003 $testDetails[2] .= ": " . mysqli_connect_error();
1004 $this->error($testDetails);
1005 }
1006 }
1007
1008 /**
1009 * @param $server
1010 * @param $testDetails
1011 */
1012 public function requireMySQLServer($server, $testDetails) {
1013 $this->testing($testDetails);
1014 $conn = $this->connect($server, NULL, NULL);
1015
1016 if ($conn || mysqli_connect_errno() < 2000) {
1017 return TRUE;
1018 }
1019 else {
1020 $testDetails[2] .= ": " . mysqli_connect_error();
1021 $this->error($testDetails);
1022 }
1023 }
1024
1025 /**
1026 * @param $version
1027 * @param $testDetails
1028 */
1029 public function requireMySQLVersion($version, $testDetails) {
1030 $this->testing($testDetails);
1031
1032 if (!mysqli_get_server_info($this->conn)) {
1033 $testDetails[2] = ts('Cannot determine the version of MySQL installed. Please ensure at least version %1 is installed.', array(1 => $version));
1034 $this->warning($testDetails);
1035 }
1036 else {
1037 list($majorRequested, $minorRequested) = explode('.', $version);
1038 list($majorHas, $minorHas) = explode('.', mysqli_get_server_info($this->conn));
1039
1040 if (($majorHas > $majorRequested) || ($majorHas == $majorRequested && $minorHas >= $minorRequested)) {
1041 return TRUE;
1042 }
1043 else {
1044 $testDetails[2] .= "{$majorHas}.{$minorHas}.";
1045 $this->error($testDetails);
1046 }
1047 }
1048 }
1049
1050 /**
1051 * @param $server
1052 * @param string $username
1053 * @param $password
1054 * @param $database
1055 * @param $testDetails
1056 */
1057 public function requireMySQLInnoDB($server, $username, $password, $database, $testDetails) {
1058 $this->testing($testDetails);
1059 $conn = $this->connect($server, $username, $password);
1060 if (!$conn) {
1061 $testDetails[2] .= ' ' . ts("Could not determine if MySQL has InnoDB support. Assuming no.");
1062 $this->error($testDetails);
1063 return;
1064 }
1065
1066 $innodb_support = FALSE;
1067 $result = mysqli_query($conn, "SHOW ENGINES");
1068 while ($values = mysqli_fetch_array($result)) {
1069 if ($values['Engine'] == 'InnoDB') {
1070 if (strtolower($values['Support']) == 'yes' ||
1071 strtolower($values['Support']) == 'default'
1072 ) {
1073 $innodb_support = TRUE;
1074 }
1075 }
1076 }
1077 if ($innodb_support) {
1078 $testDetails[3] = ts('MySQL server does have InnoDB support');
1079 }
1080 else {
1081 $testDetails[2] .= ' ' . ts('Could not determine if MySQL has InnoDB support. Assuming no');
1082 }
1083 }
1084
1085 /**
1086 * @param $server
1087 * @param string $username
1088 * @param $password
1089 * @param $database
1090 * @param $testDetails
1091 */
1092 public function requireMySQLTempTables($server, $username, $password, $database, $testDetails) {
1093 $this->testing($testDetails);
1094 $conn = $this->connect($server, $username, $password);
1095 if (!$conn) {
1096 $testDetails[2] = ts('Could not login to the database.');
1097 $this->error($testDetails);
1098 return;
1099 }
1100
1101 if (!@mysqli_select_db($conn, $database)) {
1102 $testDetails[2] = ts('Could not select the database.');
1103 $this->error($testDetails);
1104 return;
1105 }
1106
1107 $result = mysqli_query($conn, 'CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)');
1108 if (!$result) {
1109 $testDetails[2] = ts('Could not create a temp table.');
1110 $this->error($testDetails);
1111 }
1112 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
1113 }
1114
1115 /**
1116 * @param $server
1117 * @param string $username
1118 * @param $password
1119 * @param $database
1120 * @param $testDetails
1121 */
1122 public function requireMySQLTrigger($server, $username, $password, $database, $testDetails) {
1123 $this->testing($testDetails);
1124 $conn = $this->connect($server, $username, $password);
1125 if (!$conn) {
1126 $testDetails[2] = ts('Could not login to the database.');
1127 $this->error($testDetails);
1128 return;
1129 }
1130
1131 if (!@mysqli_select_db($conn, $database)) {
1132 $testDetails[2] = ts('Could not select the database.');
1133 $this->error($testDetails);
1134 return;
1135 }
1136
1137 $result = mysqli_query($conn, 'CREATE TABLE civicrm_install_temp_table_test (test text)');
1138 if (!$result) {
1139 $testDetails[2] = ts('Could not create a table in the database.');
1140 $this->error($testDetails);
1141 }
1142
1143 $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');
1144 if (!$result) {
1145 mysqli_query($conn, 'DROP TABLE civicrm_install_temp_table_test');
1146 $testDetails[2] = ts('Could not create a database trigger.');
1147 $this->error($testDetails);
1148 }
1149
1150 mysqli_query($conn, 'DROP TRIGGER civicrm_install_temp_table_test_trigger');
1151 mysqli_query($conn, 'DROP TABLE civicrm_install_temp_table_test');
1152 }
1153
1154
1155 /**
1156 * @param $server
1157 * @param string $username
1158 * @param $password
1159 * @param $database
1160 * @param $testDetails
1161 */
1162 public function requireMySQLLockTables($server, $username, $password, $database, $testDetails) {
1163 $this->testing($testDetails);
1164 $conn = $this->connect($server, $username, $password);
1165 if (!$conn) {
1166 $testDetails[2] = ts('Could not connect to the database server.');
1167 $this->error($testDetails);
1168 return;
1169 }
1170
1171 if (!@mysqli_select_db($conn, $database)) {
1172 $testDetails[2] = ts('Could not select the database.');
1173 $this->error($testDetails);
1174 return;
1175 }
1176
1177 $result = mysqli_query($conn, 'CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)');
1178 if (!$result) {
1179 $testDetails[2] = ts('Could not create a table in the database.');
1180 $this->error($testDetails);
1181 return;
1182 }
1183
1184 $result = mysqli_query($conn, 'LOCK TABLES civicrm_install_temp_table_test WRITE');
1185 if (!$result) {
1186 $testDetails[2] = ts('Could not obtain a write lock for the database table.');
1187 $this->error($testDetails);
1188 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
1189 return;
1190 }
1191
1192 $result = mysqli_query($conn, 'UNLOCK TABLES');
1193 if (!$result) {
1194 $testDetails[2] = ts('Could not release the lock for the database table.');
1195 $this->error($testDetails);
1196 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
1197 return;
1198 }
1199
1200 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
1201 }
1202
1203 /**
1204 * @param $server
1205 * @param string $username
1206 * @param $password
1207 * @param $testDetails
1208 */
1209 public function requireMySQLAutoIncrementIncrementOne($server, $username, $password, $testDetails) {
1210 $this->testing($testDetails);
1211 $conn = $this->connect($server, $username, $password);
1212 if (!$conn) {
1213 $testDetails[2] = ts('Could not connect to the database server.');
1214 $this->error($testDetails);
1215 return;
1216 }
1217
1218 $result = mysqli_query($conn, "SHOW variables like 'auto_increment_increment'");
1219 if (!$result) {
1220 $testDetails[2] = ts('Could not query database server variables.');
1221 $this->error($testDetails);
1222 return;
1223 }
1224 else {
1225 $values = mysqli_fetch_row($result);
1226 if ($values[1] == 1) {
1227 $testDetails[3] = ts('MySQL server auto_increment_increment is 1');
1228 }
1229 else {
1230 $this->error($testDetails);
1231 }
1232 }
1233 }
1234
1235 /**
1236 * @param $server
1237 * @param string $username
1238 * @param $password
1239 * @param $database
1240 * @param $minValueKB
1241 * @param $testDetails
1242 */
1243 public function requireMySQLThreadStack($server, $username, $password, $database, $minValueKB, $testDetails) {
1244 $this->testing($testDetails);
1245 $conn = $this->connect($server, $username, $password);
1246 if (!$conn) {
1247 $testDetails[2] = ts('Could not connect to the database server.');
1248 $this->error($testDetails);
1249 return;
1250 }
1251
1252 if (!@mysqli_select_db($conn, $database)) {
1253 $testDetails[2] = ts('Could not select the database.');
1254 $this->error($testDetails);
1255 return;
1256 }
1257
1258 $result = mysqli_query($conn, "SHOW VARIABLES LIKE 'thread_stack'"); // bytes => kb
1259 if (!$result) {
1260 $testDetails[2] = ts('Could not get information about the thread_stack of the database.');
1261 $this->error($testDetails);
1262 }
1263 else {
1264 $values = mysqli_fetch_row($result);
1265 if ($values[1] < (1024 * $minValueKB)) {
1266 $testDetails[2] = ts('MySQL "thread_stack" is %1 kb', array(1 => ($values[1] / 1024)));
1267 $this->error($testDetails);
1268 }
1269 }
1270 }
1271
1272 /**
1273 * @param $server
1274 * @param string $username
1275 * @param $password
1276 * @param $database
1277 * @param $testDetails
1278 * @param bool $onlyRequire
1279 */
1280 public function requireDatabaseOrCreatePermissions(
1281 $server,
1282 $username,
1283 $password,
1284 $database,
1285 $testDetails,
1286 $onlyRequire = FALSE
1287 ) {
1288 $this->testing($testDetails);
1289 $conn = $this->connect($server, $username, $password);
1290
1291 $okay = NULL;
1292 if (@mysqli_select_db($conn, $database)) {
1293 $okay = "Database '$database' exists";
1294 }
1295 elseif ($onlyRequire) {
1296 $testDetails[2] = ts("The database: '%1' does not exist.", array(1 => $database));
1297 $this->error($testDetails);
1298 return;
1299 }
1300 else {
1301 $query = sprintf("CREATE DATABASE %s", mysqli_real_escape_string($conn, $database));
1302 if (@mysqli_query($conn, $query)) {
1303 $okay = ts("Able to create a new database.");
1304 }
1305 else {
1306 $testDetails[2] .= " (" . ts("user '%1' doesn't have CREATE DATABASE permissions.", array(1 => $username)) . ")";
1307 $this->error($testDetails);
1308 return;
1309 }
1310 }
1311
1312 if ($okay) {
1313 $testDetails[3] = $okay;
1314 $this->testing($testDetails);
1315 }
1316 }
1317
1318 /**
1319 * @param $varNames
1320 * @param $errorMessage
1321 */
1322 public function requireServerVariables($varNames, $errorMessage) {
1323 //$this->testing($testDetails);
1324 foreach ($varNames as $varName) {
1325 if (!$_SERVER[$varName]) {
1326 $missing[] = '$_SERVER[' . $varName . ']';
1327 }
1328 }
1329 if (!isset($missing)) {
1330 return TRUE;
1331 }
1332 else {
1333 $testDetails[2] = " (" . ts('the following PHP variables are missing: %1', array(1 => implode(", ", $missing))) . ")";
1334 $this->error($testDetails);
1335 }
1336 }
1337
1338 /**
1339 * @param $testDetails
1340 *
1341 * @return bool
1342 */
1343 public function isRunningApache($testDetails) {
1344 $this->testing($testDetails);
1345 if (function_exists('apache_get_modules') || stristr($_SERVER['SERVER_SIGNATURE'], 'Apache')) {
1346 return TRUE;
1347 }
1348
1349 $this->warning($testDetails);
1350 return FALSE;
1351 }
1352
1353 /**
1354 * @return string
1355 */
1356 public function getBaseDir() {
1357 return dirname($_SERVER['SCRIPT_FILENAME']) . CIVICRM_DIRECTORY_SEPARATOR;
1358 }
1359
1360 /**
1361 * @param $testDetails
1362 */
1363 public function testing($testDetails) {
1364 if (!$testDetails) {
1365 return;
1366 }
1367
1368 $section = $testDetails[0];
1369 $test = $testDetails[1];
1370
1371 $message = ts("OK");
1372 if (isset($testDetails[3])) {
1373 $message .= " ($testDetails[3])";
1374 }
1375
1376 $this->tests[$section][$test] = array("good", $message);
1377 }
1378
1379 /**
1380 * @param $testDetails
1381 */
1382 public function error($testDetails) {
1383 $section = $testDetails[0];
1384 $test = $testDetails[1];
1385
1386 $this->tests[$section][$test] = array("error", $testDetails[2]);
1387 $this->errors[] = $testDetails;
1388 }
1389
1390 /**
1391 * @param $testDetails
1392 */
1393 public function warning($testDetails) {
1394 $section = $testDetails[0];
1395 $test = $testDetails[1];
1396
1397 $this->tests[$section][$test] = array("warning", $testDetails[2]);
1398 $this->warnings[] = $testDetails;
1399 }
1400
1401 /**
1402 * @return int
1403 */
1404 public function hasErrors() {
1405 return count($this->errors);
1406 }
1407
1408 /**
1409 * @return int
1410 */
1411 public function hasWarnings() {
1412 return count($this->warnings);
1413 }
1414
1415 }
1416
1417 /**
1418 * Class Installer
1419 */
1420 class Installer extends InstallRequirements {
1421 /**
1422 * @param $server
1423 * @param $username
1424 * @param $password
1425 * @param $database
1426 */
1427 public function createDatabaseIfNotExists($server, $username, $password, $database) {
1428 $conn = $this->connect($server, $username, $password);
1429
1430 if (@mysqli_select_db($conn, $database)) {
1431 // skip if database already present
1432 return;
1433 }
1434 $query = sprintf("CREATE DATABASE %s", mysqli_real_escape_string($conn, $database));
1435 if (@mysqli_query($conn, $query)) {
1436 }
1437 else {
1438 $errorTitle = ts("Oops! Could not create database %1", array(1 => $database));
1439 $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.");
1440 errorDisplayPage($errorTitle, $errorMsg);
1441 }
1442 }
1443
1444 /**
1445 * @param $config
1446 *
1447 * @return mixed
1448 */
1449 public function install($config) {
1450 global $installDirPath;
1451
1452 // create database if does not exists
1453 $this->createDatabaseIfNotExists($config['mysql']['server'],
1454 $config['mysql']['username'],
1455 $config['mysql']['password'],
1456 $config['mysql']['database']
1457 );
1458
1459 global $installDirPath;
1460
1461 // Build database
1462 require_once $installDirPath . 'civicrm.php';
1463 civicrm_main($config);
1464
1465 if (!$this->errors) {
1466 global $installType, $installURLPath;
1467
1468 $registerSiteURL = "https://civicrm.org/register-site";
1469 $commonOutputMessage
1470 = "<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>"
1471 . "<li>" . ts("We have integrated KCFinder with CKEditor and TinyMCE. This allows a user to upload images. All uploaded images are public.") . "</li>";
1472
1473 $output = NULL;
1474
1475 if (
1476 $installType == 'drupal' &&
1477 version_compare(VERSION, '7.0-rc1') >= 0
1478 ) {
1479
1480 // clean output
1481 @ob_clean();
1482
1483 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1484 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1485 $output .= '<head>';
1486 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1487 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
1488 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1489 $output .= '</head>';
1490 $output .= '<body>';
1491 $output .= '<div style="padding: 1em;"><p class="good">' . ts('CiviCRM has been successfully installed') . '</p>';
1492 $output .= '<ul>';
1493
1494 $drupalURL = civicrm_cms_base();
1495 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/people/permissions";
1496 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1497
1498 $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>";
1499 $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>";
1500 $output .= $commonOutputMessage;
1501
1502 // automatically enable CiviCRM module once it is installed successfully.
1503 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1504 global $cmsPath, $crmPath;
1505
1506 // relative / abosolute paths are not working for drupal, hence using chdir()
1507 chdir($cmsPath);
1508
1509 // Force the re-initialisation of the config singleton on the next call
1510 // since so far, we had used the Config object without loading the DB.
1511 $c = CRM_Core_Config::singleton(FALSE);
1512 $c->free();
1513
1514 include_once "./includes/bootstrap.inc";
1515 include_once "./includes/unicode.inc";
1516
1517 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
1518
1519 // prevent session information from being saved.
1520 drupal_save_session(FALSE);
1521
1522 // Force the current user to anonymous.
1523 $original_user = $GLOBALS['user'];
1524 $GLOBALS['user'] = drupal_anonymous_user();
1525
1526 // explicitly setting error reporting, since we cannot handle drupal related notices
1527 error_reporting(1);
1528
1529 // rebuild modules, so that civicrm is added
1530 system_rebuild_module_data();
1531
1532 // now enable civicrm module.
1533 module_enable(array('civicrm', 'civicrmtheme'));
1534
1535 // SystemInstallEvent will be called from here with the first call of CRM_Core_Config,
1536 // which calls Core_BAO_ConfigSetting::applyLocale(), who will default to calling
1537 // Civi::settings()->get('lcMessages');
1538 // Therefore, we need to pass the seedLanguage before that.
1539 global $civicrm_setting;
1540 $civicrm_setting['domain']['lcMessages'] = $config['seedLanguage'];
1541
1542 // clear block, page, theme, and hook caches
1543 drupal_flush_all_caches();
1544
1545 //add basic drupal permissions
1546 civicrm_install_set_drupal_perms();
1547
1548 // restore the user.
1549 $GLOBALS['user'] = $original_user;
1550 drupal_save_session(TRUE);
1551
1552 $output .= '</ul>';
1553 $output .= '</div>';
1554 $output .= '</body>';
1555 $output .= '</html>';
1556 echo $output;
1557 }
1558 elseif (
1559 $installType == 'backdrop'
1560 ) {
1561
1562 // clean output
1563 @ob_clean();
1564
1565 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1566 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1567 $output .= '<head>';
1568 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1569 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
1570 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1571 $output .= '</head>';
1572 $output .= '<body>';
1573 $output .= '<div style="padding: 1em;"><p class="good">' . ts('CiviCRM has been successfully installed') . '</p>';
1574 $output .= '<ul>';
1575
1576 $backdropURL = civicrm_cms_base();
1577 $backdropPermissionsURL = "{$backdropURL}index.php?q=admin/config/people/permissions";
1578 $backdropURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1579
1580 $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>";
1581 $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>";
1582 $output .= $commonOutputMessage;
1583
1584 // automatically enable CiviCRM module once it is installed successfully.
1585 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1586 global $cmsPath, $crmPath;
1587
1588 // relative / abosolute paths are not working for drupal, hence using chdir()
1589 chdir($cmsPath);
1590
1591 // Force the re-initialisation of the config singleton on the next call
1592 // since so far, we had used the Config object without loading the DB.
1593 $c = CRM_Core_Config::singleton(FALSE);
1594 $c->free();
1595
1596 include_once "./core/includes/bootstrap.inc";
1597 include_once "./core/includes/unicode.inc";
1598 include_once "./core/includes/config.inc";
1599
1600 backdrop_bootstrap(BACKDROP_BOOTSTRAP_FULL);
1601
1602 // prevent session information from being saved.
1603 backdrop_save_session(FALSE);
1604
1605 // Force the current user to anonymous.
1606 $original_user = $GLOBALS['user'];
1607 $GLOBALS['user'] = backdrop_anonymous_user();
1608
1609 // explicitly setting error reporting, since we cannot handle drupal related notices
1610 error_reporting(1);
1611
1612 // rebuild modules, so that civicrm is added
1613 system_rebuild_module_data();
1614
1615 // now enable civicrm module.
1616 module_enable(array('civicrm', 'civicrmtheme'));
1617
1618 // clear block, page, theme, and hook caches
1619 backdrop_flush_all_caches();
1620
1621 //add basic backdrop permissions
1622 civicrm_install_set_backdrop_perms();
1623
1624 // restore the user.
1625 $GLOBALS['user'] = $original_user;
1626 backdrop_save_session(TRUE);
1627
1628 //change the default language to one chosen
1629 if (isset($config['seedLanguage']) && $config['seedLanguage'] != 'en_US') {
1630 civicrm_api3('Setting', 'create', array(
1631 'domain_id' => 'current_domain',
1632 'lcMessages' => $config['seedLanguage'],
1633 )
1634 );
1635 }
1636
1637 $output .= '</ul>';
1638 $output .= '</div>';
1639 $output .= '</body>';
1640 $output .= '</html>';
1641 echo $output;
1642 }
1643 elseif ($installType == 'drupal' && version_compare(VERSION, '6.0') >= 0) {
1644 // clean output
1645 @ob_clean();
1646
1647 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1648 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1649 $output .= '<head>';
1650 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1651 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
1652 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1653 $output .= '</head>';
1654 $output .= '<body>';
1655 $output .= '<div style="padding: 1em;"><p class="good">' . ts("CiviCRM has been successfully installed") . '</p>';
1656 $output .= '<ul>';
1657
1658 $drupalURL = civicrm_cms_base();
1659 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/user/permissions";
1660 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1661
1662 $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>";
1663 $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>";
1664 $output .= $commonOutputMessage;
1665
1666 // explicitly setting error reporting, since we cannot handle drupal related notices
1667 error_reporting(1);
1668
1669 // automatically enable CiviCRM module once it is installed successfully.
1670 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1671 global $cmsPath, $crmPath;
1672
1673 // relative / abosolute paths are not working for drupal, hence using chdir()
1674 chdir($cmsPath);
1675
1676 // Force the re-initialisation of the config singleton on the next call
1677 // since so far, we had used the Config object without loading the DB.
1678 $c = CRM_Core_Config::singleton(FALSE);
1679 $c->free();
1680
1681 include_once "./includes/bootstrap.inc";
1682 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
1683
1684 // rebuild modules, so that civicrm is added
1685 module_rebuild_cache();
1686
1687 // now enable civicrm module.
1688 module_enable(array('civicrm'));
1689
1690 // clear block, page, theme, and hook caches
1691 drupal_flush_all_caches();
1692
1693 //add basic drupal permissions
1694 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)');
1695
1696 echo $output;
1697 }
1698 elseif ($installType == 'wordpress') {
1699 echo '<h1>' . ts('CiviCRM Installed') . '</h1>';
1700 echo '<div style="padding: 1em;"><p style="background-color: #0C0; border: 1px #070 solid; color: white;">' . ts("CiviCRM has been successfully installed") . '</p>';
1701 echo '<ul>';
1702
1703 $cmsURL = civicrm_cms_base();
1704 $cmsURL .= "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/configtask&reset=1";
1705 $wpPermissionsURL = "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/access/wp-permissions&reset=1";
1706
1707 $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>";
1708 $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>";
1709 $output .= $commonOutputMessage;
1710
1711 $output .= '</ul>';
1712 $output .= '</div>';
1713 echo $output;
1714
1715 $c = CRM_Core_Config::singleton(FALSE);
1716 $c->free();
1717 $wpInstallRedirect = admin_url('admin.php?page=CiviCRM&q=civicrm&reset=1');
1718 echo "<script>
1719 window.location = '$wpInstallRedirect';
1720 </script>";
1721 }
1722 }
1723
1724 return $this->errors;
1725 }
1726
1727 }
1728
1729 function civicrm_install_set_drupal_perms() {
1730 if (!function_exists('db_select')) {
1731 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)');
1732 }
1733 else {
1734 $perms = array(
1735 'access all custom data',
1736 'access uploaded files',
1737 'make online contributions',
1738 'profile create',
1739 'profile edit',
1740 'profile view',
1741 'register for events',
1742 'view event info',
1743 'view event participants',
1744 'access CiviMail subscribe/unsubscribe pages',
1745 );
1746
1747 // Adding a permission that has not yet been assigned to a module by
1748 // a hook_permission implementation results in a database error.
1749 // CRM-9042
1750 $allPerms = array_keys(module_invoke_all('permission'));
1751 foreach (array_diff($perms, $allPerms) as $perm) {
1752 watchdog('civicrm',
1753 'Cannot grant the %perm permission because it does not yet exist.',
1754 array('%perm' => $perm),
1755 WATCHDOG_ERROR
1756 );
1757 }
1758 $perms = array_intersect($perms, $allPerms);
1759 user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, $perms);
1760 user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, $perms);
1761 }
1762 }
1763
1764 function civicrm_install_set_backdrop_perms() {
1765 $perms = array(
1766 'access all custom data',
1767 'access uploaded files',
1768 'make online contributions',
1769 'profile create',
1770 'profile edit',
1771 'profile view',
1772 'register for events',
1773 'view event info',
1774 'view event participants',
1775 'access CiviMail subscribe/unsubscribe pages',
1776 );
1777
1778 // Adding a permission that has not yet been assigned to a module by
1779 // a hook_permission implementation results in a database error.
1780 // CRM-9042
1781 $allPerms = array_keys(module_invoke_all('permission'));
1782 foreach (array_diff($perms, $allPerms) as $perm) {
1783 watchdog('civicrm',
1784 'Cannot grant the %perm permission because it does not yet exist.',
1785 array('%perm' => $perm),
1786 WATCHDOG_ERROR
1787 );
1788 }
1789 $perms = array_intersect($perms, $allPerms);
1790 user_role_grant_permissions(BACKDROP_AUTHENTICATED_ROLE, $perms);
1791 user_role_grant_permissions(BACKDROP_ANONYMOUS_ROLE, $perms);
1792 }
1793
1794 /**
1795 * @param $cmsPath
1796 * @param $str
1797 *
1798 * @return string
1799 */
1800 function getSiteDir($cmsPath, $str) {
1801 static $siteDir = '';
1802
1803 if ($siteDir) {
1804 return $siteDir;
1805 }
1806
1807 $sites = CIVICRM_DIRECTORY_SEPARATOR . 'sites' . CIVICRM_DIRECTORY_SEPARATOR;
1808 $modules = CIVICRM_DIRECTORY_SEPARATOR . 'modules' . CIVICRM_DIRECTORY_SEPARATOR;
1809 preg_match("/" . preg_quote($sites, CIVICRM_DIRECTORY_SEPARATOR) .
1810 "([\-a-zA-Z0-9_.]+)" .
1811 preg_quote($modules, CIVICRM_DIRECTORY_SEPARATOR) . "/",
1812 $_SERVER['SCRIPT_FILENAME'], $matches
1813 );
1814 $siteDir = isset($matches[1]) ? $matches[1] : 'default';
1815
1816 if (strtolower($siteDir) == 'all') {
1817 // For this case - use drupal's way of finding out multi-site directory
1818 $uri = explode(CIVICRM_DIRECTORY_SEPARATOR, $_SERVER['SCRIPT_FILENAME']);
1819 $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
1820 for ($i = count($uri) - 1; $i > 0; $i--) {
1821 for ($j = count($server); $j > 0; $j--) {
1822 $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
1823 if (file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR .
1824 'sites' . CIVICRM_DIRECTORY_SEPARATOR . $dir
1825 )) {
1826 $siteDir = $dir;
1827 return $siteDir;
1828 }
1829 }
1830 }
1831 $siteDir = 'default';
1832 }
1833
1834 return $siteDir;
1835 }
1836
1837 /**
1838 * @param $errorTitle
1839 * @param $errorMsg
1840 * @param $showRefer
1841 */
1842 function errorDisplayPage($errorTitle, $errorMsg, $showRefer = TRUE) {
1843
1844 // Add a link to the documentation
1845 if ($showRefer) {
1846 if (is_callable(array('CRM_Utils_System', 'docURL2'))) {
1847 $docLink = CRM_Utils_System::docURL2('Installation and Upgrades', FALSE, 'Installation Guide', NULL, NULL, "wiki");
1848 }
1849 else {
1850 $docLink = '';
1851 }
1852
1853 if (function_exists('ts')) {
1854 $errorMsg .= '<p>' . ts("Refer to the online documentation for more information: ") . $docLink . '</p>';
1855 }
1856 else {
1857 $errorMsg .= '<p>' . 'Refer to the online documentation for more information: ' . $docLink . '</p>';
1858 }
1859 }
1860
1861 include 'error.html';
1862 exit();
1863 }