Merge pull request #19321 from colemanw/profileGetFieldsFix
[civicrm-core.git] / CRM / Utils / Check / Component / Env.php
CommitLineData
1e927c45
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
1e927c45 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
1e927c45 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
1e927c45
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
1e927c45 16 */
3a0d0bbd 17class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
1e927c45 18
31e80d5a 19 /**
02b3ba91 20 * @return CRM_Utils_Check_Message[]
31e80d5a
CW
21 */
22 public function checkPhpVersion() {
be2fb01f 23 $messages = [];
cc1f4988 24 $phpVersion = phpversion();
31e80d5a 25
cc1f4988 26 if (version_compare($phpVersion, CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER) >= 0) {
31e80d5a 27 $messages[] = new CRM_Utils_Check_Message(
165aab59 28 __FUNCTION__,
cc1f4988 29 ts('This system uses PHP version %1 which meets or exceeds the recommendation of %2.',
be2fb01f 30 [
cc1f4988 31 1 => $phpVersion,
f955c30c 32 2 => preg_replace(';^(\d+\.\d+(?:\.[1-9]\d*)?).*$;', '\1', CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER),
be2fb01f 33 ]),
4094935a
AP
34 ts('PHP Up-to-Date'),
35 \Psr\Log\LogLevel::INFO,
36 'fa-server'
d0c1e96f
TO
37 );
38 }
cc1f4988 39 elseif (version_compare($phpVersion, CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER) >= 0) {
d0c1e96f
TO
40 $messages[] = new CRM_Utils_Check_Message(
41 __FUNCTION__,
cc1f4988 42 ts('This system uses PHP version %1. This meets the minimum recommendations and you do not need to upgrade immediately, but the preferred version is %2.',
be2fb01f 43 [
cc1f4988
CW
44 1 => $phpVersion,
45 2 => CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER,
be2fb01f 46 ]),
4094935a
AP
47 ts('PHP Out-of-Date'),
48 \Psr\Log\LogLevel::NOTICE,
49 'fa-server'
31e80d5a
CW
50 );
51 }
cc1f4988 52 elseif (version_compare($phpVersion, CRM_Upgrade_Incremental_General::MIN_INSTALL_PHP_VER) >= 0) {
31e80d5a 53 $messages[] = new CRM_Utils_Check_Message(
165aab59 54 __FUNCTION__,
96fdb289 55 ts('This system uses PHP version %1. This meets the minimum requirements for CiviCRM to function but is not recommended. At least PHP version %2 is recommended; the preferred version is %3.',
be2fb01f 56 [
cc1f4988
CW
57 1 => $phpVersion,
58 2 => CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER,
f955c30c 59 3 => preg_replace(';^(\d+\.\d+(?:\.[1-9]\d*)?).*$;', '\1', CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER),
be2fb01f 60 ]),
4094935a
AP
61 ts('PHP Out-of-Date'),
62 \Psr\Log\LogLevel::WARNING,
63 'fa-server'
31e80d5a
CW
64 );
65 }
cc1f4988
CW
66 else {
67 $messages[] = new CRM_Utils_Check_Message(
68 __FUNCTION__,
f955c30c 69 ts('This system uses PHP version %1. To ensure the continued operation of CiviCRM, upgrade your server now. At least PHP version %2 is recommended; the preferred version is %3.',
be2fb01f 70 [
cc1f4988
CW
71 1 => $phpVersion,
72 2 => CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER,
f955c30c 73 3 => preg_replace(';^(\d+\.\d+(?:\.[1-9]\d*)?).*$;', '\1', CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER),
be2fb01f 74 ]),
4094935a
AP
75 ts('PHP Out-of-Date'),
76 \Psr\Log\LogLevel::ERROR,
77 'fa-server'
cc1f4988
CW
78 );
79 }
31e80d5a
CW
80
81 return $messages;
82 }
83
991298ee 84 /**
02b3ba91 85 * @return CRM_Utils_Check_Message[]
991298ee
TO
86 */
87 public function checkPhpMysqli() {
be2fb01f 88 $messages = [];
991298ee
TO
89
90 if (!extension_loaded('mysqli')) {
91 $messages[] = new CRM_Utils_Check_Message(
92 __FUNCTION__,
93 ts('Future versions of CiviCRM may require the PHP extension "%2". To ensure that your system will be compatible, please install it in advance. For more explanation, see <a href="%1">the announcement</a>.',
be2fb01f 94 [
991298ee
TO
95 1 => 'https://civicrm.org/blog/totten/psa-please-verify-php-extension-mysqli',
96 2 => 'mysqli',
be2fb01f 97 ]),
4094935a
AP
98 ts('Forward Compatibility: Enable "mysqli"'),
99 \Psr\Log\LogLevel::WARNING,
100 'fa-server'
991298ee
TO
101 );
102 }
103
104 return $messages;
105 }
106
d5d56ec1 107 /**
7342d7c4
TO
108 * Check that the MySQL time settings match the PHP time settings.
109 *
02b3ba91 110 * @return CRM_Utils_Check_Message[]
7342d7c4 111 */
1e927c45 112 public function checkMysqlTime() {
be2fb01f 113 $messages = [];
1e927c45
TO
114
115 $phpNow = date('Y-m-d H:i');
116 $sqlNow = CRM_Core_DAO::singleValueQuery("SELECT date_format(now(), '%Y-%m-%d %H:%i')");
8010540a 117 if (!CRM_Utils_Time::isEqual($phpNow, $sqlNow, 2.5 * 60)) {
1e927c45 118 $messages[] = new CRM_Utils_Check_Message(
165aab59 119 __FUNCTION__,
d05f3550
AS
120 ts('Timestamps reported by MySQL (eg "%1") and PHP (eg "%2" ) are mismatched.', [
121 1 => $sqlNow,
122 2 => $phpNow,
123 ]) . '<br />' . CRM_Utils_System::docURL2('sysadmin/requirements/#mysql-time'),
1b366958 124 ts('Timestamp Mismatch'),
165aab59
CW
125 \Psr\Log\LogLevel::ERROR,
126 'fa-server'
1e927c45
TO
127 );
128 }
129
130 return $messages;
131 }
7342d7c4 132
5bc392e6 133 /**
02b3ba91 134 * @return CRM_Utils_Check_Message[]
5bc392e6 135 */
7342d7c4 136 public function checkDebug() {
7342d7c4
TO
137 $config = CRM_Core_Config::singleton();
138 if ($config->debug) {
4b540f18 139 $message = new CRM_Utils_Check_Message(
165aab59 140 __FUNCTION__,
7342d7c4 141 ts('Warning: Debug is enabled in <a href="%1">system settings</a>. This should not be enabled on production servers.',
be2fb01f 142 [1 => CRM_Utils_System::url('civicrm/admin/setting/debug', 'reset=1')]),
1b366958 143 ts('Debug Mode Enabled'),
d1066fc7 144 CRM_Core_Config::environment() == 'Production' ? \Psr\Log\LogLevel::WARNING : \Psr\Log\LogLevel::INFO,
165aab59 145 'fa-bug'
7342d7c4 146 );
4b540f18
CW
147 $message->addAction(
148 ts('Disable Debug Mode'),
149 ts('Disable debug mode now?'),
150 'api3',
be2fb01f 151 ['Setting', 'create', ['debug_enabled' => 0]]
4b540f18 152 );
be2fb01f 153 return [$message];
7342d7c4
TO
154 }
155
be2fb01f 156 return [];
7342d7c4
TO
157 }
158
5bc392e6 159 /**
675e2573 160 * @param bool $force
02b3ba91 161 * @return CRM_Utils_Check_Message[]
5bc392e6 162 */
675e2573 163 public function checkOutboundMail($force = FALSE) {
be2fb01f 164 $messages = [];
7342d7c4 165
d1066fc7 166 // CiviMail doesn't work in non-production environments; skip.
675e2573 167 if (!$force && CRM_Core_Config::environment() != 'Production') {
d1066fc7
CW
168 return $messages;
169 }
170
aaffa79f 171 $mailingInfo = Civi::settings()->get('mailing_backend');
7342d7c4
TO
172 if (($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB
173 || (defined('CIVICRM_MAIL_LOG') && CIVICRM_MAIL_LOG)
174 || $mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED
175 || $mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_MOCK)
176 ) {
177 $messages[] = new CRM_Utils_Check_Message(
165aab59 178 __FUNCTION__,
7342d7c4 179 ts('Warning: Outbound email is disabled in <a href="%1">system settings</a>. Proper settings should be enabled on production servers.',
be2fb01f 180 [1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1')]),
1b366958 181 ts('Outbound Email Disabled'),
165aab59
CW
182 \Psr\Log\LogLevel::WARNING,
183 'fa-envelope'
7342d7c4
TO
184 );
185 }
186
187 return $messages;
188 }
96025800 189
1b366958
AH
190 /**
191 * Check that domain email and org name are set
675e2573 192 * @param bool $force
02b3ba91 193 * @return CRM_Utils_Check_Message[]
1b366958 194 */
675e2573 195 public function checkDomainNameEmail($force = FALSE) {
be2fb01f 196 $messages = [];
1b366958 197
d1066fc7 198 // CiviMail doesn't work in non-production environments; skip.
675e2573 199 if (!$force && CRM_Core_Config::environment() != 'Production') {
d1066fc7
CW
200 return $messages;
201 }
202
1b366958 203 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain::getNameAndEmail(TRUE);
8ce83e54
KC
204 $domain = CRM_Core_BAO_Domain::getDomain();
205 $domainName = $domain->name;
206 $fixEmailUrl = CRM_Utils_System::url("civicrm/admin/options/from_email_address", "&reset=1");
207 $fixDomainName = CRM_Utils_System::url("civicrm/admin/domain", "action=update&reset=1");
1b366958
AH
208
209 if (!$domainEmailAddress || $domainEmailAddress == 'info@EXAMPLE.ORG') {
210 if (!$domainName || $domainName == 'Default Domain Name') {
8ce83e54 211 $msg = ts("Please enter your organization's <a href=\"%1\">name, primary address </a> and <a href=\"%2\">default FROM Email Address </a> (for system-generated emails).",
be2fb01f 212 [
8ce83e54
KC
213 1 => $fixDomainName,
214 2 => $fixEmailUrl,
be2fb01f 215 ]
8ce83e54 216 );
1b366958
AH
217 }
218 else {
219 $msg = ts('Please enter a <a href="%1">default FROM Email Address</a> (for system-generated emails).',
be2fb01f 220 [1 => $fixEmailUrl]);
1b366958
AH
221 }
222 }
223 elseif (!$domainName || $domainName == 'Default Domain Name') {
224 $msg = ts("Please enter your organization's <a href=\"%1\">name and primary address</a>.",
be2fb01f 225 [1 => $fixDomainName]);
1b366958 226 }
aacaa119
AH
227
228 if (!empty($msg)) {
229 $messages[] = new CRM_Utils_Check_Message(
165aab59 230 __FUNCTION__,
aacaa119 231 $msg,
d0d5e238 232 ts('Organization Setup'),
165aab59
CW
233 \Psr\Log\LogLevel::WARNING,
234 'fa-check-square-o'
aacaa119
AH
235 );
236 }
1b366958
AH
237
238 return $messages;
239 }
240
241 /**
242 * Checks if a default bounce handling mailbox is set up
675e2573 243 * @param bool $force
02b3ba91 244 * @return CRM_Utils_Check_Message[]
1b366958 245 */
675e2573 246 public function checkDefaultMailbox($force = FALSE) {
be2fb01f 247 $messages = [];
d1066fc7
CW
248
249 // CiviMail doesn't work in non-production environments; skip.
675e2573 250 if (!$force && CRM_Core_Config::environment() != 'Production') {
d1066fc7
CW
251 return $messages;
252 }
253
1b366958
AH
254 $config = CRM_Core_Config::singleton();
255
256 if (in_array('CiviMail', $config->enableComponents) &&
257 CRM_Core_BAO_MailSettings::defaultDomain() == "EXAMPLE.ORG"
258 ) {
259 $message = new CRM_Utils_Check_Message(
165aab59 260 __FUNCTION__,
aa96ce62 261 ts('Please configure a <a href="%1">default mailbox</a> for CiviMail.',
be2fb01f 262 [1 => CRM_Utils_System::url('civicrm/admin/mailSettings', "reset=1")]),
1b366958 263 ts('Configure Default Mailbox'),
165aab59
CW
264 \Psr\Log\LogLevel::WARNING,
265 'fa-envelope'
1b366958 266 );
2a243675
CW
267 $message->addHelp(
268 ts('A default mailbox must be configured for email bounce processing.') . '<br />' .
d05f3550 269 CRM_Utils_System::docURL2('user/advanced-configuration/email-system-configuration/')
2a243675 270 );
1b366958
AH
271 $messages[] = $message;
272 }
273
274 return $messages;
275 }
aa96ce62
AH
276
277 /**
6a1cf294 278 * Checks if cron has run in the past hour (3600 seconds)
675e2573 279 * @param bool $force
02b3ba91 280 * @return CRM_Utils_Check_Message[]
6a1cf294 281 * @throws CRM_Core_Exception
aa96ce62 282 */
675e2573 283 public function checkLastCron($force = FALSE) {
fdddbfae
TO
284 // TODO: Remove this check when MINIMUM_UPGRADABLE_VERSION goes to 4.7.
285 if (CRM_Utils_System::version() !== CRM_Core_BAO_Domain::version()) {
286 return [];
287 }
288
be2fb01f 289 $messages = [];
aa96ce62 290
d1066fc7 291 // Cron doesn't work in non-production environments; skip.
675e2573 292 if (!$force && CRM_Core_Config::environment() != 'Production') {
d1066fc7
CW
293 return $messages;
294 }
295
aa96ce62
AH
296 $statusPreference = new CRM_Core_DAO_StatusPreference();
297 $statusPreference->domain_id = CRM_Core_Config::domainID();
6a1cf294 298 $statusPreference->name = __FUNCTION__;
aa96ce62 299
6a1cf294
CW
300 $level = \Psr\Log\LogLevel::INFO;
301 $now = gmdate('U');
302
303 // Get timestamp of last cron run
0296367d 304 if ($statusPreference->find(TRUE) && !empty($statusPreference->check_info)) {
6a1cf294 305 $msg = ts('Last cron run at %1.', [1 => CRM_Utils_Date::customFormat(date('c', $statusPreference->check_info))]);
fba5f6ac 306 }
6a1cf294 307 // If cron record doesn't exist, this is a new install. Make a placeholder record (prefs='new').
fba5f6ac 308 else {
6a1cf294
CW
309 $statusPreference = CRM_Core_BAO_StatusPreference::create([
310 'name' => __FUNCTION__,
311 'check_info' => $now,
312 'prefs' => 'new',
313 ]);
fba5f6ac 314 }
6a1cf294 315 $lastCron = $statusPreference->check_info;
aa96ce62 316
6a1cf294
CW
317 if ($statusPreference->prefs !== 'new' && $lastCron > $now - 3600) {
318 $title = ts('Cron Running OK');
aa96ce62 319 }
fba5f6ac 320 else {
6a1cf294
CW
321 // If placeholder record found, give one day "grace period" for admin to set-up cron
322 if ($statusPreference->prefs === 'new') {
323 $title = ts('Set-up Cron');
324 $msg = ts('No cron runs have been recorded.');
325 // After 1 day (86400 seconds) increase the error level
326 $level = ($lastCron > $now - 86400) ? \Psr\Log\LogLevel::NOTICE : \Psr\Log\LogLevel::WARNING;
327 }
328 else {
329 $title = ts('Cron Not Running');
330 // After 1 day (86400 seconds) increase the error level
331 $level = ($lastCron > $now - 86400) ? \Psr\Log\LogLevel::WARNING : \Psr\Log\LogLevel::ERROR;
332 }
23af1818 333 $msg .= '<p>' . ts('A cron job is required to execute scheduled jobs automatically.') .
d05f3550 334 '<br />' . CRM_Utils_System::docURL2('sysadmin/setup/jobs/') . '</p>';
aa96ce62
AH
335 }
336
6a1cf294
CW
337 $messages[] = new CRM_Utils_Check_Message(
338 __FUNCTION__,
339 $msg,
340 $title,
341 $level,
342 'fa-clock-o'
343 );
aa96ce62
AH
344 return $messages;
345 }
fba5f6ac 346
50574fda
TO
347 /**
348 * Recommend that sites use path-variables for their directories and URLs.
02b3ba91 349 * @return CRM_Utils_Check_Message[]
50574fda
TO
350 */
351 public function checkUrlVariables() {
be2fb01f 352 $messages = [];
50574fda 353 $hasOldStyle = FALSE;
be2fb01f 354 $settingNames = [
50574fda
TO
355 'userFrameworkResourceURL',
356 'imageUploadURL',
357 'customCSSURL',
358 'extensionsURL',
be2fb01f 359 ];
50574fda
TO
360
361 foreach ($settingNames as $settingName) {
362 $settingValue = Civi::settings()->get($settingName);
363 if (!empty($settingValue) && $settingValue{0} != '[') {
364 $hasOldStyle = TRUE;
365 break;
366 }
367 }
368
369 if ($hasOldStyle) {
370 $message = new CRM_Utils_Check_Message(
371 __FUNCTION__,
372 ts('<a href="%1">Resource URLs</a> may use absolute paths, relative paths, or variables. Absolute paths are more difficult to maintain. To maximize portability, consider using a variable in each URL (eg "<tt>[cms.root]</tt>" or "<tt>[civicrm.files]</tt>").',
be2fb01f 373 [1 => CRM_Utils_System::url('civicrm/admin/setting/url', "reset=1")]),
50574fda
TO
374 ts('Resource URLs: Make them portable'),
375 \Psr\Log\LogLevel::NOTICE,
376 'fa-server'
377 );
378 $messages[] = $message;
379 }
380
381 return $messages;
382 }
383
384 /**
385 * Recommend that sites use path-variables for their directories and URLs.
02b3ba91 386 * @return CRM_Utils_Check_Message[]
50574fda
TO
387 */
388 public function checkDirVariables() {
be2fb01f 389 $messages = [];
50574fda 390 $hasOldStyle = FALSE;
be2fb01f 391 $settingNames = [
50574fda
TO
392 'uploadDir',
393 'imageUploadDir',
394 'customFileUploadDir',
395 'customTemplateDir',
396 'customPHPPathDir',
397 'extensionsDir',
be2fb01f 398 ];
50574fda
TO
399
400 foreach ($settingNames as $settingName) {
401 $settingValue = Civi::settings()->get($settingName);
402 if (!empty($settingValue) && $settingValue{0} != '[') {
403 $hasOldStyle = TRUE;
404 break;
405 }
406 }
407
408 if ($hasOldStyle) {
409 $message = new CRM_Utils_Check_Message(
410 __FUNCTION__,
411 ts('<a href="%1">Directories</a> may use absolute paths, relative paths, or variables. Absolute paths are more difficult to maintain. To maximize portability, consider using a variable in each directory (eg "<tt>[cms.root]</tt>" or "<tt>[civicrm.files]</tt>").',
be2fb01f 412 [1 => CRM_Utils_System::url('civicrm/admin/setting/path', "reset=1")]),
50574fda
TO
413 ts('Directory Paths: Make them portable'),
414 \Psr\Log\LogLevel::NOTICE,
415 'fa-server'
416 );
417 $messages[] = $message;
418 }
419
420 return $messages;
421 }
422
3f0e59f6
AH
423 /**
424 * Check that important directories are writable.
425 *
02b3ba91 426 * @return CRM_Utils_Check_Message[]
3f0e59f6
AH
427 */
428 public function checkDirsWritable() {
be2fb01f 429 $notWritable = [];
3f0e59f6
AH
430
431 $config = CRM_Core_Config::singleton();
be2fb01f 432 $directories = [
3f0e59f6
AH
433 'uploadDir' => ts('Temporary Files Directory'),
434 'imageUploadDir' => ts('Images Directory'),
435 'customFileUploadDir' => ts('Custom Files Directory'),
be2fb01f 436 ];
3f0e59f6
AH
437
438 foreach ($directories as $directory => $label) {
439 $file = CRM_Utils_File::createFakeFile($config->$directory);
440
441 if ($file === FALSE) {
442 $notWritable[] = "$label ({$config->$directory})";
443 }
444 else {
445 $dirWithSlash = CRM_Utils_File::addTrailingSlash($config->$directory);
446 unlink($dirWithSlash . $file);
447 }
448 }
449
be2fb01f 450 $messages = [];
3f0e59f6
AH
451
452 if (!empty($notWritable)) {
453 $messages[] = new CRM_Utils_Check_Message(
454 __FUNCTION__,
be2fb01f 455 ts('The %1 is not writable. Please check your file permissions.', [
3f0e59f6
AH
456 1 => implode(', ', $notWritable),
457 'count' => count($notWritable),
458 'plural' => 'The following directories are not writable: %1. Please check your file permissions.',
be2fb01f
CW
459 ]),
460 ts('Directory not writable', [
3f0e59f6
AH
461 'count' => count($notWritable),
462 'plural' => 'Directories not writable',
be2fb01f 463 ]),
3f0e59f6
AH
464 \Psr\Log\LogLevel::ERROR,
465 'fa-ban'
466 );
467 }
468
469 return $messages;
470 }
50574fda 471
fba5f6ac
AH
472 /**
473 * Checks if new versions are available
8f90afde 474 * @param bool $force
02b3ba91 475 * @return CRM_Utils_Check_Message[]
8f90afde 476 * @throws CRM_Core_Exception
fba5f6ac 477 */
8f90afde 478 public function checkVersion($force = FALSE) {
be2fb01f 479 $messages = [];
e2fb6a98
CW
480 try {
481 $vc = new CRM_Utils_VersionCheck();
8f90afde 482 $vc->initialize($force);
e2fb6a98
CW
483 }
484 catch (Exception $e) {
485 $messages[] = new CRM_Utils_Check_Message(
486 'checkVersionError',
487 ts('Directory %1 is not writable. Please change your file permissions.',
be2fb01f 488 [1 => dirname($vc->cacheFile)]),
e2fb6a98
CW
489 ts('Directory not writable'),
490 \Psr\Log\LogLevel::ERROR,
491 'fa-times-circle-o'
492 );
493 return $messages;
494 }
fba5f6ac 495
b864507b 496 // Show a notice if the version_check job is disabled
8f90afde 497 if (!$force && empty($vc->cronJob['is_active'])) {
be2fb01f 498 $args = empty($vc->cronJob['id']) ? ['reset' => 1] : ['reset' => 1, 'action' => 'update', 'id' => $vc->cronJob['id']];
540c3e63
CW
499 $messages[] = new CRM_Utils_Check_Message(
500 'checkVersionDisabled',
be2fb01f 501 ts('The check for new versions of CiviCRM has been disabled. <a %1>Re-enable the scheduled job</a> to receive important security update notifications.', [1 => 'href="' . CRM_Utils_System::url('civicrm/admin/job', $args) . '"']),
540c3e63
CW
502 ts('Update Check Disabled'),
503 \Psr\Log\LogLevel::NOTICE,
504 'fa-times-circle-o'
505 );
506 }
507
83f064f2 508 if ($vc->isInfoAvailable) {
0770d5cb 509 foreach ($vc->getVersionMessages() ?? [] as $msg) {
88790378 510 $messages[] = new CRM_Utils_Check_Message(__FUNCTION__ . '_' . $msg['name'],
f644bc94 511 $msg['message'], $msg['title'], $msg['severity'], 'fa-cloud-upload');
d450a5f0 512 }
06576a03 513 }
fba5f6ac 514
06576a03 515 return $messages;
fba5f6ac 516 }
580ad3ef
AH
517
518 /**
519 * Checks if extensions are set up properly
02b3ba91 520 * @return CRM_Utils_Check_Message[]
580ad3ef 521 */
580ad3ef 522 public function checkExtensions() {
be2fb01f 523 $messages = [];
580ad3ef
AH
524 $extensionSystem = CRM_Extension_System::singleton();
525 $mapper = $extensionSystem->getMapper();
526 $manager = $extensionSystem->getManager();
580ad3ef
AH
527
528 if ($extensionSystem->getDefaultContainer()) {
529 $basedir = $extensionSystem->getDefaultContainer()->baseDir;
530 }
531
532 if (empty($basedir)) {
533 // no extension directory
534 $messages[] = new CRM_Utils_Check_Message(
165aab59 535 __FUNCTION__,
580ad3ef 536 ts('Your extensions directory is not set. Click <a href="%1">here</a> to set the extensions directory.',
be2fb01f 537 [1 => CRM_Utils_System::url('civicrm/admin/setting/path', 'reset=1')]),
e2fb6a98 538 ts('Directory not writable'),
165aab59
CW
539 \Psr\Log\LogLevel::NOTICE,
540 'fa-plug'
580ad3ef
AH
541 );
542 return $messages;
543 }
544
545 if (!is_dir($basedir)) {
546 $messages[] = new CRM_Utils_Check_Message(
165aab59 547 __FUNCTION__,
9329d168 548 ts('Your extensions directory path points to %1, which is not a directory. Please check your file system.',
be2fb01f 549 [1 => $basedir]),
580ad3ef 550 ts('Extensions directory incorrect'),
165aab59
CW
551 \Psr\Log\LogLevel::ERROR,
552 'fa-plug'
580ad3ef
AH
553 );
554 return $messages;
555 }
556 elseif (!is_writable($basedir)) {
557 $messages[] = new CRM_Utils_Check_Message(
698721fc 558 __FUNCTION__ . 'Writable',
599164fe 559 ts('Your extensions directory (%1) is read-only. If you would like to perform downloads or upgrades, then change the file permissions.',
be2fb01f 560 [1 => $basedir]),
37b705a0 561 ts('Read-Only Extensions'),
6e7ad3dc 562 \Psr\Log\LogLevel::NOTICE,
165aab59 563 'fa-plug'
580ad3ef 564 );
580ad3ef
AH
565 }
566
567 if (empty($extensionSystem->getDefaultContainer()->baseUrl)) {
568 $messages[] = new CRM_Utils_Check_Message(
698721fc 569 __FUNCTION__ . 'URL',
580ad3ef 570 ts('The extensions URL is not properly set. Please go to the <a href="%1">URL setting page</a> and correct it.',
be2fb01f 571 [1 => CRM_Utils_System::url('civicrm/admin/setting/url', 'reset=1')]),
165aab59
CW
572 ts('Extensions url missing'),
573 \Psr\Log\LogLevel::ERROR,
574 'fa-plug'
580ad3ef 575 );
580ad3ef
AH
576 }
577
b769826b
CW
578 if (!$extensionSystem->getBrowser()->isEnabled()) {
579 $messages[] = new CRM_Utils_Check_Message(
165aab59 580 __FUNCTION__,
b769826b
CW
581 ts('Not checking remote URL for extensions since ext_repo_url is set to false.'),
582 ts('Extensions check disabled'),
165aab59
CW
583 \Psr\Log\LogLevel::NOTICE,
584 'fa-plug'
b769826b
CW
585 );
586 return $messages;
587 }
588
589 try {
590 $remotes = $extensionSystem->getBrowser()->getExtensions();
591 }
592 catch (CRM_Extension_Exception $e) {
593 $messages[] = new CRM_Utils_Check_Message(
165aab59 594 __FUNCTION__,
b769826b
CW
595 $e->getMessage(),
596 ts('Extension download error'),
165aab59
CW
597 \Psr\Log\LogLevel::ERROR,
598 'fa-plug'
b769826b
CW
599 );
600 return $messages;
601 }
602
580ad3ef
AH
603 $keys = array_keys($manager->getStatuses());
604 sort($keys);
be2fb01f 605 $updates = $errors = $okextensions = [];
48f03858 606
580ad3ef
AH
607 foreach ($keys as $key) {
608 try {
609 $obj = $mapper->keyToInfo($key);
610 }
611 catch (CRM_Extension_Exception $ex) {
be2fb01f 612 $errors[] = ts('Failed to read extension (%1). Please refresh the extension list.', [1 => $key]);
580ad3ef
AH
613 continue;
614 }
615 $row = CRM_Admin_Page_Extensions::createExtendedInfo($obj);
616 switch ($row['status']) {
617 case CRM_Extension_Manager::STATUS_INSTALLED_MISSING:
6b409353 618 $errors[] = ts('%1 extension (%2) is installed but missing files.', [1 => $row['label'] ?? NULL, 2 => $key]);
580ad3ef
AH
619 break;
620
621 case CRM_Extension_Manager::STATUS_INSTALLED:
48f03858 622 if (!empty($remotes[$key]) && version_compare($row['version'], $remotes[$key]->version, '<')) {
a304caa7 623 $updates[] = $row['label'] . ': ' . $mapper->getUpgradeLink($remotes[$key], $row);
48f03858
CW
624 }
625 else {
626 if (empty($row['label'])) {
627 $okextensions[] = $key;
580ad3ef
AH
628 }
629 else {
a304caa7 630 $okextensions[] = ts('%1: Version %2', [
48f03858 631 1 => $row['label'],
a304caa7 632 2 => $row['version'],
be2fb01f 633 ]);
580ad3ef
AH
634 }
635 }
580ad3ef 636 break;
580ad3ef
AH
637 }
638 }
6e61248d
CW
639
640 if (!$okextensions && !$updates && !$errors) {
e2a3547b 641 $messages[] = new CRM_Utils_Check_Message(
675e2573 642 __FUNCTION__ . 'Ok',
be2fb01f 643 ts('No extensions installed. <a %1>Browse available extensions</a>.', [
81756b44 644 1 => 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1') . '"',
be2fb01f 645 ]),
6e61248d
CW
646 ts('Extensions'),
647 \Psr\Log\LogLevel::INFO,
648 'fa-plug'
e2a3547b 649 );
580ad3ef 650 }
6e61248d
CW
651
652 if ($errors) {
653 $messages[] = new CRM_Utils_Check_Message(
698721fc 654 __FUNCTION__ . 'Error',
6e61248d
CW
655 '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>',
656 ts('Extension Error'),
657 \Psr\Log\LogLevel::ERROR,
658 'fa-plug'
659 );
48f03858 660 }
6e61248d
CW
661
662 if ($updates) {
663 $messages[] = new CRM_Utils_Check_Message(
675e2573 664 __FUNCTION__ . 'Updates',
6e61248d 665 '<ul><li>' . implode('</li><li>', $updates) . '</li></ul>',
be2fb01f 666 ts('Extension Update Available', ['plural' => '%count Extension Updates Available', 'count' => count($updates)]),
6e61248d
CW
667 \Psr\Log\LogLevel::WARNING,
668 'fa-plug'
669 );
580ad3ef 670 }
097c681e 671
6e61248d 672 if ($okextensions) {
c08d5b15 673 if ($updates || $errors) {
be2fb01f 674 $message = ts('1 extension is up-to-date:', ['plural' => '%count extensions are up-to-date:', 'count' => count($okextensions)]);
c08d5b15
CW
675 }
676 else {
677 $message = ts('All extensions are up-to-date:');
678 }
6e61248d 679 $messages[] = new CRM_Utils_Check_Message(
675e2573 680 __FUNCTION__ . 'Ok',
c08d5b15 681 $message . '<ul><li>' . implode('</li><li>', $okextensions) . '</li></ul>',
6e61248d
CW
682 ts('Extensions'),
683 \Psr\Log\LogLevel::INFO,
684 'fa-plug'
685 );
686 }
580ad3ef
AH
687
688 return $messages;
689 }
690
580ad3ef 691 /**
4b540f18
CW
692 * Checks if there are pending extension upgrades.
693 *
02b3ba91 694 * @return CRM_Utils_Check_Message[]
580ad3ef
AH
695 */
696 public function checkExtensionUpgrades() {
580ad3ef 697 if (CRM_Extension_Upgrades::hasPending()) {
4b540f18 698 $message = new CRM_Utils_Check_Message(
165aab59 699 __FUNCTION__,
4b540f18
CW
700 ts('Extension upgrades should be run as soon as possible.'),
701 ts('Extension Upgrades Pending'),
165aab59
CW
702 \Psr\Log\LogLevel::ERROR,
703 'fa-plug'
580ad3ef 704 );
4b540f18
CW
705 $message->addAction(
706 ts('Run Upgrades'),
707 ts('Run extension upgrades now?'),
708 'href',
be2fb01f 709 ['path' => 'civicrm/admin/extensions/upgrade', 'query' => ['reset' => 1, 'destination' => CRM_Utils_System::url('civicrm/a/#/status')]]
4b540f18 710 );
be2fb01f 711 return [$message];
580ad3ef 712 }
be2fb01f 713 return [];
580ad3ef
AH
714 }
715
716 /**
717 * Checks if CiviCRM database version is up-to-date
02b3ba91 718 * @return CRM_Utils_Check_Message[]
580ad3ef 719 */
580ad3ef 720 public function checkDbVersion() {
be2fb01f 721 $messages = [];
580ad3ef
AH
722 $dbVersion = CRM_Core_BAO_Domain::version();
723 $upgradeUrl = CRM_Utils_System::url("civicrm/upgrade", "reset=1");
724
725 if (!$dbVersion) {
726 // if db.ver missing
727 $messages[] = new CRM_Utils_Check_Message(
165aab59 728 __FUNCTION__,
580ad3ef
AH
729 ts('Version information found to be missing in database. You will need to determine the correct version corresponding to your current database state.'),
730 ts('Database Version Missing'),
165aab59
CW
731 \Psr\Log\LogLevel::ERROR,
732 'fa-database'
580ad3ef
AH
733 );
734 }
735 elseif (!CRM_Utils_System::isVersionFormatValid($dbVersion)) {
736 $messages[] = new CRM_Utils_Check_Message(
165aab59 737 __FUNCTION__,
580ad3ef
AH
738 ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.'),
739 ts('Database Version Invalid'),
165aab59
CW
740 \Psr\Log\LogLevel::ERROR,
741 'fa-database'
580ad3ef
AH
742 );
743 }
744 elseif (stripos($dbVersion, 'upgrade')) {
745 // if db.ver indicates a partially upgraded db
746 $messages[] = new CRM_Utils_Check_Message(
165aab59 747 __FUNCTION__,
be2fb01f 748 ts('Database check failed - the database looks to have been partially upgraded. You must reload the database with the backup and try the <a href=\'%1\'>upgrade process</a> again.', [1 => $upgradeUrl]),
580ad3ef 749 ts('Database Partially Upgraded'),
165aab59
CW
750 \Psr\Log\LogLevel::ALERT,
751 'fa-database'
580ad3ef
AH
752 );
753 }
754 else {
580ad3ef 755 // if db.ver < code.ver, time to upgrade
1fbda76b 756 if (CRM_Core_BAO_Domain::isDBUpdateRequired()) {
580ad3ef 757 $messages[] = new CRM_Utils_Check_Message(
165aab59 758 __FUNCTION__,
be2fb01f 759 ts('New codebase version detected. You must visit <a href=\'%1\'>upgrade screen</a> to upgrade the database.', [1 => $upgradeUrl]),
580ad3ef 760 ts('Database Upgrade Required'),
165aab59
CW
761 \Psr\Log\LogLevel::ALERT,
762 'fa-database'
580ad3ef
AH
763 );
764 }
765
766 // if db.ver > code.ver, sth really wrong
cc52250f 767 $codeVersion = CRM_Utils_System::version();
768 if (version_compare($dbVersion, $codeVersion) > 0) {
580ad3ef 769 $messages[] = new CRM_Utils_Check_Message(
165aab59 770 __FUNCTION__,
580ad3ef
AH
771 ts('Your database is marked with an unexpected version number: %1. The v%2 codebase may not be compatible with your database state.
772 You will need to determine the correct version corresponding to your current database state. You may want to revert to the codebase
773 you were using until you resolve this problem.<br/>OR if this is a manual install from git, you might want to fix civicrm-version.php file.',
be2fb01f 774 [1 => $dbVersion, 2 => $codeVersion]
580ad3ef
AH
775 ),
776 ts('Database In Unexpected Version'),
165aab59
CW
777 \Psr\Log\LogLevel::ERROR,
778 'fa-database'
580ad3ef
AH
779 );
780 }
781 }
782
783 return $messages;
784 }
785
786 /**
02b3ba91
CW
787 * Ensure that all CiviCRM tables are InnoDB
788 * @return CRM_Utils_Check_Message[]
580ad3ef 789 */
580ad3ef 790 public function checkDbEngine() {
be2fb01f 791 $messages = [];
580ad3ef
AH
792
793 if (CRM_Core_DAO::isDBMyISAM(150)) {
794 $messages[] = new CRM_Utils_Check_Message(
165aab59 795 __FUNCTION__,
580ad3ef
AH
796 ts('Your database is configured to use the MyISAM database engine. CiviCRM requires InnoDB. You will need to convert any MyISAM tables in your database to InnoDB. Using MyISAM tables will result in data integrity issues.'),
797 ts('MyISAM Database Engine'),
165aab59
CW
798 \Psr\Log\LogLevel::ERROR,
799 'fa-database'
580ad3ef
AH
800 );
801 }
802 return $messages;
803 }
804
765e99a6 805 /**
02b3ba91 806 * Ensure reply id is set to any default value
675e2573 807 * @param bool $force
02b3ba91 808 * @return CRM_Utils_Check_Message[]
765e99a6 809 */
675e2573 810 public function checkReplyIdForMailing($force = FALSE) {
be2fb01f 811 $messages = [];
765e99a6 812
d1066fc7 813 // CiviMail doesn't work in non-production environments; skip.
675e2573 814 if (!$force && CRM_Core_Config::environment() != 'Production') {
d1066fc7
CW
815 return $messages;
816 }
817
765e99a6
JP
818 if (!CRM_Mailing_PseudoConstant::defaultComponent('Reply', '')) {
819 $messages[] = new CRM_Utils_Check_Message(
820 __FUNCTION__,
be2fb01f 821 ts('Reply Auto Responder is not set to any default value in <a %1>Headers, Footers, and Automated Messages</a>. This will disable the submit operation on any mailing created from CiviMail.', [1 => 'href="' . CRM_Utils_System::url('civicrm/admin/component', 'reset=1') . '"']),
765e99a6
JP
822 ts('No Default value for Auto Responder.'),
823 \Psr\Log\LogLevel::WARNING,
824 'fa-reply'
825 );
826 }
827 return $messages;
828 }
829
28288426
CW
830 /**
831 * Check for required mbstring extension
02b3ba91 832 * @return CRM_Utils_Check_Message[]
28288426
CW
833 */
834 public function checkMbstring() {
be2fb01f 835 $messages = [];
28288426
CW
836
837 if (!function_exists('mb_substr')) {
838 $messages[] = new CRM_Utils_Check_Message(
839 __FUNCTION__,
ade9995e 840 ts('The PHP Multibyte String extension is needed for CiviCRM to correctly handle user input among other functionality. Ask your system administrator to install it.'),
28288426 841 ts('Missing mbstring Extension'),
ade9995e 842 \Psr\Log\LogLevel::WARNING,
28288426
CW
843 'fa-server'
844 );
845 }
846 return $messages;
847 }
848
f008885c
E
849 /**
850 * Check if environment is Production.
02b3ba91 851 * @return CRM_Utils_Check_Message[]
f008885c
E
852 */
853 public function checkEnvironment() {
be2fb01f 854 $messages = [];
f008885c
E
855
856 $environment = CRM_Core_Config::environment();
857 if ($environment != 'Production') {
858 $messages[] = new CRM_Utils_Check_Message(
859 __FUNCTION__,
be2fb01f 860 ts('The environment of this CiviCRM instance is set to \'%1\'. Certain functionality like scheduled jobs has been disabled.', [1 => $environment]),
f008885c 861 ts('Non-Production Environment'),
d1066fc7 862 \Psr\Log\LogLevel::NOTICE,
f008885c
E
863 'fa-bug'
864 );
865 }
866 return $messages;
867 }
868
75615982 869 /**
870 * Check for utf8mb4 support by MySQL.
871 *
02b3ba91 872 * @return CRM_Utils_Check_Message[]
75615982 873 */
874 public function checkMysqlUtf8mb4() {
be2fb01f 875 $messages = [];
75615982 876
877 if (CRM_Core_DAO::getConnection()->phptype != 'mysqli') {
878 return $messages;
879 }
880
6bd3e3c4 881 // Use mysqli_query() to avoid logging an error message.
590e3cec
SL
882 $mb4testTableName = CRM_Utils_SQL_TempTable::build()->setCategory('utf8mb4test')->getName();
883 if (mysqli_query(CRM_Core_DAO::getConnection()->connection, 'CREATE TEMPORARY TABLE ' . $mb4testTableName . ' (id VARCHAR(255), PRIMARY KEY(id(255))) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC ENGINE=INNODB')) {
884 CRM_Core_DAO::executeQuery('DROP TEMPORARY TABLE ' . $mb4testTableName);
75615982 885 }
6bd3e3c4 886 else {
75615982 887 $messages[] = new CRM_Utils_Check_Message(
888 __FUNCTION__,
d05f3550 889 ts("Future versions of CiviCRM may require MySQL to support utf8mb4 encoding. It is recommended, though not yet required. Please discuss with your server administrator about configuring your MySQL server for utf8mb4. CiviCRM's recommended configurations are in the System Administrator Guide") . '<br />' . CRM_Utils_System::docURL2('sysadmin/requirements/#mysql-configuration'),
5aebe76c 890 ts('MySQL Emoji Support (utf8mb4)'),
75615982 891 \Psr\Log\LogLevel::WARNING,
892 'fa-database'
893 );
894 }
895 // Ensure that the MySQL driver supports utf8mb4 encoding.
e479ac61 896 $version = mysqli_get_client_info();
75615982 897 if (strpos($version, 'mysqlnd') !== FALSE) {
898 // The mysqlnd driver supports utf8mb4 starting at version 5.0.9.
899 $version = preg_replace('/^\D+([\d.]+).*/', '$1', $version);
900 if (version_compare($version, '5.0.9', '<')) {
901 $messages[] = new CRM_Utils_Check_Message(
b2a06ef7 902 __FUNCTION__ . 'mysqlnd',
75615982 903 ts('It is recommended, though not yet required, to upgrade your PHP MySQL driver (mysqlnd) to >= 5.0.9 for utf8mb4 support.'),
904 ts('PHP MySQL Driver (mysqlnd)'),
905 \Psr\Log\LogLevel::WARNING,
906 'fa-server'
907 );
908 }
909 }
910 else {
911 // The libmysqlclient driver supports utf8mb4 starting at version 5.5.3.
912 if (version_compare($version, '5.5.3', '<')) {
913 $messages[] = new CRM_Utils_Check_Message(
b2a06ef7 914 __FUNCTION__ . 'libmysqlclient',
7806826f 915 ts('It is recommended, though not yet required, to upgrade your PHP MySQL driver (libmysqlclient) to >= 5.5.3 for utf8mb4 support.'),
75615982 916 ts('PHP MySQL Driver (libmysqlclient)'),
917 \Psr\Log\LogLevel::WARNING,
918 'fa-server'
919 );
920 }
921 }
922
923 return $messages;
924 }
925
17ca7aac
SL
926 public function checkMysqlVersion() {
927 $messages = [];
928 $version = CRM_Utils_SQL::getDatabaseVersion();
965596b4 929 $minRecommendedVersion = CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_MYSQL_VER;
57ec4649 930 $mariaDbRecommendedVersion = '10.1';
b1634834
SL
931 $upcomingCiviChangeVersion = '5.34';
932 if (version_compare(CRM_Utils_SQL::getDatabaseVersion(), $minRecommendedVersion, '<')) {
17ca7aac
SL
933 $messages[] = new CRM_Utils_Check_Message(
934 __FUNCTION__,
b1634834 935 ts('To prepare for CiviCRM v%4, please upgrade MySQL. The recommended version will be MySQL v%2 or MariaDB v%3.', [
f7738e49 936 1 => $version,
57ec4649
TO
937 2 => $minRecommendedVersion . '+',
938 3 => $mariaDbRecommendedVersion . '+',
b1634834 939 4 => $upcomingCiviChangeVersion . '+',
f7738e49 940 ]),
57ec4649 941 ts('MySQL Out-of-Date'),
f7738e49
SL
942 \Psr\Log\LogLevel::NOTICE,
943 'fa-server'
944 );
945 }
965596b4 946 return $messages;
17ca7aac
SL
947 }
948
d76cb10e
SL
949 public function checkPHPIntlExists() {
950 $messages = [];
951 if (!extension_loaded('intl')) {
952 $messages[] = new CRM_Utils_Check_Message(
953 __FUNCTION__,
edeb3ee8 954 ts('This system currently does not have the PHP-Intl extension enabled. Please contact your system administrator about getting the extension enabled.'),
d76cb10e
SL
955 ts('Missing PHP Extension: INTL'),
956 \Psr\Log\LogLevel::WARNING,
957 'fa-server'
958 );
959 }
960 return $messages;
961 }
962
6bbe0cf6
SL
963 public function checkWkHtmlToPDFPath() {
964 $messages = [];
965 $wkhtmltopdfPath = CRM_Core_Config::singleton()->wkhtmltopdfPath;
966 if (!empty($wkhtmltopdfPath) && !file_exists($wkhtmltopdfPath)) {
967 $messages[] = new CRM_Utils_Check_Message(
968 __FUNCTION__,
969 ts('CiviCRM is configured to use the wkhtmltopdf package to produce PDFs however it is missing, as such the system will fall back to using the DOMPDF package, this may mean that the output is different to what was expected. You should resolve this by either clearing the setting at Administer -> System Settings -> Miscellaneous or by getting your system administrator to install the wkhtmltopdf package'),
970 ts('Missing System Package: wkhtmltopdf'),
971 \Psr\Log\LogLevel::WARNING,
972 'fa-server'
973 );
974 }
975 return $messages;
976 }
977
5bc392e6 978}