{* file to handle db changes in 5.29.alpha1 during upgrade *}
+{* https://github.com/civicrm/civicrm-core/pull/17824 *}
+UPDATE civicrm_status_pref SET name = 'checkExtensionsOk' WHERE name = 'extensionsOk';
+UPDATE civicrm_status_pref SET name = 'checkExtensionsUpdates' WHERE name = 'extensionUpdates';
+
-- The RelationshipCache is a high-level index/cache for querying relationships.
DROP TABLE IF EXISTS `civicrm_relationship_cache`;
CREATE TABLE `civicrm_relationship_cache` (
}
/**
- * Run all system checks.
+ * Run all enabled system checks.
*
* This functon is wrapped by the System.check api.
*
* @param bool $max
* Whether to return just the maximum non-hushed severity
*
- * @return array
- * Array of CRM_Utils_Check_Message objects
+ * @return CRM_Utils_Check_Message[]
*/
public static function checkAll($max = FALSE) {
- $messages = [];
- foreach (glob(__DIR__ . '/Check/Component/*.php') as $filePath) {
- $className = 'CRM_Utils_Check_Component_' . basename($filePath, '.php');
- /* @var CRM_Utils_Check_Component $check */
- $check = new $className();
- if ($check->isEnabled()) {
- $messages = array_merge($messages, $check->checkAll());
- }
- }
-
- CRM_Utils_Hook::check($messages);
+ $messages = self::checkStatus();
uasort($messages, [__CLASS__, 'severitySort']);
return ($max) ? $maxSeverity : $messages;
}
+ /**
+ * @param array $statusNames
+ * Optionally specify the names of specific checks to run, or leave empty to run all
+ * @param bool $includeDisabled
+ * Run checks that have been explicitly disabled (default false)
+ *
+ * @return CRM_Utils_Check_Message[]
+ */
+ public static function checkStatus($statusNames = [], $includeDisabled = FALSE) {
+ $messages = [];
+ $checksNeeded = $statusNames;
+ foreach (glob(__DIR__ . '/Check/Component/*.php') as $filePath) {
+ $className = 'CRM_Utils_Check_Component_' . basename($filePath, '.php');
+ /* @var CRM_Utils_Check_Component $component */
+ $component = new $className();
+ if ($includeDisabled || $component->isEnabled()) {
+ $messages = array_merge($messages, $component->checkAll($statusNames, $includeDisabled));
+ }
+ if ($statusNames) {
+ // Early return if we have already run (or skipped) all the requested checks.
+ $checksNeeded = array_diff($checksNeeded, $component->getAllChecks());
+ if (!$checksNeeded) {
+ return $messages;
+ }
+ }
+ }
+
+ CRM_Utils_Hook::check($messages, $statusNames, $includeDisabled);
+
+ return $messages;
+ }
+
/**
* @param int $level
* @return string
return TRUE;
}
+ /**
+ * Get the names of all check functions in this class
+ *
+ * @return string[]
+ */
+ public function getAllChecks() {
+ return array_filter(get_class_methods($this), function($method) {
+ return $method !== 'checkAll' && strpos($method, 'check') === 0;
+ });
+ }
+
/**
* Run all checks in this class.
*
- * @return array
- * [CRM_Utils_Check_Message]
+ * @param array $requestedChecks
+ * Optionally specify the names of specific checks requested, or leave empty to run all
+ * @param bool $includeDisabled
+ * Run checks that have been explicitly disabled (default false)
*
- * @throws \API_Exception
+ * @return CRM_Utils_Check_Message[]
+ *
+ * @throws API_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
- public function checkAll() {
+ public function checkAll($requestedChecks = [], $includeDisabled = FALSE) {
$messages = [];
- foreach (get_class_methods($this) as $method) {
+ foreach ($this->getAllChecks() as $method) {
// Note that we should check if the test is disabled BEFORE running it in case it's disabled for performance.
- if ($method !== 'checkAll' && strpos($method, 'check') === 0 && !$this->isDisabled($method)) {
- $messages = array_merge($messages, $this->$method());
+ if ($this->isRequested($method, $requestedChecks) && ($includeDisabled || !$this->isDisabled($method))) {
+ $messages = array_merge($messages, $this->$method($includeDisabled));
}
}
return $messages;
}
+ /**
+ * Is this check one of those requested
+ *
+ * @param string $method
+ * @param array $requestedChecks
+ * @return bool
+ */
+ private function isRequested($method, $requestedChecks) {
+ if (!$requestedChecks) {
+ return TRUE;
+ }
+ foreach ($requestedChecks as $name) {
+ if (strpos($name, $method) === 0) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
/**
* Is the specified check disabled.
*
}
/**
+ * @param bool $force
* @return CRM_Utils_Check_Message[]
*/
- public function checkOutboundMail() {
+ public function checkOutboundMail($force = FALSE) {
$messages = [];
// CiviMail doesn't work in non-production environments; skip.
- if (CRM_Core_Config::environment() != 'Production') {
+ if (!$force && CRM_Core_Config::environment() != 'Production') {
return $messages;
}
/**
* Check that domain email and org name are set
+ * @param bool $force
* @return CRM_Utils_Check_Message[]
*/
- public function checkDomainNameEmail() {
+ public function checkDomainNameEmail($force = FALSE) {
$messages = [];
// CiviMail doesn't work in non-production environments; skip.
- if (CRM_Core_Config::environment() != 'Production') {
+ if (!$force && CRM_Core_Config::environment() != 'Production') {
return $messages;
}
/**
* Checks if a default bounce handling mailbox is set up
+ * @param bool $force
* @return CRM_Utils_Check_Message[]
*/
- public function checkDefaultMailbox() {
+ public function checkDefaultMailbox($force = FALSE) {
$messages = [];
// CiviMail doesn't work in non-production environments; skip.
- if (CRM_Core_Config::environment() != 'Production') {
+ if (!$force && CRM_Core_Config::environment() != 'Production') {
return $messages;
}
/**
* Checks if cron has run in the past hour (3600 seconds)
+ * @param bool $force
* @return CRM_Utils_Check_Message[]
* @throws CRM_Core_Exception
*/
- public function checkLastCron() {
+ public function checkLastCron($force = FALSE) {
$messages = [];
// Cron doesn't work in non-production environments; skip.
- if (CRM_Core_Config::environment() != 'Production') {
+ if (!$force && CRM_Core_Config::environment() != 'Production') {
return $messages;
}
if (!$okextensions && !$updates && !$errors) {
$messages[] = new CRM_Utils_Check_Message(
- 'extensionsOk',
+ __FUNCTION__ . 'Ok',
ts('No extensions installed. <a %1>Browse available extensions</a>.', [
1 => 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1') . '"',
]),
if ($updates) {
$messages[] = new CRM_Utils_Check_Message(
- 'extensionUpdates',
+ __FUNCTION__ . 'Updates',
'<ul><li>' . implode('</li><li>', $updates) . '</li></ul>',
ts('Extension Update Available', ['plural' => '%count Extension Updates Available', 'count' => count($updates)]),
\Psr\Log\LogLevel::WARNING,
$message = ts('All extensions are up-to-date:');
}
$messages[] = new CRM_Utils_Check_Message(
- 'extensionsOk',
+ __FUNCTION__ . 'Ok',
$message . '<ul><li>' . implode('</li><li>', $okextensions) . '</li></ul>',
ts('Extensions'),
\Psr\Log\LogLevel::INFO,
/**
* Ensure reply id is set to any default value
+ * @param bool $force
* @return CRM_Utils_Check_Message[]
*/
- public function checkReplyIdForMailing() {
+ public function checkReplyIdForMailing($force = FALSE) {
$messages = [];
// CiviMail doesn't work in non-production environments; skip.
- if (CRM_Core_Config::environment() != 'Production') {
+ if (!$force && CRM_Core_Config::environment() != 'Production') {
return $messages;
}
/**
* Check system status.
*
- * @param array $messages
- * Array<CRM_Utils_Check_Message>. A list of messages regarding system status.
+ * @param CRM_Utils_Check_Message[] $messages
+ * A list of messages regarding system status
+ * @param array $statusNames
+ * If specified, only these checks are being requested and others should be skipped
+ * @param bool $includeDisabled
+ * Run checks that have been explicitly disabled (default false)
* @return mixed
*/
- public static function check(&$messages) {
- return self::singleton()
- ->invoke(['messages'], $messages, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_check');
+ public static function check(&$messages, $statusNames = [], $includeDisabled = FALSE) {
+ return self::singleton()->invoke(['messages'],
+ $messages, $statusNames, $includeDisabled,
+ self::$_nullObject, self::$_nullObject, self::$_nullObject,
+ 'civicrm_check'
+ );
}
/**
/**
* Retrieve system notices, warnings, errors, etc.
+ * @method bool getIncludeDisabled()
*/
class Check extends \Civi\Api4\Generic\BasicGetAction {
+ /**
+ * Run checks that have been explicitly disabled (default false)
+ * @var bool
+ */
+ protected $includeDisabled = FALSE;
+
+ /**
+ * @param bool $includeDisabled
+ * @return Check
+ */
+ public function setIncludeDisabled(bool $includeDisabled): Check {
+ $this->includeDisabled = $includeDisabled;
+ return $this;
+ }
+
protected function getRecords() {
- $messages = [];
- foreach (\CRM_Utils_Check::checkAll() as $message) {
+ $messages = $names = [];
+
+ // Filtering by name relies on the component check rather than the api arrayQuery
+ // @see \CRM_Utils_Check_Component::isCheckable
+ foreach ($this->where as $i => $clause) {
+ if ($clause[0] == 'name' && !empty($clause[2]) && in_array($clause[1], ['=', 'IN'], TRUE)) {
+ $names = (array) $clause[2];
+ unset($this->where[$i]);
+ break;
+ }
+ }
+
+ foreach (\CRM_Utils_Check::checkStatus($names, $this->includeDisabled) as $message) {
$messages[] = $message->toArray();
}
return $messages;
protected function _itemsToGet($field) {
foreach ($this->where as $clause) {
// Look for exact-match operators (=, IN, or LIKE with no wildcard)
- if ($clause[0] == $field && (in_array($clause[1], ['=', 'IN']) || ($clause[1] == 'LIKE' && !(is_string($clause[2]) && strpos($clause[2], '%') !== FALSE)))) {
+ if ($clause[0] == $field && (in_array($clause[1], ['=', 'IN'], TRUE) || ($clause[1] == 'LIKE' && !(is_string($clause[2]) && strpos($clause[2], '%') !== FALSE)))) {
return (array) $clause[2];
}
}
--- /dev/null
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace api\v4\Entity;
+
+use Civi\Api4\Setting;
+use Civi\Api4\StatusPreference;
+use Civi\Api4\System;
+use api\v4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class SystemTest extends UnitTestCase {
+
+ public function testSystemCheck() {
+ $origEnv = \CRM_Core_Config::environment();
+ $hooks = \CRM_Utils_Hook::singleton();
+ $hooks->setHook('civicrm_check', [$this, 'hook_civicrm_check']);
+
+ // Test on non-prod site
+ Setting::set()->addValue('environment', 'Development')->setCheckPermissions(FALSE)->execute();
+
+ StatusPreference::delete()->setCheckPermissions(FALSE)->addWhere('name', '=', 'checkLastCron')->execute();
+
+ // Won't run on non-prod site without $includeDisabled
+ $check = System::check()->addWhere('name', '=', 'checkLastCron')->execute();
+ // Will have skipped our hook because name matched a core check
+ $this->assertCount(0, $check);
+
+ // This should only run the php check
+ $check = System::check()->addWhere('name', '=', 'checkPhpVersion')->setIncludeDisabled(TRUE)->execute();
+ // Hook should have been skipped because name clause was fulfilled
+ $this->assertCount(1, $check);
+
+ // Ensure cron check has not run
+ $this->assertCount(0, StatusPreference::get()->setCheckPermissions(FALSE)->addWhere('name', '=', 'checkLastCron')->execute());
+
+ // Will run on non-prod site with $includeDisabled.
+ // Giving a more-specific name will run all checks with less-specific names too
+ $check = System::check()->addWhere('name', '=', 'checkLastCronAbc')->setIncludeDisabled(TRUE)->execute()->indexBy('name');
+ // Will have run our hook too because name wasn't an exact match
+ $this->assertCount(2, $check);
+ $this->assertEquals('Ok', $check['hook_civicrm_check']['title']);
+
+ // We know the cron check has run because it would have left a record marked 'new'
+ $record = StatusPreference::get()->setCheckPermissions(FALSE)->addWhere('name', '=', 'checkLastCron')->execute()->first();
+ $this->assertEquals('new', $record['prefs']);
+
+ // Restore env
+ Setting::set()->addValue('environment', $origEnv)->setCheckPermissions(FALSE)->execute();
+ $hooks->reset();
+ }
+
+ public function hook_civicrm_check(&$messages, $statusNames, $includeDisabled) {
+ $messages[] = new \CRM_Utils_Check_Message(
+ __FUNCTION__,
+ 'Hook running',
+ 'Ok',
+ \Psr\Log\LogLevel::DEBUG
+ );
+ }
+
+}