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