<?php
use Civi\Api4\Managed;
+use Civi\Api4\Utils\CoreUtil;
/**
* The ManagedEntities system allows modules to add records to the database
/**
* Force-revert a record back to its original state.
- * @param array $params
- * Key->value properties of CRM_Core_DAO_Managed used to match an existing record
+ * @param string $entityType
+ * @param $entityId
+ * @return bool
*/
- public function revert(array $params) {
+ public function revert(string $entityType, $entityId): bool {
$mgd = new \CRM_Core_DAO_Managed();
- $mgd->copyValues($params);
+ $mgd->entity_type = $entityType;
+ $mgd->entity_id = $entityId;
$mgd->find(TRUE);
$declarations = $this->getDeclarations([$mgd->module]);
$declarations = CRM_Utils_Array::findAll($declarations, [
'entity' => $mgd->entity_type,
]);
if ($mgd->id && isset($declarations[0])) {
- $this->updateExistingEntity(['update' => 'always'] + $declarations[0] + $mgd->toArray());
+ $item = ['update' => 'always'] + $declarations[0] + $mgd->toArray();
+ $this->backfillDefaults($item);
+ $this->updateExistingEntity($item);
return TRUE;
}
return FALSE;
}
+ /**
+ * Backfill default values to restore record to a pristine state
+ *
+ * @param array $item Managed APIv4 record
+ */
+ private function backfillDefaults(array &$item): void {
+ if ($item['params']['version'] != 4) {
+ return;
+ }
+ // Fetch default values for fields that are writeable
+ $condition = [['type', '=', 'Field'], ['readonly', 'IS EMPTY'], ['default_value', '!=', 'now']];
+ // Exclude "weight" as that auto-adjusts
+ if (in_array('SortableEntity', CoreUtil::getInfoItem($item['entity_type'], 'type'), TRUE)) {
+ $weightCol = CoreUtil::getInfoItem($item['entity_type'], 'order_by');
+ $condition[] = ['name', '!=', $weightCol];
+ }
+ $getFields = civicrm_api4($item['entity_type'], 'getFields', [
+ 'checkPermissions' => FALSE,
+ 'action' => 'create',
+ 'where' => $condition,
+ ]);
+ $defaultValues = $getFields->indexBy('name')->column('default_value');
+ $item['params']['values'] += $defaultValues;
+ }
+
/**
* Take appropriate action on every managed entity.
*
case 'unused':
if (CRM_Core_BAO_Managed::isApi4ManagedType($item['entity_type'])) {
- $getRefCount = \Civi\Api4\Utils\CoreUtil::getRefCount($item['entity_type'], $item['entity_id']);
+ $getRefCount = CoreUtil::getRefCount($item['entity_type'], $item['entity_id']);
}
else {
$getRefCount = civicrm_api3($item['entity_type'], 'getrefcount', [
* @throws \CRM_Core_Exception
*/
public function testRevertSavedSearch(): void {
+ $originalState = [
+ 'name' => 'TestManagedSavedSearch',
+ 'label' => 'Test Saved Search',
+ 'description' => 'Original state',
+ 'api_entity' => 'Contact',
+ 'api_params' => [
+ 'version' => 4,
+ 'select' => ['id'],
+ 'orderBy' => ['id', 'ASC'],
+ ],
+ ];
$this->_managedEntities[] = [
// Setting module to 'civicrm' works for the test but not sure we should actually support that
// as it's probably better to package stuff in a core extension instead of core itself.
'update' => 'never',
'params' => [
'version' => 4,
- 'values' => [
- 'name' => 'TestManagedSavedSearch',
- 'label' => 'Test Saved Search',
- 'description' => 'Original state',
- 'api_entity' => 'Contact',
- 'api_params' => [
- 'version' => 4,
- 'select' => ['id'],
- 'orderBy' => ['id', 'ASC'],
- ],
- ],
+ 'values' => $originalState,
],
];
$search = SavedSearch::get(FALSE)
->addWhere('name', '=', 'TestManagedSavedSearch')
- ->addSelect('description', 'local_modified_date')
+ ->addSelect('*', 'local_modified_date')
->execute()->single();
- $this->assertEquals('Original state', $search['description']);
+ foreach ($originalState as $fieldName => $originalValue) {
+ $this->assertEquals($originalValue, $search[$fieldName]);
+ }
+ $this->assertNull($search['expires_date']);
$this->assertNull($search['local_modified_date']);
SavedSearch::update(FALSE)
->addValue('id', $search['id'])
->addValue('description', 'Altered state')
+ ->addValue('expires_date', 'now + 1 year')
->execute();
$time = $this->getCurrentTimestamp();
$search = SavedSearch::get(FALSE)
->addWhere('name', '=', 'TestManagedSavedSearch')
- ->addSelect('description', 'has_base', 'base_module', 'local_modified_date')
+ ->addSelect('*', 'has_base', 'base_module', 'local_modified_date')
->execute()->single();
$this->assertEquals('Altered state', $search['description']);
// Check calculated fields
// local_modified_date should reflect the update just made
$this->assertGreaterThanOrEqual($time, $search['local_modified_date']);
$this->assertLessThanOrEqual($this->getCurrentTimestamp(), $search['local_modified_date']);
+ $this->assertGreaterThan($time, $search['expires_date']);
SavedSearch::revert(FALSE)
->addWhere('name', '=', 'TestManagedSavedSearch')
// Entity should be revered to original state
$result = SavedSearch::get(FALSE)
->addWhere('name', '=', 'TestManagedSavedSearch')
- ->addSelect('description', 'has_base', 'base_module', 'local_modified_date')
+ ->addSelect('*', 'has_base', 'base_module', 'local_modified_date')
->execute();
$search = $result->single();
- $this->assertEquals('Original state', $search['description']);
+ foreach ($originalState as $fieldName => $originalValue) {
+ $this->assertEquals($originalValue, $search[$fieldName]);
+ }
+ $this->assertNull($search['expires_date']);
// Check calculated fields
$this->assertTrue($search['has_base']);
$this->assertEquals('civicrm', $search['base_module']);