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