Merge pull request #20802 from civicrm/5.39
[civicrm-core.git] / tests / phpunit / CRM / Core / BAO / CustomFieldTest.php
1 <?php
2
3 use Civi\Api4\CustomField;
4
5 /**
6 * Class CRM_Core_BAO_CustomFieldTest
7 *
8 * @group headless
9 */
10 class CRM_Core_BAO_CustomFieldTest extends CiviUnitTestCase {
11
12 use CRMTraits_Custom_CustomDataTrait;
13
14 protected $customFieldID;
15
16 /**
17 * Clean up after test.
18 *
19 * @throws \Exception
20 */
21 public function tearDown(): void {
22 $this->quickCleanup([], TRUE);
23 parent::tearDown();
24 }
25
26 /**
27 * Test creating a custom field.
28 */
29 public function testCreateCustomField() {
30 $customGroup = $this->createCustomField();
31 $customFieldID = $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customGroup['id'], 'id', 'custom_group_id',
32 'Database check for created CustomField.'
33 );
34 $fields = [
35 'id' => $customFieldID,
36 'label' => 'editTestFld',
37 'is_active' => 1,
38 'data_type' => 'String',
39 'html_type' => 'Text',
40 'custom_group_id' => $customGroup['id'],
41 ];
42
43 CRM_Core_BAO_CustomField::create($fields);
44 $this->assertDBNotNull('CRM_Core_DAO_CustomField', 1, 'id', 'is_active', 'Database check for edited CustomField.');
45 $this->assertDBNotNull('CRM_Core_DAO_CustomField', $fields['label'], 'id', 'label', 'Database check for edited CustomField.');
46
47 $dbFieldName = $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customFieldID, 'name', 'id', 'Database check for edited CustomField.');
48 $dbColumnName = $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customFieldID, 'column_name', 'id', 'Database check for edited CustomField.');
49 $this->assertEquals(strtolower("{$dbFieldName}_{$customFieldID}"), $dbColumnName,
50 "Column name ends in ID");
51
52 $this->customGroupDelete($customGroup['id']);
53 }
54
55 /**
56 * Test changing a data type from multiple-choice to Text.
57 */
58 public function testChangeDataType() {
59 $customGroup = $this->createCustomField();
60 $fields = [
61 'label' => 'Radio to Text',
62 'is_active' => 1,
63 'data_type' => 'String',
64 'html_type' => 'Radio',
65 'custom_group_id' => $customGroup['id'],
66 'option_type' => 1,
67 'option_label' => ["One", "Two"],
68 'option_value' => [1, 2],
69 'option_weight' => [1, 2],
70 'option_status' => [1, 1],
71 ];
72 $customField = CRM_Core_BAO_CustomField::create($fields);
73 $this->assertNotNull($customField->option_group_id);
74 $fieldsNew = [
75 'id' => $customField->id,
76 'html_type' => 'Text',
77 'custom_group_id' => $customGroup['id'],
78 ];
79 $customFieldModified = CRM_Core_BAO_CustomField::create($fieldsNew);
80 $this->assertFalse($customFieldModified->option_group_id ?? FALSE);
81 }
82
83 /**
84 * Test custom field create accepts passed column name.
85 */
86 public function testCreateCustomFieldColumnName() {
87 $customGroup = $this->customGroupCreate(['extends' => 'Individual']);
88 $fields = [
89 'label' => 'testFld 2',
90 'column_name' => 'special_colname',
91 'data_type' => 'String',
92 'html_type' => 'Text',
93 'custom_group_id' => $customGroup['id'],
94 ];
95 CRM_Core_BAO_CustomField::create($fields);
96 $customFieldID = $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customGroup['id'], 'id', 'custom_group_id',
97 'Database check for created CustomField.'
98 );
99 $dbColumnName = $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customFieldID, 'column_name', 'id', 'Database check for edited CustomField.');
100 $this->assertEquals($fields['column_name'], $dbColumnName,
101 "Column name set as specified");
102
103 $this->customGroupDelete($customGroup['id']);
104 }
105
106 /**
107 * Test that name is used for the column.
108 */
109 public function testCreateCustomFieldName() {
110 $customGroup = $this->customGroupCreate(['extends' => 'Individual']);
111 $fields = [
112 'label' => 'testFld 2',
113 'name' => 'special_fldlname',
114 'data_type' => 'String',
115 'html_type' => 'Text',
116 'custom_group_id' => $customGroup['id'],
117 ];
118 CRM_Core_BAO_CustomField::create($fields);
119 $customFieldID = $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customGroup['id'], 'id', 'custom_group_id',
120 'Database check for created CustomField.'
121 );
122 $dbFieldName = $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customFieldID, 'name', 'id', 'Database check for edited CustomField.');
123 $this->assertEquals($fields['name'], $dbFieldName,
124 "Column name set as specified");
125
126 $this->customGroupDelete($customGroup['id']);
127 }
128
129 /**
130 * Test get fields function.
131 */
132 public function testGetFields() {
133 $customGroup = $this->customGroupCreate(['extends' => 'Individual']);
134 $fields = [
135 'label' => 'testFld1',
136 'data_type' => 'String',
137 'html_type' => 'Text',
138 'is_active' => 1,
139 'custom_group_id' => $customGroup['id'],
140 ];
141 CRM_Core_BAO_CustomField::create($fields);
142 $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customGroup['id'], 'id', 'custom_group_id',
143 'Database check for created CustomField.'
144 );
145 $fields = [
146 'label' => 'testFld2',
147 'data_type' => 'String',
148 'html_type' => 'Text',
149 'is_active' => 1,
150 'custom_group_id' => $customGroup['id'],
151 ];
152 CRM_Core_BAO_CustomField::create($fields);
153 $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customGroup['id'], 'id', 'custom_group_id',
154 'Database check for created CustomField.'
155 );
156
157 $this->customGroupDelete($customGroup['id']);
158 }
159
160 /**
161 * @throws \Exception
162 */
163 public function testGetDisplayedValues() {
164 $customGroup = $this->customGroupCreate(['extends' => 'Individual']);
165 $fieldsToCreate = [
166 [
167 'data_type' => 'Country',
168 'html_type' => 'Select',
169 'tests' => [
170 'United States' => 1228,
171 '' => NULL,
172 ],
173 ],
174 [
175 'data_type' => 'StateProvince',
176 'html_type' => 'Select',
177 'serialize' => 1,
178 'tests' => [
179 '' => 0,
180 'Alabama' => 1000,
181 'Alabama, Alaska' => [1000, 1001],
182 ],
183 ],
184 [
185 'data_type' => 'String',
186 'html_type' => 'Radio',
187 'option_values' => [
188 'key' => 'KeyLabel',
189 ],
190 'tests' => [
191 'KeyLabel' => 'key',
192 ],
193 ],
194 [
195 'data_type' => 'String',
196 'html_type' => 'CheckBox',
197 'option_values' => [
198 'key1' => 'Label1',
199 'key2' => 'Label2',
200 'key3' => 'Label3',
201 'key4' => 'Label4',
202 ],
203 'tests' => [
204 'Label1' => ['key1'],
205 'Label2' => 'key2',
206 'Label2, Label3' => ['key2', 'key3'],
207 'Label3, Label4' => CRM_Utils_Array::implodePadded(['key3', 'key4']),
208 'Label1, Label4' => ['key1' => 1, 'key4' => 1],
209 ],
210 ],
211 [
212 'data_type' => 'Date',
213 'html_type' => 'Select Date',
214 'date_format' => 'd M yy',
215 'time_format' => 1,
216 'tests' => [
217 '1 Jun 1999 1:30PM' => '1999-06-01 13:30',
218 '' => '',
219 ],
220 ],
221 [
222 'data_type' => 'Money',
223 'html_type' => 'Radio',
224 'option_values' => [
225 '10' => '10 USD',
226 '10.1' => '10.1 USD',
227 '10.99' => '10.99 USD',
228 ],
229 'tests' => [
230 '10 USD' => '10.00',
231 '10.1 USD' => '10.10',
232 '10.99 USD' => '10.99',
233 ],
234 ],
235 ];
236 foreach ($fieldsToCreate as $num => $field) {
237 $params = $field + ['label' => 'test field ' . $num, 'custom_group_id' => $customGroup['id']];
238 unset($params['tests']);
239 $createdField = $this->callAPISuccess('customField', 'create', $params);
240 foreach ($field['tests'] as $expected => $input) {
241 $this->assertEquals($expected, CRM_Core_BAO_CustomField::displayValue($input, $createdField['id']));
242 }
243 }
244
245 $this->customGroupDelete($customGroup['id']);
246 }
247
248 /**
249 * Test CRM_Core_BAO_CustomField::displayValue.
250 *
251 * @throws \CRM_Core_Exception
252 * @throws \Exception
253 */
254 public function testGetDisplayedValuesContactRef() {
255 $customGroup = $this->customGroupCreate(['extends' => 'Individual']);
256 $params = [
257 'data_type' => 'ContactReference',
258 'html_type' => 'Autocomplete-Select',
259 'label' => 'test ref',
260 'custom_group_id' => $customGroup['id'],
261 ];
262 $createdField = $this->callAPISuccess('customField', 'create', $params);
263 $contact1 = $this->individualCreate();
264 $contact2 = $this->individualCreate(['custom_' . $createdField['id'] => $contact1]);
265 $contact1Details = $this->callAPISuccess('Contact', 'getsingle', ['id' => $contact1]);
266 $this->assertEquals($contact1Details['display_name'], CRM_Core_BAO_CustomField::displayValue($contact2, $createdField['id']));
267 $this->assertEquals("Bob", CRM_Core_BAO_CustomField::displayValue("Bob", $createdField['id']));
268
269 $this->contactDelete($contact2);
270 $this->contactDelete($contact1);
271 $this->customGroupDelete($customGroup['id']);
272 }
273
274 public function testDeleteCustomField() {
275 $customGroup = $this->customGroupCreate(['extends' => 'Individual']);
276 $fields = [
277 'custom_group_id' => $customGroup['id'],
278 'label' => 'Throwaway Field',
279 'dataType' => 'Memo',
280 'htmlType' => 'TextArea',
281 ];
282
283 $customField = $this->customFieldCreate($fields);
284 $fieldObject = new CRM_Core_BAO_CustomField();
285 $fieldObject->id = $customField['id'];
286 $fieldObject->find(TRUE);
287 CRM_Core_BAO_CustomField::deleteField($fieldObject);
288 $this->assertDBNull('CRM_Core_DAO_CustomField', $customGroup['id'], 'id',
289 'custom_group_id', 'Database check for deleted Custom Field.'
290 );
291 $this->customGroupDelete($customGroup['id']);
292 }
293
294 /**
295 * Move a custom field from $groupA to $groupB.
296 *
297 * Make sure that data records are correctly matched and created.
298 *
299 * @throws \CRM_Core_Exception
300 */
301 public function testMoveField() {
302 $countriesByName = array_flip(CRM_Core_PseudoConstant::country(FALSE, FALSE));
303 $this->assertTrue($countriesByName['Andorra'] > 0);
304 $groups = [
305 'A' => $this->customGroupCreate([
306 'title' => 'Test_Group A',
307 'name' => 'test_group_a',
308 'extends' => ['Individual'],
309 'style' => 'Inline',
310 'is_multiple' => 0,
311 'is_active' => 1,
312 'version' => 3,
313 ]),
314 'B' => $this->customGroupCreate([
315 'title' => 'Test_Group B',
316 'name' => 'test_group_b',
317 'extends' => ['Individual'],
318 'style' => 'Inline',
319 'is_multiple' => 0,
320 'is_active' => 1,
321 'version' => 3,
322 ]),
323 ];
324 $groupA = $groups['A']['values'][$groups['A']['id']];
325 $groupB = $groups['B']['values'][$groups['B']['id']];
326 $countryA = $this->customFieldCreate([
327 'custom_group_id' => $groups['A']['id'],
328 'label' => 'Country A',
329 'dataType' => 'Country',
330 'htmlType' => 'Select',
331 'default_value' => NULL,
332 ]);
333 $countryB = $this->customFieldCreate([
334 'custom_group_id' => $groups['A']['id'],
335 'label' => 'Country B',
336 'dataType' => 'Country',
337 'htmlType' => 'Select',
338 'default_value' => NULL,
339 ]);
340 $countryC = $this->customFieldCreate([
341 'custom_group_id' => $groups['B']['id'],
342 'label' => 'Country C',
343 'dataType' => 'Country',
344 'htmlType' => 'Select',
345 'default_value' => NULL,
346 ]);
347
348 $fields = [
349 'countryA' => $countryA['values'][$countryA['id']],
350 'countryB' => $countryB['values'][$countryB['id']],
351 'countryC' => $countryC['values'][$countryC['id']],
352 ];
353 $contacts = [
354 'alice' => $this->individualCreate([
355 'first_name' => 'Alice',
356 'last_name' => 'Albertson',
357 'custom_' . $fields['countryA']['id'] => $countriesByName['Andorra'],
358 'custom_' . $fields['countryB']['id'] => $countriesByName['Barbados'],
359 ]),
360 'bob' => $this->individualCreate([
361 'first_name' => 'Bob',
362 'last_name' => 'Roberts',
363 'custom_' . $fields['countryA']['id'] => $countriesByName['Austria'],
364 'custom_' . $fields['countryB']['id'] => $countriesByName['Bermuda'],
365 'custom_' . $fields['countryC']['id'] => $countriesByName['Chad'],
366 ]),
367 'carol' => $this->individualCreate([
368 'first_name' => 'Carol',
369 'last_name' => 'Carolson',
370 'custom_' . $fields['countryC']['id'] => $countriesByName['Cambodia'],
371 ]),
372 ];
373
374 // Move!
375 CRM_Core_BAO_CustomField::moveField($fields['countryB']['id'], $groupB['id']);
376
377 // Group[A] no longer has fields[countryB]
378 try {
379 $this->assertDBQuery(1, "SELECT {$fields['countryB']['column_name']} FROM " . $groupA['table_name']);
380 $this->fail('Expected exception when querying column on wrong table');
381 }
382 catch (PEAR_Exception$e) {
383 }
384 $errorScope = NULL;
385
386 // Alice: Group[B] has fields[countryB], but fields[countryC] did not exist before
387 $this->assertDBQuery(1,
388 "SELECT count(*) FROM {$groupB['table_name']}
389 WHERE entity_id = %1
390 AND {$fields['countryB']['column_name']} = %3
391 AND {$fields['countryC']['column_name']} is null",
392 [
393 1 => [$contacts['alice'], 'Integer'],
394 3 => [$countriesByName['Barbados'], 'Integer'],
395 ]
396 );
397
398 // Bob: Group[B] has merged fields[countryB] and fields[countryC] on the same record
399 $this->assertDBQuery(1,
400 "SELECT count(*) FROM {$groupB['table_name']}
401 WHERE entity_id = %1
402 AND {$fields['countryB']['column_name']} = %3
403 AND {$fields['countryC']['column_name']} = %4",
404 [
405 1 => [$contacts['bob'], 'Integer'],
406 3 => [$countriesByName['Bermuda'], 'Integer'],
407 4 => [$countriesByName['Chad'], 'Integer'],
408 ]
409 );
410
411 // Carol: Group[B] still has fields[countryC] but did not get fields[countryB]
412 $this->assertDBQuery(1,
413 "SELECT count(*) FROM {$groupB['table_name']}
414 WHERE entity_id = %1
415 AND {$fields['countryB']['column_name']} is null
416 AND {$fields['countryC']['column_name']} = %4",
417 [
418 1 => [$contacts['carol'], 'Integer'],
419 4 => [$countriesByName['Cambodia'], 'Integer'],
420 ]
421 );
422
423 $this->customGroupDelete($groups['A']['id']);
424 $this->customGroupDelete($groupB['id']);
425 }
426
427 /**
428 * Test get custom field id function.
429 *
430 * @throws \CiviCRM_API3_Exception
431 */
432 public function testGetCustomFieldID() {
433 $this->createCustomField();
434 $fieldID = CRM_Core_BAO_CustomField::getCustomFieldID('testFld');
435 $this->assertEquals($this->customFieldID, $fieldID);
436
437 $fieldID = CRM_Core_BAO_CustomField::getCustomFieldID('testFld', 'new custom group');
438 $this->assertEquals($this->customFieldID, $fieldID);
439
440 $fieldID = CRM_Core_BAO_CustomField::getCustomFieldID('testFld', 'new custom group', TRUE);
441 $this->assertEquals('custom_' . $this->customFieldID, $fieldID);
442
443 // create field with same name in a different group
444 $this->createCustomField('other custom group');
445 $otherFieldID = CRM_Core_BAO_CustomField::getCustomFieldID('testFld', 'other custom group');
446 // make sure it does not return the field ID of the first field
447 $this->assertNotEquals($fieldID, $otherFieldID);
448 }
449
450 /**
451 * Create a custom field
452 *
453 * @param string $groupTitle
454 *
455 * @return array
456 */
457 protected function createCustomField($groupTitle = 'new custom group') {
458 $customGroup = $this->customGroupCreate([
459 'extends' => 'Individual',
460 'title' => $groupTitle,
461 ]);
462 $fields = [
463 'label' => 'testFld',
464 'data_type' => 'String',
465 'html_type' => 'Text',
466 'custom_group_id' => $customGroup['id'],
467 ];
468 $field = CRM_Core_BAO_CustomField::create($fields);
469 $this->customFieldID = $field->id;
470 return $customGroup;
471 }
472
473 /**
474 * Test the getFieldsForImport function.
475 *
476 * @throws \Exception
477 */
478 public function testGetFieldsForImport() {
479 $this->entity = 'Contact';
480 $this->createCustomGroupWithFieldsOfAllTypes();
481 $customGroupID = $this->ids['CustomGroup']['Custom Group'];
482 $expected = [
483 $this->getCustomFieldName('country') => [
484 'name' => $this->getCustomFieldName('country'),
485 'type' => 1,
486 'title' => 'Country',
487 'headerPattern' => '//',
488 'import' => 1,
489 'custom_field_id' => $this->getCustomFieldID('country'),
490 'options_per_line' => NULL,
491 'text_length' => NULL,
492 'data_type' => 'Country',
493 'html_type' => 'Select',
494 'is_search_range' => '0',
495 'id' => $this->getCustomFieldID('country'),
496 'label' => 'Country',
497 'groupTitle' => 'Custom Group',
498 'default_value' => NULL,
499 'custom_group_id' => $customGroupID,
500 'extends' => 'Contact',
501 'extends_entity_column_value' => NULL,
502 'extends_entity_column_id' => NULL,
503 'is_view' => '0',
504 'is_multiple' => '0',
505 'option_group_id' => NULL,
506 'date_format' => NULL,
507 'time_format' => NULL,
508 'is_required' => 0,
509 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
510 'column_name' => $this->getCustomFieldColumnName('country'),
511 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('country'),
512 'extends_table' => 'civicrm_contact',
513 'search_table' => 'contact_a',
514 'serialize' => 0,
515 'pseudoconstant' => [
516 'table' => 'civicrm_country',
517 'keyColumn' => 'id',
518 'labelColumn' => 'name',
519 'nameColumn' => 'iso_code',
520 ],
521 ],
522 $this->getCustomFieldName('multi_country') => [
523 'name' => $this->getCustomFieldName('multi_country'),
524 'type' => 1,
525 'title' => 'Country-multi',
526 'headerPattern' => '//',
527 'import' => 1,
528 'custom_field_id' => $this->getCustomFieldID('multi_country'),
529 'options_per_line' => NULL,
530 'text_length' => NULL,
531 'data_type' => 'Country',
532 'html_type' => 'Select',
533 'is_search_range' => '0',
534 'id' => $this->getCustomFieldID('multi_country'),
535 'label' => 'Country-multi',
536 'groupTitle' => 'Custom Group',
537 'default_value' => NULL,
538 'custom_group_id' => $customGroupID,
539 'extends' => 'Contact',
540 'extends_entity_column_value' => NULL,
541 'extends_entity_column_id' => NULL,
542 'is_view' => '0',
543 'is_multiple' => '0',
544 'option_group_id' => NULL,
545 'date_format' => NULL,
546 'time_format' => NULL,
547 'is_required' => 0,
548 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
549 'column_name' => $this->getCustomFieldColumnName('multi_country'),
550 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('multi_country'),
551 'extends_table' => 'civicrm_contact',
552 'search_table' => 'contact_a',
553 'serialize' => 1,
554 'pseudoconstant' => [
555 'table' => 'civicrm_country',
556 'keyColumn' => 'id',
557 'labelColumn' => 'name',
558 'nameColumn' => 'iso_code',
559 ],
560 ],
561 $this->getCustomFieldName('file') => [
562 'name' => $this->getCustomFieldName('file'),
563 'type' => 2,
564 'title' => 'My file',
565 'headerPattern' => '//',
566 'import' => 1,
567 'custom_field_id' => $this->getCustomFieldID('file'),
568 'options_per_line' => NULL,
569 'text_length' => NULL,
570 'data_type' => 'File',
571 'html_type' => 'File',
572 'is_search_range' => '0',
573 'id' => $this->getCustomFieldID('file'),
574 'label' => 'My file',
575 'groupTitle' => 'Custom Group',
576 'default_value' => NULL,
577 'custom_group_id' => $customGroupID,
578 'extends' => 'Contact',
579 'extends_entity_column_value' => NULL,
580 'extends_entity_column_id' => NULL,
581 'is_view' => '0',
582 'is_multiple' => '0',
583 'option_group_id' => NULL,
584 'date_format' => NULL,
585 'time_format' => NULL,
586 'is_required' => 0,
587 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
588 'column_name' => 'my_file_' . $this->getCustomFieldID('file'),
589 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.my_file_' . $this->getCustomFieldID('file'),
590 'extends_table' => 'civicrm_contact',
591 'search_table' => 'contact_a',
592 'serialize' => 0,
593 ],
594 $this->getCustomFieldName('text') => [
595 'name' => $this->getCustomFieldName('text'),
596 'type' => 2,
597 'title' => 'Enter text here',
598 'headerPattern' => '//',
599 'import' => 1,
600 'custom_field_id' => $this->getCustomFieldID('text'),
601 'options_per_line' => NULL,
602 'text_length' => 300,
603 'data_type' => 'String',
604 'html_type' => 'Text',
605 'is_search_range' => '0',
606 'id' => $this->getCustomFieldID('text'),
607 'label' => 'Enter text here',
608 'groupTitle' => 'Custom Group',
609 'default_value' => 'xyz',
610 'custom_group_id' => $customGroupID,
611 'extends' => 'Contact',
612 'extends_entity_column_value' => NULL,
613 'extends_entity_column_id' => NULL,
614 'is_view' => '0',
615 'is_multiple' => '0',
616 'option_group_id' => NULL,
617 'date_format' => NULL,
618 'time_format' => NULL,
619 'is_required' => 0,
620 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
621 'column_name' => 'enter_text_here_' . $this->getCustomFieldID('text'),
622 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.enter_text_here_' . $this->getCustomFieldID('text'),
623 'extends_table' => 'civicrm_contact',
624 'search_table' => 'contact_a',
625 'maxlength' => 300,
626 'serialize' => 0,
627 ],
628 $this->getCustomFieldName('select_string') => [
629 'name' => $this->getCustomFieldName('select_string'),
630 'type' => 2,
631 'title' => 'Pick Color',
632 'headerPattern' => '//',
633 'import' => 1,
634 'custom_field_id' => $this->getCustomFieldID('select_string'),
635 'options_per_line' => NULL,
636 'text_length' => NULL,
637 'data_type' => 'String',
638 'html_type' => 'Select',
639 'is_search_range' => '0',
640 'id' => $this->getCustomFieldID('select_string'),
641 'label' => 'Pick Color',
642 'groupTitle' => 'Custom Group',
643 'default_value' => NULL,
644 'custom_group_id' => $customGroupID,
645 'extends' => 'Contact',
646 'extends_entity_column_value' => NULL,
647 'extends_entity_column_id' => NULL,
648 'is_view' => '0',
649 'is_multiple' => '0',
650 'option_group_id' => $this->getOptionGroupID('select_string'),
651 'date_format' => NULL,
652 'time_format' => NULL,
653 'is_required' => 0,
654 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
655 'column_name' => 'pick_color_' . $this->getCustomFieldID('select_string'),
656 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.pick_color_' . $this->getCustomFieldID('select_string'),
657 'extends_table' => 'civicrm_contact',
658 'search_table' => 'contact_a',
659 'serialize' => 0,
660 'pseudoconstant' => [
661 'optionGroupName' => $this->callAPISuccessGetValue('CustomField', ['id' => $this->getCustomFieldID('select_string'), 'return' => 'option_group_id.name']),
662 'optionEditPath' => 'civicrm/admin/options/' . $this->callAPISuccessGetValue('CustomField', ['id' => $this->getCustomFieldID('select_string'), 'return' => 'option_group_id.name']),
663 ],
664 ],
665 $this->getCustomFieldName('select_date') => [
666 'name' => $this->getCustomFieldName('select_date'),
667 'type' => 4,
668 'title' => 'Test Date',
669 'headerPattern' => '//',
670 'import' => 1,
671 'custom_field_id' => $this->getCustomFieldID('select_date'),
672 'options_per_line' => NULL,
673 'text_length' => NULL,
674 'data_type' => 'Date',
675 'html_type' => 'Select Date',
676 'is_search_range' => '1',
677 'date_format' => 'mm/dd/yy',
678 'time_format' => '1',
679 'id' => $this->getCustomFieldID('select_date'),
680 'label' => 'Test Date',
681 'groupTitle' => 'Custom Group',
682 'default_value' => '20090711',
683 'custom_group_id' => $customGroupID,
684 'extends' => 'Contact',
685 'extends_entity_column_value' => NULL,
686 'extends_entity_column_id' => NULL,
687 'is_view' => '0',
688 'is_multiple' => '0',
689 'option_group_id' => NULL,
690 'is_required' => '0',
691 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
692 'column_name' => 'test_date_' . $this->getCustomFieldID('select_date'),
693 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.test_date_' . $this->getCustomFieldID('select_date'),
694 'extends_table' => 'civicrm_contact',
695 'search_table' => 'contact_a',
696 'serialize' => 0,
697 ],
698 $this->getCustomFieldName('link') => [
699 'name' => $this->getCustomFieldName('link'),
700 'type' => 2,
701 'title' => 'test_link',
702 'headerPattern' => '//',
703 'import' => 1,
704 'custom_field_id' => $this->getCustomFieldID('link'),
705 'options_per_line' => NULL,
706 'text_length' => NULL,
707 'data_type' => 'Link',
708 'html_type' => 'Link',
709 'is_search_range' => '0',
710 'id' => $this->getCustomFieldID('link'),
711 'label' => 'test_link',
712 'groupTitle' => 'Custom Group',
713 'default_value' => 'http://civicrm.org',
714 'custom_group_id' => $customGroupID,
715 'extends' => 'Contact',
716 'extends_entity_column_value' => NULL,
717 'extends_entity_column_id' => NULL,
718 'is_view' => '0',
719 'is_multiple' => '0',
720 'option_group_id' => NULL,
721 'date_format' => NULL,
722 'time_format' => NULL,
723 'is_required' => 0,
724 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
725 'column_name' => 'test_link_' . $this->getCustomFieldID('link'),
726 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.test_link_' . $this->getCustomFieldID('link'),
727 'extends_table' => 'civicrm_contact',
728 'search_table' => 'contact_a',
729 'serialize' => 0,
730 ],
731 $this->getCustomFieldName('int') => [
732 'name' => $this->getCustomFieldName('int'),
733 'type' => CRM_Utils_Type::T_INT,
734 'title' => 'Enter integer here',
735 'headerPattern' => '//',
736 'import' => 1,
737 'custom_field_id' => $this->getCustomFieldID('int'),
738 'options_per_line' => NULL,
739 'text_length' => NULL,
740 'data_type' => 'Int',
741 'html_type' => 'Text',
742 'is_search_range' => '1',
743 'id' => $this->getCustomFieldID('int'),
744 'label' => 'Enter integer here',
745 'groupTitle' => 'Custom Group',
746 'default_value' => '4',
747 'custom_group_id' => $customGroupID,
748 'extends' => 'Contact',
749 'extends_entity_column_value' => NULL,
750 'extends_entity_column_id' => NULL,
751 'is_view' => '0',
752 'is_multiple' => '0',
753 'option_group_id' => NULL,
754 'date_format' => NULL,
755 'time_format' => NULL,
756 'is_required' => 0,
757 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
758 'column_name' => $this->getCustomFieldColumnName('int'),
759 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('int'),
760 'extends_table' => 'civicrm_contact',
761 'search_table' => 'contact_a',
762 'serialize' => 0,
763 ],
764 $this->getCustomFieldName('contact_reference') => [
765 'name' => $this->getCustomFieldName('contact_reference'),
766 'type' => CRM_Utils_Type::T_INT,
767 'title' => 'Contact reference field',
768 'headerPattern' => '//',
769 'import' => 1,
770 'custom_field_id' => $this->getCustomFieldID('contact_reference'),
771 'options_per_line' => NULL,
772 'text_length' => NULL,
773 'data_type' => 'ContactReference',
774 'html_type' => 'Autocomplete-Select',
775 'is_search_range' => '0',
776 'id' => $this->getCustomFieldID('contact_reference'),
777 'label' => 'Contact reference field',
778 'groupTitle' => 'Custom Group',
779 'default_value' => NULL,
780 'custom_group_id' => $customGroupID,
781 'extends' => 'Contact',
782 'extends_entity_column_value' => NULL,
783 'extends_entity_column_id' => NULL,
784 'is_view' => '0',
785 'is_multiple' => '0',
786 'option_group_id' => NULL,
787 'date_format' => NULL,
788 'time_format' => NULL,
789 'is_required' => 0,
790 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
791 'column_name' => $this->getCustomFieldColumnName('contact_reference'),
792 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('contact_reference'),
793 'extends_table' => 'civicrm_contact',
794 'search_table' => 'contact_a',
795 'serialize' => 0,
796 ],
797 $this->getCustomFieldName('state') => [
798 'name' => $this->getCustomFieldName('state'),
799 'id' => $this->getCustomFieldID('state'),
800 'label' => 'State',
801 'headerPattern' => '//',
802 'title' => 'State',
803 'custom_field_id' => $this->getCustomFieldID('state'),
804 'groupTitle' => 'Custom Group',
805 'default_value' => NULL,
806 'custom_group_id' => $customGroupID,
807 'extends' => 'Contact',
808 'extends_entity_column_value' => NULL,
809 'extends_entity_column_id' => NULL,
810 'is_view' => '0',
811 'is_multiple' => '0',
812 'option_group_id' => NULL,
813 'date_format' => NULL,
814 'time_format' => NULL,
815 'is_required' => 0,
816 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
817 'column_name' => $this->getCustomFieldColumnName('state'),
818 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('state'),
819 'extends_table' => 'civicrm_contact',
820 'search_table' => 'contact_a',
821 'serialize' => 0,
822 'pseudoconstant' => [
823 'table' => 'civicrm_state_province',
824 'keyColumn' => 'id',
825 'labelColumn' => 'name',
826 ],
827 'import' => 1,
828 'data_type' => 'StateProvince',
829 'type' => 1,
830 'html_type' => 'Select',
831 'text_length' => NULL,
832 'options_per_line' => NULL,
833 'is_search_range' => '0',
834 ],
835 $this->getCustomFieldName('multi_state') => [
836 'id' => $this->getCustomFieldID('multi_state'),
837 'label' => 'State-multi',
838 'headerPattern' => '//',
839 'title' => 'State-multi',
840 'custom_field_id' => $this->getCustomFieldID('multi_state'),
841 'groupTitle' => 'Custom Group',
842 'default_value' => NULL,
843 'custom_group_id' => $customGroupID,
844 'extends' => 'Contact',
845 'extends_entity_column_value' => NULL,
846 'extends_entity_column_id' => NULL,
847 'is_view' => '0',
848 'is_multiple' => '0',
849 'option_group_id' => NULL,
850 'date_format' => NULL,
851 'time_format' => NULL,
852 'is_required' => 0,
853 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
854 'column_name' => $this->getCustomFieldColumnName('multi_state'),
855 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('multi_state'),
856 'extends_table' => 'civicrm_contact',
857 'search_table' => 'contact_a',
858 'serialize' => 1,
859 'pseudoconstant' => [
860 'table' => 'civicrm_state_province',
861 'keyColumn' => 'id',
862 'labelColumn' => 'name',
863 ],
864 'import' => 1,
865 'data_type' => 'StateProvince',
866 'name' => $this->getCustomFieldName('multi_state'),
867 'type' => 1,
868 'html_type' => 'Select',
869 'text_length' => NULL,
870 'options_per_line' => NULL,
871 'is_search_range' => '0',
872 ],
873 $this->getCustomFieldName('boolean') => [
874 'id' => $this->getCustomFieldID('boolean'),
875 'label' => 'Yes No',
876 'headerPattern' => '//',
877 'title' => 'Yes No',
878 'custom_field_id' => $this->getCustomFieldID('boolean'),
879 'groupTitle' => 'Custom Group',
880 'default_value' => NULL,
881 'custom_group_id' => $customGroupID,
882 'extends' => 'Contact',
883 'extends_entity_column_value' => NULL,
884 'extends_entity_column_id' => NULL,
885 'is_view' => '0',
886 'is_multiple' => '0',
887 'option_group_id' => NULL,
888 'date_format' => NULL,
889 'time_format' => NULL,
890 'is_required' => 0,
891 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
892 'column_name' => $this->getCustomFieldColumnName('boolean'),
893 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('boolean'),
894 'extends_table' => 'civicrm_contact',
895 'search_table' => 'contact_a',
896 'import' => 1,
897 'data_type' => 'Boolean',
898 'name' => $this->getCustomFieldName('boolean'),
899 'type' => 16,
900 'html_type' => 'Radio',
901 'text_length' => NULL,
902 'options_per_line' => NULL,
903 'is_search_range' => '0',
904 'serialize' => 0,
905 'pseudoconstant' => [
906 'callback' => 'CRM_Core_SelectValues::boolean',
907 ],
908 ],
909 $this->getCustomFieldName('checkbox') => [
910 'name' => $this->getCustomFieldName('checkbox'),
911 'custom_field_id' => $this->getCustomFieldID('checkbox'),
912 'id' => $this->getCustomFieldID('checkbox'),
913 'groupTitle' => 'Custom Group',
914 'default_value' => NULL,
915 'option_group_id' => $this->getOptionGroupID('checkbox'),
916 'custom_group_id' => $customGroupID,
917 'extends' => 'Contact',
918 'extends_entity_column_value' => NULL,
919 'extends_entity_column_id' => NULL,
920 'is_view' => '0',
921 'is_multiple' => '0',
922 'date_format' => NULL,
923 'time_format' => NULL,
924 'is_required' => 0,
925 'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
926 'column_name' => $this->getCustomFieldColumnName('checkbox'),
927 'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('checkbox'),
928 'extends_table' => 'civicrm_contact',
929 'search_table' => 'contact_a',
930 'import' => 1,
931 'label' => 'Pick Shade',
932 'headerPattern' => '//',
933 'title' => 'Pick Shade',
934 'data_type' => 'String',
935 'type' => 2,
936 'html_type' => 'CheckBox',
937 'text_length' => NULL,
938 'options_per_line' => NULL,
939 'is_search_range' => '0',
940 'serialize' => '1',
941 'pseudoconstant' => [
942 'optionGroupName' => $this->getOptionGroupName('checkbox'),
943 'optionEditPath' => 'civicrm/admin/options/' . $this->getOptionGroupName('checkbox'),
944
945 ],
946 ],
947 ];
948 $this->assertEquals($expected, CRM_Core_BAO_CustomField::getFieldsForImport());
949 }
950
951 /**
952 * Test the bulk create function works.
953 */
954 public function testBulkCreate(): void {
955 $customGroup = $this->customGroupCreate([
956 'extends' => 'Individual',
957 'title' => 'my bulk group',
958 ]);
959 CustomField::save(FALSE)->setRecords([
960 [
961 'label' => 'Test',
962 'data_type' => 'String',
963 'html_type' => 'Text',
964 'column_name' => 'my_text',
965 ],
966 [
967 'label' => 'test_link',
968 'data_type' => 'Link',
969 'html_type' => 'Link',
970 'is_search_range' => '0',
971 ],
972 ])->setDefaults(
973 [
974 'custom_group_id' => $customGroup['id'],
975 'is_active' => 1,
976 'is_searchable' => 1,
977 ])->execute();
978 $dao = CRM_Core_DAO::executeQuery(('SHOW CREATE TABLE ' . $customGroup['values'][$customGroup['id']]['table_name']));
979 $dao->fetch();
980 $this->assertStringContainsString('`test_link_2` varchar(255) COLLATE ' . CRM_Core_BAO_SchemaHandler::getInUseCollation() . ' DEFAULT NULL', $dao->Create_Table);
981 $this->assertStringContainsString('KEY `INDEX_my_text` (`my_text`)', $dao->Create_Table);
982 }
983
984 /**
985 * Check that outputting the display value for a file field with No description doesn't generate error
986 */
987 public function testFileDisplayValueNoDescription() {
988 $customGroup = $this->customGroupCreate([
989 'extends' => 'Individual',
990 'title' => 'Test Contact File Custom Group',
991 ]);
992 $fileField = $this->customFieldCreate([
993 'custom_group_id' => $customGroup['id'],
994 'data_type' => 'File',
995 'html_type' => 'File',
996 'default_value' => '',
997 ]);
998 $filePath = Civi::paths()->getPath('[civicrm.files]/custom/test_file.txt');
999 $file = $this->callAPISuccess('File', 'create', [
1000 'uri' => $filePath,
1001 ]);
1002 $individual = $this->individualCreate(['custom_' . $fileField['id'] => $file['id']]);
1003 $expectedDisplayValue = CRM_Core_BAO_File::paperIconAttachment('*', $file['id'])[$file['id']];
1004 $this->assertEquals($expectedDisplayValue, CRM_Core_BAO_CustomField::displayValue($file['id'], $fileField['id']));
1005 }
1006
1007 /**
1008 * Test for hook_civicrm_alterCustomFieldDisplayValue().
1009 */
1010 public function testAlterCustomFieldDisplayValueHook() {
1011 CRM_Utils_Hook_UnitTests::singleton()->setHook('civicrm_alterCustomFieldDisplayValue', [$this, 'alterCustomFieldDisplayValue']);
1012 $customGroupId = $this->customGroupCreate([
1013 'extends' => 'Individual',
1014 'title' => 'Test Contactcustom Group',
1015 ])['id'];
1016 $fieldId = $this->customFieldCreate([
1017 'custom_group_id' => $customGroupId,
1018 'name' => 'alter_cf_field',
1019 'label' => 'Alter CF Field',
1020 ])['id'];
1021 $contactId = $this->individualCreate(['custom_' . $fieldId => 'Test']);
1022
1023 $this->assertEquals('Test', $this->callAPISuccessGetValue('Contact',
1024 ['id' => $contactId, 'return' => "custom_{$fieldId}"]
1025 ));
1026
1027 $values = [];
1028 $fields = [
1029 'custom_' . $fieldId => $this->callAPISuccess('Contact', 'getfield', [
1030 'name' => 'custom_' . $fieldId,
1031 'action' => 'get',
1032 ])['values'],
1033 ];
1034
1035 // CRM_Core_BAO_UFGroup::getValues() invokes CRM_Core_BAO_CustomField::displayValue() function.
1036 CRM_Core_BAO_UFGroup::getValues($contactId, $fields, $values);
1037 $this->assertEquals('New value', $values['Alter CF Field']);
1038 }
1039
1040 /**
1041 * @param string $displayValue
1042 * @param mixed $value
1043 * @param int $entityId
1044 * @param array $fieldInfo
1045 *
1046 */
1047 public function alterCustomFieldDisplayValue(&$displayValue, $value, $entityId, $fieldInfo) {
1048 if ($fieldInfo['name'] == 'alter_cf_field') {
1049 $displayValue = 'New value';
1050 }
1051 }
1052
1053 /**
1054 * Test for single select Autocomplete custom field.
1055 *
1056 */
1057 public function testSingleSelectAutoComplete() {
1058 $customGroupId = $this->customGroupCreate([
1059 'extends' => 'Individual',
1060 ])['id'];
1061 $colors = ['Y' => 'Yellow', 'G' => 'Green'];
1062 $fieldId = $this->createAutoCompleteCustomField([
1063 'custom_group_id' => $customGroupId,
1064 'option_values' => $colors,
1065 ])['id'];
1066 $contactId = $this->individualCreate(['custom_' . $fieldId => 'Y']);
1067 $value = $this->callAPISuccessGetValue('Contact', [
1068 'id' => $contactId,
1069 'return' => 'custom_' . $fieldId,
1070 ]);
1071 $this->assertEquals('Y', $value);
1072 }
1073
1074 /**
1075 * Test for multi select Autocomplete custom field.
1076 *
1077 */
1078 public function testMultiSelectAutoComplete() {
1079 $customGroupId = $this->customGroupCreate([
1080 'extends' => 'Individual',
1081 ])['id'];
1082 $colors = ['Y' => 'Yellow', 'G' => 'Green'];
1083 $fieldId = $this->createAutoCompleteCustomField([
1084 'custom_group_id' => $customGroupId,
1085 'serialize' => '1',
1086 'option_values' => $colors,
1087 ])['id'];
1088 $contactId = $this->individualCreate(['custom_' . $fieldId => ['Y', 'G']]);
1089 $value = $this->callAPISuccessGetValue('Contact', [
1090 'id' => $contactId,
1091 'return' => 'custom_' . $fieldId,
1092 ]);
1093 $this->assertEquals(array_keys($colors), $value);
1094 }
1095
1096 }