// $min_multiple = $this->add('text', 'min_multiple', ts('Minimum number of multiple records'), $attributes['min_multiple'] );
// $this->addRule('min_multiple', ts('is a numeric field') , 'numeric');
- $max_multiple = $this->add('text', 'max_multiple', ts('Maximum number of multiple records'), $attributes['max_multiple']);
+ $max_multiple = $this->add('number', 'max_multiple', ts('Maximum number of multiple records'), $attributes['max_multiple']);
$this->addRule('max_multiple', ts('is a numeric field'), 'numeric');
//allow to edit settings if custom set is empty CRM-5258
'Link' => 'Url',
];
$inputType = $map[$inputType] ?? $inputType;
+ if ($dataTypeName === 'ContactReference') {
+ $inputType = 'EntityRef';
+ }
if (in_array($inputType, ['Select', 'EntityRef'], TRUE) && !empty($data['serialize'])) {
$inputAttrs['multiple'] = TRUE;
}
public function addComputedFields(&$record) {
$name = $record['name'];
// Ex: $allPaths['viewIndividual'][0] == '/var/www/foo/afform/view-individual'].
- $allPaths = $this->findFilePaths()[$name];
+ $allPaths = $this->findFilePaths()[$name] ?? [];
// $activeLayoutPath = $this->findFilePath($name, self::LAYOUT_FILE);
// $activeMetaPath = $this->findFilePath($name, self::METADATA_FILE);
$localLayoutPath = $this->createSiteLocalPath($name, self::LAYOUT_FILE);
foreach ($this->values[$entityName] ?? [] as $values) {
// Only accept values from fields on the form
$values['fields'] = array_intersect_key($values['fields'] ?? [], $entity['fields']);
+ // Only accept joins set on the form
+ $values['joins'] = array_intersect_key($values['joins'] ?? [], $entity['joins']);
+ foreach ($values['joins'] as $joinEntity => &$joinValues) {
+ // Enforce the limit set by join[max]
+ $joinValues = array_slice($joinValues, 0, $entity['joins'][$joinEntity]['max'] ?? NULL);
+ // Only accept values from join fields on the form
+ foreach ($joinValues as $index => $vals) {
+ $joinValues[$index] = array_intersect_key($vals, $entity['joins'][$joinEntity]['fields']);
+ }
+ }
$entityValues[$entityName][] = $values;
}
// Predetermined values override submitted values
*
* @group headless
*/
-class api_v4_AfformUsageTest extends api_v4_AfformTestCase {
- use \Civi\Test\Api3TestTrait;
- use \Civi\Test\ContactTestTrait;
-
- protected static $layouts = [];
-
- protected $formName;
+class api_v4_AfformContactUsageTest extends api_v4_AfformUsageTestCase {
public static function setUpBeforeClass(): void {
parent::setUpBeforeClass();
EOHTML;
}
- public function setUp(): void {
- parent::setUp();
- Civi\Api4\Afform::revert(FALSE)
- ->addWhere('type', '=', 'block')
- ->execute();
- $this->formName = 'mock' . rand(0, 100000);
- }
-
- public function tearDown(): void {
- Civi\Api4\Afform::revert(FALSE)
- ->addWhere('name', '=', $this->formName)
- ->execute();
- parent::tearDown();
- }
-
public function testAboutMeAllowed(): void {
$this->useValues([
'layout' => self::$layouts['aboutMe'],
$this->assertEquals($orgEmail, $contact['org.display_name']);
}
- protected function useValues($values) {
- $defaults = [
- 'title' => 'My form',
- 'name' => $this->formName,
- ];
- $full = array_merge($defaults, $values);
- Civi\Api4\Afform::create(FALSE)
- ->setLayoutFormat('html')
- ->setValues($full)
- ->execute();
- }
-
}
--- /dev/null
+<?php
+
+/**
+ * Test case for Afform.prefill and Afform.submit.
+ *
+ * @group headless
+ */
+class api_v4_AfformCustomFieldUsageTest extends api_v4_AfformUsageTestCase {
+
+ public static function setUpBeforeClass(): void {
+ parent::setUpBeforeClass();
+
+ self::$layouts['customMulti'] = <<<EOHTML
+<af-form ctrl="afform">
+ <af-entity data="{contact_type: 'Individual'}" type="Contact" name="Individual1" label="Individual 1" actions="{create: true, update: true}" security="FBAC" />
+ <fieldset af-fieldset="Individual1">
+ <legend class="af-text">Individual 1</legend>
+ <afblock-name-individual></afblock-name-individual>
+ <div af-join="Custom_MyThings" af-repeat="Add" max="2">
+ <afjoin-custom-my-things></afjoin-custom-my-things>
+ </div>
+ </fieldset>
+ <button class="af-button btn-primary" crm-icon="fa-check" ng-click="afform.submit()">Submit</button>
+</af-form>
+EOHTML;
+ }
+
+ /**
+ * Checks that by creating a multi-record field group,
+ * Afform has automatically generated a block to go with it,
+ * which can be submitted multiple times
+ */
+ public function testMultiRecordCustomBlock(): void {
+ $customGroup = \Civi\Api4\CustomGroup::create(FALSE)
+ ->addValue('name', 'MyThings')
+ ->addValue('title', 'My Things')
+ ->addValue('style', 'Tab with table')
+ ->addValue('extends', 'Contact')
+ ->addValue('is_multiple', TRUE)
+ ->addValue('max_multiple', 2)
+ ->addChain('fields', \Civi\Api4\CustomField::save()
+ ->addDefault('custom_group_id', '$id')
+ ->setRecords([
+ ['name' => 'my_text', 'label' => 'My Text', 'data_type' => 'String', 'html_type' => 'Text'],
+ ['name' => 'my_friend', 'label' => 'My Friend', 'data_type' => 'ContactReference', 'html_type' => 'Autocomplete-Select'],
+ ])
+ )
+ ->execute();
+
+ // Creating a custom group should automatically create an afform block
+ $block = \Civi\Api4\Afform::get()
+ ->addWhere('name', '=', 'afjoinCustom_MyThings')
+ ->setLayoutFormat('shallow')
+ ->setFormatWhitespace(TRUE)
+ ->execute()->first();
+ $this->assertEquals(2, $block['repeat']);
+ $this->assertEquals('my_text', $block['layout'][0]['name']);
+ $this->assertEquals('my_friend', $block['layout'][1]['name']);
+
+ $cid1 = $this->individualCreate([], 1);
+ $cid2 = $this->individualCreate([], 2);
+
+ $this->useValues([
+ 'layout' => self::$layouts['customMulti'],
+ 'permission' => CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION,
+ ]);
+ $firstName = uniqid(__FUNCTION__);
+ $values = [
+ 'Individual1' => [
+ [
+ 'fields' => [
+ 'first_name' => $firstName,
+ 'last_name' => 'tester',
+ ],
+ 'joins' => [
+ 'Custom_MyThings' => [
+ ['my_text' => 'One', 'my_friend' => $cid1],
+ ['my_text' => 'Two', 'my_friend' => $cid2],
+ ['my_text' => 'Not allowed', 'my_friend' => $cid2],
+ ],
+ ],
+ ],
+ ],
+ ];
+ Civi\Api4\Afform::submit()
+ ->setName($this->formName)
+ ->setValues($values)
+ ->execute();
+ $contact = \Civi\Api4\Contact::get(FALSE)
+ ->addWhere('first_name', '=', $firstName)
+ ->addJoin('Custom_MyThings AS Custom_MyThings', 'LEFT', ['id', '=', 'Custom_MyThings.entity_id'])
+ ->addSelect('Custom_MyThings.my_text', 'Custom_MyThings.my_friend')
+ ->addOrderBy('Custom_MyThings.id')
+ ->execute();
+ $this->assertEquals('One', $contact[0]['Custom_MyThings.my_text']);
+ $this->assertEquals($cid1, $contact[0]['Custom_MyThings.my_friend']);
+ $this->assertEquals('Two', $contact[1]['Custom_MyThings.my_text']);
+ $this->assertEquals($cid2, $contact[1]['Custom_MyThings.my_friend']);
+ $this->assertTrue(empty($contact[2]));
+ }
+
+}
--- /dev/null
+<?php
+
+/**
+ * Test case for Afform.prefill and Afform.submit.
+ *
+ * @group headless
+ */
+abstract class api_v4_AfformUsageTestCase extends api_v4_AfformTestCase {
+ use \Civi\Test\Api3TestTrait;
+ use \Civi\Test\ContactTestTrait;
+
+ protected static $layouts = [];
+
+ protected $formName;
+
+ public function setUp(): void {
+ parent::setUp();
+ Civi\Api4\Afform::revert(FALSE)
+ ->addWhere('type', '=', 'block')
+ ->execute();
+ $this->formName = 'mock' . rand(0, 100000);
+ }
+
+ public function tearDown(): void {
+ Civi\Api4\Afform::revert(FALSE)
+ ->addWhere('name', '=', $this->formName)
+ ->execute();
+ parent::tearDown();
+ }
+
+ protected function useValues($values) {
+ $defaults = [
+ 'title' => 'My form',
+ 'name' => $this->formName,
+ ];
+ $full = array_merge($defaults, $values);
+ Civi\Api4\Afform::create(FALSE)
+ ->setLayoutFormat('html')
+ ->setValues($full)
+ ->execute();
+ }
+
+}
->execute()->first();
$this->assertEquals('Custom_' . $multiGroup['name'], CoreUtil::getApiNameFromTableName($multiGroup['table_name']));
+ $this->assertEquals($multiGroup['table_name'], CoreUtil::getTableName('Custom_' . $multiGroup['name']));
}
public function testGetApiClass() {