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