},
bindToController: {
crmAutocomplete: '<',
- crmAutocompleteParams: '<'
+ crmAutocompleteParams: '<',
+ multiple: '<'
},
controller: function($element, $timeout) {
var ctrl = this;
$timeout(function() {
- $element.crmAutocomplete(ctrl.crmAutocomplete, ctrl.crmAutocompleteParams);
+ $element.crmAutocomplete(ctrl.crmAutocomplete, ctrl.crmAutocompleteParams, {
+ multiple: ctrl.multiple
+ });
// Ensure widget is updated when model changes
if (ctrl.ngModel) {
ctrl.ngModel.$render = function() {
// Add existing entity field
$idField = CoreUtil::getIdFieldName($entityName);
$fields[$idField]['readonly'] = FALSE;
- $fields[$idField]['input_type'] = 'Existing';
+ $fields[$idField]['input_type'] = 'EntityRef';
$fields[$idField]['is_id'] = TRUE;
+ $fields[$idField]['fk_entity'] = $entityName;
$fields[$idField]['label'] = E::ts('Existing %1', [1 => CoreUtil::getInfoItem($entityName, 'title')]);
// Mix in alterations declared by afform entities
$afEntity = self::getMetadata()['entities'][$entityName] ?? [];
-<li ng-if="$ctrl.fieldDefn.input_type !== 'Existing'">
+<li ng-if="!$ctrl.fieldDefn.is_id">
<div href ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown">
<label>{{:: ts('Type:') }}</label>
<select class="form-control" ng-model="getSet('input_type')" ng-model-options="{getterSetter: true}" title="{{:: ts('Field type') }}">
</select>
</div>
</li>
-<li ng-if="$ctrl.fieldDefn.input_type === 'Existing'" title="{{:: ts('Use a saved search to filter the autocomplete results') }}">
+<li ng-if="$ctrl.fieldDefn.input_type === 'EntityRef'" title="{{:: ts('Use a saved search to filter the autocomplete results') }}">
<div href ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown">
<label>{{:: ts('Saved Search:') }}</label>
<input class="form-control" crm-entityref="{entity: 'SavedSearch', api: {id_field: 'name', params: {api_entity: $ctrl.getEntity().name}}}" ng-model="getSet('saved_search')" ng-model-options="{getterSetter: true}">
{{:: ts('Required') }}
</a>
</li>
-<li ng-if="$ctrl.fieldDefn.input_type !== 'Existing'">
+<li ng-if="!$ctrl.fieldDefn.is_id">
<a href ng-click="toggleDefaultValue(); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Pre-fill this field with a value') }}">
<i class="crm-i fa-{{ $ctrl.hasDefaultValue ? 'check-' : '' }}square-o"></i>
{{:: ts('Default value') }}
</a>
</li>
-<li ng-if="$ctrl.fieldDefn.input_type !== 'Existing' && $ctrl.hasDefaultValue">
+<li ng-if="$ctrl.hasDefaultValue">
<form ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown form-inline">
<input class="form-control" af-gui-field-value="$ctrl.fieldDefn" ng-model="getSet('afform_default')" ng-model-options="{getterSetter: true}" >
</form>
};
this.getFkEntity = function() {
- var defn = ctrl.getDefn(),
- fkEntity = defn.is_id ? ctrl.container.getMainEntityType() : defn.fk_entity;
+ var fkEntity = ctrl.getDefn().fk_entity;
return ctrl.editor.meta.entities[fkEntity];
};
+++ /dev/null
-<div class="form-inline">
- <div class="input-group">
- <input autocomplete="off" type="text" class="form-control" placeholder="{{:: ts('Select %1', {1: $ctrl.getFkEntity().label}) }}" title="{{:: ts('Click to add placeholder text') }}" ng-model="getSet('input_attrs.placeholder')" ng-model-options="$ctrl.editor.debounceWithGetterSetter">
- <div class="input-group-btn">
- <button type="button" class="btn btn-default" disabled><i class="crm-i fa-search"></i></button>
- </div>
- </div>
-</div>
if ($inputType === 'Select' || $inputType === 'ChainSelect') {
$fieldInfo['input_attrs']['placeholder'] = E::ts('Select');
}
- elseif ($inputType === 'EntityRef') {
+ elseif ($inputType === 'EntityRef' && empty($field['is_id'])) {
$info = civicrm_api4('Entity', 'get', [
'where' => [['name', '=', $fieldInfo['fk_entity']]],
'checkPermissions' => FALSE,
// Id field for selecting existing entity
if ($action === 'create' && $field['name'] === CoreUtil::getIdFieldName($entityName)) {
$entityTitle = CoreUtil::getInfoItem($entityName, 'title');
- $field['input_type'] = 'Existing';
- $field['entity'] = $entityName;
+ $field['input_type'] = 'EntityRef';
+ $field['fk_entity'] = $entityName;
+ $field['is_id'] = TRUE;
$field['label'] = E::ts('Existing %1', [1 => $entityTitle]);
$field['input_attrs']['placeholder'] = E::ts('Select %1', [1 => $entityTitle]);
}
use Civi\Afform\FormDataModel;
use Civi\API\Events;
use Civi\Api4\Afform;
+use Civi\Api4\Utils\CoreUtil;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
}
$formDataModel = new FormDataModel($afform['layout']);
$entity = $formDataModel->getEntity($entityName);
+ $isId = $fieldName === CoreUtil::getIdFieldName($entity['type']);
+ // For the "Existing Entity" selector,
// Look up the "type" fields (e.g. contact_type, activity_type_id, case_type_id, etc)
- $typeFields = [];
- if ($entity['type'] === 'Contact') {
- $typeFields = ['contact_type', 'contact_sub_type'];
- }
- else {
- $extends = array_column(\CRM_Core_BAO_CustomGroup::getCustomGroupExtendsOptions(), 'grouping', 'id');
- $typeFields = (array) ($extends[$entity['type']] ?? NULL);
- }
- // If entity has a type set in the values, auto-apply that to filters
- foreach ($typeFields as $typeField) {
- if (!empty($entity['data'][$typeField])) {
- $apiRequest->addFilter($typeField, $entity['data'][$typeField]);
+ // And apply it as a filter if specified on the form.
+ if ($isId) {
+ $typeFields = [];
+ if ($entity['type'] === 'Contact') {
+ $typeFields = ['contact_type', 'contact_sub_type'];
+ }
+ else {
+ $extends = array_column(\CRM_Core_BAO_CustomGroup::getCustomGroupExtendsOptions(), 'grouping', 'id');
+ $typeFields = (array) ($extends[$entity['type']] ?? NULL);
+ }
+ // If entity has a type set in the values, auto-apply that to filters
+ foreach ($typeFields as $typeField) {
+ if (!empty($entity['data'][$typeField])) {
+ $apiRequest->addFilter($typeField, $entity['data'][$typeField]);
+ }
}
}
}
};
+ ctrl.isReadonly = function() {
+ if (ctrl.defn.is_id) {
+ return ctrl.afFieldset.getEntity().actions.update === false;
+ }
+ // TODO: Not actually used, but could be used if we wanted to render displayOnly
+ // fields as more than just raw data. I think we probably ought to do so for entityRef fields
+ // Since the ids are kind of meaningless. Making that change would require adding a function
+ // to get the widget template rather than just concatenating the input_type into an ngInclude.
+ return ctrl.defn.input_type === 'DisplayOnly';
+ };
+
// ngChange callback from Existing entity field
- ctrl.onSelectExisting = function() {
- var val = $scope.getSetSelect();
- var entity = ctrl.afFieldset.modelName;
- var index = ctrl.getEntityIndex();
- ctrl.afFieldset.afFormCtrl.loadData(entity, index, val);
+ ctrl.onSelectEntity = function() {
+ if (ctrl.defn.is_id) {
+ var val = $scope.getSetSelect();
+ var entity = ctrl.afFieldset.modelName;
+ var index = ctrl.getEntityIndex();
+ ctrl.afFieldset.afFormCtrl.loadData(entity, index, val);
+ }
};
// Params for the Afform.submitFile API when uploading a file field
-<input class="form-control" id="{{:: fieldId }}" ng-required="$ctrl.defn.required" ng-model="getSetSelect" ng-model-options="{getterSetter: true}" crm-entityref="{entity: $ctrl.defn.fk_entity, select: {multiple: !!$ctrl.defn.input_attrs.multiple, placeholder: $ctrl.defn.input_attrs.placeholder}}" >
+<input id="{{:: fieldId }}"
+ class="form-control"
+ ng-disabled="$ctrl.isReadonly()"
+ ng-required="$ctrl.defn.required"
+ ng-model="getSetSelect"
+ ng-model-options="{getterSetter: true}"
+ crm-autocomplete="$ctrl.defn.fk_entity"
+ crm-autocomplete-params="{formName: 'afform:' + $ctrl.afFieldset.getFormName(), fieldName: $ctrl.afFieldset.modelName + ':' + $ctrl.fieldName}"
+ multiple="$ctrl.defn.input_attrs.multiple"
+ placeholder="{{:: $ctrl.defn.input_attrs.placeholder }}"
+ ng-change="$ctrl.onSelectEntity()" >
+++ /dev/null
-<input id="{{:: fieldId }}"
- class="form-control"
- ng-disabled="$ctrl.afFieldset.getEntity().actions.update === false"
- ng-model="getSetSelect"
- ng-model-options="{getterSetter: true}"
- crm-autocomplete="$ctrl.defn.entity"
- crm-autocomplete-params="{formName: 'afform:' + $ctrl.afFieldset.getFormName(), fieldName: $ctrl.afFieldset.modelName + ':' + $ctrl.fieldName}"
- placeholder="{{:: $ctrl.defn.input_attrs.placeholder }}"
- ng-change="$ctrl.onSelectExisting()" >
// Ensure the "Existing" contact field exists
$this->assertEquals('Existing Contact', $individualFields['id']['label']);
- $this->assertEquals('Existing', $individualFields['id']['input_type']);
+ $this->assertEquals('EntityRef', $individualFields['id']['input_type']);
}
}