Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CustomFields - Improve metadata about which custom groups belong to which entities #23336

Merged
merged 3 commits into from
May 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
401 changes: 308 additions & 93 deletions CRM/Core/BAO/CustomGroup.php

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion CRM/Core/DAO.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ protected function assignTestValue($fieldName, &$fieldDef, $counter) {
else {
$options = CRM_Core_PseudoConstant::get($daoName, $fieldName);
if (is_array($options)) {
$this->$dbName = $options[0];
$this->$dbName = $options[0] ?? NULL;
}
else {
$defaultValues = explode(',', $options);
Expand Down
14 changes: 7 additions & 7 deletions CRM/Core/DAO/AllCoreTables.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,11 @@ public static function convertEntityNameToCamel(string $name, $legacyV3 = FALSE)
// This map only applies to APIv3
$map = [
'acl' => 'Acl',
'ACL' => 'Acl',
'im' => 'Im',
'IM' => 'Im',
'pcp' => 'Pcp',
];
if ($legacyV3 && isset($map[$name])) {
return $map[$name];
if ($legacyV3 && isset($map[strtolower($name)])) {
return $map[strtolower($name)];
}

$fragments = explode('_', $name);
Expand All @@ -234,9 +233,10 @@ public static function convertEntityNameToCamel(string $name, $legacyV3 = FALSE)
$fragment = 'UF' . ucfirst(substr($fragment, 2));
}
}
// Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated)
if ($fragments[0] === 'Uf') {
$fragments[0] = 'UF';
// Exceptions to CamelCase: UFGroup, UFJoin, UFMatch, UFField, ACL, IM, PCP
$exceptions = ['Uf', 'Acl', 'Im', 'Pcp'];
if (in_array($fragments[0], $exceptions)) {
$fragments[0] = strtoupper($fragments[0]);
}
return implode('', $fragments);
}
Expand Down
7 changes: 5 additions & 2 deletions CRM/Core/DAO/CustomGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/Core/CustomGroup.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
* (GenCodeChecksum:65d78bdab80228a63775214faab08ee0)
* (GenCodeChecksum:fb6ed39a4e35bd2bcdc1a6cd549f4976)
*/

/**
Expand Down Expand Up @@ -348,7 +348,7 @@ public static function &fields() {
'bao' => 'CRM_Core_BAO_CustomGroup',
'localizable' => 0,
'pseudoconstant' => [
'callback' => 'CRM_Core_SelectValues::customGroupExtends',
'callback' => 'CRM_Core_BAO_CustomGroup::getCustomGroupExtendsOptions',
],
'add' => '1.1',
],
Expand Down Expand Up @@ -385,6 +385,9 @@ public static function &fields() {
'bao' => 'CRM_Core_BAO_CustomGroup',
'localizable' => 0,
'serialize' => self::SERIALIZE_SEPARATOR_BOOKEND,
'pseudoconstant' => [
'callback' => 'CRM_Core_BAO_CustomGroup::getExtendsEntityColumnValueOptions',
],
'add' => '1.6',
],
'style' => [
Expand Down
2 changes: 1 addition & 1 deletion CRM/Core/PseudoConstant.php
Original file line number Diff line number Diff line change
Expand Up @@ -1598,7 +1598,7 @@ public static function renderOptionsFromTablePseudoconstant($pseudoconstant, &$p
* List of options, each as a record of id+name+label.
* Ex: [['id' => 123, 'name' => 'foo_bar', 'label' => 'Foo Bar']]
*/
private static function formatArrayOptions($context, array &$options) {
public static function formatArrayOptions($context, array &$options) {
// Already flat; return keys/values according to context
if (!isset($options[0]) || !is_array($options[0])) {
// For validate context, machine names are expected in place of labels.
Expand Down
30 changes: 7 additions & 23 deletions CRM/Core/SelectValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,33 +223,17 @@ public static function customHtmlType() {
}

/**
* Various pre defined extensions for dynamic properties and groups.
* List of entities to present on the Custom Group form.
*
* @return array
* Includes pseudo-entities for Participant, in order to present sub-types on the form.
*
* @return array
*/
public static function customGroupExtends() {
$customGroupExtends = [
'Activity' => ts('Activities'),
'Relationship' => ts('Relationships'),
'Contribution' => ts('Contributions'),
'ContributionRecur' => ts('Recurring Contributions'),
'Group' => ts('Groups'),
'Membership' => ts('Memberships'),
'Event' => ts('Events'),
'Participant' => ts('Participants'),
'ParticipantRole' => ts('Participants (Role)'),
'ParticipantEventName' => ts('Participants (Event Name)'),
'ParticipantEventType' => ts('Participants (Event Type)'),
'Pledge' => ts('Pledges'),
'Grant' => ts('Grants'),
'Address' => ts('Addresses'),
'Campaign' => ts('Campaigns'),
];
$contactTypes = ['Contact' => ts('Contacts')] + self::contactType();
$extendObjs = CRM_Core_OptionGroup::values('cg_extend_objects');
$customGroupExtends = array_merge($contactTypes, $customGroupExtends, $extendObjs);
return $customGroupExtends;
$customGroupExtends = array_column(CRM_Core_BAO_CustomGroup::getCustomGroupExtendsOptions(), 'label', 'id');
// ParticipantRole, ParticipantEventName, etc.
$pseudoSelectors = CRM_Core_OptionGroup::values('custom_data_type', FALSE, FALSE, FALSE, NULL, 'label', TRUE, FALSE, 'name');
return array_merge($customGroupExtends, $pseudoSelectors);
}

/**
Expand Down
3 changes: 1 addition & 2 deletions CRM/Custom/Page/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,13 @@ public function browse() {
$customGroup[$id]['extends_display'] = $customGroupExtends[$customGroup[$id]['extends']];
}

//fix for Displaying subTypes
// FIXME: This hardcoded array is mostly redundant with CRM_Core_BAO_CustomGroup::getSubTypes
$subTypes = [];

$subTypes['Activity'] = CRM_Core_PseudoConstant::activityType(FALSE, TRUE, FALSE, 'label', TRUE);
$subTypes['Contribution'] = CRM_Contribute_PseudoConstant::financialType();
$subTypes['Membership'] = CRM_Member_BAO_MembershipType::getMembershipTypes(FALSE);
$subTypes['Event'] = CRM_Core_OptionGroup::values('event_type');
$subTypes['Grant'] = CRM_Core_OptionGroup::values('grant_type');
$subTypes['Campaign'] = CRM_Campaign_PseudoConstant::campaignType();
$subTypes['Participant'] = [];
$subTypes['ParticipantRole'] = CRM_Core_OptionGroup::values('participant_role');
Expand Down
21 changes: 21 additions & 0 deletions CRM/Upgrade/Incremental/sql/5.50.alpha1.mysql.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,24 @@ INSERT IGNORE INTO `civicrm_state_province` (`country_id`, `abbreviation`, `name
INSERT IGNORE INTO `civicrm_state_province` (`country_id`, `abbreviation`, `name`) VALUES (@country_id, 'SMI', 'Smiths');
INSERT IGNORE INTO `civicrm_state_province` (`country_id`, `abbreviation`, `name`) VALUES (@country_id, 'SOU', 'Southampton');
INSERT IGNORE INTO `civicrm_state_province` (`country_id`, `abbreviation`, `name`) VALUES (@country_id, 'WAR', 'Warwick');

SELECT @option_group_id_cgeo := max(id) FROM civicrm_option_group WHERE name = 'cg_extend_objects';

UPDATE civicrm_option_value
SET grouping = 'case_type_id', {localize field='description'}description = NULL{/localize}
WHERE option_group_id = @option_group_id_cgeo AND value = 'Case';

SELECT @option_group_id_cdt := max(id) FROM civicrm_option_group WHERE name = 'custom_data_type';

UPDATE civicrm_option_value
SET {localize field='label'}label = '{ts escape="sql"}Participants (Role){/ts}'{/localize}, grouping = 'role_id'
WHERE option_group_id = @option_group_id_cdt AND name = 'ParticipantRole';

UPDATE civicrm_option_value
SET {localize field='label'}label = '{ts escape="sql"}Participants (Event Name){/ts}'{/localize}, grouping = 'event_id'
WHERE option_group_id = @option_group_id_cdt AND name = 'ParticipantEventName';

UPDATE civicrm_option_value
SET {localize field='label'}label = '{ts escape="sql"}Participants (Event Type){/ts}'{/localize}, grouping = 'event_id.event_type_id'
WHERE option_group_id = @option_group_id_cdt AND name = 'ParticipantEventType';

Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,24 @@

use Civi\Api4\Service\Spec\RequestSpec;

class CustomGroupCreationSpecProvider implements Generic\SpecProviderInterface {
class CustomGroupSpecProvider implements Generic\SpecProviderInterface {

/**
* @inheritDoc
*/
public function modifySpec(RequestSpec $spec) {
$spec->getFieldByName('extends')->setRequired(TRUE);
$action = $spec->getAction();

$spec->getFieldByName('extends')
->setRequired($action === 'create')
->setSuffixes(['name', 'label', 'grouping']);
}

/**
* @inheritDoc
*/
public function applies($entity, $action) {
return $entity === 'CustomGroup' && $action === 'create';
return $entity === 'CustomGroup';
}

}
8 changes: 8 additions & 0 deletions Civi/Api4/Service/Spec/SpecFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,14 @@ private static function addOptionProps(&$options, $spec, $baoName, $fieldName, $
}
}
}
elseif ($returnFormat && !empty($pseudoconstant['callback'])) {
$callbackOptions = call_user_func(\Civi\Core\Resolver::singleton()->get($pseudoconstant['callback']), NULL, [], $values);
foreach ($callbackOptions as $callbackOption) {
if (is_array($callbackOption) && !empty($callbackOption['id']) && isset($optionIndex[$callbackOption['id']])) {
$options[$optionIndex[$callbackOption['id']]] += $callbackOption;
}
}
}
}
}
if (isset($props)) {
Expand Down
8 changes: 1 addition & 7 deletions Civi/Api4/Utils/CoreUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,14 @@ public static function getOperators() {
* @throws \Civi\API\Exception\UnauthorizedException
*/
public static function getCustomGroupExtends(string $entityName) {
// Custom_group.extends pretty much maps 1-1 with entity names, except for a couple oddballs (Contact, Participant).
// Custom_group.extends pretty much maps 1-1 with entity names, except for Contact.
switch ($entityName) {
case 'Contact':
return [
'extends' => array_merge(['Contact'], array_keys(\CRM_Core_SelectValues::contactType())),
'column' => 'id',
];

case 'Participant':
return [
'extends' => ['Participant', 'ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'],
'column' => 'id',
];

case 'RelationshipCache':
return [
'extends' => ['Relationship'],
Expand Down
10 changes: 5 additions & 5 deletions Civi/Api4/Utils/FormattingUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class FormattingUtil {
/**
* @var string[]
*/
public static $pseudoConstantSuffixes = ['name', 'abbr', 'label', 'color', 'description', 'icon'];
public static $pseudoConstantSuffixes = ['name', 'abbr', 'label', 'color', 'description', 'icon', 'grouping'];

/**
* Massage values into the format the BAO expects for a write operation
Expand Down Expand Up @@ -192,7 +192,6 @@ public static function formatDateValue($format, $value, &$operator = NULL, $inde
* @throws \CRM_Core_Exception
*/
public static function formatOutputValues(&$results, $fields, $action = 'get', $selectAliases = []) {
$fieldOptions = [];
foreach ($results as &$result) {
$contactTypePaths = [];
foreach ($result as $key => $value) {
Expand All @@ -211,16 +210,17 @@ public static function formatOutputValues(&$results, $fields, $action = 'get', $
}
// Evaluate pseudoconstant suffixes
$suffix = strrpos($fieldName, ':');
$fieldOptions = NULL;
if ($suffix) {
$fieldOptions[$fieldName] = $fieldOptions[$fieldName] ?? self::getPseudoconstantList($field, $fieldName, $result, $action);
$fieldOptions = self::getPseudoconstantList($field, $fieldName, $result, $action);
$dataType = NULL;
}
if ($fieldExpr->supportsExpansion) {
if (!empty($field['serialize']) && is_string($value)) {
$value = \CRM_Core_DAO::unSerializeField($value, $field['serialize']);
}
if (isset($fieldOptions[$fieldName])) {
$value = self::replacePseudoconstant($fieldOptions[$fieldName], $value);
if (isset($fieldOptions)) {
$value = self::replacePseudoconstant($fieldOptions, $value);
}
}
// Keep track of contact types for self::contactFieldsToRemove
Expand Down
10 changes: 2 additions & 8 deletions api/v3/CustomGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,10 @@
*/

/**
* Use this API to create a new group.
*
* The 'extends' value accepts an array or a comma separated string.
* e.g array(
* 'Individual','Contact') or 'Individual,Contact'
* See the CRM Data Model for custom_group property definitions
* $params['class_name'] is a required field, class being extended.
* Create or modify a custom field group.
*
* @param array $params
* Array per getfields metadata.
* For legacy reasons, 'extends' can be passed as an array (for setting Participant column_value)
*
* @return array
* @todo $params['extends'] is array format - is that std compatible
Expand Down
4 changes: 2 additions & 2 deletions api/v3/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* API result array
*/
function civicrm_api3_rule_create($params) {
return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params, 'Rule');
return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params, 'DedupeRule');
}

/**
Expand Down Expand Up @@ -67,5 +67,5 @@ function civicrm_api3_rule_delete($params) {
* API result array
*/
function civicrm_api3_rule_get($params) {
return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, TRUE, 'Rule');
return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, TRUE, 'DedupeRule');
}
2 changes: 1 addition & 1 deletion api/v3/RuleGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@ function civicrm_api3_rule_group_delete($params) {
* API result array
*/
function civicrm_api3_rule_group_get($params) {
return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params);
return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, TRUE, 'DedupeRuleGroup');
}
8 changes: 6 additions & 2 deletions api/v3/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ function _civicrm_api3_get_DAO($name) {
if ($name === 'MailingRecipients') {
return 'CRM_Mailing_DAO_Recipients';
}
if ($name === 'AclRole') {
if ($name === 'AclRole' || $name === 'ACLRole') {
return 'CRM_ACL_DAO_ACLEntityRole';
}
// FIXME: DAO should be renamed CRM_SMS_DAO_SmsProvider
Expand Down Expand Up @@ -1128,6 +1128,10 @@ function _civicrm_api3_custom_format_params($params, &$values, $extends, $entity
* @param string $entity
*/
function _civicrm_api3_format_params_for_create(&$params, $entity) {
if (!$entity) {
return;
}
$entity = CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel($entity);
$nonGenericEntities = array_merge(['Contact'], CRM_Contact_BAO_ContactType::basicTypes(TRUE));

$customFieldEntities = array_diff_key(CRM_Core_SelectValues::customGroupExtends(), array_fill_keys($nonGenericEntities, 1));
Expand Down Expand Up @@ -1958,7 +1962,7 @@ function _civicrm_api_get_fields($entity, $unique = FALSE, &$params = []) {
* @return array
*/
function _civicrm_api_get_custom_fields($entity, &$params) {
$entity = _civicrm_api_get_camel_name($entity);
$entity = CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel($entity);
if ($entity == 'Contact') {
// Use sub-type if available, otherwise "NULL" to fetch from all contact types
$entity = $params['contact_type'] ?? NULL;
Expand Down
24 changes: 24 additions & 0 deletions ext/civigrant/managed/OptionValue_cg_extends_objects_grant.mgd.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
use CRM_Grant_ExtensionUtil as E;

// This enables custom fields for Grant entities
return [
[
'name' => 'cg_extend_objects:Grant',
'entity' => 'OptionValue',
'cleanup' => 'always',
'update' => 'always',
'params' => [
'version' => 4,
'values' => [
'option_group_id.name' => 'cg_extend_objects',
'label' => E::ts('Grants'),
'value' => 'Grant',
'name' => 'civicrm_grant',
'is_reserved' => TRUE,
'is_active' => TRUE,
'grouping' => 'grant_type_id',
],
],
],
];
21 changes: 21 additions & 0 deletions tests/phpunit/CRM/Core/BAO/CustomGroupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -687,4 +687,25 @@ public function testAllowedGroupNames(string $extends, string $name, bool $isAll
$this->assertEquals($expectedName, $group->name);
}

public function testCustomGroupExtends() {
$extends = \CRM_Core_SelectValues::customGroupExtends();
$this->assertArrayHasKey('Contribution', $extends);
$this->assertArrayHasKey('Case', $extends);
$this->assertArrayHasKey('Contact', $extends);
$this->assertArrayHasKey('Individual', $extends);
$this->assertArrayHasKey('Household', $extends);
$this->assertArrayHasKey('Organization', $extends);
$this->assertArrayHasKey('Participant', $extends);
$this->assertArrayHasKey('ParticipantRole', $extends);
$this->assertArrayHasKey('ParticipantEventName', $extends);
$this->assertArrayHasKey('ParticipantEventType', $extends);
}

public function testMapTableName() {
$this->assertEquals('civicrm_case', CRM_Core_BAO_CustomGroup::mapTableName('Case'));
$this->assertEquals('civicrm_contact', CRM_Core_BAO_CustomGroup::mapTableName('Contact'));
$this->assertEquals('civicrm_contact', CRM_Core_BAO_CustomGroup::mapTableName('Individual'));
$this->assertEquals('civicrm_participant', CRM_Core_BAO_CustomGroup::mapTableName('Participant'));
}

}
Loading