Skip to content

Commit

Permalink
dev/core#4905 - Add CRM_Core_BAO_CustomGroup::getFiltered
Browse files Browse the repository at this point in the history
  • Loading branch information
colemanw committed Jan 12, 2024
1 parent 5402c04 commit bbb569a
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 55 deletions.
12 changes: 6 additions & 6 deletions CRM/Contact/Page/View/Summary.php
Original file line number Diff line number Diff line change
Expand Up @@ -417,14 +417,14 @@ public function getTabs(array $contact) {
}

// now add all the custom tabs
$extends = ['Contact', $this->get('contactType')];
$style = ['Tab', 'Tab with table'];
$activeGroups = CRM_Core_BAO_CustomGroup::getPermitted(CRM_Core_Permission::VIEW);
$filters = [
'is_active' => TRUE,
'extends' => $this->get('contactType'),
'style' => ['Tab', 'Tab with table'],
];
$activeGroups = CRM_Core_BAO_CustomGroup::getFiltered($filters, CRM_Core_Permission::VIEW);

foreach ($activeGroups as $group) {
if (!in_array($group['extends'], $extends) || !in_array($group['style'], $style)) {
continue;
}
$id = "custom_{$group['id']}";
$allTabs[] = [
'id' => $id,
Expand Down
70 changes: 45 additions & 25 deletions CRM/Core/BAO/CustomGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,43 +29,62 @@ public static function self_hook_civicrm_post(\Civi\Core\Event\PostEvent $e): vo
}

/**
* Retrieve all enabled custom groups and fields in a nested array.
* Retrieve custom groups and fields in a nested array, with optional filters and permissions applied.
*
* @return array[]
*/
public static function getActive(): array {
$allGroups = self::getAll();
$allGroups = array_values(array_filter($allGroups, fn($group) => $group['is_active']));
foreach ($allGroups as $groupIndex => $group) {
$allGroups[$groupIndex]['fields'] = array_values(array_filter($group['fields'], fn($field) => $field['is_active']));
}
return $allGroups;
}

/**
* Same as self::getActive() but returns only active groups and
* fields which the given user is allowed to see.
* With no params, this returns the same output as self::getAll().
*
* @param int $permissionType (CRM_Core_Permission::VIEW or CRM_Core_Permission::EDIT)
* @param array $filters
* [key => value] pairs to filter each custom group.
* - $filters[extends] will auto-expand Contact types
* - $filters[is_active] will also filter the fields
* @param int|null $permissionType
* Check permission: (CRM_Core_Permission::VIEW | CRM_Core_Permission::EDIT)
* @param int|null $userId
* User contact id for permission check (defaults to current user)
* @return array[]
*/
public static function getPermitted(int $permissionType, ?int $userId = NULL) {
if (!in_array($permissionType, [CRM_Core_Permission::EDIT, CRM_Core_Permission::VIEW], TRUE)) {
throw new CRM_Core_Exception('permissionType must be CRM_Core_Permission::VIEW or CRM_Core_Permission::EDIT');
}
$allGroups = self::getActive();
$allowedGroupIds = CRM_Core_Permission::customGroup($permissionType, FALSE, $userId);
$allGroups = array_filter($allGroups, function($group) use ($allowedGroupIds) {
return in_array($group['id'], $allowedGroupIds);
public static function getFiltered(array $filters = [], int $permissionType = NULL, int $userId = NULL): array {
if (isset($permissionType)) {
if (!in_array($permissionType, [CRM_Core_Permission::EDIT, CRM_Core_Permission::VIEW], TRUE)) {
throw new CRM_Core_Exception('permissionType must be CRM_Core_Permission::VIEW or CRM_Core_Permission::EDIT');
}
$filters['id'] = CRM_Core_Permission::customGroup($permissionType, FALSE, $userId);
}
if (!empty($filters['extends']) && is_string($filters['extends'])) {
$contactTypes = CRM_Contact_BAO_ContactType::basicTypes(TRUE);
if ($filters['extends'] === 'Contact') {
$filters['extends'] = array_merge(['Contact'], $contactTypes);
}
elseif (in_array($filters['extends'], $contactTypes, TRUE)) {
$filters['extends'] = ['Contact', $filters['extends']];
}
}
$allGroups = array_filter(self::getAll(), function($group) use ($filters) {
foreach ($filters as $key => $value) {
if (is_array($value)) {
if (!in_array($group[$key], $value)) {
return FALSE;
}
}
elseif ($group[$key] != $value) {
return FALSE;
}
}
return TRUE;
});
// The `is_active` filter applies to fields as well as groups.
if (!empty($filters['is_active'])) {
foreach ($allGroups as $groupIndex => $group) {
$allGroups[$groupIndex]['fields'] = array_values(array_filter($group['fields'], fn($field) => $field['is_active']));
}
}
return array_values($allGroups);
}

/**
* Fetch all custom groups and fields in a nested array.
*
* Avoids calling the API to prevent recursion or early-bootstrap issues.
* Output includes all custom group data + fields.
*
* @return array[]
*/
Expand All @@ -80,6 +99,7 @@ public static function getAll(): array {
$select[] = "f.`$fieldKey` AS `field__$fieldKey`";
}
}
// Avoid calling the API to prevent recursion or early-bootstrap issues.
$data = \CRM_Utils_SQL_Select::from('civicrm_custom_group g')
->join('f', 'LEFT JOIN civicrm_custom_field f ON g.id = f.custom_group_id')
->select($select)
Expand Down
12 changes: 7 additions & 5 deletions Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,14 @@ public function __construct($targetTable, $alias, $isMultiRecord, $columns) {
/**
* @inheritDoc
*/
public function getEntityFields() {
public function getEntityFields(): array {
$entityFields = [];
$baseEntity = CoreUtil::getApiNameFromTableName($this->getBaseTable());
foreach (\CRM_Core_BAO_CustomGroup::getActive() as $customGroup) {
if ($customGroup['table_name'] !== $this->getTargetTable()) {
continue;
}
$filters = [
'is_active' => TRUE,
'table_name' => $this->getTargetTable(),
];
foreach (\CRM_Core_BAO_CustomGroup::getFiltered($filters) as $customGroup) {
foreach ($customGroup['fields'] as $fieldArray) {
$entityFields[] = SpecFormatter::arrayToField($fieldArray, $baseEntity, $customGroup);
}
Expand Down
11 changes: 6 additions & 5 deletions Civi/Api4/Service/Schema/SchemaMapBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,12 @@ private function addCustomFields(SchemaMap $map, Table $baseTable, string $entit
if (!$customInfo) {
return;
}

foreach (\CRM_Core_BAO_CustomGroup::getActive() as $customGroup) {
if (!$customGroup['fields'] || !in_array($customGroup['extends'], $customInfo['extends'])) {
continue;
}
$filters = [
'extends' => $customInfo['extends'],
'is_active' => TRUE,
'fields' => TRUE,
];
foreach (\CRM_Core_BAO_CustomGroup::getFiltered($filters) as $customGroup) {
$customTable = new Table($customGroup['table_name']);

// Add entity_id join from multi-record custom group to the base entity
Expand Down
20 changes: 10 additions & 10 deletions Civi/Api4/Service/Spec/SpecGatherer.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,18 +158,21 @@ private function addCustomFields(string $entity, RequestSpec $spec, bool $checkP
$grouping = 'contact_sub_type';
}

$filters = [
'is_active' => TRUE,
'extends' => $customInfo['extends'],
'is_multiple' => FALSE,
];
$permissionType = NULL;
if ($checkPermissions) {
$permissionType = in_array($spec->getAction(), ['create', 'update', 'save', 'delete', 'replace']) ?
\CRM_Core_Permission::EDIT :
\CRM_Core_Permission::VIEW;
$customGroups = \CRM_Core_BAO_CustomGroup::getPermitted($permissionType);
}
else {
$customGroups = \CRM_Core_BAO_CustomGroup::getActive();
}
$customGroups = \CRM_Core_BAO_CustomGroup::getFiltered($filters, $permissionType);

foreach ($customGroups as $customGroup) {
if ($this->customGroupBelongsTo($customGroup, $customInfo['extends'], $values, $grouping)) {
if ($this->customGroupBelongsTo($customGroup, $values, $grouping)) {
foreach ($customGroup['fields'] as $fieldArray) {
$field = SpecFormatter::arrayToField($fieldArray, $entity, $customGroup);
$spec->addFieldSpec($field);
Expand All @@ -179,12 +182,9 @@ private function addCustomFields(string $entity, RequestSpec $spec, bool $checkP
}

/**
* Check if custom group belongs to $extends entity and meets criteria from $values
* Check if custom group meets criteria from $values
*/
private function customGroupBelongsTo(array $customGroup, array $extends, array $values, $grouping): bool {
if ($customGroup['is_multiple'] || !in_array($customGroup['extends'], $extends)) {
return FALSE;
}
private function customGroupBelongsTo(array $customGroup, array $values, $grouping): bool {
// No values or grouping = no filtering needed
if (empty($values) ||
(empty($customGroup['extends_entity_column_value']) && empty($customGroup['extends_entity_column_id']))
Expand Down
17 changes: 13 additions & 4 deletions tests/phpunit/CRM/Core/BAO/CustomGroupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,33 @@ public function tearDown(): void {
parent::tearDown();
}

public function testLoadAll(): void {
public function testGetFiltered(): void {
$this->quickCleanup([], TRUE);

$activeGroup = $this->CustomGroupCreate(['title' => 'ActiveGroup', 'weight' => 1]);
$activeGroup = $this->CustomGroupCreate(['title' => 'ActiveGroup', 'weight' => 1, 'extends' => 'Household']);
$this->customFieldCreate(['label' => 'Active', 'custom_group_id' => $activeGroup['id']]);
$this->customFieldCreate(['label' => 'Disabled', 'is_active' => 0, 'custom_group_id' => $activeGroup['id']]);

$inactiveGroup = $this->CustomGroupCreate(['title' => 'InactiveGroup', 'weight' => 2, 'is_active' => 0]);
$inactiveGroup = $this->CustomGroupCreate(['title' => 'InactiveGroup', 'weight' => 2, 'is_active' => 0, 'extends' => 'Activity']);
$this->customFieldCreate(['label' => 'Inactive', 'custom_group_id' => $inactiveGroup['id']]);

$allGroups = CRM_Core_BAO_CustomGroup::getAll();
$activeGroups = CRM_Core_BAO_CustomGroup::getActive();
$activeGroups = CRM_Core_BAO_CustomGroup::getFiltered(['is_active' => TRUE]);

$this->assertCount(2, $allGroups);
$this->assertCount(2, $allGroups[0]['fields']);
$this->assertCount(1, $allGroups[1]['fields']);
$this->assertCount(1, $activeGroups);
$this->assertEquals($activeGroup['id'], $activeGroups[0]['id']);
$this->assertCount(1, $activeGroups[0]['fields']);

$activityGroups = CRM_Core_BAO_CustomGroup::getFiltered(['extends' => 'Activity']);
$this->assertCount(1, $activityGroups);
$this->assertEquals($inactiveGroup['id'], $activityGroups[0]['id']);

$contactGroups = CRM_Core_BAO_CustomGroup::getFiltered(['extends' => 'Contact']);
$this->assertEquals($activeGroup['id'], $contactGroups[0]['id']);
$this->assertCount(2, $contactGroups[0]['fields']);
}

/**
Expand Down

0 comments on commit bbb569a

Please sign in to comment.