['text' => ts('Contact Fields'), 'is_contact' => TRUE], 'address' => ['text' => ts('Address Fields'), 'is_contact' => TRUE], 'communication' => ['text' => ts('Communication Fields'), 'is_contact' => TRUE], ]; $optionMap = [ 'civicrm_website' => 'website_type_id', 'civicrm_phone' => 'phone_type_id', 'civicrm_im' => 'im_provider_id', ]; // Whitelist of field properties we actually care about; others will be discarded $fieldProps = ['id', 'text', 'has_location', 'option_list', 'relationship_type_id', 'related_contact_type']; $relTypes = civicrm_api3('RelationshipType', 'get', ['options' => ['limit' => 0]])['values']; // Add component fields $compFields = []; $compLabels = CRM_Core_BAO_Mapping::addComponentFields($compFields, 'Export', $exportMode); foreach ($compLabels as $comp => $label) { $categories[$comp] = ['text' => $label]; foreach ($compFields[$comp] as $key => $field) { $field['text'] = $field['title']; $field['id'] = $key; $categories[$comp]['children'][] = array_intersect_key($field, array_flip($fieldProps)); } } // Unset groups, tags, notes for component export if ($exportMode != CRM_Export_Form_Select::CONTACT_EXPORT) { foreach (array_keys($fieldGroups) as $contactType) { CRM_Utils_Array::remove($fieldGroups[$contactType], 'groups', 'tags', 'notes'); } } // Now combine all those redundant lists of fields into a single list with categories foreach ($fieldGroups as $contactType => $fields) { // 'related' was like a poor-mans optgroup. unset($fields['related']); foreach ($fields as $key => $field) { $group = 'contact'; $field['text'] = $field['title']; $field['id'] = $key; $field['has_location'] = !empty($field['hasLocationType']); if (isset($field['table_name']) && isset($optionMap[$field['table_name']])) { $field['option_list'] = $optionMap[$field['table_name']]; $group = 'communication'; } elseif (!empty($field['has_location'])) { $group = 'address'; } if ($key == 'email') { $group = 'communication'; } if (!empty($field['custom_group_id'])) { $group = $field['custom_group_id']; $categories[$group]['text'] = $field['groupTitle']; $categories[$group]['is_contact'] = TRUE; } if (!empty($field['related'])) { $group = 'related'; $categories[$group]['text'] = ts('Related Contact Info'); list($type, , $dir) = explode('_', $key); $field['related_contact_type'] = $relTypes[$type]["contact_sub_type_$dir"] ?? $relTypes[$type]["contact_type_$dir"] ?? '*'; // Skip relationship types targeting disabled contacts if ($field['related_contact_type'] != '*' && !isset($fieldGroups[$field['related_contact_type']])) { continue; } } if (empty($categories[$group]['children'][$key])) { // Discard unwanted field props to save space $categories[$group]['children'][$key] = array_intersect_key($field, array_flip($fieldProps)); } // Set contact_type, which gets added to on every iteration $categories[$group]['children'][$key]['contact_type'][] = $contactType; // If a field applies to every contact type, remove the contact_type flag as it's redundant if (count($fieldGroups) == count($categories[$group]['children'][$key]['contact_type'])) { unset($categories[$group]['children'][$key]['contact_type']); } } } // We needed meaningful keys while organizing fields but if we send them client-side they'll just be in the way foreach ($categories as &$category) { $category['children'] = array_values($category['children']); } return array_values($categories); } }