diff --git a/CRM/Core/BAO/CustomGroup.php b/CRM/Core/BAO/CustomGroup.php index 568e92a6336f..d8c189a09e86 100644 --- a/CRM/Core/BAO/CustomGroup.php +++ b/CRM/Core/BAO/CustomGroup.php @@ -45,37 +45,29 @@ public static function create(&$params) { $group->title = $params['title']; } - $extends = CRM_Utils_Array::value('extends', $params, []); - $extendsEntity = $extends[0] ?? NULL; - - $participantEntities = [ - 'ParticipantRole', - 'ParticipantEventName', - 'ParticipantEventType', - ]; - - if (in_array($extendsEntity, $participantEntities)) { - $group->extends = 'Participant'; - } - else { - $group->extends = $extendsEntity; - } - - $group->extends_entity_column_id = 'null'; - if (in_array($extendsEntity, $participantEntities) - ) { - $group->extends_entity_column_id = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $extendsEntity, 'value', 'name'); - } - - // this is format when form get submit. - $extendsChildType = $extends[1] ?? NULL; + $extendsChildType = NULL; // lets allow user to pass direct child type value, CRM-6893 if (!empty($params['extends_entity_column_value'])) { $extendsChildType = $params['extends_entity_column_value']; } if (!CRM_Utils_System::isNull($extendsChildType)) { + $b = self::getMungedEntity($params['extends'], $params['extends_entity_column_id'] ?? NULL); + $registeredSubTypes = self::getSubTypes()[$b]; + if (is_array($extendsChildType)) { + foreach ($extendsChildType as $childType) { + if (!array_key_exists($childType, $registeredSubTypes) && !in_array($childType, $registeredSubTypes, TRUE)) { + throw new CRM_Core_Exception('Supplied Sub type is not valid for the specified entitiy'); + } + } + } + else { + if (!array_key_exists($extendsChildType, $registeredSubTypes) && !in_array($extendsChildType, $registeredSubTypes, TRUE)) { + throw new CRM_Core_Exception('Supplied Sub type is not valid for the specified entitiy'); + } + $extendsChildType = [$extendsChildType]; + } $extendsChildType = implode(CRM_Core_DAO::VALUE_SEPARATOR, $extendsChildType); - if (CRM_Utils_Array::value(0, $extends) == 'Relationship') { + if ($params['extends'] == 'Relationship') { $extendsChildType = str_replace(['_a_b', '_b_a'], [ '', '', @@ -107,6 +99,8 @@ public static function create(&$params) { 'is_active', 'is_multiple', 'icon', + 'extends_entity_column_id', + 'extends', ]; $current_db_version = CRM_Core_BAO_Domain::version(); $is_public_version = version_compare($current_db_version, '4.7.19', '>='); @@ -200,7 +194,7 @@ public static function create(&$params) { $params['id'], 'table_name' ); - CRM_Core_BAO_SchemaHandler::changeFKConstraint($table, self::mapTableName($extendsEntity)); + CRM_Core_BAO_SchemaHandler::changeFKConstraint($table, self::mapTableName($params['extends'])); } $transaction->commit(); @@ -2200,4 +2194,57 @@ private static function buildGroupTree($entityType, $toReturn, $subTypes, $query return [$multipleFieldGroups, $groupTree]; } + public static function getSubTypes(): array { + $sel2 = []; + $activityType = CRM_Core_PseudoConstant::activityType(FALSE, TRUE, FALSE, 'label', TRUE); + + $eventType = CRM_Core_OptionGroup::values('event_type'); + $grantType = CRM_Core_OptionGroup::values('grant_type'); + $campaignTypes = CRM_Campaign_PseudoConstant::campaignType(); + $membershipType = CRM_Member_BAO_MembershipType::getMembershipTypes(FALSE); + $participantRole = CRM_Core_OptionGroup::values('participant_role'); + + asort($activityType); + asort($eventType); + asort($grantType); + asort($membershipType); + asort($participantRole); + + $sel2['Event'] = $eventType; + $sel2['Grant'] = $grantType; + $sel2['Activity'] = $activityType; + $sel2['Campaign'] = $campaignTypes; + $sel2['Membership'] = $membershipType; + $sel2['ParticipantRole'] = $participantRole; + $sel2['ParticipantEventName'] = CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )"); + $sel2['ParticipantEventType'] = $eventType; + $sel2['Contribution'] = CRM_Contribute_PseudoConstant::financialType(); + $sel2['Relationship'] = CRM_Custom_Form_Group::getRelationshipTypes(); + + $sel2['Individual'] = CRM_Contact_BAO_ContactType::subTypePairs('Individual', FALSE, NULL); + $sel2['Household'] = CRM_Contact_BAO_ContactType::subTypePairs('Household', FALSE, NULL); + $sel2['Organization'] = CRM_Contact_BAO_ContactType::subTypePairs('Organization', FALSE, NULL); + + CRM_Core_BAO_CustomGroup::getExtendedObjectTypes($sel2); + return $sel2; + } + + /** + * Get the munged entity. + * + * This is the entity eg. Relationship or the name of the sub entity + * e.g ParticipantRole. + * + * @param string $extends + * @param int|null $extendsEntityColumn + * + * @return string + */ + protected static function getMungedEntity($extends, $extendsEntityColumn = NULL) { + if (!$extendsEntityColumn || $extendsEntityColumn === 'null') { + return $extends; + } + return CRM_Core_OptionGroup::values('custom_data_type', FALSE, FALSE, FALSE, NULL, 'name')[$extendsEntityColumn]; + } + } diff --git a/CRM/Custom/Form/Group.php b/CRM/Custom/Form/Group.php index ebb516f79939..ddf49e1c3a80 100644 --- a/CRM/Custom/Form/Group.php +++ b/CRM/Custom/Form/Group.php @@ -176,38 +176,8 @@ public function buildQuickForm() { $this->assign('contactTypes', json_encode($contactTypes)); $sel1 = ["" => ts("- select -")] + CRM_Core_SelectValues::customGroupExtends(); - $sel2 = []; - $activityType = CRM_Core_PseudoConstant::activityType(FALSE, TRUE, FALSE, 'label', TRUE); - - $eventType = CRM_Core_OptionGroup::values('event_type'); - $grantType = CRM_Core_OptionGroup::values('grant_type'); - $campaignTypes = CRM_Campaign_PseudoConstant::campaignType(); - $membershipType = CRM_Member_BAO_MembershipType::getMembershipTypes(FALSE); - $participantRole = CRM_Core_OptionGroup::values('participant_role'); - ksort($sel1); - asort($activityType); - asort($eventType); - asort($grantType); - asort($membershipType); - asort($participantRole); - - $sel2['Event'] = $eventType; - $sel2['Grant'] = $grantType; - $sel2['Activity'] = $activityType; - $sel2['Campaign'] = $campaignTypes; - $sel2['Membership'] = $membershipType; - $sel2['ParticipantRole'] = $participantRole; - $sel2['ParticipantEventName'] = CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )"); - $sel2['ParticipantEventType'] = $eventType; - $sel2['Contribution'] = CRM_Contribute_PseudoConstant::financialType(); - $sel2['Relationship'] = self::getRelationshipTypes(); - - $sel2['Individual'] = CRM_Contact_BAO_ContactType::subTypePairs('Individual', FALSE, NULL); - $sel2['Household'] = CRM_Contact_BAO_ContactType::subTypePairs('Household', FALSE, NULL); - $sel2['Organization'] = CRM_Contact_BAO_ContactType::subTypePairs('Organization', FALSE, NULL); - - CRM_Core_BAO_CustomGroup::getExtendedObjectTypes($sel2); + $sel2 = CRM_Core_BAO_CustomGroup::getSubTypes(); foreach ($sel2 as $main => $sub) { if (!empty($sel2[$main])) { @@ -443,7 +413,8 @@ public function postProcess() { $params['created_date'] = date('YmdHis'); } - $group = CRM_Core_BAO_CustomGroup::create($params); + $result = civicrm_api3('CustomGroup', 'create', $params); + $group = $result['values'][$result['id']]; // reset the cache Civi::cache('fields')->flush(); @@ -451,14 +422,14 @@ public function postProcess() { CRM_Core_BAO_Cache::resetCaches(); if ($this->_action & CRM_Core_Action::UPDATE) { - CRM_Core_Session::setStatus(ts('Your custom field set \'%1 \' has been saved.', [1 => $group->title]), ts('Saved'), 'success'); + CRM_Core_Session::setStatus(ts('Your custom field set \'%1 \' has been saved.', [1 => $group['title']]), ts('Saved'), 'success'); } else { // Jump directly to adding a field if popups are disabled $action = CRM_Core_Resources::singleton()->ajaxPopupsEnabled ? '' : '/add'; - $url = CRM_Utils_System::url("civicrm/admin/custom/group/field$action", 'reset=1&new=1&gid=' . $group->id . '&action=' . ($action ? 'add' : 'browse')); + $url = CRM_Utils_System::url("civicrm/admin/custom/group/field$action", 'reset=1&new=1&gid=' . $group['id'] . '&action=' . ($action ? 'add' : 'browse')); CRM_Core_Session::setStatus(ts("Your custom field set '%1' has been added. You can add custom fields now.", - [1 => $group->title] + [1 => $group['title']] ), ts('Saved'), 'success'); $session = CRM_Core_Session::singleton(); $session->replaceUserContext($url); diff --git a/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php b/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php index e0615aae143b..625a09575ce9 100644 --- a/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php +++ b/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php @@ -27,14 +27,9 @@ class CustomGroupPreCreationSubscriber extends Generic\PreCreationSubscriber { * @param \Civi\Api4\Generic\DAOCreateAction $request */ protected function modify(DAOCreateAction $request) { - $extends = $request->getValue('extends'); $title = $request->getValue('title'); $name = $request->getValue('name'); - if (is_string($extends)) { - $request->addValue('extends', [$extends]); - } - if (NULL === $title && $name) { $request->addValue('title', $name); } diff --git a/api/v3/CustomGroup.php b/api/v3/CustomGroup.php index 3c5519fc706f..f45c9ca3028b 100644 --- a/api/v3/CustomGroup.php +++ b/api/v3/CustomGroup.php @@ -40,6 +40,29 @@ function civicrm_api3_custom_group_create($params) { return civicrm_api3_create_error("First item in params['extends'] must be a class name (e.g. 'Contact')."); } + if (!isset($params['extends_entity_column_value']) && isset($params['extends'][1])) { + $extendsEntity = $params['extends'][0] ?? NULL; + $participantEntities = [ + 'ParticipantRole', + 'ParticipantEventName', + 'ParticipantEventType', + ]; + $params['extends_entity_column_id'] = 'null'; + if (in_array($extendsEntity, $participantEntities) + ) { + $params['extends_entity_column_id'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $extendsEntity, 'value', 'name'); + } + $params['extends_entity_column_value'] = $params['extends'][1]; + if (in_array($extendsEntity, $participantEntities)) { + $params['extends'] = 'Participant'; + } + else { + $params['extends'] = $extendsEntity; + } + } + elseif (isset($params['extends']) && (!isset($params['extends'][1]) || empty($params['extends'][1]))) { + $params['extends'] = $params['extends'][0]; + } if (isset($params['extends_entity_column_value']) && !is_array($params['extends_entity_column_value'])) { // BAO fails if this is a string, but API getFields says this must be a string, so we'll do a double backflip $params['extends_entity_column_value'] = CRM_Utils_Array::explodePadded($params['extends_entity_column_value']); diff --git a/tests/phpunit/CRMTraits/Custom/CustomDataTrait.php b/tests/phpunit/CRMTraits/Custom/CustomDataTrait.php index 95cbb922f561..ef904881c664 100644 --- a/tests/phpunit/CRMTraits/Custom/CustomDataTrait.php +++ b/tests/phpunit/CRMTraits/Custom/CustomDataTrait.php @@ -46,7 +46,7 @@ public function createCustomGroupWithFieldsOfAllTypes($groupParams = []) { public function createCustomGroup($params = []) { $params = array_merge([ 'title' => 'Custom Group', - 'extends' => [$this->entity ?? 'Contact'], + 'extends' => $this->entity ?? 'Contact', 'weight' => 5, 'style' => 'Inline', 'max_multiple' => 0, diff --git a/tests/phpunit/api/v3/CustomGroupTest.php b/tests/phpunit/api/v3/CustomGroupTest.php index e689a66bf401..1c38a6c09f58 100644 --- a/tests/phpunit/api/v3/CustomGroupTest.php +++ b/tests/phpunit/api/v3/CustomGroupTest.php @@ -175,8 +175,7 @@ public function testCustomGroupExtendsMultipleCreate() { 'is_active' => 1, ]; - $result = $this->callAPIFailure('custom_group', 'create', $params, - 'implode(): Invalid arguments passed'); + $result = $this->callAPIFailure('custom_group', 'create', $params, 'Supplied Sub type is not valid for the specified entitiy'); } /** @@ -369,4 +368,32 @@ public function testUpdateCustomGroup() { $this->customGroupDelete($customGroupId); } + /** + * Test that as per the form that if the extends column is passed as + * - ['ParticipantEventType', [4]] Where 4 = Meeting Event Type that we can create a custom group correctly + */ + public function testParticipantEntityCustomGroup() { + $customGroup = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, ['extends' => ['ParticipantEventType', [4]]])); + $result = array_shift($customGroup['values']); + $this->assertEquals(3, $result['extends_entity_column_id']); + $this->assertEquals('Participant', $result['extends']); + $this->customGroupDelete($result['id']); + } + + /** + * Test that without any fields we can change the entity type of the custom group and fields are correctly updated + */ + public function testChangeEntityCustomGroup() { + $customGroup = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, ['extends' => ['ParticipantEventType', [4]]])); + $result = array_shift($customGroup['values']); + $this->assertEquals(3, $result['extends_entity_column_id']); + $this->assertEquals('Participant', $result['extends']); + $customGroup = $this->callAPISuccess($this->_entity, 'create', ['id' => $customGroup['id'], 'extends' => ['Individual', []]]); + $result = array_shift($customGroup['values']); + $this->assertTrue(empty($result['extends_entity_column_id'])); + $this->assertTrue(empty($result['extends_entity_column_value'])); + $this->assertEquals('Individual', $result['extends']); + $this->customGroupDelete($result['id']); + } + }