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