Merge pull request #22576 from seamuslee001/update_jquery_ui
[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);
e1499714 363 if (!empty($settingValue) && $settingValue[0] != '[') {
50574fda
TO
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);
e1499714 402 if (!empty($settingValue) && $settingValue[0] != '[') {
50574fda
TO
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
a7a8ef62
CW
603 $stauses = $manager->getStatuses();
604 $keys = array_keys($stauses);
605 $enabled = array_keys(array_filter($stauses, function($status) {
606 return $status === CRM_Extension_Manager::STATUS_INSTALLED;
607 }));
580ad3ef 608 sort($keys);
be2fb01f 609 $updates = $errors = $okextensions = [];
48f03858 610
580ad3ef
AH
611 foreach ($keys as $key) {
612 try {
613 $obj = $mapper->keyToInfo($key);
614 }
615 catch (CRM_Extension_Exception $ex) {
be2fb01f 616 $errors[] = ts('Failed to read extension (%1). Please refresh the extension list.', [1 => $key]);
580ad3ef
AH
617 continue;
618 }
619 $row = CRM_Admin_Page_Extensions::createExtendedInfo($obj);
620 switch ($row['status']) {
621 case CRM_Extension_Manager::STATUS_INSTALLED_MISSING:
6b409353 622 $errors[] = ts('%1 extension (%2) is installed but missing files.', [1 => $row['label'] ?? NULL, 2 => $key]);
580ad3ef
AH
623 break;
624
625 case CRM_Extension_Manager::STATUS_INSTALLED:
a7a8ef62
CW
626 if (!empty($row['requires']) && array_diff($row['requires'], $enabled)) {
627 $errors[] = ts('%1 extension depends on %2, which is not enabled.', [1 => $row['label'] ?? $key, 2 => implode(', ', array_diff($row['requires'], $enabled))]);
628 }
629 elseif (!empty($remotes[$key]) && version_compare($row['version'], $remotes[$key]->version, '<')) {
a304caa7 630 $updates[] = $row['label'] . ': ' . $mapper->getUpgradeLink($remotes[$key], $row);
48f03858
CW
631 }
632 else {
633 if (empty($row['label'])) {
634 $okextensions[] = $key;
580ad3ef
AH
635 }
636 else {
a304caa7 637 $okextensions[] = ts('%1: Version %2', [
48f03858 638 1 => $row['label'],
a304caa7 639 2 => $row['version'],
be2fb01f 640 ]);
580ad3ef
AH
641 }
642 }
580ad3ef 643 break;
580ad3ef
AH
644 }
645 }
6e61248d
CW
646
647 if (!$okextensions && !$updates && !$errors) {
e2a3547b 648 $messages[] = new CRM_Utils_Check_Message(
675e2573 649 __FUNCTION__ . 'Ok',
be2fb01f 650 ts('No extensions installed. <a %1>Browse available extensions</a>.', [
81756b44 651 1 => 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1') . '"',
be2fb01f 652 ]),
6e61248d
CW
653 ts('Extensions'),
654 \Psr\Log\LogLevel::INFO,
655 'fa-plug'
e2a3547b 656 );
580ad3ef 657 }
6e61248d
CW
658
659 if ($errors) {
660 $messages[] = new CRM_Utils_Check_Message(
698721fc 661 __FUNCTION__ . 'Error',
6e61248d
CW
662 '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>',
663 ts('Extension Error'),
664 \Psr\Log\LogLevel::ERROR,
665 'fa-plug'
666 );
48f03858 667 }
6e61248d
CW
668
669 if ($updates) {
670 $messages[] = new CRM_Utils_Check_Message(
675e2573 671 __FUNCTION__ . 'Updates',
6e61248d 672 '<ul><li>' . implode('</li><li>', $updates) . '</li></ul>',
be2fb01f 673 ts('Extension Update Available', ['plural' => '%count Extension Updates Available', 'count' => count($updates)]),
6e61248d
CW
674 \Psr\Log\LogLevel::WARNING,
675 'fa-plug'
676 );
580ad3ef 677 }
097c681e 678
6e61248d 679 if ($okextensions) {
c08d5b15 680 if ($updates || $errors) {
be2fb01f 681 $message = ts('1 extension is up-to-date:', ['plural' => '%count extensions are up-to-date:', 'count' => count($okextensions)]);
c08d5b15
CW
682 }
683 else {
684 $message = ts('All extensions are up-to-date:');
685 }
36368cfd 686 natcasesort($okextensions);
6e61248d 687 $messages[] = new CRM_Utils_Check_Message(
675e2573 688 __FUNCTION__ . 'Ok',
c08d5b15 689 $message . '<ul><li>' . implode('</li><li>', $okextensions) . '</li></ul>',
6e61248d
CW
690 ts('Extensions'),
691 \Psr\Log\LogLevel::INFO,
692 'fa-plug'
693 );
694 }
580ad3ef
AH
695
696 return $messages;
697 }
698
08d32cba 699 /**
700 * @return CRM_Utils_Check_Message[]
701 */
702 public function checkScheduledJobLogErrors() {
703 $jobs = civicrm_api3('Job', 'get', [
704 'sequential' => 1,
705 'return' => ["id", "name", "last_run"],
706 'is_active' => 1,
707 'options' => ['limit' => 0],
708 ]);
709 $html = '';
710 foreach ($jobs['values'] as $job) {
711 $lastExecutionMessage = civicrm_api3('JobLog', 'get', [
712 'sequential' => 1,
713 'return' => ["description"],
714 'job_id' => $job['id'],
715 'options' => ['sort' => "id desc", 'limit' => 1],
716 ])['values'][0]['description'] ?? NULL;
717 if (!empty($lastExecutionMessage) && strpos($lastExecutionMessage, 'Failure') !== FALSE) {
718 $viewLogURL = CRM_Utils_System::url('civicrm/admin/joblog', "jid={$job['id']}&reset=1");
62f17a51 719 $html .= '<tr>
720 <td>' . $job['name'] . ' </td>
721 <td>' . $lastExecutionMessage . '</td>
722 <td>' . $job['last_run'] . '</td>
723 <td><a href="' . $viewLogURL . '">' . ts('View Job Log') . '</a></td>
724 </tr>';
08d32cba 725 }
726 }
727 if (empty($html)) {
728 return [];
729 }
730
62f17a51 731 $message = '<p>' . ts('The following scheduled jobs failed on the last run:') . '</p>
732 <p><table><thead><tr><th>' . ts('Job') . '</th><th>' . ts('Message') . '</th><th>' . ts('Last Run') . '</th><th></th>
733 </tr></thead><tbody>' . $html . '
734 </tbody></table></p>';
08d32cba 735
736 $msg = new CRM_Utils_Check_Message(
737 __FUNCTION__,
62f17a51 738 $message,
08d32cba 739 ts('Scheduled Job Failures'),
740 \Psr\Log\LogLevel::WARNING,
741 'fa-server'
742 );
743 $messages[] = $msg;
744 return $messages;
745 }
746
580ad3ef 747 /**
4b540f18
CW
748 * Checks if there are pending extension upgrades.
749 *
02b3ba91 750 * @return CRM_Utils_Check_Message[]
580ad3ef
AH
751 */
752 public function checkExtensionUpgrades() {
580ad3ef 753 if (CRM_Extension_Upgrades::hasPending()) {
4b540f18 754 $message = new CRM_Utils_Check_Message(
165aab59 755 __FUNCTION__,
4b540f18
CW
756 ts('Extension upgrades should be run as soon as possible.'),
757 ts('Extension Upgrades Pending'),
165aab59
CW
758 \Psr\Log\LogLevel::ERROR,
759 'fa-plug'
580ad3ef 760 );
4b540f18
CW
761 $message->addAction(
762 ts('Run Upgrades'),
763 ts('Run extension upgrades now?'),
764 'href',
be2fb01f 765 ['path' => 'civicrm/admin/extensions/upgrade', 'query' => ['reset' => 1, 'destination' => CRM_Utils_System::url('civicrm/a/#/status')]]
4b540f18 766 );
be2fb01f 767 return [$message];
580ad3ef 768 }
be2fb01f 769 return [];
580ad3ef
AH
770 }
771
772 /**
773 * Checks if CiviCRM database version is up-to-date
02b3ba91 774 * @return CRM_Utils_Check_Message[]
580ad3ef 775 */
580ad3ef 776 public function checkDbVersion() {
be2fb01f 777 $messages = [];
580ad3ef
AH
778 $dbVersion = CRM_Core_BAO_Domain::version();
779 $upgradeUrl = CRM_Utils_System::url("civicrm/upgrade", "reset=1");
780
781 if (!$dbVersion) {
782 // if db.ver missing
783 $messages[] = new CRM_Utils_Check_Message(
165aab59 784 __FUNCTION__,
580ad3ef
AH
785 ts('Version information found to be missing in database. You will need to determine the correct version corresponding to your current database state.'),
786 ts('Database Version Missing'),
165aab59
CW
787 \Psr\Log\LogLevel::ERROR,
788 'fa-database'
580ad3ef
AH
789 );
790 }
791 elseif (!CRM_Utils_System::isVersionFormatValid($dbVersion)) {
792 $messages[] = new CRM_Utils_Check_Message(
165aab59 793 __FUNCTION__,
580ad3ef
AH
794 ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.'),
795 ts('Database Version Invalid'),
165aab59
CW
796 \Psr\Log\LogLevel::ERROR,
797 'fa-database'
580ad3ef
AH
798 );
799 }
800 elseif (stripos($dbVersion, 'upgrade')) {
801 // if db.ver indicates a partially upgraded db
802 $messages[] = new CRM_Utils_Check_Message(
165aab59 803 __FUNCTION__,
be2fb01f 804 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 805 ts('Database Partially Upgraded'),
165aab59
CW
806 \Psr\Log\LogLevel::ALERT,
807 'fa-database'
580ad3ef
AH
808 );
809 }
810 else {
580ad3ef 811 // if db.ver < code.ver, time to upgrade
1fbda76b 812 if (CRM_Core_BAO_Domain::isDBUpdateRequired()) {
580ad3ef 813 $messages[] = new CRM_Utils_Check_Message(
165aab59 814 __FUNCTION__,
be2fb01f 815 ts('New codebase version detected. You must visit <a href=\'%1\'>upgrade screen</a> to upgrade the database.', [1 => $upgradeUrl]),
580ad3ef 816 ts('Database Upgrade Required'),
165aab59
CW
817 \Psr\Log\LogLevel::ALERT,
818 'fa-database'
580ad3ef
AH
819 );
820 }
821
822 // if db.ver > code.ver, sth really wrong
cc52250f 823 $codeVersion = CRM_Utils_System::version();
824 if (version_compare($dbVersion, $codeVersion) > 0) {
580ad3ef 825 $messages[] = new CRM_Utils_Check_Message(
165aab59 826 __FUNCTION__,
580ad3ef
AH
827 ts('Your database is marked with an unexpected version number: %1. The v%2 codebase may not be compatible with your database state.
828 You will need to determine the correct version corresponding to your current database state. You may want to revert to the codebase
829 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 830 [1 => $dbVersion, 2 => $codeVersion]
580ad3ef
AH
831 ),
832 ts('Database In Unexpected Version'),
165aab59
CW
833 \Psr\Log\LogLevel::ERROR,
834 'fa-database'
580ad3ef
AH
835 );
836 }
837 }
838
839 return $messages;
840 }
841
842 /**
02b3ba91
CW
843 * Ensure that all CiviCRM tables are InnoDB
844 * @return CRM_Utils_Check_Message[]
580ad3ef 845 */
580ad3ef 846 public function checkDbEngine() {
be2fb01f 847 $messages = [];
580ad3ef
AH
848
849 if (CRM_Core_DAO::isDBMyISAM(150)) {
850 $messages[] = new CRM_Utils_Check_Message(
165aab59 851 __FUNCTION__,
580ad3ef
AH
852 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.'),
853 ts('MyISAM Database Engine'),
165aab59
CW
854 \Psr\Log\LogLevel::ERROR,
855 'fa-database'
580ad3ef
AH
856 );
857 }
858 return $messages;
859 }
860
765e99a6 861 /**
02b3ba91 862 * Ensure reply id is set to any default value
675e2573 863 * @param bool $force
02b3ba91 864 * @return CRM_Utils_Check_Message[]
765e99a6 865 */
675e2573 866 public function checkReplyIdForMailing($force = FALSE) {
be2fb01f 867 $messages = [];
765e99a6 868
d1066fc7 869 // CiviMail doesn't work in non-production environments; skip.
675e2573 870 if (!$force && CRM_Core_Config::environment() != 'Production') {
d1066fc7
CW
871 return $messages;
872 }
873
765e99a6
JP
874 if (!CRM_Mailing_PseudoConstant::defaultComponent('Reply', '')) {
875 $messages[] = new CRM_Utils_Check_Message(
876 __FUNCTION__,
be2fb01f 877 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
878 ts('No Default value for Auto Responder.'),
879 \Psr\Log\LogLevel::WARNING,
880 'fa-reply'
881 );
882 }
883 return $messages;
884 }
885
28288426
CW
886 /**
887 * Check for required mbstring extension
02b3ba91 888 * @return CRM_Utils_Check_Message[]
28288426
CW
889 */
890 public function checkMbstring() {
be2fb01f 891 $messages = [];
28288426
CW
892
893 if (!function_exists('mb_substr')) {
894 $messages[] = new CRM_Utils_Check_Message(
895 __FUNCTION__,
ade9995e 896 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 897 ts('Missing mbstring Extension'),
ade9995e 898 \Psr\Log\LogLevel::WARNING,
28288426
CW
899 'fa-server'
900 );
901 }
902 return $messages;
903 }
904
f008885c
E
905 /**
906 * Check if environment is Production.
02b3ba91 907 * @return CRM_Utils_Check_Message[]
f008885c
E
908 */
909 public function checkEnvironment() {
be2fb01f 910 $messages = [];
f008885c
E
911
912 $environment = CRM_Core_Config::environment();
913 if ($environment != 'Production') {
914 $messages[] = new CRM_Utils_Check_Message(
915 __FUNCTION__,
be2fb01f 916 ts('The environment of this CiviCRM instance is set to \'%1\'. Certain functionality like scheduled jobs has been disabled.', [1 => $environment]),
f008885c 917 ts('Non-Production Environment'),
d1066fc7 918 \Psr\Log\LogLevel::NOTICE,
f008885c
E
919 'fa-bug'
920 );
921 }
922 return $messages;
923 }
924
75615982 925 /**
926 * Check for utf8mb4 support by MySQL.
927 *
02b3ba91 928 * @return CRM_Utils_Check_Message[]
75615982 929 */
930 public function checkMysqlUtf8mb4() {
be2fb01f 931 $messages = [];
75615982 932
933 if (CRM_Core_DAO::getConnection()->phptype != 'mysqli') {
934 return $messages;
935 }
936
6bd3e3c4 937 // Use mysqli_query() to avoid logging an error message.
590e3cec
SL
938 $mb4testTableName = CRM_Utils_SQL_TempTable::build()->setCategory('utf8mb4test')->getName();
939 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')) {
940 CRM_Core_DAO::executeQuery('DROP TEMPORARY TABLE ' . $mb4testTableName);
75615982 941 }
6bd3e3c4 942 else {
75615982 943 $messages[] = new CRM_Utils_Check_Message(
944 __FUNCTION__,
d05f3550 945 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 946 ts('MySQL Emoji Support (utf8mb4)'),
75615982 947 \Psr\Log\LogLevel::WARNING,
948 'fa-database'
949 );
950 }
951 // Ensure that the MySQL driver supports utf8mb4 encoding.
e479ac61 952 $version = mysqli_get_client_info();
75615982 953 if (strpos($version, 'mysqlnd') !== FALSE) {
954 // The mysqlnd driver supports utf8mb4 starting at version 5.0.9.
955 $version = preg_replace('/^\D+([\d.]+).*/', '$1', $version);
956 if (version_compare($version, '5.0.9', '<')) {
957 $messages[] = new CRM_Utils_Check_Message(
b2a06ef7 958 __FUNCTION__ . 'mysqlnd',
75615982 959 ts('It is recommended, though not yet required, to upgrade your PHP MySQL driver (mysqlnd) to >= 5.0.9 for utf8mb4 support.'),
960 ts('PHP MySQL Driver (mysqlnd)'),
961 \Psr\Log\LogLevel::WARNING,
962 'fa-server'
963 );
964 }
965 }
966 else {
967 // The libmysqlclient driver supports utf8mb4 starting at version 5.5.3.
968 if (version_compare($version, '5.5.3', '<')) {
969 $messages[] = new CRM_Utils_Check_Message(
b2a06ef7 970 __FUNCTION__ . 'libmysqlclient',
7806826f 971 ts('It is recommended, though not yet required, to upgrade your PHP MySQL driver (libmysqlclient) to >= 5.5.3 for utf8mb4 support.'),
75615982 972 ts('PHP MySQL Driver (libmysqlclient)'),
973 \Psr\Log\LogLevel::WARNING,
974 'fa-server'
975 );
976 }
977 }
978
979 return $messages;
980 }
981
17ca7aac
SL
982 public function checkMysqlVersion() {
983 $messages = [];
984 $version = CRM_Utils_SQL::getDatabaseVersion();
965596b4 985 $minRecommendedVersion = CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_MYSQL_VER;
57ec4649 986 $mariaDbRecommendedVersion = '10.1';
b1634834
SL
987 $upcomingCiviChangeVersion = '5.34';
988 if (version_compare(CRM_Utils_SQL::getDatabaseVersion(), $minRecommendedVersion, '<')) {
17ca7aac
SL
989 $messages[] = new CRM_Utils_Check_Message(
990 __FUNCTION__,
b1634834 991 ts('To prepare for CiviCRM v%4, please upgrade MySQL. The recommended version will be MySQL v%2 or MariaDB v%3.', [
f7738e49 992 1 => $version,
57ec4649
TO
993 2 => $minRecommendedVersion . '+',
994 3 => $mariaDbRecommendedVersion . '+',
b1634834 995 4 => $upcomingCiviChangeVersion . '+',
f7738e49 996 ]),
57ec4649 997 ts('MySQL Out-of-Date'),
f7738e49
SL
998 \Psr\Log\LogLevel::NOTICE,
999 'fa-server'
1000 );
1001 }
965596b4 1002 return $messages;
17ca7aac
SL
1003 }
1004
d76cb10e
SL
1005 public function checkPHPIntlExists() {
1006 $messages = [];
1007 if (!extension_loaded('intl')) {
1008 $messages[] = new CRM_Utils_Check_Message(
1009 __FUNCTION__,
edeb3ee8 1010 ts('This system currently does not have the PHP-Intl extension enabled. Please contact your system administrator about getting the extension enabled.'),
d76cb10e
SL
1011 ts('Missing PHP Extension: INTL'),
1012 \Psr\Log\LogLevel::WARNING,
1013 'fa-server'
1014 );
1015 }
1016 return $messages;
1017 }
1018
5bc392e6 1019}