3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
12 use Civi\Api4\OptionGroup
;
15 * Test APIv3 civicrm_create_custom_group
20 class api_v3_CustomFieldTest
extends CiviUnitTestCase
{
23 * Clean up after test.
25 * @throws \CRM_Core_Exception
27 public function tearDown(): void
{
31 'civicrm_entity_file',
37 * Check with no label.
39 public function testCustomFieldCreateWithoutLabel() {
40 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'text_test_group']);
42 'custom_group_id' => $customGroup['id'],
43 'name' => 'test_textfield2',
44 'html_type' => 'Text',
45 'data_type' => 'String',
46 'default_value' => 'abc',
53 $customField = $this->callAPIFailure('custom_field', 'create', $params);
54 $this->assertEquals($customField['error_message'], 'Mandatory key(s) missing from params array: label');
60 public function testCustomFieldCreateWithEdit() {
61 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'text_test_group']);
63 'custom_group_id' => $customGroup['id'],
64 'name' => 'test_textfield2',
66 'html_type' => 'Text',
67 'data_type' => 'String',
68 'default_value' => 'abc',
75 $customField = $this->callAPIAndDocument('custom_field', 'create', $params, __FUNCTION__
, __FILE__
);
76 $params['id'] = $customField['id'];
77 $customField = $this->callAPISuccess('custom_field', 'create', $params);
79 $this->assertNotNull($customField['id']);
83 * Check without groupId.
85 public function testCustomFieldCreateWithoutGroupID() {
87 'name' => 'test_textfield1',
89 'html_type' => 'Text',
90 'data_type' => 'String',
91 'default_value' => 'abc',
99 $customField = $this->callAPIFailure('custom_field', 'create', $fieldParams);
100 $this->assertEquals($customField['error_message'], 'Mandatory key(s) missing from params array: custom_group_id');
104 * Check for Each data type: loop through available form input types
106 public function testCustomFieldCreateAllAvailableFormInputs() {
107 $gid = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'testAllFormInputs']);
109 $dtype = CRM_Core_BAO_CustomField
::dataType();
110 $htype = CRM_Custom_Form_Field
::$_dataToHTML;
112 // Legacy html types returned by v3
113 $htype['StateProvince'] = ['Select State/Province'];
114 $htype['Country'] = ['Select Country'];
116 foreach ($dtype as $dkey => $dvalue) {
117 foreach ($htype[$dkey] as $hvalue) {
118 $this->_loopingCustomFieldCreateTest($this->_buildParams($gid['id'], $hvalue, $dkey));
124 * Can't figure out the point of this?
128 * @param array $params
130 public function _loopingCustomFieldCreateTest($params) {
131 $customField = $this->callAPISuccess('custom_field', 'create', $params);
132 $this->assertNotNull($customField['id']);
133 $this->getAndCheck($params, $customField['id'], 'CustomField');
143 public function _buildParams($gid, $htype, $dtype) {
144 $params = $this->_buildBasicParams($gid, $htype, $dtype);
145 /* //Not Working for any type. Maybe redundant with testCustomFieldCreateWithOptionValues()
146 if ($htype == 'Multi-Select')
147 $params = array_merge($params, array(
148 'option_label' => array( 'Label1','Label2'),
149 'option_value' => array( 'val1', 'val2' ),
150 'option_weight' => array( 1, 2),
151 'option_status' => array( 1, 1),
165 public function _buildBasicParams($gid, $htype, $dtype) {
167 'custom_group_id' => $gid,
168 'label' => $dtype . $htype,
169 'html_type' => $htype,
170 'data_type' => $dtype,
173 'is_searchable' => 0,
180 * Check with data type - Options with option_values
182 public function testCustomFieldCreateWithEmptyOptionGroup(): void
{
183 $customGroup = $this->customGroupCreate(['extends' => 'Contact', 'title' => 'select_test_group']);
185 'custom_group_id' => $customGroup['id'],
186 'label' => 'Country',
187 'html_type' => 'Select',
188 'data_type' => 'String',
191 'is_searchable' => 0,
195 $customField = $this->callAPISuccess('CustomField', 'create', $params);
196 $this->assertNotNull($customField['id']);
197 $optionGroupID = $this->callAPISuccess('CustomField', 'getvalue', [
198 'id' => $customField['id'],
199 'return' => 'option_group_id',
202 $this->assertTrue(is_numeric($optionGroupID) && ($optionGroupID > 0));
203 $optionGroup = $this->callAPISuccess('option_group', 'getsingle', [
204 'id' => $optionGroupID,
206 $this->assertEquals('Country', $optionGroup['title']);
207 $optionValueCount = $this->callAPISuccess('option_value', 'getcount', [
208 'option_group_id' => $optionGroupID,
210 $this->assertEquals(0, $optionValueCount);
214 * Check with non-ascii labels
216 public function testCustomFieldCreateWithNonAsciiLabel() {
217 $customGroup = $this->customGroupCreate(['extends' => 'Contact', 'title' => 'select_test_group']);
219 'custom_group_id' => $customGroup['id'],
220 'label' => 'ôôôô',
221 'html_type' => 'Select',
222 'data_type' => 'String',
225 'is_searchable' => 0,
228 $customField = $this->callAPISuccess('CustomField', 'create', $params);
229 $this->assertNotNull($customField['id']);
230 $params['label'] = 'Ã Ã Ã ';
231 $customField = $this->callAPISuccess('CustomField', 'create', $params);
232 $this->assertNotNull($customField['id']);
236 * Test custom field with existing option group.
238 public function testCustomFieldExistingOptionGroup() {
239 $customGroup = $this->customGroupCreate(['extends' => 'Organization', 'title' => 'test_group']);
241 'custom_group_id' => $customGroup['id'],
242 // Just to say something:
243 'label' => 'Organization Gender',
244 'html_type' => 'Select',
245 'data_type' => 'Int',
248 'is_searchable' => 0,
250 // Option group id 3: gender
251 'option_group_id' => 3,
254 $customField = $this->callAPISuccess('custom_field', 'create', $params);
255 $this->assertNotNull($customField['id']);
256 $optionGroupID = $this->callAPISuccess('custom_field', 'getvalue', [
257 'id' => $customField['id'],
258 'return' => 'option_group_id',
261 $this->assertEquals($optionGroupID, 3);
265 * Test adding an optionGroup to an existing field doesn't cause a fatal error.
267 * (this was happening due to a check running despite no existing option_group_id)
269 * @throws \CiviCRM_API3_Exception
271 public function testUpdateCustomFieldAddOptionGroup() {
272 $customGroup = $this->customGroupCreate(['extends' => 'Organization', 'title' => 'test_group']);
274 'custom_group_id' => $customGroup['id'],
275 'label' => 'Organization Gender',
276 'html_type' => 'Text',
277 'data_type' => 'Int',
280 $customField = $this->callAPISuccess('custom_field', 'create', $params);
281 $this->callAPISuccess('CustomField', 'create', [
282 'option_group_id' => civicrm_api3('OptionGroup', 'getvalue', ['options' => ['limit' => 1], 'return' => 'id']),
283 'id' => $customField['id'],
284 'html_type' => 'Select',
289 * Test custom field get works & return param works
291 public function testCustomFieldGetReturnOptions() {
292 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'test_group']);
293 $customField = $this->customFieldCreate(['custom_group_id' => $customGroup['id']]);
295 $result = $this->callAPISuccess('custom_field', 'getsingle', [
296 'id' => $customField['id'],
297 'return' => 'data_type',
299 $this->assertTrue(array_key_exists('data_type', $result));
300 $this->assertFalse(array_key_exists('custom_group_id', $result));
304 * Test custom field get works & return param works
306 public function testCustomFieldGetReturnArray() {
307 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'test_group']);
308 $customField = $this->customFieldCreate(['custom_group_id' => $customGroup['id']]);
310 $result = $this->callAPISuccess('custom_field', 'getsingle', [
311 'id' => $customField['id'],
312 'return' => ['data_type'],
314 $this->assertTrue(array_key_exists('data_type', $result));
315 $this->assertFalse(array_key_exists('custom_group_id', $result));
319 * Test custom field get works & return param works
321 public function testCustomFieldGetReturnTwoOptions() {
322 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'test_group']);
323 $customField = $this->customFieldCreate(['custom_group_id' => $customGroup['id']]);
325 $result = $this->callAPISuccess('custom_field', 'getsingle', [
326 'id' => $customField['id'],
327 'return' => 'data_type, custom_group_id',
329 $this->assertTrue(array_key_exists('data_type', $result));
330 $this->assertTrue(array_key_exists('custom_group_id', $result));
331 $this->assertFalse(array_key_exists('label', $result));
334 public function testCustomFieldCreateWithOptionValues() {
335 $customGroup = $this->customGroupCreate(['extends' => 'Contact', 'title' => 'select_test_group']);
353 'custom_group_id' => $customGroup['id'],
354 'label' => 'Our special field',
355 'html_type' => 'Select',
356 'data_type' => 'String',
359 'is_searchable' => 0,
361 'option_values' => $option_values,
365 $customField = $this->callAPISuccess('custom_field', 'create', $params);
367 $this->assertAPISuccess($customField);
368 $this->assertNotNull($customField['id']);
370 'options' => ['get_options' => 'custom_' . $customField['id']],
371 'action' => 'create',
373 $description = "Demonstrates retrieving metadata with custom field options.";
374 $subfile = "GetFieldsOptions";
375 $fields = $this->callAPIAndDocument('contact', 'getfields', $getFieldsParams, __FUNCTION__
, 'ContactTest.php', $description, $subfile);
376 $this->assertArrayHasKey('options', $fields['values']['custom_' . $customField['id']]);
377 $this->assertEquals('Label1', $fields['values']['custom_' . $customField['id']]['options'][1]);
379 'field' => 'custom_' . $customField['id'],
381 $description = "Demonstrates retrieving options for a custom field.";
382 $subfile = "GetOptions";
383 $result = $this->callAPIAndDocument('contact', 'getoptions', $getOptionsArray, __FUNCTION__
, 'ContactTest.php', $description, '');
384 $this->assertEquals('Label1', $result['values'][1]);
387 ///////////////// civicrm_custom_field_delete methods
390 * Check without Field ID.
392 public function testCustomFieldDeleteWithoutFieldID() {
394 $customField = $this->callAPIFailure('custom_field', 'delete', $params,
395 'Mandatory key(s) missing from params array: id');
399 * Check without valid array.
401 public function testCustomFieldDelete() {
402 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'test_group']);
403 $customField = $this->customFieldCreate(['custom_group_id' => $customGroup['id']]);
404 $this->assertNotNull($customField['id']);
407 'id' => $customField['id'],
409 $result = $this->callAPIAndDocument('custom_field', 'delete', $params, __FUNCTION__
, __FILE__
);
411 $this->assertAPISuccess($result);
415 * Check That any associated Mapping Field Entries are also removed.
417 public function testCustomFieldDeleteWithMappingField() {
418 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'test_group']);
419 $customField = $this->customFieldCreate(['custom_group_id' => $customGroup['id']]);
420 $this->assertNotNull($customField['id']);
421 $mapping = $this->callAPISuccess('Mapping', 'create', [
422 'name' => 'test mapping',
423 'mapping_type_id' => 'Export Contact',
425 $mappingField = $this->callAPISuccess('MappingField', 'create', [
426 'mapping_id' => $mapping['id'],
427 'name' => 'custom_' . $customField['id'],
429 'column_number' => 0,
431 $mappingFieldCheck = $this->callAPISuccess('MappingField', 'get', ['mapping_id' => $mapping['id']]);
432 $this->assertCount(1, $mappingFieldCheck['values']);
434 'id' => $customField['id'],
436 $this->callAPISuccess('custom_field', 'delete', $params);
437 $mappingFieldCheck = $this->callAPISuccess('MappingField', 'get', ['mapping_id' => $mapping['id']]);
438 $this->assertCount(0, $mappingFieldCheck['values']);
439 $this->callAPISuccess('Mapping', 'delete', ['id' => $mapping['id']]);
443 * Check for Option Value.
445 public function testCustomFieldOptionValueDelete() {
446 $customGroup = $this->customGroupCreate(['extends' => 'Contact', 'title' => 'ABC']);
447 $customOptionValueFields = $this->customFieldOptionValueCreate($customGroup, 'fieldABC');
449 'id' => $customOptionValueFields,
452 $customField = $this->callAPISuccess('custom_field', 'delete', $customOptionValueFields);
456 * If there's one custom group for "Contact" and one for "Activity", then "Contact.getfields"
457 * and "Activity.getfields" should return only their respective fields (not the other's fields),
458 * and unrelated entities should return no custom fields.
460 public function testGetfields_CrossEntityPollution() {
461 $auxEntities = ['Email', 'Address', 'LocBlock', 'Membership', 'ContributionPage', 'ReportInstance'];
462 $allEntities = array_merge(['Contact', 'Activity'], $auxEntities);
464 // Baseline - getfields doesn't reporting any customfields for any entities
465 foreach ($allEntities as $entity) {
468 $this->getCustomFieldKeys($this->callAPISuccess($entity, 'getfields', [])),
469 "Baseline custom fields for $entity should be empty"
474 $contactGroup = $this->customGroupCreate(['extends' => 'Contact', 'title' => 'test_group_c']);
475 $contactField = $this->customFieldCreate([
476 'custom_group_id' => $contactGroup['id'],
477 'label' => 'For Contacts',
479 $indivGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'test_group_i']);
480 $indivField = $this->customFieldCreate(['custom_group_id' => $indivGroup['id'], 'label' => 'For Individuals']);
481 $activityGroup = $this->customGroupCreate(['extends' => 'Activity', 'title' => 'test_group_a']);
482 $activityField = $this->customFieldCreate([
483 'custom_group_id' => $activityGroup['id'],
484 'label' => 'For Activities',
489 ['custom_' . $contactField['id'], 'custom_' . $indivField['id']],
490 $this->getCustomFieldKeys($this->callAPISuccess('Contact', 'getfields', [])),
491 'Contact custom fields'
494 ['custom_' . $contactField['id'], 'custom_' . $indivField['id']],
495 $this->getCustomFieldKeys($this->callAPISuccess('Individual', 'getfields', [])),
496 'Individual custom fields'
499 ['custom_' . $contactField['id']],
500 $this->getCustomFieldKeys($this->callAPISuccess('Organization', 'getfields', [])),
501 'Organization custom fields'
504 ['custom_' . $activityField['id']],
505 $this->getCustomFieldKeys($this->callAPISuccess('Activity', 'getfields', [])),
506 'Activity custom fields'
508 foreach ($auxEntities as $entity) {
511 $this->getCustomFieldKeys($this->callAPISuccess($entity, 'getfields', [])),
512 "Custom fields for $entity should be empty"
518 * Test setting and getting a custom file field value.
520 * Uses the "attachment" api for setting value.
522 public function testCustomFileField() {
523 $customGroup = $this->customGroupCreate(['title' => 'attachment_test_group']);
525 'custom_group_id' => $customGroup['id'],
526 'name' => 'test_file_attachment',
527 'label' => 'test_file_attachment',
528 'html_type' => 'File',
529 'data_type' => 'File',
532 $customField = $this->callAPISuccess('custom_field', 'create', $params);
533 $cfId = 'custom_' . $customField['id'];
535 $cid = $this->individualCreate();
537 $attachment = $this->callAPISuccess('attachment', 'create', [
538 'name' => CRM_Utils_String
::createRandom(5, CRM_Utils_String
::ALPHANUMERIC
) . '_testCustomFileField.txt',
539 'mime_type' => 'text/plain',
540 'content' => 'My test content',
541 'field_name' => $cfId,
544 $this->assertAttachmentExistence(TRUE, $attachment);
546 $result = $this->callAPISuccess('contact', 'getsingle', [
551 $this->assertEquals($attachment['id'], $result[$cfId]);
554 public function testUpdateCustomField() {
555 $customGroup = $this->customGroupCreate(['extends' => 'Individual']);
556 $params = ['id' => $customGroup['id'], 'is_active' => 0];
557 $result = $this->callAPISuccess('CustomGroup', 'create', $params);
558 $result = array_shift($result['values']);
560 $this->assertEquals(0, $result['is_active']);
562 $this->customGroupDelete($customGroup['id']);
566 * @throws \API_Exception
567 * @throws \CRM_Core_Exception
569 public function testCustomFieldCreateWithOptionGroupName(): void
{
570 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'test_custom_group']);
571 OptionGroup
::create()->setValues(['name' => 'abc'])->execute();
573 'custom_group_id' => $customGroup['id'],
574 'name' => 'Activity type',
575 'label' => 'Activity type',
576 'data_type' => 'String',
577 'html_type' => 'Select',
578 'option_group_id' => 'abc',
580 $this->callAPISuccess('CustomField', 'create', $params);
584 * @param $getFieldsResult
588 public function getCustomFieldKeys($getFieldsResult) {
589 $isCustom = function ($key) {
590 return 0 === strpos($key, 'custom_');
592 $r = array_values(array_filter(array_keys($getFieldsResult['values']), $isCustom));
598 * @throws \CRM_Core_Exception
600 public function testMakeSearchableContactReferenceFieldUnsearchable() {
601 $this->customGroupCreate([
602 'name' => 'testCustomGroup',
603 'title' => 'testCustomGroup',
604 'extends' => 'Individual',
607 'name' => 'testCustomField',
608 'label' => 'testCustomField',
609 'custom_group_id' => 'testCustomGroup',
610 'data_type' => 'ContactReference',
611 'html_type' => 'Autocomplete-Select',
612 'is_searchable' => '1',
614 $result = $this->callAPISuccess('CustomField', 'create', $params);
616 'id' => $result['id'],
617 'is_searchable' => 0,
619 $result = $this->callAPISuccess('CustomField', 'create', $params);
623 * Test disabling a searchable contact reference field.
625 public function testDisableSearchableContactReferenceField() {
626 $customGroup = $this->customGroupCreate([
627 'name' => 'testCustomGroup',
628 'title' => 'testCustomGroup',
629 'extends' => 'Individual',
632 'name' => 'testCustomField',
633 'label' => 'testCustomField',
634 'custom_group_id' => 'testCustomGroup',
635 'data_type' => 'ContactReference',
636 'html_type' => 'Autocomplete-Select',
637 'is_searchable' => '1',
639 $result = $this->callAPISuccess('CustomField', 'create', $params);
641 'id' => $result['id'],
644 $this->callAPISuccess('CustomField', 'create', $params);
647 public function testLegacyHtmlType() {
648 $customGroup = $this->customGroupCreate([
649 'name' => 'testCustomGroup',
650 'title' => 'testCustomGroup',
651 'extends' => 'Individual',
653 $f1 = $this->callAPISuccess('CustomField', 'create', [
654 'label' => 'SingleSelect',
655 'custom_group_id' => 'testCustomGroup',
656 'data_type' => 'String',
657 'html_type' => 'Select',
658 'option_values' => [1 => 'One', 2 => 'Two'],
660 $f2 = $this->callAPISuccess('CustomField', 'create', [
661 'label' => 'CheckBoxes',
662 'custom_group_id' => 'testCustomGroup',
663 'data_type' => 'String',
664 'html_type' => 'CheckBox',
665 'option_values' => [1 => 'One', 2 => 'Two'],
667 $f3 = $this->callAPISuccess('CustomField', 'create', [
668 'label' => 'MultiSelect',
669 'custom_group_id' => 'testCustomGroup',
670 'data_type' => 'String',
671 'html_type' => 'Multi-Select',
672 'option_values' => [1 => 'One', 2 => 'Two'],
675 $result = $this->callAPISuccess('CustomField', 'get', [
676 'custom_group_id' => 'testCustomGroup',
677 'html_type' => 'Multi-Select',
680 $this->assertCount(1, $result['values']);
681 $this->assertEquals('MultiSelect', $result['values'][0]['label']);
683 $result = $this->callAPISuccess('CustomField', 'get', [
684 'custom_group_id' => 'testCustomGroup',
685 'html_type' => ['IN' => ['Multi-Select', 'CheckBox']],
688 $this->assertCount(2, $result['values']);
690 $result = $this->callAPISuccess('CustomField', 'get', [
691 'custom_group_id' => 'testCustomGroup',
692 'html_type' => 'Select',
695 $this->assertCount(1, $result['values']);
696 $this->assertEquals('SingleSelect', $result['values'][0]['label']);
698 $result = $this->callAPISuccess('CustomField', 'get', [
699 'custom_group_id' => 'testCustomGroup',
700 'html_type' => ['IN' => ['Select']],
703 $this->assertCount(1, $result['values']);
704 $this->assertEquals('SingleSelect', $result['values'][0]['label']);
707 public function testLegacyStateCountryTypes() {
708 $customGroup = $this->customGroupCreate([
709 'name' => 'testCustomGroup',
710 'title' => 'testCustomGroup',
711 'extends' => 'Individual',
713 $f1 = $this->callAPISuccess('CustomField', 'create', [
714 'label' => 'CountrySelect',
715 'custom_group_id' => 'testCustomGroup',
716 'data_type' => 'Country',
717 'html_type' => 'Select Country',
719 $f2 = $this->callAPISuccess('CustomField', 'create', [
720 'label' => 'StateSelect',
721 'custom_group_id' => 'testCustomGroup',
722 'data_type' => 'StateProvince',
723 'html_type' => 'Select State/Province',
725 $f3 = $this->callAPISuccess('CustomField', 'create', [
726 'label' => 'MultiSelectSP',
727 'custom_group_id' => 'testCustomGroup',
728 'data_type' => 'StateProvince',
729 'html_type' => 'Multi-Select State/Province',
731 $f4 = $this->callAPISuccess('CustomField', 'create', [
732 'label' => 'MultiSelectCountry',
733 'custom_group_id' => 'testCustomGroup',
734 'data_type' => 'Country',
735 'html_type' => 'Select Country',
739 $result = $this->callAPISuccess('CustomField', 'get', [
740 'custom_group_id' => 'testCustomGroup',
741 'html_type' => 'Multi-Select State/Province',
744 $this->assertCount(1, $result['values']);
745 $this->assertEquals('MultiSelectSP', $result['values'][0]['label']);
746 $this->assertEquals('Multi-Select State/Province', $result['values'][0]['html_type']);
747 $this->assertEquals('1', $result['values'][0]['serialize']);
749 $result = $this->callAPISuccess('CustomField', 'get', [
750 'custom_group_id' => 'testCustomGroup',
751 'html_type' => 'Multi-Select Country',
754 $this->assertCount(1, $result['values']);
755 $this->assertEquals('MultiSelectCountry', $result['values'][0]['label']);
756 $this->assertEquals('Multi-Select Country', $result['values'][0]['html_type']);
757 $this->assertEquals('1', $result['values'][0]['serialize']);
759 $result = $this->callAPISuccess('CustomField', 'get', [
760 'custom_group_id' => 'testCustomGroup',
761 'html_type' => 'Select Country',
764 $this->assertCount(1, $result['values']);
765 $this->assertEquals('CountrySelect', $result['values'][0]['label']);
766 $this->assertEquals('Select Country', $result['values'][0]['html_type']);
767 $this->assertEquals('0', $result['values'][0]['serialize']);
769 $result = $this->callAPISuccess('CustomField', 'get', [
770 'custom_group_id' => 'testCustomGroup',
771 'html_type' => 'Select State/Province',
774 $this->assertCount(1, $result['values']);
775 $this->assertEquals('StateSelect', $result['values'][0]['label']);
776 $this->assertEquals('Select State/Province', $result['values'][0]['html_type']);
777 $this->assertEquals('0', $result['values'][0]['serialize']);