namespace Civi\AfformAdmin;
+use Civi\Api4\Entity;
use Civi\Api4\Utils\CoreUtil;
use CRM_AfformAdmin_ExtensionUtil as E;
}
$info = \Civi\Api4\Entity::get(FALSE)
->addWhere('name', '=', $entityName)
- ->addSelect('title', 'icon')
->execute()->first();
if (!$info) {
// Disabled contact type or nonexistent api entity
return NULL;
}
- return [
- 'entity' => $entityName,
+ return self::entityToAfformMeta($info);
+ }
+
+ /**
+ * Converts info from API.Entity.get to an array of afform entity metadata
+ * @param array $info
+ * @return array
+ */
+ private static function entityToAfformMeta(array $info): array {
+ $meta = [
+ 'entity' => $info['name'],
'label' => $info['title'],
'icon' => $info['icon'],
];
+ // Custom entities are always type 'join'
+ if (in_array('CustomValue', $info['type'], TRUE)) {
+ $meta['type'] = 'join';
+ $max = (int) \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', substr($info['name'], 7), 'max_multiple', 'name');
+ $meta['repeat_max'] = $max ?: NULL;
+ }
+ return $meta;
}
/**
'icon' => 'fa-pencil-square-o',
'fields' => [],
],
- 'Contact' => self::getApiEntity('Contact'),
],
];
+ // Explicitly load Contact and Custom entities because they do not have afformEntity files
+ $entities = Entity::get(TRUE)
+ ->addClause('OR', ['name', '=', 'Contact'], ['type', 'CONTAINS', 'CustomValue'])
+ ->execute()->indexBy('name');
+ foreach ($entities as $name => $entity) {
+ $data['entities'][$name] = self::entityToAfformMeta($entity);
+ }
+
$contactTypes = \CRM_Contact_BAO_ContactType::basicTypeInfo();
// Call getFields on getFields to get input type labels
}
if ($entities) {
$blockInfo = Afform::get($this->checkPermissions)
- ->addSelect('name', 'title', 'entity_type', 'join_entity', 'directive_name', 'repeat')
+ ->addSelect('name', 'title', 'entity_type', 'join_entity', 'directive_name')
->setWhere($where)
->addWhere('type', '=', 'block')
->addWhere('entity_type', 'IN', $entities)
<?php
return [
+ 'type' => 'primary',
'defaults' => "{
data: {
source_contact_id: 'user_contact_id',
--- /dev/null
+<?php
+return [
+ 'type' => 'join',
+ 'repeat_max' => NULL,
+];
--- /dev/null
+<?php
+return [
+ 'type' => 'join',
+ 'repeat_max' => NULL,
+];
<?php
return [
+ 'type' => 'primary',
'defaults' => "{
data: {
contact_type: 'Household',
--- /dev/null
+<?php
+return [
+ 'type' => 'join',
+ 'repeat_max' => NULL,
+];
<?php
return [
+ 'type' => 'primary',
'defaults' => "{
data: {
contact_type: 'Individual',
<?php
return [
+ 'type' => 'primary',
'defaults' => "{
data: {
contact_type: 'Organization',
--- /dev/null
+<?php
+return [
+ 'type' => 'join',
+ 'repeat_max' => NULL,
+];
--- /dev/null
+<?php
+return [
+ 'type' => 'join',
+ 'repeat_max' => NULL,
+];
if (ctrl.tab === 'form') {
_.each(CRM.afGuiEditor.entities, function(entity, name) {
- if (entity.defaults) {
+ if (entity.type === 'primary') {
links.push({
url: '#create/form/' + name,
label: entity.label,
) {
var item = {"#tag": block.join_entity ? "div" : directive};
if (block.join_entity) {
+ var joinEntity = afGui.getEntity(block.join_entity);
+ // Skip adding block if entity does not exist
+ if (!joinEntity) {
+ return;
+ }
item['af-join'] = block.join_entity;
item['#children'] = [{"#tag": directive}];
- }
- if (block.repeat) {
item['af-repeat'] = ts('Add');
item.min = '1';
- if (typeof block.repeat === 'number') {
- item.max = '' + block.repeat;
+ if (typeof joinEntity.repeat_max === 'number') {
+ item.max = '' + joinEntity.repeat_max;
}
}
$scope.blockList.push(item);
</label>
<span ng-style="{visibility: $ctrl.node['af-repeat'] || $ctrl.node['af-repeat'] === '' ? 'visible' : 'hidden'}">
<input type="number" class="form-control" ng-model="getSetMin" ng-model-options="{getterSetter: true}" placeholder="{{:: ts('min') }}" min="0" step="1" />
- - <input type="number" class="form-control" ng-model="getSetMax" ng-model-options="{getterSetter: true}" placeholder="{{:: ts('max') }}" min="2" step="1" />
+ - <input type="number" class="form-control" ng-model="getSetMax" ng-model-options="{getterSetter: true}" placeholder="{{:: ts('max') }}" min="2" max="{{:: getRepeatMax() }}" step="1" />
</span>
</div>
</li>
}
};
+ // Sets min value for af-repeat as a string, returns it as an int
$scope.getSetMin = function(val) {
if (arguments.length) {
if (ctrl.node.max && val > parseInt(ctrl.node.max, 10)) {
return ctrl.node.min ? parseInt(ctrl.node.min, 10) : null;
};
+ // Sets max value for af-repeat as a string, returns it as an int
$scope.getSetMax = function(val) {
if (arguments.length) {
if (ctrl.node.min && val && val < parseInt(ctrl.node.min, 10)) {
return ctrl.node.max ? parseInt(ctrl.node.max, 10) : null;
};
+ // Returns the maximum number of repeats allowed if this is a joined entity with a limit
+ // Value comes from civicrm_custom_group.max_multiple for custom entities,
+ // or from afformEntity php file for core entities.
+ $scope.getRepeatMax = function() {
+ if (ctrl.join) {
+ return afGui.getEntity(ctrl.join).repeat_max || '';
+ }
+ return '';
+ };
+
$scope.pickAddIcon = function() {
afGui.pickIcon().then(function(val) {
ctrl.node['add-icon'] = val;
'permission' => 'access CiviCRM',
'join_entity' => 'Custom_' . $custom['name'],
'entity_type' => $custom['extends'],
- 'repeat' => $custom['max_multiple'] ?: TRUE,
'has_base' => TRUE,
];
if ($getLayout) {
'tab' => ts('Contact Summary Tab'),
],
],
- [
- 'name' => 'repeat',
- 'data_type' => 'Mixed',
- ],
[
'name' => 'server_route',
],
"title": "Contact Address(es)",
"type": "block",
"entity_type": "Contact",
- "join_entity": "Address",
- "repeat": true
+ "join_entity": "Address"
}
"title": "Contact Email(s)",
"type": "block",
"entity_type": "Contact",
- "join_entity": "Email",
- "repeat": true
+ "join_entity": "Email"
}
"title": "Contact IM(s)",
"type": "block",
"entity_type": "Contact",
- "join_entity": "IM",
- "repeat": true
+ "join_entity": "IM"
}
"title": "Contact Phone(s)",
"type": "block",
"entity_type": "Contact",
- "join_entity": "Phone",
- "repeat": true
+ "join_entity": "Phone"
}
"title": "Contact Website(s)",
"type": "block",
"entity_type": "Contact",
- "join_entity": "Website",
- "repeat": true
+ "join_entity": "Website"
}