diff --git a/CRM/Activity/DAO/ActivityContact.php b/CRM/Activity/DAO/ActivityContact.php index 7f1bfd317fba..c789e4f0ed75 100644 --- a/CRM/Activity/DAO/ActivityContact.php +++ b/CRM/Activity/DAO/ActivityContact.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Activity/ActivityContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:3f147b2507b1e11a7df971be191161d1) + * (GenCodeChecksum:1263921d2a6832e26c5a7e34c684e35b) */ /** @@ -52,7 +52,7 @@ class CRM_Activity_DAO_ActivityContact extends CRM_Core_DAO { public $contact_id; /** - * Nature of this contact's role in the activity: 1 assignee, 2 creator, 3 focus or target. + * Determines the contact's role in the activity (source, target, or assignee). * * @var int */ @@ -146,8 +146,8 @@ public static function &fields() { 'record_type_id' => [ 'name' => 'record_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Record Type ID'), - 'description' => ts('Nature of this contact\'s role in the activity: 1 assignee, 2 creator, 3 focus or target.'), + 'title' => ts('Activity Contact Type'), + 'description' => ts('Determines the contact\'s role in the activity (source, target, or assignee).'), 'where' => 'civicrm_activity_contact.record_type_id', 'table_name' => 'civicrm_activity_contact', 'entity' => 'ActivityContact', @@ -155,6 +155,7 @@ public static function &fields() { 'localizable' => 0, 'html' => [ 'type' => 'Select', + 'label' => ts("Contact Role"), ], 'pseudoconstant' => [ 'optionGroupName' => 'activity_contacts', diff --git a/CRM/Contact/DAO/RelationshipCache.php b/CRM/Contact/DAO/RelationshipCache.php index 5701d5c3e673..c888435bfd5d 100644 --- a/CRM/Contact/DAO/RelationshipCache.php +++ b/CRM/Contact/DAO/RelationshipCache.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/RelationshipCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ba039fcadc13e48749f965343301ec1d) + * (GenCodeChecksum:ec899f1ccb7f617701d7108dc4282691) */ /** @@ -246,6 +246,9 @@ public static function &fields() { 'entity' => 'RelationshipCache', 'bao' => 'CRM_Contact_BAO_RelationshipCache', 'localizable' => 0, + 'html' => [ + 'label' => ts("Relationship to contact"), + ], 'pseudoconstant' => [ 'callback' => 'CRM_Core_PseudoConstant::relationshipTypeOptions', ], @@ -280,6 +283,9 @@ public static function &fields() { 'entity' => 'RelationshipCache', 'bao' => 'CRM_Contact_BAO_RelationshipCache', 'localizable' => 0, + 'html' => [ + 'label' => ts("Relationship from contact"), + ], 'pseudoconstant' => [ 'callback' => 'CRM_Core_PseudoConstant::relationshipTypeOptions', ], diff --git a/Civi/Api4/ActivityContact.php b/Civi/Api4/ActivityContact.php index 1af0de36ffa3..39ec1b3b95df 100644 --- a/Civi/Api4/ActivityContact.php +++ b/Civi/Api4/ActivityContact.php @@ -20,11 +20,12 @@ namespace Civi\Api4; /** - * ActivityContact Entity. + * ActivityContact BridgeEntity. * - * This entity adds a record which relate a contact to activity. + * This connects a contact to an activity. * - * Creating a new ActivityContact requires at minimum a contact_id and activity_id. + * The record_type_id field determines the contact's role in the activity (source, target, or assignee). + * @ui_join_filters record_type_id * * @see \Civi\Api4\Activity * @package Civi\Api4 diff --git a/Civi/Api4/Address.php b/Civi/Api4/Address.php index 038e544ea71e..242afbe0bdcb 100644 --- a/Civi/Api4/Address.php +++ b/Civi/Api4/Address.php @@ -28,6 +28,8 @@ * Creating a new address requires at minimum a contact's ID and location type ID * and other attributes (although optional) like street address, city, country etc. * + * @ui_join_filters location_type_id + * * @package Civi\Api4 */ class Address extends Generic\DAOEntity { diff --git a/Civi/Api4/Entity.php b/Civi/Api4/Entity.php index ac61fcf3892b..7bb80be3d1d7 100644 --- a/Civi/Api4/Entity.php +++ b/Civi/Api4/Entity.php @@ -103,6 +103,11 @@ public static function getFields($checkPermissions = TRUE) { 'data_type' => 'Array', 'description' => 'Connecting fields for EntityBridge types', ], + [ + 'name' => 'ui_join_filters', + 'data_type' => 'Array', + 'description' => 'When joining entities in the UI, which fields should be presented by default in the ON clause', + ], ]; }))->setCheckPermissions($checkPermissions); } diff --git a/Civi/Api4/RelationshipCache.php b/Civi/Api4/RelationshipCache.php index 4c6281c3a877..8009477cc9c6 100644 --- a/Civi/Api4/RelationshipCache.php +++ b/Civi/Api4/RelationshipCache.php @@ -24,6 +24,7 @@ * * @see \Civi\Api4\Relationship * @bridge near_contact_id far_contact_id + * @ui_join_filters near_relation * @package Civi\Api4 */ class RelationshipCache extends Generic\AbstractEntity { diff --git a/Civi/Api4/Utils/ReflectionUtils.php b/Civi/Api4/Utils/ReflectionUtils.php index ce3f709a2c35..781a87b12726 100644 --- a/Civi/Api4/Utils/ReflectionUtils.php +++ b/Civi/Api4/Utils/ReflectionUtils.php @@ -88,9 +88,9 @@ public static function parseDocBlock($comment) { elseif ($key == 'return') { $info['return'] = explode('|', $words[0]); } - elseif ($key == 'options') { + elseif ($key == 'options' || $key == 'ui_join_filters') { $val = str_replace(', ', ',', implode(' ', $words)); - $info['options'] = explode(',', $val); + $info[$key] = explode(',', $val); } elseif ($key == 'throws' || $key == 'see') { $info[$key][] = implode(' ', $words); diff --git a/ext/search/Civi/Search/Admin.php b/ext/search/Civi/Search/Admin.php index 6363b53c647b..f359c93fc214 100644 --- a/ext/search/Civi/Search/Admin.php +++ b/ext/search/Civi/Search/Admin.php @@ -61,7 +61,7 @@ public static function getOperators():array { public static function getSchema() { $schema = []; $entities = \Civi\Api4\Entity::get() - ->addSelect('name', 'title', 'type', 'title_plural', 'description', 'icon', 'paths', 'dao', 'bridge') + ->addSelect('name', 'title', 'type', 'title_plural', 'description', 'icon', 'paths', 'dao', 'bridge', 'ui_join_filters') ->addWhere('searchable', '=', TRUE) ->addOrderBy('title_plural') ->setChain([ @@ -142,6 +142,7 @@ public static function getJoins(array $allowedEntities) { ) { continue; } + // For dynamic references getTargetEntities will return multiple targets; for normal joins this loop will only run once foreach ($reference->getTargetEntities() as $targetTable => $targetEntityName) { if (!isset($allowedEntities[$targetEntityName]) || $targetEntityName === $entity['name']) { continue; @@ -158,6 +159,7 @@ public static function getJoins(array $allowedEntities) { 'description' => $dynamicCol ? '' : $keyField['label'], 'entity' => $targetEntityName, 'conditions' => self::getJoinConditions($keyField['name'], $alias . '.' . $reference->getTargetKey(), $targetTable, $dynamicCol), + 'defaults' => self::getJoinDefaults($alias, $targetEntity), 'alias' => $alias, 'multi' => FALSE, ]; @@ -168,6 +170,7 @@ public static function getJoins(array $allowedEntities) { 'description' => $dynamicCol ? '' : $keyField['label'], 'entity' => $entity['name'], 'conditions' => self::getJoinConditions($reference->getTargetKey(), $alias . '.' . $keyField['name'], $targetTable, $dynamicCol ? $alias . '.' . $dynamicCol : NULL), + 'defaults' => self::getJoinDefaults($alias, $entity), 'alias' => $alias, 'multi' => TRUE, ]; @@ -192,6 +195,7 @@ public static function getJoins(array $allowedEntities) { [$bridge], self::getJoinConditions('id', $alias . '.' . $baseKey, NULL, NULL) ), + 'defaults' => self::getJoinDefaults($alias, $targetEntity, $entity), 'bridge' => $bridge, 'alias' => $alias, 'multi' => TRUE, @@ -206,6 +210,7 @@ public static function getJoins(array $allowedEntities) { [$bridge], self::getJoinConditions($reference->getTargetKey(), $alias . '.' . $keyField['name'], $targetTable, $dynamicCol ? $alias . '.' . $dynamicCol : NULL) ), + 'defaults' => self::getJoinDefaults($alias, $baseEntity, $entity), 'bridge' => $bridge, 'alias' => $alias, 'multi' => TRUE, @@ -246,4 +251,29 @@ private static function getJoinConditions($nearCol, $farCol, $targetTable, $dyna return $conditions; } + /** + * @param $alias + * @param array ...$entities + * @return array + */ + private static function getJoinDefaults($alias, ...$entities):array { + $conditions = []; + foreach ($entities as $entity) { + foreach ($entity['ui_join_filters'] ?? [] as $fieldName) { + $field = civicrm_api4($entity['name'], 'getFields', [ + 'select' => ['options'], + 'where' => [['name', '=', $fieldName]], + 'loadOptions' => ['name'], + ])->first(); + $value = isset($field['options'][0]) ? json_encode($field['options'][0]['name']) : ''; + $conditions[] = [ + $alias . '.' . $fieldName . ($value ? ':name' : ''), + '=', + $value, + ]; + } + } + return $conditions; + } + } diff --git a/ext/search/ang/crmSearchAdmin.module.js b/ext/search/ang/crmSearchAdmin.module.js index 7e737d109667..660611ffa2e0 100644 --- a/ext/search/ang/crmSearchAdmin.module.js +++ b/ext/search/ang/crmSearchAdmin.module.js @@ -116,17 +116,21 @@ // Add the numbered suffix to the join conditions // If this is a deep join, also add the base entity prefix var prefix = alias.replace(new RegExp('_?' + join.alias + '_?\\d?\\d?$'), ''); - _.each(result.conditions, function(condition) { + function replaceRefs(condition) { if (_.isArray(condition)) { _.each(condition, function(ref, side) { - if (side !== 1 && _.includes(ref, '.')) { - condition[side] = ref.replace(join.alias + '.', alias + '.'); - } else if (side !== 1 && prefix.length && !_.includes(ref, '"') && !_.includes(ref, "'")) { - condition[side] = prefix + '.' + ref; + if (side !== 1 && typeof ref === 'string') { + if (_.includes(ref, '.')) { + condition[side] = ref.replace(join.alias + '.', alias + '.'); + } else if (prefix.length && !_.includes(ref, '"') && !_.includes(ref, "'")) { + condition[side] = prefix + '.' + ref; + } } }); } - }); + } + _.each(result.conditions, replaceRefs); + _.each(result.defaults, replaceRefs); return result; } function getFieldAndJoin(fieldName, entityName) { diff --git a/ext/search/ang/crmSearchAdmin/compose/criteria.html b/ext/search/ang/crmSearchAdmin/compose/criteria.html index 78c75a391a47..eefa730caab6 100644 --- a/ext/search/ang/crmSearchAdmin/compose/criteria.html +++ b/ext/search/ang/crmSearchAdmin/compose/criteria.html @@ -4,8 +4,11 @@