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/.
8 * Copyright (c) 2006-7, SilverStripe Limited - www.silverstripe.com
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
16 * Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
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.
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.
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.
39 * Changes and modifications (c) 2007-2017 by CiviCRM LLC
46 ini_set('max_execution_time', 3000);
48 if (stristr(PHP_OS
, 'WIN')) {
49 define('CIVICRM_DIRECTORY_SEPARATOR', '/');
50 define('CIVICRM_WINDOWS', 1);
53 define('CIVICRM_DIRECTORY_SEPARATOR', DIRECTORY_SEPARATOR
);
54 define('CIVICRM_WINDOWS', 0);
57 // set installation type - drupal
59 if (defined('PANTHEON_ENVIRONMENT')) {
60 ini_set('session.save_handler', 'files');
65 // unset civicrm session if any
66 if (array_key_exists('CiviCRM', $_SESSION)) {
67 unset($_SESSION['CiviCRM']);
70 if (isset($_GET['civicrm_install_type'])) {
71 $_SESSION['civicrm_install_type'] = $_GET['civicrm_install_type'];
74 if (!isset($_SESSION['civicrm_install_type'])) {
75 $_SESSION['civicrm_install_type'] = "drupal";
82 global $installDirPath;
83 global $installURLPath;
85 $installType = strtolower($_SESSION['civicrm_install_type']);
87 if ($installType == 'drupal' ||
$installType == 'backdrop') {
88 $crmPath = dirname(dirname($_SERVER['SCRIPT_FILENAME']));
89 $installDirPath = $installURLPath = '';
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
;
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);
102 $pkgPath = $crmPath . DIRECTORY_SEPARATOR
. 'packages';
104 require_once $crmPath . '/CRM/Core/ClassLoader.php';
105 CRM_Core_ClassLoader
::singleton()->register();
108 if (isset($_POST['loadGenerated'])) {
112 require_once dirname(__FILE__
) . CIVICRM_DIRECTORY_SEPARATOR
. 'langs.php';
113 foreach ($langs as $locale => $_) {
114 if ($locale == 'en_US') {
117 if (!file_exists(implode(CIVICRM_DIRECTORY_SEPARATOR
, array($crmPath, 'sql', "civicrm_data.$locale.mysql")))) {
118 unset($langs[$locale]);
123 // This is mostly sympbolic, since nothing we do during the install
124 // really requires CIVICRM_UF to be defined.
125 $installTypeToUF = array(
126 'wordpress' => 'WordPress',
127 'drupal' => 'Drupal',
128 'backdrop' => 'Backdrop',
131 $uf = (isset($installTypeToUF[$installType]) ?
$installTypeToUF[$installType] : 'Drupal');
132 define('CIVICRM_UF', $uf);
134 // Set the Locale (required by CRM_Core_Config)
138 $seedLanguage = 'en_US';
140 // CRM-16801 This validates that seedLanguage is valid by looking in $langs.
141 // NB: the variable is initial a $_REQUEST for the initial page reload,
142 // then becomes a $_POST when the installation form is submitted.
143 if (isset($_REQUEST['seedLanguage']) and isset($langs[$_REQUEST['seedLanguage']])) {
144 $seedLanguage = $_REQUEST['seedLanguage'];
145 $tsLocale = $_REQUEST['seedLanguage'];
148 $config = CRM_Core_Config
::singleton(FALSE);
149 $GLOBALS['civicrm_default_error_scope'] = NULL;
151 // The translation files are in the parent directory (l10n)
152 $i18n = CRM_Core_I18n
::singleton();
154 // Support for Arabic, Hebrew, Farsi, etc.
155 // Used in the template.html
156 $short_lang_code = CRM_Core_I18n_PseudoConstant
::shortForLong($tsLocale);
157 $text_direction = (CRM_Core_I18n
::isLanguageRTL($tsLocale) ?
'rtl' : 'ltr');
160 if ($installType == 'drupal') {
161 //CRM-6840 -don't force to install in sites/all/modules/
162 $object = new CRM_Utils_System_Drupal();
163 $cmsPath = $object->cmsRootPath();
165 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
166 $alreadyInstalled = file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
167 'sites' . CIVICRM_DIRECTORY_SEPARATOR
.
168 $siteDir . CIVICRM_DIRECTORY_SEPARATOR
.
169 'civicrm.settings.php'
172 elseif ($installType == 'backdrop') {
173 $object = new CRM_Utils_System_Backdrop();
174 $cmsPath = $object->cmsRootPath();
175 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
176 $alreadyInstalled = file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR
. 'civicrm.settings.php');
178 elseif ($installType == 'wordpress') {
179 $cmsPath = WP_PLUGIN_DIR
. DIRECTORY_SEPARATOR
. 'civicrm';
180 $upload_dir = wp_upload_dir();
181 $files_dirname = $upload_dir['basedir'] . DIRECTORY_SEPARATOR
. 'civicrm';
182 $wp_civi_settings = $upload_dir['basedir'] . DIRECTORY_SEPARATOR
. 'civicrm' . DIRECTORY_SEPARATOR
. 'civicrm.settings.php';
183 $wp_civi_settings_deprectated = CIVICRM_PLUGIN_DIR
. 'civicrm.settings.php';
184 if (file_exists($wp_civi_settings_deprectated)) {
185 $alreadyInstalled = $wp_civi_settings_deprectated;
187 elseif (file_exists($wp_civi_settings)) {
188 $alreadyInstalled = $wp_civi_settings;
192 if ($installType == 'drupal') {
193 // Lets check only /modules/.
194 $pattern = '/' . preg_quote(CIVICRM_DIRECTORY_SEPARATOR
. 'modules', CIVICRM_DIRECTORY_SEPARATOR
) . '/';
196 if (!preg_match($pattern, str_replace("\\", "/", $_SERVER['SCRIPT_FILENAME']))) {
197 $directory = implode(CIVICRM_DIRECTORY_SEPARATOR
, array('sites', 'all', 'modules'));
198 $errorTitle = ts("Oops! Please correct your install location");
199 $errorMsg = ts("Please untar (uncompress) your downloaded copy of CiviCRM in the <strong>%1</strong> directory below your Drupal root directory.", array(1 => $directory));
200 errorDisplayPage($errorTitle, $errorMsg);
204 if ($installType == 'backdrop') {
205 // Lets check only /modules/.
206 $pattern = '/' . preg_quote(CIVICRM_DIRECTORY_SEPARATOR
. 'modules', CIVICRM_DIRECTORY_SEPARATOR
) . '/';
208 if (!preg_match($pattern, str_replace("\\", "/", $_SERVER['SCRIPT_FILENAME']))) {
209 $directory = 'modules';
210 $errorTitle = ts("Oops! Please correct your install location");
211 $errorMsg = ts("Please untar (uncompress) your downloaded copy of CiviCRM in the <strong>%1</strong> directory below your Drupal root directory.", array(1 => $directory));
212 errorDisplayPage($errorTitle, $errorMsg);
216 // Exit with error if CiviCRM has already been installed.
217 if ($alreadyInstalled) {
218 $errorTitle = ts("Oops! CiviCRM is already installed");
219 $settings_directory = $cmsPath;
221 if ($installType == 'drupal') {
222 $settings_directory = implode(CIVICRM_DIRECTORY_SEPARATOR
, array(
223 ts('[your Drupal root directory]'),
228 if ($installType == 'backdrop') {
229 $settings_directory = implode(CIVICRM_DIRECTORY_SEPARATOR
, array(
230 ts('[your Backdrop root directory]'),
235 $docLink = CRM_Utils_System
::docURL2('Installation and Upgrades', FALSE, ts('Installation Guide'), NULL, NULL, "wiki");
236 $errorMsg = ts("CiviCRM has already been installed. <ul><li>To <strong>start over</strong>, you must delete or rename the existing CiviCRM settings file - <strong>civicrm.settings.php</strong> - from <strong>%1</strong>.</li><li>To <strong>upgrade an existing installation</strong>, <a href='%2'>refer to the online documentation</a>.</li></ul>", array(1 => $settings_directory, 2 => $docLink));
237 errorDisplayPage($errorTitle, $errorMsg, FALSE);
240 $versionFile = $crmPath . CIVICRM_DIRECTORY_SEPARATOR
. 'civicrm-version.php';
241 if (file_exists($versionFile)) {
242 require_once $versionFile;
243 $civicrm_version = civicrmVersion();
246 $civicrm_version = 'unknown';
249 if ($installType == 'drupal') {
250 // Ensure that they have downloaded the correct version of CiviCRM
251 if ($civicrm_version['cms'] != 'Drupal' && $civicrm_version['cms'] != 'Drupal6') {
252 $errorTitle = ts("Oops! Incorrect CiviCRM version");
253 $errorMsg = ts("This installer can only be used for the Drupal version of CiviCRM.");
254 errorDisplayPage($errorTitle, $errorMsg);
257 define('DRUPAL_ROOT', $cmsPath);
258 $drupalVersionFiles = array(
260 implode(CIVICRM_DIRECTORY_SEPARATOR
, array($cmsPath, 'modules', 'system', 'system.module')),
262 implode(CIVICRM_DIRECTORY_SEPARATOR
, array($cmsPath, 'includes', 'bootstrap.inc')),
264 foreach ($drupalVersionFiles as $drupalVersionFile) {
265 if (file_exists($drupalVersionFile)) {
266 require_once $drupalVersionFile;
270 // Bootstrap Drupal to get settings
271 drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION
);
273 if (!defined('VERSION') or version_compare(VERSION
, '6.0') < 0) {
274 $errorTitle = ts("Oops! Incorrect Drupal version");
275 $errorMsg = ts("This version of CiviCRM can only be used with Drupal 6.x or 7.x. Please ensure that '%1' exists if you are running Drupal 7.0 and over.", array(1 => implode("' or '", $drupalVersionFiles)));
276 errorDisplayPage($errorTitle, $errorMsg);
279 elseif ($installType == 'backdrop') {
280 // Ensure that they have downloaded the correct version of CiviCRM
281 if ($civicrm_version['cms'] != 'Backdrop') {
282 $errorTitle = ts("Oops! Incorrect CiviCRM version");
283 $errorMsg = ts("This installer can only be used for the Backdrop version of CiviCRM.");
284 errorDisplayPage($errorTitle, $errorMsg);
287 define('BACKDROP_ROOT', $cmsPath);
289 $backdropVersionFiles = array(
291 implode(CIVICRM_DIRECTORY_SEPARATOR
, array($cmsPath, 'core', 'includes', 'bootstrap.inc')),
293 foreach ($backdropVersionFiles as $backdropVersionFile) {
294 if (file_exists($backdropVersionFile)) {
295 require_once $backdropVersionFile;
298 if (!defined('BACKDROP_VERSION') or version_compare(BACKDROP_VERSION
, '1.0') < 0) {
299 $errorTitle = ts("Oops! Incorrect Backdrop version");
300 $errorMsg = ts("This version of CiviCRM can only be used with Backdrop 1.x. Please ensure that '%1' exists if you are running Backdrop 1.0 and over.", array(1 => implode("' or '", $backdropVersionFiles)));
301 errorDisplayPage($errorTitle, $errorMsg);
304 elseif ($installType == 'wordpress') {
306 $civicrm_version['cms'] = 'WordPress';
308 // Ensure that they have downloaded the correct version of CiviCRM
309 if ($civicrm_version['cms'] != 'WordPress') {
310 $errorTitle = ts("Oops! Incorrect CiviCRM version");
311 $errorMsg = ts("This installer can only be used for the WordPress version of CiviCRM.");
312 errorDisplayPage($errorTitle, $errorMsg);
316 // Load CiviCRM database config
317 if (isset($_POST['mysql'])) {
318 $databaseConfig = $_POST['mysql'];
321 if ($installType == 'wordpress') {
322 // Load WP database config
323 if (isset($_POST['mysql'])) {
324 $databaseConfig = $_POST['mysql'];
327 $databaseConfig = array(
329 "username" => DB_USER
,
330 "password" => DB_PASSWORD
,
331 "database" => DB_NAME
,
336 if ($installType == 'drupal') {
337 // Load drupal database config
338 if (isset($_POST['drupal'])) {
339 $drupalConfig = $_POST['drupal'];
342 $drupalConfig = array(
343 "server" => $databases['default']['default']['host'] . ':' . $databases['default']['default']['port'],
344 "username" => $databases['default']['default']['username'],
345 "password" => $databases['default']['default']['password'],
346 "database" => $databases['default']['default']['database'],
351 if ($installType == 'backdrop') {
352 // Load backdrop database config
353 if (isset($_POST['backdrop'])) {
354 $backdropConfig = $_POST['backdrop'];
357 $backdropConfig = array(
358 "server" => "localhost",
359 "username" => "backdrop",
361 "database" => "backdrop",
366 // By default set CiviCRM database to be same as CMS database
367 if (!isset($databaseConfig)) {
368 if (($installType == 'drupal') && (isset($drupalConfig))) {
369 $databaseConfig = $drupalConfig;
371 if (($installType == 'backdrop') && (isset($backdropConfig))) {
372 $databaseConfig = $backdropConfig;
376 // Check requirements
377 $req = new InstallRequirements();
380 if ($req->hasErrors()) {
381 $hasErrorOtherThanDatabase = TRUE;
384 if ($databaseConfig) {
385 $dbReq = new InstallRequirements();
386 $dbReq->checkdatabase($databaseConfig, 'CiviCRM');
387 if ($installType == 'drupal') {
388 $dbReq->checkdatabase($drupalConfig, 'Drupal');
390 if ($installType == 'backdrop') {
391 $dbReq->checkdatabase($backdropConfig, 'Backdrop');
396 if (isset($_POST['go']) && !$req->hasErrors() && !$dbReq->hasErrors()) {
397 // Confirm before reinstalling
398 if (!isset($_POST['force_reinstall']) && $alreadyInstalled) {
399 include $installDirPath . 'template.html';
402 $inst = new Installer();
403 $inst->install($_POST);
406 // Show the config form
409 include $installDirPath . 'template.html';
413 * This class checks requirements
414 * Each of the requireXXX functions takes an argument which gives a user description of the test. It's an array
416 * $description[0] - The test category
417 * $description[1] - The test title
418 * $description[2] - The test error to show, if it goes wrong
420 class InstallRequirements
{
421 var $errors, $warnings, $tests, $conn;
423 // @see CRM_Upgrade_Form::MINIMUM_THREAD_STACK
424 const MINIMUM_THREAD_STACK
= 192;
427 * Just check that the database configuration is okay.
428 * @param $databaseConfig
431 public function checkdatabase($databaseConfig, $dbName) {
432 if ($this->requireFunction('mysqli_connect',
434 ts("PHP Configuration"),
436 ts("MySQL support not included in PHP."),
440 $this->requireMySQLServer($databaseConfig['server'],
442 ts("MySQL %1 Configuration", array(1 => $dbName)),
443 ts("Does the server exist?"),
444 ts("Can't find the a MySQL server on '%1'.", array(1 => $databaseConfig['server'])),
445 $databaseConfig['server'],
448 if ($this->requireMysqlConnection($databaseConfig['server'],
449 $databaseConfig['username'],
450 $databaseConfig['password'],
452 ts("MySQL %1 Configuration", array(1 => $dbName)),
453 ts("Are the access credentials correct?"),
454 ts("That username/password doesn't work"),
458 @$this->requireMySQLVersion("5.1",
460 ts("MySQL %1 Configuration", array(1 => $dbName)),
461 ts("MySQL version at least %1", array(1 => '5.1')),
462 ts("MySQL version %1 or higher is required, you are running MySQL %2.", array(1 => '5.1', 2 => mysqli_get_server_info($this->conn
))),
463 ts("MySQL %1", array(1 => mysqli_get_server_info($this->conn
))),
466 $this->requireMySQLAutoIncrementIncrementOne($databaseConfig['server'],
467 $databaseConfig['username'],
468 $databaseConfig['password'],
470 ts("MySQL %1 Configuration", array(1 => $dbName)),
471 ts("Is auto_increment_increment set to 1"),
472 ts("An auto_increment_increment value greater than 1 is not currently supported. Please see issue CRM-7923 for further details and potential workaround."),
475 $testDetails = array(
476 ts("MySQL %1 Configuration", array(1 => $dbName)),
477 ts("Is the provided database name valid?"),
478 ts("The database name provided is not valid. Please use only 0-9, a-z, A-Z, _ and - as characters in the name."),
480 if (!CRM_Core_DAO
::requireSafeDBName($databaseConfig['database'])) {
481 $this->error($testDetails);
485 $this->testing($testDetails);
487 $this->requireMySQLThreadStack($databaseConfig['server'],
488 $databaseConfig['username'],
489 $databaseConfig['password'],
490 $databaseConfig['database'],
491 self
::MINIMUM_THREAD_STACK
,
493 ts("MySQL %1 Configuration", array(1 => $dbName)),
494 ts("Does MySQL thread_stack meet minimum (%1k)", array(1 => self
::MINIMUM_THREAD_STACK
)),
496 // "The MySQL thread_stack does not meet minimum " . CRM_Upgrade_Form::MINIMUM_THREAD_STACK . "k. Please update thread_stack in my.cnf.",
500 $onlyRequire = ($dbName == 'Drupal' ||
$dbName == 'Backdrop') ?
TRUE : FALSE;
501 $this->requireDatabaseOrCreatePermissions(
502 $databaseConfig['server'],
503 $databaseConfig['username'],
504 $databaseConfig['password'],
505 $databaseConfig['database'],
507 ts("MySQL %1 Configuration", array(1 => $dbName)),
508 ts("Can I access/create the database?"),
509 ts("I can't create new databases and the database '%1' doesn't exist.", array(1 => $databaseConfig['database'])),
513 if ($dbName != 'Drupal' && $dbName != 'Backdrop') {
514 $this->requireMySQLInnoDB($databaseConfig['server'],
515 $databaseConfig['username'],
516 $databaseConfig['password'],
517 $databaseConfig['database'],
519 ts("MySQL %1 Configuration", array(1 => $dbName)),
520 ts("Can I access/create InnoDB tables in the database?"),
521 ts("Unable to create InnoDB tables. MySQL InnoDB support is required for CiviCRM but is either not available or not enabled in this MySQL database server."),
524 $this->requireMySQLTempTables($databaseConfig['server'],
525 $databaseConfig['username'],
526 $databaseConfig['password'],
527 $databaseConfig['database'],
529 ts("MySQL %1 Configuration", array(1 => $dbName)),
530 ts('Can I create temporary tables in the database?'),
531 ts('Unable to create temporary tables. This MySQL user is missing the CREATE TEMPORARY TABLES privilege.'),
534 $this->requireMySQLLockTables($databaseConfig['server'],
535 $databaseConfig['username'],
536 $databaseConfig['password'],
537 $databaseConfig['database'],
539 ts("MySQL %1 Configuration", array(1 => $dbName)),
540 ts('Can I create lock tables in the database?'),
541 ts('Unable to lock tables. This MySQL user is missing the LOCK TABLES privilege.'),
544 $this->requireMySQLTrigger($databaseConfig['server'],
545 $databaseConfig['username'],
546 $databaseConfig['password'],
547 $databaseConfig['database'],
549 ts("MySQL %1 Configuration", array(1 => $dbName)),
550 ts('Can I create triggers in the database?'),
551 ts('Unable to create triggers. This MySQL user is missing the CREATE TRIGGERS privilege.'),
559 * Connect via mysqli.
561 * This is exactly the same as mysqli_connect(), except that it accepts
562 * the port as part of the `$host`.
564 * @param string $host
565 * Ex: 'localhost', 'localhost:3307', '127.0.0.1:3307', '[::1]', '[::1]:3307'.
566 * @param string $username
567 * @param string $password
568 * @param string $database
571 protected function connect($host, $username, $password, $database = '') {
572 $hostParts = explode(':', $host);
573 if (count($hostParts) > 1 && strrpos($host, ']') !== strlen($host) - 1) {
574 $port = array_pop($hostParts);
575 $host = implode(':', $hostParts);
580 $conn = @mysqli_connect
($host, $username, $password, $database, $port);
585 * Check everything except the database.
587 public function check() {
588 global $crmPath, $installType;
590 $this->errors
= NULL;
592 // See also: CRM_Upgrade_Incremental_General::MIN_INSTALL_PHP_VER
593 $this->requirePHPVersion('5.3.4', array(
594 ts("PHP Configuration"),
595 ts("PHP5 installed"),
597 ts("PHP version %1", array(1 => phpversion())),
600 // Check that we can identify the root folder successfully
601 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR
. 'README.md',
603 ts("File permissions"),
604 ts("Does the webserver know where files are stored?"),
605 ts("The webserver isn't letting me identify where files are stored."),
611 // CRM-6485: make sure the path does not contain PATH_SEPARATOR, as we don’t know how to escape it
612 $this->requireNoPathSeparator(
614 ts("File permissions"),
615 ts('Does the CiviCRM path contain PATH_SEPARATOR?'),
616 ts('The path %1 contains PATH_SEPARATOR (the %2 character).', array(1 => $this->getBaseDir(), 2 => PATH_SEPARATOR
)),
621 $requiredDirectories = array('CRM', 'packages', 'templates', 'js', 'api', 'i', 'sql');
622 foreach ($requiredDirectories as $dir) {
623 $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR
. $dir,
625 ts("File permissions"),
626 ts("Folder '%1' exists?", array(1 => $dir)),
627 ts("There is no '%1' folder.", array(1 => $dir)),
632 $configIDSiniDir = NULL;
634 $siteDir = getSiteDir($cmsPath, $_SERVER['SCRIPT_FILENAME']);
635 if ($installType == 'drupal') {
637 // make sure that we can write to sites/default and files/
638 $writableDirectories = array(
639 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
640 'sites' . CIVICRM_DIRECTORY_SEPARATOR
.
641 $siteDir . CIVICRM_DIRECTORY_SEPARATOR
.
643 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
644 'sites' . CIVICRM_DIRECTORY_SEPARATOR
.
648 elseif ($installType == 'backdrop') {
650 // make sure that we can write to sites/default and files/
651 $writableDirectories = array(
652 $cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
657 elseif ($installType == 'wordpress') {
658 // make sure that we can write to uploads/civicrm/
659 $upload_dir = wp_upload_dir();
660 $files_dirname = $upload_dir['basedir'] . DIRECTORY_SEPARATOR
. 'civicrm';
661 if (!file_exists($files_dirname)) {
662 wp_mkdir_p($files_dirname);
664 $writableDirectories = array($files_dirname);
667 foreach ($writableDirectories as $dir) {
668 $dirName = CIVICRM_WINDOWS ?
$dir : CIVICRM_DIRECTORY_SEPARATOR
. $dir;
669 $testDetails = array(
670 ts("File permissions"),
671 ts("Is the %1 folder writeable?", array(1 => $dir)),
674 $this->requireWriteable($dirName, $testDetails, TRUE);
677 //check for Config.IDS.ini, file may exist in re-install
678 $configIDSiniDir = array($cmsPath, 'sites', $siteDir, 'files', 'civicrm', 'upload', 'Config.IDS.ini');
680 if (is_array($configIDSiniDir) && !empty($configIDSiniDir)) {
681 $configIDSiniFile = implode(CIVICRM_DIRECTORY_SEPARATOR
, $configIDSiniDir);
682 if (file_exists($configIDSiniFile)) {
683 unlink($configIDSiniFile);
687 // Check for rewriting
688 if (isset($_SERVER['SERVER_SOFTWARE'])) {
689 $webserver = strip_tags(trim($_SERVER['SERVER_SOFTWARE']));
691 elseif (isset($_SERVER['SERVER_SIGNATURE'])) {
692 $webserver = strip_tags(trim($_SERVER['SERVER_SIGNATURE']));
695 if ($webserver == '') {
696 $webserver = ts("I can't tell what webserver you are running");
699 // Check for $_SERVER configuration
700 $this->requireServerVariables(array('SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME'), array(
701 ts("Webserver config"),
702 ts("Recognised webserver"),
703 ts("You seem to be using an unsupported webserver. The server variables SCRIPT_NAME, HTTP_HOST, SCRIPT_FILENAME need to be set."),
706 // Check for MySQL support
707 $this->requireFunction('mysqli_connect', array(
708 ts("PHP Configuration"),
710 ts("MySQL support not included in PHP."),
713 // Check for XML support
714 $this->requireFunction('simplexml_load_file', array(
715 ts("PHP Configuration"),
716 ts("SimpleXML support"),
717 ts("SimpleXML support not included in PHP."),
720 // Check for JSON support
721 $this->requireFunction('json_encode', array(
722 ts("PHP Configuration"),
724 ts("JSON support not included in PHP."),
727 // Check for xcache_isset and emit warning if exists
728 $this->checkXCache(array(
729 ts("PHP Configuration"),
730 ts("XCache compatibility"),
731 ts("XCache is installed and there are known compatibility issues between XCache and CiviCRM. Consider using an alternative PHP caching mechanism or disable PHP caching altogether."),
734 // Check memory allocation
735 $this->requireMemory(32 * 1024 * 1024,
738 ts("PHP Configuration"),
739 ts("Memory allocated (PHP config option 'memory_limit')"),
740 ts("CiviCRM needs a minimum of %1 MB allocated to PHP, but recommends %2 MB.", array(1 => 32, 2 => 64)),
741 ini_get("memory_limit"),
745 return $this->errors
;
750 * @param $recommended
751 * @param $testDetails
753 public function requireMemory($min, $recommended, $testDetails) {
754 $this->testing($testDetails);
755 $mem = $this->getPHPMemory();
757 if ($mem < $min && $mem > 0) {
758 $testDetails[2] .= " " . ts("You only have %1 allocated", array(1 => ini_get("memory_limit")));
759 $this->error($testDetails);
761 elseif ($mem < $recommended && $mem > 0) {
762 $testDetails[2] .= " " . ts("You only have %1 allocated", array(1 => ini_get("memory_limit")));
763 $this->warning($testDetails);
766 $testDetails[2] .= " " . ts("We can't determine how much memory you have allocated. Install only if you're sure you've allocated at least %1 MB.", array(1 => 32));
767 $this->warning($testDetails);
774 public function getPHPMemory() {
775 $memString = ini_get("memory_limit");
777 switch (strtolower(substr($memString, -1))) {
779 return round(substr($memString, 0, -1) * 1024);
782 return round(substr($memString, 0, -1) * 1024 * 1024);
785 return round(substr($memString, 0, -1) * 1024 * 1024 * 1024);
788 return round($memString);
792 public function listErrors() {
794 echo "<p>" . ts("The following problems are preventing me from installing CiviCRM:") . "</p>";
795 foreach ($this->errors
as $error) {
796 echo "<li>" . htmlentities($error) . "</li>";
802 * @param null $section
804 public function showTable($section = NULL) {
806 $tests = $this->tests
[$section];
807 echo "<table class=\"testResults\" width=\"100%\">";
808 foreach ($tests as $test => $result) {
809 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
814 foreach ($this->tests
as $section => $tests) {
815 echo "<h3>$section</h3>";
816 echo "<table class=\"testResults\" width=\"100%\">";
818 foreach ($tests as $test => $result) {
819 echo "<tr class=\"$result[0]\"><td>$test</td><td>" . nl2br(htmlentities($result[1])) . "</td></tr>";
827 * @param string $funcName
828 * @param $testDetails
832 public function requireFunction($funcName, $testDetails) {
833 $this->testing($testDetails);
835 if (!function_exists($funcName)) {
836 $this->error($testDetails);
845 * @param $testDetails
847 public function checkXCache($testDetails) {
848 if (function_exists('xcache_isset') &&
849 ini_get('xcache.size') > 0
851 $this->testing($testDetails);
852 $this->warning($testDetails);
858 * @param $testDetails
859 * @param null $maxVersion
861 public function requirePHPVersion($minVersion, $testDetails, $maxVersion = NULL) {
863 $this->testing($testDetails);
865 $phpVersion = phpversion();
866 $aboveMinVersion = version_compare($phpVersion, $minVersion) >= 0;
867 $belowMaxVersion = $maxVersion ?
version_compare($phpVersion, $maxVersion) < 0 : TRUE;
869 if ($aboveMinVersion && $belowMaxVersion) {
870 if (version_compare(phpversion(), CRM_Upgrade_Incremental_General
::MIN_RECOMMENDED_PHP_VER
) < 0) {
871 $testDetails[2] = ts('This webserver is running an outdated version of PHP (%1). It is strongly recommended to upgrade to PHP %2 or later, as older versions can present a security risk.', array(
873 2 => CRM_Upgrade_Incremental_General
::MIN_RECOMMENDED_PHP_VER
,
875 $this->warning($testDetails);
880 if (!$testDetails[2]) {
881 if (!$aboveMinVersion) {
882 $testDetails[2] = ts("You need PHP version %1 or later, only %2 is installed. Please upgrade your server, or ask your web-host to do so.", array(1 => $minVersion, 2 => $phpVersion));
885 $testDetails[2] = ts("PHP version %1 is not supported. PHP version earlier than %2 is required. You might want to downgrade your server, or ask your web-host to do so.", array(1 => $maxVersion, 2 => $phpVersion));
889 $this->error($testDetails);
893 * @param string $filename
894 * @param $testDetails
895 * @param bool $absolute
897 public function requireFile($filename, $testDetails, $absolute = FALSE) {
898 $this->testing($testDetails);
900 $filename = $this->getBaseDir() . $filename;
902 if (!file_exists($filename)) {
903 $testDetails[2] .= " (" . ts("file '%1' not found", array(1 => $filename)) . ')';
904 $this->error($testDetails);
909 * @param $testDetails
911 public function requireNoPathSeparator($testDetails) {
912 $this->testing($testDetails);
913 if (substr_count($this->getBaseDir(), PATH_SEPARATOR
)) {
914 $this->error($testDetails);
919 * @param string $filename
920 * @param $testDetails
922 public function requireNoFile($filename, $testDetails) {
923 $this->testing($testDetails);
924 $filename = $this->getBaseDir() . $filename;
925 if (file_exists($filename)) {
926 $testDetails[2] .= " (" . ts("file '%1' found", array(1 => $filename)) . ")";
927 $this->error($testDetails);
932 * @param string $filename
933 * @param $testDetails
935 public function moveFileOutOfTheWay($filename, $testDetails) {
936 $this->testing($testDetails);
937 $filename = $this->getBaseDir() . $filename;
938 if (file_exists($filename)) {
939 if (file_exists("$filename.bak")) {
942 rename($filename, "$filename.bak");
947 * @param string $filename
948 * @param $testDetails
949 * @param bool $absolute
951 public function requireWriteable($filename, $testDetails, $absolute = FALSE) {
952 $this->testing($testDetails);
954 $filename = $this->getBaseDir() . $filename;
957 if (!is_writable($filename)) {
959 if (function_exists('posix_getpwuid')) {
960 $user = posix_getpwuid(posix_geteuid());
961 $name = '- ' . $user['name'] . ' -';
964 if (!isset($testDetails[2])) {
965 $testDetails[2] = NULL;
967 $testDetails[2] .= ts("The user account used by your web-server %1 needs to be granted write access to the following directory in order to configure the CiviCRM settings file:", array(1 => $name)) . "\n$filename";
968 $this->error($testDetails);
973 * @param string $moduleName
974 * @param $testDetails
976 public function requireApacheModule($moduleName, $testDetails) {
977 $this->testing($testDetails);
978 if (!in_array($moduleName, apache_get_modules())) {
979 $this->error($testDetails);
985 * @param string $username
987 * @param $testDetails
989 public function requireMysqlConnection($server, $username, $password, $testDetails) {
990 $this->testing($testDetails);
991 $this->conn
= $this->connect($server, $username, $password);
997 $testDetails[2] .= ": " . mysqli_connect_error();
998 $this->error($testDetails);
1004 * @param $testDetails
1006 public function requireMySQLServer($server, $testDetails) {
1007 $this->testing($testDetails);
1008 $conn = $this->connect($server, NULL, NULL);
1010 if ($conn ||
mysqli_connect_errno() < 2000) {
1014 $testDetails[2] .= ": " . mysqli_connect_error();
1015 $this->error($testDetails);
1021 * @param $testDetails
1023 public function requireMySQLVersion($version, $testDetails) {
1024 $this->testing($testDetails);
1026 if (!mysqli_get_server_info($this->conn
)) {
1027 $testDetails[2] = ts('Cannot determine the version of MySQL installed. Please ensure at least version %1 is installed.', array(1 => $version));
1028 $this->warning($testDetails);
1031 list($majorRequested, $minorRequested) = explode('.', $version);
1032 list($majorHas, $minorHas) = explode('.', mysqli_get_server_info($this->conn
));
1034 if (($majorHas > $majorRequested) ||
($majorHas == $majorRequested && $minorHas >= $minorRequested)) {
1038 $testDetails[2] .= "{$majorHas}.{$minorHas}.";
1039 $this->error($testDetails);
1046 * @param string $username
1049 * @param $testDetails
1051 public function requireMySQLInnoDB($server, $username, $password, $database, $testDetails) {
1052 $this->testing($testDetails);
1053 $conn = $this->connect($server, $username, $password);
1055 $testDetails[2] .= ' ' . ts("Could not determine if MySQL has InnoDB support. Assuming no.");
1056 $this->error($testDetails);
1060 $innodb_support = FALSE;
1061 $result = mysqli_query($conn, "SHOW ENGINES");
1062 while ($values = mysqli_fetch_array($result)) {
1063 if ($values['Engine'] == 'InnoDB') {
1064 if (strtolower($values['Support']) == 'yes' ||
1065 strtolower($values['Support']) == 'default'
1067 $innodb_support = TRUE;
1071 if ($innodb_support) {
1072 $testDetails[3] = ts('MySQL server does have InnoDB support');
1075 $testDetails[2] .= ' ' . ts('Could not determine if MySQL has InnoDB support. Assuming no');
1081 * @param string $username
1084 * @param $testDetails
1086 public function requireMySQLTempTables($server, $username, $password, $database, $testDetails) {
1087 $this->testing($testDetails);
1088 $conn = $this->connect($server, $username, $password);
1090 $testDetails[2] = ts('Could not login to the database.');
1091 $this->error($testDetails);
1095 if (!@mysqli_select_db
($conn, $database)) {
1096 $testDetails[2] = ts('Could not select the database.');
1097 $this->error($testDetails);
1101 $result = mysqli_query($conn, 'CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)');
1103 $testDetails[2] = ts('Could not create a temp table.');
1104 $this->error($testDetails);
1106 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
1111 * @param string $username
1114 * @param $testDetails
1116 public function requireMySQLTrigger($server, $username, $password, $database, $testDetails) {
1117 $this->testing($testDetails);
1118 $conn = $this->connect($server, $username, $password);
1120 $testDetails[2] = ts('Could not login to the database.');
1121 $this->error($testDetails);
1125 if (!@mysqli_select_db
($conn, $database)) {
1126 $testDetails[2] = ts('Could not select the database.');
1127 $this->error($testDetails);
1131 $result = mysqli_query($conn, 'CREATE TABLE civicrm_install_temp_table_test (test text)');
1133 $testDetails[2] = ts('Could not create a table in the database.');
1134 $this->error($testDetails);
1137 $result = mysqli_query($conn, 'CREATE TRIGGER civicrm_install_temp_table_test_trigger BEFORE INSERT ON civicrm_install_temp_table_test FOR EACH ROW BEGIN END');
1139 mysqli_query($conn, 'DROP TABLE civicrm_install_temp_table_test');
1140 $testDetails[2] = ts('Could not create a database trigger.');
1141 $this->error($testDetails);
1144 mysqli_query($conn, 'DROP TRIGGER civicrm_install_temp_table_test_trigger');
1145 mysqli_query($conn, 'DROP TABLE civicrm_install_temp_table_test');
1151 * @param string $username
1154 * @param $testDetails
1156 public function requireMySQLLockTables($server, $username, $password, $database, $testDetails) {
1157 $this->testing($testDetails);
1158 $conn = $this->connect($server, $username, $password);
1160 $testDetails[2] = ts('Could not connect to the database server.');
1161 $this->error($testDetails);
1165 if (!@mysqli_select_db
($conn, $database)) {
1166 $testDetails[2] = ts('Could not select the database.');
1167 $this->error($testDetails);
1171 $result = mysqli_query($conn, 'CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)');
1173 $testDetails[2] = ts('Could not create a table in the database.');
1174 $this->error($testDetails);
1178 $result = mysqli_query($conn, 'LOCK TABLES civicrm_install_temp_table_test WRITE');
1180 $testDetails[2] = ts('Could not obtain a write lock for the database table.');
1181 $this->error($testDetails);
1182 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
1186 $result = mysqli_query($conn, 'UNLOCK TABLES');
1188 $testDetails[2] = ts('Could not release the lock for the database table.');
1189 $this->error($testDetails);
1190 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
1194 $result = mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
1199 * @param string $username
1201 * @param $testDetails
1203 public function requireMySQLAutoIncrementIncrementOne($server, $username, $password, $testDetails) {
1204 $this->testing($testDetails);
1205 $conn = $this->connect($server, $username, $password);
1207 $testDetails[2] = ts('Could not connect to the database server.');
1208 $this->error($testDetails);
1212 $result = mysqli_query($conn, "SHOW variables like 'auto_increment_increment'");
1214 $testDetails[2] = ts('Could not query database server variables.');
1215 $this->error($testDetails);
1219 $values = mysqli_fetch_row($result);
1220 if ($values[1] == 1) {
1221 $testDetails[3] = ts('MySQL server auto_increment_increment is 1');
1224 $this->error($testDetails);
1231 * @param string $username
1234 * @param $minValueKB
1235 * @param $testDetails
1237 public function requireMySQLThreadStack($server, $username, $password, $database, $minValueKB, $testDetails) {
1238 $this->testing($testDetails);
1239 $conn = $this->connect($server, $username, $password);
1241 $testDetails[2] = ts('Could not connect to the database server.');
1242 $this->error($testDetails);
1246 if (!@mysqli_select_db
($conn, $database)) {
1247 $testDetails[2] = ts('Could not select the database.');
1248 $this->error($testDetails);
1252 $result = mysqli_query($conn, "SHOW VARIABLES LIKE 'thread_stack'"); // bytes => kb
1254 $testDetails[2] = ts('Could not get information about the thread_stack of the database.');
1255 $this->error($testDetails);
1258 $values = mysqli_fetch_row($result);
1259 if ($values[1] < (1024 * $minValueKB)) {
1260 $testDetails[2] = ts('MySQL "thread_stack" is %1 kb', array(1 => ($values[1] / 1024)));
1261 $this->error($testDetails);
1268 * @param string $username
1271 * @param $testDetails
1272 * @param bool $onlyRequire
1274 public function requireDatabaseOrCreatePermissions(
1280 $onlyRequire = FALSE
1282 $this->testing($testDetails);
1283 $conn = $this->connect($server, $username, $password);
1286 if (@mysqli_select_db
($conn, $database)) {
1287 $okay = "Database '$database' exists";
1289 elseif ($onlyRequire) {
1290 $testDetails[2] = ts("The database: '%1' does not exist.", array(1 => $database));
1291 $this->error($testDetails);
1295 $query = sprintf("CREATE DATABASE %s", mysqli_real_escape_string($conn, $database));
1296 if (@mysqli_query
($conn, $query)) {
1297 $okay = ts("Able to create a new database.");
1300 $testDetails[2] .= " (" . ts("user '%1' doesn't have CREATE DATABASE permissions.", array(1 => $username)) . ")";
1301 $this->error($testDetails);
1307 $testDetails[3] = $okay;
1308 $this->testing($testDetails);
1314 * @param $errorMessage
1316 public function requireServerVariables($varNames, $errorMessage) {
1317 //$this->testing($testDetails);
1318 foreach ($varNames as $varName) {
1319 if (!$_SERVER[$varName]) {
1320 $missing[] = '$_SERVER[' . $varName . ']';
1323 if (!isset($missing)) {
1327 $testDetails[2] = " (" . ts('the following PHP variables are missing: %1', array(1 => implode(", ", $missing))) . ")";
1328 $this->error($testDetails);
1333 * @param $testDetails
1337 public function isRunningApache($testDetails) {
1338 $this->testing($testDetails);
1339 if (function_exists('apache_get_modules') ||
stristr($_SERVER['SERVER_SIGNATURE'], 'Apache')) {
1343 $this->warning($testDetails);
1350 public function getBaseDir() {
1351 return dirname($_SERVER['SCRIPT_FILENAME']) . CIVICRM_DIRECTORY_SEPARATOR
;
1355 * @param $testDetails
1357 public function testing($testDetails) {
1358 if (!$testDetails) {
1362 $section = $testDetails[0];
1363 $test = $testDetails[1];
1365 $message = ts("OK");
1366 if (isset($testDetails[3])) {
1367 $message .= " ($testDetails[3])";
1370 $this->tests
[$section][$test] = array("good", $message);
1374 * @param $testDetails
1376 public function error($testDetails) {
1377 $section = $testDetails[0];
1378 $test = $testDetails[1];
1380 $this->tests
[$section][$test] = array("error", $testDetails[2]);
1381 $this->errors
[] = $testDetails;
1385 * @param $testDetails
1387 public function warning($testDetails) {
1388 $section = $testDetails[0];
1389 $test = $testDetails[1];
1391 $this->tests
[$section][$test] = array("warning", $testDetails[2]);
1392 $this->warnings
[] = $testDetails;
1398 public function hasErrors() {
1399 return count($this->errors
);
1405 public function hasWarnings() {
1406 return count($this->warnings
);
1414 class Installer
extends InstallRequirements
{
1421 public function createDatabaseIfNotExists($server, $username, $password, $database) {
1422 $conn = $this->connect($server, $username, $password);
1424 if (@mysqli_select_db
($conn, $database)) {
1425 // skip if database already present
1428 $query = sprintf("CREATE DATABASE %s", mysqli_real_escape_string($conn, $database));
1429 if (@mysqli_query
($conn, $query)) {
1432 $errorTitle = ts("Oops! Could not create database %1", array(1 => $database));
1433 $errorMsg = ts("We encountered an error when attempting to create the database. Please check your MySQL server permissions and the database name and try again.");
1434 errorDisplayPage($errorTitle, $errorMsg);
1443 public function install($config) {
1444 global $installDirPath;
1446 // create database if does not exists
1447 $this->createDatabaseIfNotExists($config['mysql']['server'],
1448 $config['mysql']['username'],
1449 $config['mysql']['password'],
1450 $config['mysql']['database']
1453 global $installDirPath;
1456 require_once $installDirPath . 'civicrm.php';
1457 civicrm_main($config);
1459 if (!$this->errors
) {
1460 global $installType, $installURLPath;
1462 $registerSiteURL = "https://civicrm.org/register-site";
1463 $commonOutputMessage
1464 = "<li>" . ts("Have you registered this site at CiviCRM.org? If not, please help strengthen the CiviCRM ecosystem by taking a few minutes to <a %1>fill out the site registration form</a>. The information collected will help us prioritize improvements, target our communications and build the community. If you have a technical role for this site, be sure to check Keep in Touch to receive technical updates (a low volume mailing list).", array(1 => "href='$registerSiteURL' target='_blank'")) . "</li>"
1465 . "<li>" . ts("We have integrated KCFinder with CKEditor and TinyMCE. This allows a user to upload images. All uploaded images are public.") . "</li>";
1470 $installType == 'drupal' &&
1471 version_compare(VERSION
, '7.0-rc1') >= 0
1477 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1478 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1479 $output .= '<head>';
1480 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1481 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
1482 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1483 $output .= '</head>';
1484 $output .= '<body>';
1485 $output .= '<div style="padding: 1em;"><p class="good">' . ts('CiviCRM has been successfully installed') . '</p>';
1488 $drupalURL = civicrm_cms_base();
1489 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/people/permissions";
1490 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1492 $output .= "<li>" . ts("Drupal user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(1 => "target='_blank' href='{$drupalPermissionsURL}'", 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'")) . "</li>";
1493 $output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$drupalURL'")) . "</li>";
1494 $output .= $commonOutputMessage;
1496 // automatically enable CiviCRM module once it is installed successfully.
1497 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1498 global $cmsPath, $crmPath;
1500 // relative / abosolute paths are not working for drupal, hence using chdir()
1503 // Force the re-initialisation of the config singleton on the next call
1504 // since so far, we had used the Config object without loading the DB.
1505 $c = CRM_Core_Config
::singleton(FALSE);
1508 include_once "./includes/bootstrap.inc";
1509 include_once "./includes/unicode.inc";
1511 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL
);
1513 // prevent session information from being saved.
1514 drupal_save_session(FALSE);
1516 // Force the current user to anonymous.
1517 $original_user = $GLOBALS['user'];
1518 $GLOBALS['user'] = drupal_anonymous_user();
1520 // explicitly setting error reporting, since we cannot handle drupal related notices
1523 // rebuild modules, so that civicrm is added
1524 system_rebuild_module_data();
1526 // now enable civicrm module.
1527 module_enable(array('civicrm', 'civicrmtheme'));
1529 // SystemInstallEvent will be called from here with the first call of CRM_Core_Config,
1530 // which calls Core_BAO_ConfigSetting::applyLocale(), who will default to calling
1531 // Civi::settings()->get('lcMessages');
1532 // Therefore, we need to pass the seedLanguage before that.
1533 global $civicrm_setting;
1534 $civicrm_setting['domain']['lcMessages'] = $config['seedLanguage'];
1536 // clear block, page, theme, and hook caches
1537 drupal_flush_all_caches();
1539 //add basic drupal permissions
1540 civicrm_install_set_drupal_perms();
1542 // restore the user.
1543 $GLOBALS['user'] = $original_user;
1544 drupal_save_session(TRUE);
1547 $output .= '</div>';
1548 $output .= '</body>';
1549 $output .= '</html>';
1553 $installType == 'backdrop'
1559 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1560 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1561 $output .= '<head>';
1562 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1563 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
1564 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1565 $output .= '</head>';
1566 $output .= '<body>';
1567 $output .= '<div style="padding: 1em;"><p class="good">' . ts('CiviCRM has been successfully installed') . '</p>';
1570 $backdropURL = civicrm_cms_base();
1571 $backdropPermissionsURL = "{$backdropURL}index.php?q=admin/config/people/permissions";
1572 $backdropURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1574 $output .= "<li>" . ts("Backdrop user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(1 => "target='_blank' href='{$backdropPermissionsURL}'", 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'")) . "</li>";
1575 $output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$backdropURL'")) . "</li>";
1576 $output .= $commonOutputMessage;
1578 // automatically enable CiviCRM module once it is installed successfully.
1579 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1580 global $cmsPath, $crmPath;
1582 // relative / abosolute paths are not working for drupal, hence using chdir()
1585 // Force the re-initialisation of the config singleton on the next call
1586 // since so far, we had used the Config object without loading the DB.
1587 $c = CRM_Core_Config
::singleton(FALSE);
1590 include_once "./core/includes/bootstrap.inc";
1591 include_once "./core/includes/unicode.inc";
1592 include_once "./core/includes/config.inc";
1594 backdrop_bootstrap(BACKDROP_BOOTSTRAP_FULL
);
1596 // prevent session information from being saved.
1597 backdrop_save_session(FALSE);
1599 // Force the current user to anonymous.
1600 $original_user = $GLOBALS['user'];
1601 $GLOBALS['user'] = backdrop_anonymous_user();
1603 // explicitly setting error reporting, since we cannot handle drupal related notices
1606 // rebuild modules, so that civicrm is added
1607 system_rebuild_module_data();
1609 // now enable civicrm module.
1610 module_enable(array('civicrm', 'civicrmtheme'));
1612 // clear block, page, theme, and hook caches
1613 backdrop_flush_all_caches();
1615 //add basic backdrop permissions
1616 civicrm_install_set_backdrop_perms();
1618 // restore the user.
1619 $GLOBALS['user'] = $original_user;
1620 backdrop_save_session(TRUE);
1622 //change the default language to one chosen
1623 if (isset($config['seedLanguage']) && $config['seedLanguage'] != 'en_US') {
1624 civicrm_api3('Setting', 'create', array(
1625 'domain_id' => 'current_domain',
1626 'lcMessages' => $config['seedLanguage'],
1632 $output .= '</div>';
1633 $output .= '</body>';
1634 $output .= '</html>';
1637 elseif ($installType == 'drupal' && version_compare(VERSION
, '6.0') >= 0) {
1641 $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1642 $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1643 $output .= '<head>';
1644 $output .= '<title>' . ts('CiviCRM Installed') . '</title>';
1645 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
1646 $output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
1647 $output .= '</head>';
1648 $output .= '<body>';
1649 $output .= '<div style="padding: 1em;"><p class="good">' . ts("CiviCRM has been successfully installed") . '</p>';
1652 $drupalURL = civicrm_cms_base();
1653 $drupalPermissionsURL = "{$drupalURL}index.php?q=admin/user/permissions";
1654 $drupalURL .= "index.php?q=civicrm/admin/configtask&reset=1";
1656 $output .= "<li>" . ts("Drupal user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(1 => "target='_blank' href='{$drupalPermissionsURL}'", 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'")) . "</li>";
1657 $output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$drupalURL'")) . "</li>";
1658 $output .= $commonOutputMessage;
1660 // explicitly setting error reporting, since we cannot handle drupal related notices
1663 // automatically enable CiviCRM module once it is installed successfully.
1664 // so we need to Bootstrap Drupal, so that we can call drupal hooks.
1665 global $cmsPath, $crmPath;
1667 // relative / abosolute paths are not working for drupal, hence using chdir()
1670 // Force the re-initialisation of the config singleton on the next call
1671 // since so far, we had used the Config object without loading the DB.
1672 $c = CRM_Core_Config
::singleton(FALSE);
1675 include_once "./includes/bootstrap.inc";
1676 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL
);
1678 // rebuild modules, so that civicrm is added
1679 module_rebuild_cache();
1681 // now enable civicrm module.
1682 module_enable(array('civicrm'));
1684 // clear block, page, theme, and hook caches
1685 drupal_flush_all_caches();
1687 //add basic drupal permissions
1688 db_query('UPDATE {permission} SET perm = CONCAT( perm, \', access CiviMail subscribe/unsubscribe pages, access all custom data, access uploaded files, make online contributions, profile create, profile edit, profile view, register for events, view event info\') WHERE rid IN (1, 2)');
1692 elseif ($installType == 'wordpress') {
1693 echo '<h1>' . ts('CiviCRM Installed') . '</h1>';
1694 echo '<div style="padding: 1em;"><p style="background-color: #0C0; border: 1px #070 solid; color: white;">' . ts("CiviCRM has been successfully installed") . '</p>';
1697 $cmsURL = civicrm_cms_base();
1698 $cmsURL .= "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/configtask&reset=1";
1699 $wpPermissionsURL = "wp-admin/admin.php?page=CiviCRM&q=civicrm/admin/access/wp-permissions&reset=1";
1701 $output .= "<li>" . ts("WordPress user permissions have been automatically set - giving Anonymous and Subscribers access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(1 => "target='_blank' href='{$wpPermissionsURL}'", 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'")) . "</li>";
1702 $output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$cmsURL'")) . "</li>";
1703 $output .= $commonOutputMessage;
1708 $c = CRM_Core_Config
::singleton(FALSE);
1710 $wpInstallRedirect = admin_url("?page=CiviCRM&q=civicrm&reset=1");
1712 window.location = '$wpInstallRedirect';
1717 return $this->errors
;
1722 function civicrm_install_set_drupal_perms() {
1723 if (!function_exists('db_select')) {
1724 db_query('UPDATE {permission} SET perm = CONCAT( perm, \', access CiviMail subscribe/unsubscribe pages, access all custom data, access uploaded files, make online contributions, profile listings and forms, register for events, view event info, view event participants\') WHERE rid IN (1, 2)');
1728 'access all custom data',
1729 'access uploaded files',
1730 'make online contributions',
1734 'register for events',
1736 'view event participants',
1737 'access CiviMail subscribe/unsubscribe pages',
1740 // Adding a permission that has not yet been assigned to a module by
1741 // a hook_permission implementation results in a database error.
1743 $allPerms = array_keys(module_invoke_all('permission'));
1744 foreach (array_diff($perms, $allPerms) as $perm) {
1746 'Cannot grant the %perm permission because it does not yet exist.',
1747 array('%perm' => $perm),
1751 $perms = array_intersect($perms, $allPerms);
1752 user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID
, $perms);
1753 user_role_grant_permissions(DRUPAL_ANONYMOUS_RID
, $perms);
1757 function civicrm_install_set_backdrop_perms() {
1759 'access all custom data',
1760 'access uploaded files',
1761 'make online contributions',
1765 'register for events',
1767 'view event participants',
1768 'access CiviMail subscribe/unsubscribe pages',
1771 // Adding a permission that has not yet been assigned to a module by
1772 // a hook_permission implementation results in a database error.
1774 $allPerms = array_keys(module_invoke_all('permission'));
1775 foreach (array_diff($perms, $allPerms) as $perm) {
1777 'Cannot grant the %perm permission because it does not yet exist.',
1778 array('%perm' => $perm),
1782 $perms = array_intersect($perms, $allPerms);
1783 user_role_grant_permissions(BACKDROP_AUTHENTICATED_ROLE
, $perms);
1784 user_role_grant_permissions(BACKDROP_ANONYMOUS_ROLE
, $perms);
1793 function getSiteDir($cmsPath, $str) {
1794 static $siteDir = '';
1800 $sites = CIVICRM_DIRECTORY_SEPARATOR
. 'sites' . CIVICRM_DIRECTORY_SEPARATOR
;
1801 $modules = CIVICRM_DIRECTORY_SEPARATOR
. 'modules' . CIVICRM_DIRECTORY_SEPARATOR
;
1802 preg_match("/" . preg_quote($sites, CIVICRM_DIRECTORY_SEPARATOR
) .
1803 "([\-a-zA-Z0-9_.]+)" .
1804 preg_quote($modules, CIVICRM_DIRECTORY_SEPARATOR
) . "/",
1805 $_SERVER['SCRIPT_FILENAME'], $matches
1807 $siteDir = isset($matches[1]) ?
$matches[1] : 'default';
1809 if (strtolower($siteDir) == 'all') {
1810 // For this case - use drupal's way of finding out multi-site directory
1811 $uri = explode(CIVICRM_DIRECTORY_SEPARATOR
, $_SERVER['SCRIPT_FILENAME']);
1812 $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
1813 for ($i = count($uri) - 1; $i > 0; $i--) {
1814 for ($j = count($server); $j > 0; $j--) {
1815 $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
1816 if (file_exists($cmsPath . CIVICRM_DIRECTORY_SEPARATOR
.
1817 'sites' . CIVICRM_DIRECTORY_SEPARATOR
. $dir
1824 $siteDir = 'default';
1831 * @param $errorTitle
1835 function errorDisplayPage($errorTitle, $errorMsg, $showRefer = TRUE) {
1837 $docLink = CRM_Utils_System
::docURL2('Installation and Upgrades', FALSE, 'Installation Guide', NULL, NULL, "wiki");
1839 if (function_exists('ts')) {
1840 $errorMsg .= '<p>' . ts("<a %1>Refer to the online documentation for more information</a>", array(1 => "href='$docLink'")) . '</p>';
1843 $errorMsg .= '<p>' . sprintf("<a %s>Refer to the online documentation for more information</a>", "href='$docLink'") . '</p>';
1847 include 'error.html';