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