SearchKit - Hide invalid links and prevent error
authorcolemanw <coleman@civicrm.org>
Fri, 2 Feb 2024 16:28:11 +0000 (11:28 -0500)
committercolemanw <coleman@civicrm.org>
Fri, 2 Feb 2024 21:16:11 +0000 (16:16 -0500)
Fixes dev/core#4961

Civi/Api4/Action/GetLinks.php
ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchRunTest.php

index 32930ef39c87543aebefdbd2918a58fe112f49ca..5751ddb14a92b5b7b26d97edb47c76c229b003cf 100644 (file)
@@ -129,7 +129,7 @@ class GetLinks extends BasicGetAction {
     // Text was translated with `%1` placeholders preserved so it could be cached
     // Now we'll replace `%1` placeholders with the entityTitle, unless FALSE
     $entityTitle = $this->entityTitle === TRUE ? CoreUtil::getInfoItem($this->getEntityName(), 'title') : $this->entityTitle;
-    foreach ($links as &$link) {
+    foreach ($links as $index => &$link) {
       // Swap placeholders with $entityTitle (TRUE means use default title)
       if ($entityTitle !== FALSE && !empty($link['text'])) {
         $link['text'] = str_replace('%1', $entityTitle, $link['text']);
@@ -142,6 +142,12 @@ class GetLinks extends BasicGetAction {
           if (isset($value)) {
             $link['path'] = str_replace($token['token'], $value, $link['path']);
           }
+          // If $values was supplied, treat all tokens as mandatory and remove links with null values
+          // This hides invalid links from SearchKit e.g. `civicrm/group/edit?id=null`
+          else {
+            unset($links[$index]);
+            break;
+          }
         }
       }
     }
index 4cd399708e8e7e7d05ec78668fc15e79d57601f3..a56e78bae001d01c00d0fcfaa822f7d03bf1719e 100644 (file)
@@ -175,6 +175,67 @@ class SearchRunTest extends Api4TestBase implements TransactionalInterface {
     $this->assertCount(2, $count);
   }
 
+  public function testDefaultDisplayLinks(): void {
+    $group1 = $this->createTestRecord('Group', ['title' => uniqid('a')])['id'];
+    $group2 = $this->createTestRecord('Group', ['title' => uniqid('b')])['id'];
+    $contact1 = $this->createTestRecord('Individual', ['last_name' => 'b', 'first_name' => 'b'])['id'];
+    $contact2 = $this->createTestRecord('Individual', ['last_name' => 'a', 'first_name' => 'a'])['id'];
+    // Add both contacts to group2
+    $this->saveTestRecords('GroupContact', [
+      'records' => [
+        ['contact_id' => $contact1, 'group_id' => $group2],
+        ['contact_id' => $contact2, 'group_id' => $group2],
+      ],
+    ]);
+
+    $params = [
+      'checkPermissions' => FALSE,
+      'return' => 'page:1',
+      'savedSearch' => [
+        'api_entity' => 'Group',
+        'api_params' => [
+          'version' => 4,
+          'select' => [
+            'title',
+            'Group_GroupContact_Contact_01.sort_name',
+          ],
+          'join' => [
+            [
+              'Contact AS Group_GroupContact_Contact_01',
+              'LEFT',
+              'GroupContact',
+              ['id', '=', 'Group_GroupContact_Contact_01.group_id'],
+              ['Group_GroupContact_Contact_01.status:name', '=', '"Added"'],
+            ],
+          ],
+          'where' => [],
+        ],
+      ],
+      'display' => NULL,
+      'sort' => [
+        ['title', 'ASC'],
+        ['Group_GroupContact_Contact_01.sort_name', 'ASC'],
+      ],
+      'filters' => ['id' => [$group1, $group2]],
+    ];
+
+    $result = civicrm_api4('SearchDisplay', 'run', $params);
+    $this->assertCount(1, $result[0]['columns'][0]['links']);
+    $this->assertNull($result[0]['columns'][1]['val']);
+    $this->assertArrayNotHasKey('links', $result[0]['columns'][1]);
+    $this->assertCount(1, $result[1]['columns'][0]['links']);
+    $this->assertCount(1, $result[1]['columns'][1]['links']);
+    $this->assertCount(1, $result[2]['columns'][0]['links']);
+    $this->assertCount(1, $result[2]['columns'][1]['links']);
+    // No contact links in 1st row since the group is empty
+    $this->assertNotContains('View Contact', array_column($result[0]['columns'][2]['links'], 'text'));
+    $this->assertNotContains('Delete Contact', array_column($result[0]['columns'][2]['links'], 'text'));
+    $this->assertContains('View Contact', array_column($result[1]['columns'][2]['links'], 'text'));
+    $this->assertContains('Delete Contact', array_column($result[1]['columns'][2]['links'], 'text'));
+    $this->assertContains('View Contact', array_column($result[2]['columns'][2]['links'], 'text'));
+    $this->assertContains('Delete Contact', array_column($result[2]['columns'][2]['links'], 'text'));
+  }
+
   /**
    * Test return values are augmented by tokens.
    */