diff --git a/CRM/Event/DAO/Participant.php b/CRM/Event/DAO/Participant.php
index aa218caad23c..f8b518b604dd 100644
--- a/CRM/Event/DAO/Participant.php
+++ b/CRM/Event/DAO/Participant.php
@@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/Event/Participant.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:68158e9be20e798236ea9fb43036ebad)
+ * (GenCodeChecksum:020be0037929bdfd423d5bfcc6141601)
*/
/**
@@ -47,6 +47,7 @@ class CRM_Event_DAO_Participant extends CRM_Core_DAO {
'add' => 'civicrm/participant/add?action=add&context=standalone&reset=1',
'view' => 'civicrm/contact/view/participant?id=[id]&cid=[contact_id]&action=view&reset=1',
'update' => 'civicrm/contact/view/participant?id=[id]&cid=[contact_id]&action=update&reset=1',
+ 'detach' => 'civicrm/event/selfsvcupdate?reset=1&pid=[id]&is_backoffice=1',
'delete' => 'civicrm/participant/delete?id=[id]&reset=1',
];
diff --git a/ext/civi_event/Civi/Api4/Service/Links/ParticipantLinksProvider.php b/ext/civi_event/Civi/Api4/Service/Links/ParticipantLinksProvider.php
new file mode 100644
index 000000000000..339b71b25161
--- /dev/null
+++ b/ext/civi_event/Civi/Api4/Service/Links/ParticipantLinksProvider.php
@@ -0,0 +1,72 @@
+ ['alterParticipantLinksResult', -50],
+ ];
+ }
+
+ /**
+ * Customize event participant links
+ *
+ * @param \Civi\API\Event\RespondEvent $e
+ * @return void
+ * @throws \CRM_Core_Exception
+ */
+ public static function alterParticipantLinksResult(RespondEvent $e): void {
+ $request = $e->getApiRequest();
+ if ($request['version'] == 4 && $request->getEntityName() === 'Participant' && is_a($request, '\Civi\Api4\Action\GetLinks')) {
+ $links = (array) $e->getResponse();
+ $addLinkIndex = self::getActionIndex($links, 'add');
+ $transferLinkIndex = self::getActionIndex($links, 'detach');
+ if (isset($transferLinkIndex)) {
+ if ($request->getCheckPermissions() && !\CRM_Core_Permission::check('edit event participants')) {
+ unset($links[$transferLinkIndex]);
+ }
+ }
+ if (isset($addLinkIndex)) {
+ $contactId = $request->getValue('contact_id');
+ if ($request->getCheckPermissions() && !\CRM_Core_Permission::check('edit event participants')) {
+ unset($links[$addLinkIndex]);
+ }
+ elseif ($contactId) {
+ // Update add link appropriate to the context of viewing a single contact
+ $links[$addLinkIndex]['icon'] = 'fa-ticket';
+ $links[$addLinkIndex]['text'] = ts('Register for Event');
+ $links[$addLinkIndex]['path'] = "civicrm/contact/view/participant?reset=1&action=add&cid=$contactId&context=participant";
+ if ($request->getExpandMultiple() && \CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) {
+ // 2nd add link for credit card registrations
+ $ccLink = $links[$addLinkIndex];
+ $ccLink['text'] = ts('Submit Credit Card Event Registration');
+ $ccLink['icon'] = 'fa-credit-card';
+ $ccLink['path'] = "civicrm/contact/view/participant?reset=1&action=add&cid=$contactId&context=participant&mode=live";
+ $links[] = $ccLink;
+ }
+ }
+ }
+
+ $e->getResponse()->exchangeArray(array_values($links));
+ }
+ }
+
+}
diff --git a/ext/civicrm_admin_ui/ang/afsearchTabParticipant.aff.html b/ext/civicrm_admin_ui/ang/afsearchTabParticipant.aff.html
new file mode 100644
index 000000000000..74fccae75756
--- /dev/null
+++ b/ext/civicrm_admin_ui/ang/afsearchTabParticipant.aff.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/ext/civicrm_admin_ui/ang/afsearchTabParticipant.aff.php b/ext/civicrm_admin_ui/ang/afsearchTabParticipant.aff.php
new file mode 100644
index 000000000000..6dea6855b20a
--- /dev/null
+++ b/ext/civicrm_admin_ui/ang/afsearchTabParticipant.aff.php
@@ -0,0 +1,16 @@
+ 'search',
+ 'title' => E::ts('Events'),
+ 'placement' => [
+ 'contact_summary_tab',
+ ],
+ 'summary_weight' => 40,
+ 'icon' => 'fa-calendar',
+ 'permission' => [
+ 'access CiviCRM',
+ 'access CiviEvent',
+ ],
+];
diff --git a/ext/civicrm_admin_ui/managed/SavedSearch_Contact_Summary_Events.mgd.php b/ext/civicrm_admin_ui/managed/SavedSearch_Contact_Summary_Events.mgd.php
new file mode 100644
index 000000000000..d08894a6646c
--- /dev/null
+++ b/ext/civicrm_admin_ui/managed/SavedSearch_Contact_Summary_Events.mgd.php
@@ -0,0 +1,242 @@
+ 'SavedSearch_Contact_Summary_Events',
+ 'entity' => 'SavedSearch',
+ 'cleanup' => 'unused',
+ 'update' => 'unmodified',
+ 'params' => [
+ 'version' => 4,
+ 'values' => [
+ 'name' => 'Contact_Summary_Events',
+ 'label' => E::ts('Contact Summary Events'),
+ 'api_entity' => 'Participant',
+ 'api_params' => [
+ 'version' => 4,
+ 'select' => [
+ 'event_id.title',
+ 'fee_level',
+ 'fee_amount',
+ 'register_date',
+ 'Participant_Event_event_id_01.start_date',
+ 'Participant_Event_event_id_01.end_date',
+ 'status_id:label',
+ 'contact_id.sort_name',
+ 'role_id:label',
+ ],
+ 'orderBy' => [],
+ 'where' => [],
+ 'groupBy' => [],
+ 'join' => [
+ [
+ 'Event AS Participant_Event_event_id_01',
+ 'LEFT',
+ [
+ 'event_id',
+ '=',
+ 'Participant_Event_event_id_01.id',
+ ],
+ ],
+ [
+ 'Contact AS Participant_Contact_contact_id_01',
+ 'LEFT',
+ [
+ 'contact_id',
+ '=',
+ 'Participant_Contact_contact_id_01.id',
+ ],
+ ],
+ ],
+ 'having' => [],
+ ],
+ ],
+ 'match' => [
+ 'name',
+ ],
+ ],
+ ],
+ [
+ 'name' => 'SavedSearch_Contact_Summary_Events_SearchDisplay_Contact_Summary_Events_Tab',
+ 'entity' => 'SearchDisplay',
+ 'cleanup' => 'unused',
+ 'update' => 'unmodified',
+ 'params' => [
+ 'version' => 4,
+ 'values' => [
+ 'name' => 'Contact_Summary_Events_Tab',
+ 'label' => E::ts('Contact Summary Events Tab'),
+ 'saved_search_id.name' => 'Contact_Summary_Events',
+ 'type' => 'table',
+ 'settings' => [
+ 'description' => NULL,
+ 'sort' => [],
+ 'limit' => 50,
+ 'pager' => [
+ 'show_count' => TRUE,
+ 'expose_limit' => TRUE,
+ 'hide_single' => TRUE,
+ ],
+ 'placeholder' => 5,
+ 'columns' => [
+ [
+ 'type' => 'html',
+ 'key' => 'event_id.title',
+ 'dataType' => 'String',
+ 'label' => E::ts('Event'),
+ 'sortable' => TRUE,
+ 'rewrite' => '[event_id.title]',
+ ],
+ [
+ 'type' => 'field',
+ 'key' => 'fee_level',
+ 'dataType' => 'Text',
+ 'label' => E::ts('Fee level'),
+ 'sortable' => TRUE,
+ ],
+ [
+ 'type' => 'field',
+ 'key' => 'fee_amount',
+ 'dataType' => 'Money',
+ 'label' => E::ts('Amount'),
+ 'sortable' => TRUE,
+ ],
+ [
+ 'type' => 'field',
+ 'key' => 'register_date',
+ 'dataType' => 'Timestamp',
+ 'label' => E::ts('Registered'),
+ 'sortable' => TRUE,
+ ],
+ [
+ 'type' => 'html',
+ 'key' => 'Participant_Event_event_id_01.start_date',
+ 'dataType' => 'Timestamp',
+ 'label' => E::ts('Event Date(s)'),
+ 'sortable' => TRUE,
+ 'rewrite' => '[Participant_Event_event_id_01.start_date] -
[Participant_Event_event_id_01.end_date]',
+ ],
+ [
+ 'type' => 'field',
+ 'key' => 'status_id:label',
+ 'dataType' => 'Integer',
+ 'label' => E::ts('Status'),
+ 'sortable' => TRUE,
+ ],
+ [
+ 'type' => 'field',
+ 'key' => 'role_id:label',
+ 'dataType' => 'String',
+ 'label' => E::ts('Participant Role'),
+ 'sortable' => TRUE,
+ ],
+ [
+ 'text' => '',
+ 'style' => 'default',
+ 'size' => 'btn-xs',
+ 'icon' => 'fa-bars',
+ 'links' => [
+ [
+ 'entity' => 'Participant',
+ 'action' => 'view',
+ 'join' => '',
+ 'target' => 'crm-popup',
+ 'icon' => 'fa-ticket',
+ 'text' => E::ts('View'),
+ 'style' => 'default',
+ 'path' => '',
+ 'task' => '',
+ 'condition' => [],
+ ],
+ [
+ 'entity' => 'Participant',
+ 'action' => 'update',
+ 'join' => '',
+ 'target' => 'crm-popup',
+ 'icon' => 'fa-pencil',
+ 'text' => E::ts('Edit'),
+ 'style' => 'default',
+ 'path' => '',
+ 'task' => '',
+ 'condition' => [],
+ ],
+ [
+ 'entity' => 'Participant',
+ 'action' => 'detach',
+ 'icon' => 'fa-random',
+ 'text' => E::ts('Transfer or Cancel'),
+ 'style' => 'default',
+ 'condition' => [
+ 'status_id:name',
+ 'NOT IN',
+ ['Cancelled', 'Transferred'],
+ ],
+ 'task' => '',
+ 'join' => '',
+ 'target' => 'crm-popup',
+ ],
+ [
+ 'entity' => 'Participant',
+ 'action' => 'delete',
+ 'join' => '',
+ 'target' => 'crm-popup',
+ 'icon' => 'fa-trash',
+ 'text' => E::ts('Delete'),
+ 'style' => 'danger',
+ 'path' => '',
+ 'task' => '',
+ 'condition' => [],
+ ],
+ [
+ 'icon' => 'fa-calendar',
+ 'text' => E::ts('View Event Info'),
+ 'style' => 'default',
+ 'condition' => [],
+ 'task' => '',
+ 'entity' => 'Event',
+ 'action' => 'view',
+ 'join' => 'Participant_Event_event_id_01',
+ ],
+ [
+ 'path' => 'civicrm/event/search?reset=1&force=1&event=[event_id]',
+ 'icon' => 'fa-search',
+ 'text' => E::ts('View Participants'),
+ 'style' => 'default',
+ 'condition' => [],
+ 'task' => '',
+ 'entity' => '',
+ 'action' => '',
+ 'join' => '',
+ ],
+ ],
+ 'type' => 'menu',
+ 'alignment' => 'text-right',
+ ],
+ ],
+ 'actions' => FALSE,
+ 'classes' => [
+ 'table',
+ 'table-striped',
+ ],
+ 'toolbar' => [
+ [
+ 'entity' => 'Participant',
+ 'text' => E::ts('Add Event Registration'),
+ 'icon' => 'fa-plus',
+ 'target' => 'crm-popup',
+ 'action' => 'add',
+ 'style' => 'default',
+ 'join' => '',
+ 'task' => '',
+ ],
+ ],
+ ],
+ ],
+ 'match' => [
+ 'saved_search_id',
+ 'name',
+ ],
+ ],
+ ],
+];
diff --git a/xml/schema/Event/Participant.xml b/xml/schema/Event/Participant.xml
index 09b2ddb319b2..1e57d1ed51ee 100644
--- a/xml/schema/Event/Participant.xml
+++ b/xml/schema/Event/Participant.xml
@@ -13,6 +13,7 @@
civicrm/participant/add?action=add&context=standalone&reset=1
civicrm/contact/view/participant?id=[id]&cid=[contact_id]&action=view&reset=1
civicrm/contact/view/participant?id=[id]&cid=[contact_id]&action=update&reset=1
+ civicrm/event/selfsvcupdate?reset=1&pid=[id]&is_backoffice=1
civicrm/participant/delete?id=[id]&reset=1