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