Tim Otten [Mon, 7 Jun 2021 10:12:10 +0000 (03:12 -0700)]
(REF) Consolidate calls to `Hook::checkAccess()`. Define initial value `$granted=NULL`.
Regarding invocations:
* Before: There are three different ways `Hook::checkAccess()` may be invoked, e.g.
* `CRM_Core_DAO::checkAccess()`, which sprinkles in a call to `static::_checkAccess()` before `Hook::checkAccess()`
* `CRM_Core_BAO_CustomValue::checkAccess()`, which sprinkles in a call to `checkAccessDelegated()` after `Hook::checkAccess()`
* `CoreUtil::checkAccessRecord()`, which delegates to one of the above (if appropriate) or else calls `Hook::checkAccess()`
* `CoreUtil::checkAccessRecord()` is the most general entry-point
* After: There is one way to invoke `Hook::checkAccess()`, and it incorporates some qausi/unofficial listeners.
* `CoreUtil::checkAccessRecord()` is still the most general entry-point.
* `CoreUtil::checkAccessRecord()` fires `Hook::checkAccess()` unconditionally
* `CoreUtil::checkAccessRecord()` calls `CRM_Core_DAO::checkAccess()` and/or `CRM_Core_BAO_CustomValue::_checkAccess()`,
which are now quasi/unofficial listeners for the hook
Regarding initialization and passing of `$granted`:
* Before: The value of `$granted` defaults to `TRUE`. Listeners may flip between `TRUE`/`FALSE`. The value of `$granted` is passed to each listener.
* After: The value of `$granted` defaults to `NULL`. Listeners may flip to `TRUE`/`FALSE`. If it remains `NULL` until the end, then it's treated as `TRUE`.
The value of `$granted` is not passed to each listener.
* Comment: IMHO, this is an overall simplification. If you pass in `$granted`, then each listener has to decide
whether/how to mix the inputted value with its own decision. (Ex: Should it be `return $grantedInput &&
$myGrantedDecision` or `return $grantedInput || $myGrantedDecision` or `return $myGrantedDecision`? That choice appears to be
carefully informed by the context of what steps ran before.) In the updated protocol, each `_checkAccess()` a smaller scope.
Tim Otten [Mon, 7 Jun 2021 06:28:43 +0000 (23:28 -0700)]
(REF) Change CoreUtil::checkAccess() to CoreUtil::checkAccessRecord()
This change invovles a few things:
1. Pass the `AbstractAction $apiRequest` instead of the tuple `string $entity, string $action`.
2. There are a couple cases where we don't actually want to re-use the current `$apiRequest`.
Switch these using `checkAccessDelegated()`.
3. Always resolve the userID before calling `checkAccessRecord()`. `$userID===null` can mean
two different things (ie "active user" vs "anonymous user"). By
resolving this once before we do any work with `checkAccess()`, we ensure that it will
consistently mean "anonymous user" (even if there are multiple rounds of delegation).
3. Change the name from `checkAccess()` to `checkAccessRecord`. There are a few flavors of
`...checkAccess...`, and this makes it easier to differentiate when skimming.
Tim Otten [Mon, 7 Jun 2021 06:05:40 +0000 (23:05 -0700)]
(REF) Consistently pass `string $entity` to all flavors of checkAccess
1. This removes the special-case where `CustomValue::checkAccess()` needs an extra parameter
to identify the target entity.
2. This lines things up to do the swap from `_checkAccess()` to a hook/event listener
Tim Otten [Mon, 7 Jun 2021 03:13:12 +0000 (20:13 -0700)]
(REF) Isolate calls to $bao::checkAccess. Prefer CoreUtil::checkAccessDelegate.
Code paths:
* Before: There are many callers to `$bao::checkAccess()`.
* After: There is only one caller to `$bao::checkAccess()` (ie `CoreUtil`).
Delegation mechanics:
* Before: When delegating access-control to another entity, various things invoke `$bao::checkAccess()`.
* After: When delegating access-control to another entity, various things invoke `CoreUtil::checkAccessDelegated()`
Tim Otten [Fri, 4 Jun 2021 23:42:29 +0000 (16:42 -0700)]
CoreUtil::checkAccess() - Accept optional argument $userID
Technically, there is an inheritable contract-change here - modifying
`isAuthorized()` to accept the current user ID. However, I grepped
universe for references:
```
[bknix-min:~/bknix/build/universe] grep -ri isAuthorized $( find -name Civi )
```
And all references were internal to `civicrm-core.git`. This makes some
sense, given the available alternative extension-points
(`Civi\Api4\$ENTITY::permissions()` and `civi.api.authorize`).
Tim Otten [Mon, 7 Jun 2021 00:53:49 +0000 (17:53 -0700)]
(REF) AuthorizeEvent - Extract AuthorizedTrait
The primary purpose of this is to provide a trait (`AuthorizedTrait`) to
describe the common semantics of of coarse-grained authorization check and
the upcoming fine-grained authorization check.
The extracted trait makes a few small changes:
* Change the default value from `FALSE` to `NULL`. In grepping universe for
consumers of `isAuthorized(0`, I could only find consumers that used
bool-ish values. So this should be the same for them. However, for
future cases, it will allow some distinction between NULL/FALSE.
* Use more type-hints. The type should be nullable-boolean.
* Mutators should be amenable to fluent style (e.g. `$event->authorize()->stopPropagation()`).
Tim Otten [Mon, 7 Jun 2021 11:40:44 +0000 (04:40 -0700)]
ConformanceTest - Add support for read-only entities
Tim Otten [Fri, 4 Jun 2021 05:59:48 +0000 (22:59 -0700)]
ConformanceTest - Add coverage for checkAccess
Coleman Watts [Sat, 8 May 2021 00:20:43 +0000 (20:20 -0400)]
Implement checkAccess for EntityTags and Notes
Coleman Watts [Thu, 6 May 2021 17:39:21 +0000 (13:39 -0400)]
Implement checkAccess for custom entities
Coleman Watts [Wed, 5 May 2021 19:30:03 +0000 (15:30 -0400)]
Implement _checkAccess for Contact BAO and related entities (email, phone, etc.)
Implements the _checkAccess BAO callback for contacts and the related entities
listed in _civicrm_api3_check_edit_permissions.
Switch APIv4 to stop using _civicrm_api3_check_edit_permissions
now that the checks are implemented in the BAO.
Also fixes a couple permission check functions to respect $userID variable.
Coleman Watts [Tue, 27 Apr 2021 18:51:02 +0000 (14:51 -0400)]
APIv4 - Add checkAccess action
Call checkAccess action before creating, updating or deleting
eileen [Sun, 11 Apr 2021 20:22:52 +0000 (08:22 +1200)]
Add BAO function and hook for checkAccess
This adds a static ::checkAccess function to all BAOs, which dispatches to
a protected _checkAccess function in that BAO, as well as a new hook:
hook_civicrm_checkAccess($entity, $action, $record, $contactID, &$granted)
Coleman Watts [Fri, 4 Jun 2021 06:12:23 +0000 (23:12 -0700)]
UFJoin - Update addSelectWhereClause
Tim Otten [Fri, 4 Jun 2021 06:05:19 +0000 (23:05 -0700)]
DAOCreateAction - Fill defaults before validating write
Tim Otten [Fri, 4 Jun 2021 03:13:48 +0000 (20:13 -0700)]
FinancialItem - Provide defaults so that stricter ConformanceTest will pas
Context: There were three separate, concurrent PRs - two added more tests
and events to APIv4, and the third added a new entity (FinancialItem).
FinancialItem got merged first. I'm working reconciling the other two...
and discovered that `FinancialItem` isn't passing.
Problem: When the `ConformanceTest` creates a `FinancialItem`, it doesn't
fill in valid values for `entity_table,entity_id`. These values are
important to the access-control criteria used in reading-back data.
Tim Otten [Thu, 20 May 2021 06:17:25 +0000 (23:17 -0700)]
Add ValidateValuesTest
Tim Otten [Thu, 20 May 2021 05:11:29 +0000 (22:11 -0700)]
ValidateValues - Provide optional access to complete records
Tim Otten [Tue, 27 Apr 2021 21:38:38 +0000 (14:38 -0700)]
APIv4 - When running validateValues(), fire an event
Tim Otten [Fri, 4 Jun 2021 04:38:04 +0000 (21:38 -0700)]
(REF) Civi/API/Event - Extract RequestTrait
Tim Otten [Thu, 20 May 2021 02:34:16 +0000 (19:34 -0700)]
LazyArray - Add helper class for lazily-loaded lists
Tim Otten [Wed, 19 May 2021 22:21:23 +0000 (15:21 -0700)]
(REF) APIv4 UpdateAction - Add skeletal 'validateValues()` method
Tim Otten [Wed, 19 May 2021 23:45:51 +0000 (16:45 -0700)]
(REF) APIv4 BatchAction - Split 'getBatchRecords()' in two
The original form (`getBatchRecords()`) still returns an array of items.
The alternate form (`getBatchAction()`) returns an API call for fetching the
batchs - but this API call may be further refined (e.g. selecting different
fields or data-pages).
Tim Otten [Tue, 18 May 2021 08:56:37 +0000 (01:56 -0700)]
(REF) Consolidate the 'dispatchSubevent()' methods
Eileen McNaughton [Mon, 7 Jun 2021 01:18:19 +0000 (13:18 +1200)]
Merge pull request #20507 from colemanw/apiSmartGroups
SearchKit - Add API filter for contacts in groups and smart groups
Seamus Lee [Mon, 7 Jun 2021 00:38:36 +0000 (10:38 +1000)]
Merge pull request #20520 from seamuslee001/php8_more_template_guard
[php8-compat] Fix api_v3_PaymentTest failures by putting in more guar…
Tim Otten [Sun, 6 Jun 2021 23:59:48 +0000 (16:59 -0700)]
Merge pull request #20499 from JMAConsulting/add_permission_fi_api4
dev/core#2486 - Use read-only permissions for FinancialItem API
Tim Otten [Sun, 6 Jun 2021 23:50:06 +0000 (16:50 -0700)]
Merge pull request #20513 from demeritcowboy/regen-better
dev/core#1549 - Malleate civicrm_generated so that long lines are split
Seamus Lee [Sun, 6 Jun 2021 22:53:56 +0000 (22:53 +0000)]
[php8-compat] Fix api_v3_PaymentTest failures by putting in more guards into message templates
Eileen McNaughton [Sun, 6 Jun 2021 21:58:39 +0000 (09:58 +1200)]
Merge pull request #20516 from seamuslee001/php8_more_test_fixes
[php8-compat][REF] Fix some more test failures in php8
Eileen McNaughton [Sun, 6 Jun 2021 21:45:52 +0000 (09:45 +1200)]
Merge pull request #20518 from seamuslee001/php8_contribution_page
[php8-compat][REF] Fix api_v3_contributionpagetest on php8
Coleman Watts [Sun, 6 Jun 2021 20:32:11 +0000 (16:32 -0400)]
APIv4 - Include child groups in contact group filter
Coleman Watts [Fri, 4 Jun 2021 20:46:05 +0000 (16:46 -0400)]
SearchKit - Add API filter for contacts in groups and smart groups
Adds 'type' property to API getFields to distinguish regular fields
from custom fields, extra fields and filters.
Implements `Contact.groups` as a filter, which internally adds a temp-table
and incorporates it into the query.
Seamus Lee [Sun, 6 Jun 2021 07:54:13 +0000 (07:54 +0000)]
[php8-compat][REF] Fix api_v3_contributionpagetest on php8
Eileen McNaughton [Sun, 6 Jun 2021 05:57:34 +0000 (17:57 +1200)]
Merge pull request #20517 from seamuslee001/php8_more_template_test_fixes
[php8-compat][REF] Fix more php8 test failures caused by template issues
Seamus Lee [Sun, 6 Jun 2021 02:19:23 +0000 (02:19 +0000)]
[php8-compat][REF] Fix more php8 test failures caused by template issues
Seamus Lee [Sun, 6 Jun 2021 02:06:45 +0000 (02:06 +0000)]
[php8-compat][REF] Fix some more test failures in php8
Seamus Lee [Sun, 6 Jun 2021 00:59:37 +0000 (10:59 +1000)]
Merge pull request #20512 from seamuslee001/php8_contribution_api_tests
[php8-compat] Update smarty templates and some php files to get the a…
Seamus Lee [Sun, 6 Jun 2021 00:56:54 +0000 (10:56 +1000)]
Merge pull request #20515 from seamuslee001/php8_required_optional
[php8-compat] Fix some more examples of where required parameters are…
Seamus Lee [Sat, 5 Jun 2021 03:22:24 +0000 (03:22 +0000)]
[php8-compat] Update smarty templates and some php files to get the api_v3_contribution testclass to pass on php8
Seamus Lee [Sat, 5 Jun 2021 23:13:40 +0000 (23:13 +0000)]
[php8-compat] Fix some more examples of where required parameters are after optional parameters in fucntion declaration
demeritcowboy [Sat, 5 Jun 2021 03:31:04 +0000 (23:31 -0400)]
split long lines in civicrm_generated
Eileen McNaughton [Sat, 5 Jun 2021 01:36:21 +0000 (13:36 +1200)]
Merge pull request #20331 from mattwire/suppresslegacywarnings
Allow legacy warnings to be fully suppressed in PropertyBag
Seamus Lee [Sat, 5 Jun 2021 00:27:05 +0000 (10:27 +1000)]
Merge pull request #20509 from seamuslee001/fix_beautifier_notice
[php8-compat] Fix php beautifier notice by conditionally assinging dy…
Seamus Lee [Sat, 5 Jun 2021 00:24:00 +0000 (10:24 +1000)]
Merge pull request #20508 from seamuslee001/product_oddness
[php8-compat] Fix issue in APIv3 Where by because product has a colum…
Seamus Lee [Sat, 5 Jun 2021 00:12:11 +0000 (10:12 +1000)]
Merge pull request #20502 from seamuslee001/spaceship
[php8-compat] Fix issue with returning bool from uasort by using the …
Eileen McNaughton [Sat, 5 Jun 2021 00:11:49 +0000 (12:11 +1200)]
Merge pull request #20500 from seamuslee001/php8_zip_test
[php8-compat][NFC] Fix using ZipArchive::open on an empty file
Eileen McNaughton [Sat, 5 Jun 2021 00:09:41 +0000 (12:09 +1200)]
Merge pull request #20504 from JMAConsulting/add_aclrole_api4_entity
ACLEntityRole BAO tidy fixes
Seamus Lee [Fri, 4 Jun 2021 23:50:58 +0000 (09:50 +1000)]
Merge pull request #20503 from seamuslee001/upgrade_fixes
[php8-compat] fix Upgrade call back issues by making functions static…
Seamus Lee [Fri, 4 Jun 2021 07:20:13 +0000 (17:20 +1000)]
[php8-compat] Fix issue with returning bool from uasort by using the spaceship operator
demeritcowboy [Fri, 4 Jun 2021 22:48:01 +0000 (18:48 -0400)]
Merge pull request #20498 from seamuslee001/fix_authx_drupal89
[REF] Fix Authx tests on Druapl 8/9 by ensuring that we only return a…
Seamus Lee [Fri, 4 Jun 2021 22:41:45 +0000 (22:41 +0000)]
[php8-compat] Fix php beautifier notice by conditionally assinging dynamic foreign key to the template
Seamus Lee [Fri, 4 Jun 2021 22:38:55 +0000 (22:38 +0000)]
[php8-compat] Fix issue in APIv3 Where by because product has a column called options the testCreateSingleValueAlter triggers a cannot access offset of type string on string in php8
Seamus Lee [Fri, 4 Jun 2021 07:23:34 +0000 (07:23 +0000)]
[php8-compat] fix Upgrade call back issues by making functions static and also fixing an issue with an array key not existing when checking obsolete extensions
Seamus Lee [Fri, 4 Jun 2021 21:54:49 +0000 (07:54 +1000)]
Merge pull request #20506 from colemanw/deleteActivityPreCreationSubscriber
APIv4 - Delete undocumented deprecated activityType lookup
Seamus Lee [Fri, 4 Jun 2021 21:42:33 +0000 (07:42 +1000)]
Merge pull request #20501 from JMAConsulting/api4_batch_spec
Add APIv4 Batch.create spec
Monish Deb [Fri, 4 Jun 2021 07:48:52 +0000 (13:18 +0530)]
ACLEntityRole tidy fixes
Monish Deb [Fri, 4 Jun 2021 07:12:08 +0000 (12:42 +0530)]
Add APIv4 Batch.create spec
Coleman Watts [Fri, 4 Jun 2021 13:06:09 +0000 (09:06 -0400)]
APIv4 - Delete undocumented deprecated pseudoconstant lookup for Activity type
Seamus Lee [Fri, 4 Jun 2021 07:14:38 +0000 (17:14 +1000)]
[php8-compat][NFC] Fix using ZipArchive::open on an empty file
Monish Deb [Fri, 4 Jun 2021 06:35:28 +0000 (12:05 +0530)]
Add permissions for financial_item entity
Seamus Lee [Fri, 4 Jun 2021 06:43:54 +0000 (16:43 +1000)]
Merge pull request #20496 from seamuslee001/php8_array_key_actionscheduletest
[php8-compat][NFC] Fix issue where by we are tryiing to access array keys …
Seamus Lee [Fri, 4 Jun 2021 00:36:49 +0000 (00:36 +0000)]
[php8-compat] Fix issue where by we are tryiing to access array keys that haven't been created yet
Add in code comment
Eileen McNaughton [Fri, 4 Jun 2021 03:24:49 +0000 (15:24 +1200)]
Merge pull request #20497 from seamuslee001/more_required_after_optional
[REF][php8-compat] Fix more instances of where there is a required pa…
Tim Otten [Fri, 4 Jun 2021 02:27:20 +0000 (19:27 -0700)]
Merge pull request #20488 from eileenmcnaughton/cust_strict
Clarify types on `hook_custom` and `hook_customPre`
Seamus Lee [Fri, 4 Jun 2021 01:03:21 +0000 (11:03 +1000)]
[REF] Fix Authx tests on Druapl 8/9 by ensuring that we only return an id for the user id if it is greater than 0
Seamus Lee [Fri, 4 Jun 2021 00:42:47 +0000 (00:42 +0000)]
[REF][php8-compat] Fix more instances of where there is a required parameter for a function after an optional one and fix an issue where by a NULL function property is treated as not exisiting in php8
Seamus Lee [Fri, 4 Jun 2021 00:38:13 +0000 (10:38 +1000)]
Merge pull request #20479 from demeritcowboy/userload
dev/core#2636 - Authx - Undefined function in drupal 9
colemanw [Thu, 3 Jun 2021 23:47:14 +0000 (19:47 -0400)]
Merge pull request #20375 from JMAConsulting/core-65
Prevent adding duplicate dashlet if present with same name and label
Seamus Lee [Thu, 3 Jun 2021 21:48:36 +0000 (07:48 +1000)]
Merge pull request #20470 from colemanw/entityGet
APIv4 Entity.get refactor to be more efficient
Eileen McNaughton [Thu, 3 Jun 2021 21:46:07 +0000 (09:46 +1200)]
Merge pull request #20492 from colemanw/dedupeRename
Cleanup references to old dedupe class names
colemanw [Thu, 3 Jun 2021 16:32:03 +0000 (12:32 -0400)]
Merge pull request #20474 from JMAConsulting/add_aclrole_api4_entity
dev/core#2486 Add ACLEntityRole APIv4 Entity
Monish Deb [Wed, 2 Jun 2021 05:40:36 +0000 (11:10 +0530)]
Add AclRole Api4 Entity
Coleman Watts [Thu, 3 Jun 2021 13:41:57 +0000 (09:41 -0400)]
Cleanup references to old dedupe class names
colemanw [Thu, 3 Jun 2021 12:28:48 +0000 (08:28 -0400)]
Merge pull request #20466 from JMAConsulting/add_deduperule_api4
Add DedupeRule, DedupeRuleGroup and DedupeException API4 entity
Seamus Lee [Thu, 3 Jun 2021 09:19:22 +0000 (19:19 +1000)]
Merge pull request #20491 from seamuslee001/array_cache_undefined
[php8-compat] Fix undefined property on Array Cache class in wordpres…
Seamus Lee [Thu, 3 Jun 2021 07:21:44 +0000 (07:21 +0000)]
[php8-compat] Fix undefined property on Array Cache class in wordpress on PHP8
Monish Deb [Thu, 3 Jun 2021 06:17:52 +0000 (11:47 +0530)]
test failure fix and add API4 files
Seamus Lee [Thu, 3 Jun 2021 05:56:26 +0000 (15:56 +1000)]
Merge pull request #20490 from seamuslee001/required_after_optional_fix
[REF][php8-compat] Further fixes where there is a required paramater …
Eileen McNaughton [Thu, 3 Jun 2021 05:00:24 +0000 (17:00 +1200)]
Merge pull request #20478 from totten/master-translation-table-only
dev/translation#67 - Define "Translation" table. Add during installation/upgrade.
Eileen McNaughton [Thu, 3 Jun 2021 04:56:38 +0000 (16:56 +1200)]
Merge pull request #20489 from colemanw/getFieldsFix
APIv4 - Cleanup getFields, add @internal flag for non-public field attributes
Seamus Lee [Thu, 3 Jun 2021 04:11:02 +0000 (14:11 +1000)]
[REF][php8-compat] Further fixes where there is a required paramater after an optional paramater
demeritcowboy [Thu, 3 Jun 2021 02:50:46 +0000 (22:50 -0400)]
Merge pull request #20487 from eileenmcnaughton/cust_value
Remove some unused variables
Coleman Watts [Thu, 3 Jun 2021 01:31:54 +0000 (21:31 -0400)]
APIv4 - Cleanup getFields, add @internal flag for non-public field attributes
Eileen McNaughton [Thu, 3 Jun 2021 01:43:26 +0000 (13:43 +1200)]
Merge pull request #20485 from seamuslee001/fix_not_always_defined_constant
[php8-compat][REF] Fix php8 error on undefined constant CIVICRM_DISAB…
Eileen McNaughton [Thu, 3 Jun 2021 00:56:16 +0000 (12:56 +1200)]
Clarify types on custom hooks
The custom & customPre hooks are not called elsewhere in gituniverse
so we can add type hints and fix casting & comments to make it
clearer what the variables are
Tim Otten [Thu, 3 Jun 2021 00:51:05 +0000 (17:51 -0700)]
(NFC) CRM_Utils_Hook::translateFields - Tidy up comments
The previous comments reflected a developmental iteration.
Eileen McNaughton [Thu, 3 Jun 2021 00:35:36 +0000 (12:35 +1200)]
Remove some unused variables
Seamus Lee [Wed, 2 Jun 2021 23:50:20 +0000 (09:50 +1000)]
Merge pull request #20486 from civicrm/5.38
5.38
Seamus Lee [Wed, 2 Jun 2021 23:37:03 +0000 (09:37 +1000)]
[php8-compat][REF] Fix php8 error on undefined constant CIVICRM_DISABLE_DEFAULT_MENU
Seamus Lee [Wed, 2 Jun 2021 23:30:06 +0000 (09:30 +1000)]
Merge pull request #20483 from seamuslee001/hash_equals_string
[php8-compat][NFC] Ensure that the 2nd parameter of hash_equals is a …
colemanw [Wed, 2 Jun 2021 23:25:29 +0000 (19:25 -0400)]
Merge pull request #20484 from agh1/5.38.0-releasenotes-final
5.38.0 release notes: added late changes
Eileen McNaughton [Wed, 2 Jun 2021 23:07:12 +0000 (11:07 +1200)]
Merge pull request #20480 from colemanw/afformCustomFields
Afform - Fix custom field handling and add tests
Andrew Hunt [Wed, 2 Jun 2021 22:46:41 +0000 (18:46 -0400)]
5.38.0 release notes: added late changes
colemanw [Wed, 2 Jun 2021 22:17:08 +0000 (18:17 -0400)]
Merge pull request #20458 from eileenmcnaughton/group2
Extract code that populates temp table for an individual group
Seamus Lee [Wed, 2 Jun 2021 21:48:57 +0000 (07:48 +1000)]
[php8-compat][NFC] Ensure that the 2nd parameter of hash_equals is a string in authx
Eileen McNaughton [Wed, 2 Jun 2021 20:59:52 +0000 (08:59 +1200)]
Merge pull request #20481 from colemanw/getFieldsFix
APIv4 - Fix getFields to respect default_value from getFields
Tim Otten [Wed, 2 Jun 2021 20:27:16 +0000 (13:27 -0700)]
dev/translation#67 - Add titles to XML+DAO
Coleman Watts [Wed, 2 Jun 2021 19:09:10 +0000 (15:09 -0400)]
APIv4 - Fix getFields to respect default_value from getFields
GetFields had an odd way of setting defaults for field metadata;
this reuses the default_value property instead for more internal consistency.
Coleman Watts [Wed, 2 Jun 2021 15:15:02 +0000 (11:15 -0400)]
Afform - Fix custom field handling and add tests
This ensures custom fields are handled properly by Afform,
including multi-record custom field groups & their autogenerated blocks,
and contact reference fields.