Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions ext/afform/core/tests/phpunit/Civi/Afform/AfformGetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,20 @@ public function testAfformAutocomplete(): void {
}

public function testGetSearchDisplays() {
$exampleLayout = <<<AFFORM
<div>
<crm-search-display-grid search-name="foo" display-name="foo-bar" />
<div>
<crm-search-display type="list" search-name="genericTag" />
</div>
</div>
< crm-search-display-table search-name='foo' display-name = 'bar-food' >
AFFORM;

Afform::create()
->addValue('name', $this->formName)
->addValue('title', 'Test Form')
->addValue('layout', '<div><crm-search-display-grid search-name="foo" display-name="foo-bar" /></div>< crm-search-display-table search-name=\'foo\' display-name = \'bar-food\' >')
->addValue('layout', $exampleLayout)
->setLayoutFormat('html')
->execute();

Expand All @@ -141,7 +151,7 @@ public function testGetSearchDisplays() {
->addWhere('search_displays', 'CONTAINS', 'foo.foo-bar')
->execute()->single();

$this->assertEquals(['foo.foo-bar', 'foo.bar-food'], $result['search_displays']);
$this->assertEquals(['foo.foo-bar', 'genericTag', 'foo.bar-food'], $result['search_displays']);
}

public function testGetLayoutWithLegacyContactTypeConversion() {
Expand Down
2 changes: 1 addition & 1 deletion ext/chart_kit/ang/crmChartKit.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
'ui.bootstrap',
'crmSearchDisplay',
],
'basePages' => ['civicrm/search', 'civicrm/admin/search'],
'basePages' => [],
'bundles' => ['bootstrap3', 'chart_kit'],
'exports' => [
'crm-search-display-chart-kit' => 'E',
Expand Down
2 changes: 1 addition & 1 deletion ext/civi_campaign/ang/afsearchCampaignDashboard.aff.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<af-field name="campaign_type_id" defn="{input_attrs: {multiple: true, placeholder: 'Filter by type'}, label: false}" />
<af-field name="status_id" defn="{input_attrs: {multiple: true, placeholder: 'Filter by status'}, label: false}" />
</fieldset>
<crm-search-display-table search-name="Administer_Campaigns" display-name="Campaigns_Table" total-count="$parent.count"></crm-search-display-table>
<crm-search-display search-name="Administer_Campaigns" display-name="Campaigns_Table" total-count="$parent.count"></crm-search-display>
</div>
</af-tab>
<af-tab title="Surveys">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1781,7 +1781,7 @@ private function loadAfform() {
}
$displays = \CRM_Utils_Array::findAll(
$fieldset,
['#tag' => $this->display['type:name'], 'search-name' => $this->savedSearch['name'], 'display-name' => $this->display['name']]
['search-name' => $this->savedSearch['name'], 'display-name' => $this->display['name']]
);
if (!$displays) {
continue;
Expand Down
5 changes: 2 additions & 3 deletions ext/search_kit/Civi/Api4/Action/SearchDisplay/Run.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,15 +265,14 @@ private function addSubsearchDisplaySettings(SearchDisplayRunResult $result): vo
$displayName = $col['subsearch']['display'] ?? NULL;
if ($searchName && $displayName) {
$searchDisplay = SearchDisplay::get(FALSE)
->addSelect('settings', 'saved_search_id.api_entity')
->addSelect('settings', 'saved_search_id.api_entity', 'type')
->addWhere('name', '=', $displayName)
->addWhere('saved_search_id.name', '=', $searchName)
->execute()->first();
if ($searchDisplay) {
$result->subsearch ??= [];
$result->subsearch["{$searchName}.{$displayName}"] = [
// Passing 'type' to support non-table displays might be nice but would add complexity
// 'type' => $searchDisplay['type:name'],
'type' => $searchDisplay['type'],
'api_entity' => $searchDisplay['saved_search_id.api_entity'],
'settings' => $searchDisplay['settings'],
];
Expand Down
90 changes: 48 additions & 42 deletions ext/search_kit/Civi/Search/AfformSearchMetadataInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,51 +28,57 @@ class AfformSearchMetadataInjector {
public static function preprocess($e) {
$changeSet = \Civi\Angular\ChangeSet::create('searchSettings')
->alterHtml(';\\.aff\\.html$;', function($doc, $path) {
// Display-type-specific tags e.g. `crm-search-display-table`
$displayTags = array_column(\Civi\Search\Display::getDisplayTypes(['name']), 'name');
// Include generic tag
$displayTags[] = 'crm-search-display';

if ($displayTags) {
foreach (pq(implode(',', $displayTags), $doc) as $component) {
$searchName = pq($component)->attr('search-name');
$displayName = pq($component)->attr('display-name');
if ($searchName) {
// Fetch search display if name is provided
if (is_string($displayName) && strlen($displayName)) {
$searchDisplayGet = \Civi\Api4\SearchDisplay::get(FALSE)
->addWhere('name', '=', $displayName)
->addWhere('saved_search_id.name', '=', $searchName);
}
// Fall-back to the default display
else {
$displayName = NULL;
$searchDisplayGet = \Civi\Api4\SearchDisplay::getDefault(FALSE)
->setSavedSearch($searchName);
}
try {
$display = $searchDisplayGet
->addSelect('settings', 'saved_search_id.api_entity', 'saved_search_id.api_params')
->execute()->single();
$savedSearch = \CRM_Utils_Array::filterByPrefix($display, 'saved_search_id.');
}
catch (\CRM_Core_Exception $e) {
return;
}
// Note: Should be kept in-sync with \Civi\Api4\Action\SearchDisplay\GetMarkup::doTask
pq($component)->attr('settings', htmlspecialchars(\CRM_Utils_JS::encode($display['settings'] ?? []), ENT_COMPAT));
pq($component)->attr('api-entity', htmlspecialchars($savedSearch['api_entity'], ENT_COMPAT));
pq($component)->attr('search', htmlspecialchars(\CRM_Utils_JS::encode($searchName), ENT_COMPAT));
pq($component)->attr('display', htmlspecialchars(\CRM_Utils_JS::encode($displayName), ENT_COMPAT));
foreach (pq(implode(',', $displayTags), $doc) as $component) {
$searchName = pq($component)->attr('search-name');
$displayName = pq($component)->attr('display-name');
if ($searchName) {
// Fetch search display if name is provided
if (is_string($displayName) && strlen($displayName)) {
$searchDisplayGet = \Civi\Api4\SearchDisplay::get(FALSE)
->addWhere('name', '=', $displayName)
->addWhere('saved_search_id.name', '=', $searchName);
}
// Fall-back to the default display
else {
$displayName = NULL;
$searchDisplayGet = \Civi\Api4\SearchDisplay::getDefault(FALSE)
->setSavedSearch($searchName);
}
try {
$display = $searchDisplayGet
->addSelect('type', 'settings', 'saved_search_id.api_entity', 'saved_search_id.api_params')
->execute()->single();
$savedSearch = \CRM_Utils_Array::filterByPrefix($display, 'saved_search_id.');
}
catch (\CRM_Core_Exception $e) {
return;
}
// Note: Should be kept in-sync with \Civi\Api4\Action\SearchDisplay\GetMarkup::doTask
pq($component)->attr('settings', htmlspecialchars(\CRM_Utils_JS::encode($display['settings'] ?? []), ENT_COMPAT));
pq($component)->attr('api-entity', htmlspecialchars($savedSearch['api_entity'], ENT_COMPAT));
pq($component)->attr('search', htmlspecialchars(\CRM_Utils_JS::encode($searchName), ENT_COMPAT));
pq($component)->attr('display', htmlspecialchars(\CRM_Utils_JS::encode($displayName), ENT_COMPAT));

// Add `type` if not included with a generic tag
if (pq($component)->is('crm-search-display') && pq($component)->attr('type') === NULL) {
pq($component)->attr('type', htmlspecialchars($display['type'], ENT_COMPAT));
}

// Add entity names to the fieldset so that afform can populate field metadata
$fieldset = pq($component)->parents('[af-fieldset]');
if ($fieldset->length) {
$entityList = FormDataModel::getSearchEntities($savedSearch);
$fieldset->attr('api-entities', htmlspecialchars(\CRM_Utils_JS::encode($entityList), ENT_COMPAT));
// Add field metadata for aggregate fields because they are not in the schema.
// Normal entity fields will be handled by AfformMetadataInjector
foreach (Meta::getCalcFields($savedSearch['api_entity'], $savedSearch['api_params']) as $fieldInfo) {
foreach (pq("af-field[name='{$fieldInfo['name']}']", $doc) as $afField) {
\Civi\Afform\AfformMetadataInjector::setFieldMetadata($afField, $fieldInfo);
}
// Add entity names to the fieldset so that afform can populate field metadata
$fieldset = pq($component)->parents('[af-fieldset]');
if ($fieldset->length) {
$entityList = FormDataModel::getSearchEntities($savedSearch);
$fieldset->attr('api-entities', htmlspecialchars(\CRM_Utils_JS::encode($entityList), ENT_COMPAT));
// Add field metadata for aggregate fields because they are not in the schema.
// Normal entity fields will be handled by AfformMetadataInjector
foreach (Meta::getCalcFields($savedSearch['api_entity'], $savedSearch['api_params']) as $fieldInfo) {
foreach (pq("af-field[name='{$fieldInfo['name']}']", $doc) as $afField) {
\Civi\Afform\AfformMetadataInjector::setFieldMetadata($afField, $fieldInfo);
}
}
}
Expand Down
35 changes: 21 additions & 14 deletions ext/search_kit/Civi/Search/Display.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,20 @@ class Display {
/**
* @return array
*/
public static function getPartials($moduleName, $module) {
$partials = [];
foreach (self::getDisplayTypes(['id', 'name']) as $type) {
$partials["~/$moduleName/displayType/{$type['id']}.html"] =
'<' . $type['name'] . ' api-entity="{{:: $ctrl.apiEntity }}" search="$ctrl.searchName" display="$ctrl.display.name" settings="$ctrl.display.settings" filters="$ctrl.filters"></' . $type['name'] . '>';
}
return $partials;
}

/**
* @return array
*/
public static function getDisplayTypes(array $props):array {
public static function getDisplayTypes(array $props, bool $onlyViewable = FALSE): array {
try {
return \Civi\Api4\SearchDisplay::getFields(FALSE)
if ($onlyViewable && !in_array('grouping', $props)) {
$props[] = 'grouping';
}
$options = \Civi\Api4\SearchDisplay::getFields(FALSE)
->setLoadOptions(array_diff($props, ['tag']))
->addWhere('name', '=', 'type')
->execute()
->first()['options'];
if ($onlyViewable) {
return array_filter($options, fn($type) => $type['grouping'] !== 'non-viewable');
}
return $options;
}
catch (\Exception $e) {
return [];
Expand Down Expand Up @@ -79,4 +74,16 @@ public static function getEntityLinks(string $entity, $addLabel = FALSE, ?array
return $links;
}

/**
* Return settings for the crmSearchDisplay angular module.
* @return array
*/
public static function getModuleSettings(): array {
$viewableTypes = self::getDisplayTypes(['id', 'name'], TRUE);

return [
'viewableDisplayTypes' => array_column($viewableTypes, 'name', 'id'),
];
}

}
2 changes: 1 addition & 1 deletion ext/search_kit/ang/crmSearchAdmin.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
],
'bundles' => ['bootstrap3'],
'basePages' => ['civicrm/admin/search'],
'requires' => ['crmUi', 'crmUtil', 'ngRoute', 'ui.sortable', 'ui.bootstrap', 'api4', 'crmSearchTasks', 'crmRouteBinder', 'crmDialog', 'md5'],
'requires' => ['crmUi', 'crmUtil', 'ngRoute', 'ui.sortable', 'ui.bootstrap', 'crmSearchDisplay', 'crmSearchTasks', 'crmRouteBinder', 'crmDialog', 'md5'],
'settingsFactory' => ['\Civi\Search\Admin', 'getAdminSettings'],
'permissions' => [
// Note the super permission "all CiviCRM permissions and ACLs" is used in the JS layer to determine if users can create search displays that bypass ACLs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
select: ['name', 'label'],
where: [
['saved_search_id.name', '=', searchName],
// For now this is the only type of embedded display we support
['type:name', '=', 'crm-search-display-table'],
// Include all viewable display types
['type', 'IN', Object.keys(CRM.crmSearchDisplay.viewableDisplayTypes)],
],
}],
savedSearch: ['SavedSearch', 'get', {
Expand Down
16 changes: 13 additions & 3 deletions ext/search_kit/ang/crmSearchDisplay.ang.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
<?php
// Search Display base module - provides services used commonly by search display implementations.
// Search Display base module - provides search display wrapper and base services.

// Generate dynamic list of dependencies
// To prevent circular dependencies in Anguar, only these two are declared clientside.
$requires = ['api4', 'ngSanitize'];
// Add all viewable display type Angular modules
foreach (\Civi\Search\Display::getDisplayTypes(['id', 'name'], TRUE) as $displayType) {
$requires[] = CRM_Utils_String::convertStringToCamel($displayType['name'], FALSE);
}

return [
'js' => [
'ang/crmSearchDisplay.module.js',
Expand All @@ -13,8 +22,9 @@
'css/crmSearchDisplay.css',
],
'basePages' => [],
'requires' => ['api4', 'ngSanitize'],
'requires' => $requires,
'exports' => [
'crm-search-display-table' => 'E',
'crm-search-display' => 'E',
],
'settingsFactory' => ['\Civi\Search\Display', 'getModuleSettings'],
];
4 changes: 2 additions & 2 deletions ext/search_kit/ang/crmSearchDisplay.module.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(function(angular, $, _) {
"use strict";

// Declare module
angular.module('crmSearchDisplay', CRM.angRequires('crmSearchDisplay'));
// Note: We're not using CRM.angRequires here to avoid circular dependencies with search display types. See crmSearchDisplay.ang.php
angular.module('crmSearchDisplay', ['api4', 'ngSanitize']);

})(angular, CRM.$, CRM._);
6 changes: 3 additions & 3 deletions ext/search_kit/ang/crmSearchDisplay/colType/subsearch.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<i ng-if=":: colData.icons.right[$index]" class="crm-i {{:: colData.icons.right[$index] }}" role="img" aria-hidden="true"></i>
</summary>
<div class="crm-search-display-subsearch-dropdown-body">
<crm-search-display-table ng-if="colData.hasBeenOpened" search="colData.subsearch.search" display="colData.subsearch.display" api-entity="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].api_entity }}" settings="$ctrl.results.subsearch[colData.subsearch.search_and_display].settings" filters="colData.subsearch.filters"></crm-search-display-table>
<crm-search-display type="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].type }}" ng-if="colData.hasBeenOpened" search="colData.subsearch.search" display="colData.subsearch.display" api-entity="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].api_entity }}" settings="$ctrl.results.subsearch[colData.subsearch.search_and_display].settings" filters="colData.subsearch.filters"></crm-search-display>
</div>
</details>
<details ng-if="colData.subsearch.subsearch_mode === 'collapsed'" class="crm-search-display-subsearch-accordion">
Expand All @@ -15,7 +15,7 @@
<i ng-if=":: colData.icons.right[$index]" class="crm-i {{:: colData.icons.right[$index] }}" role="img" aria-hidden="true"></i>
</summary>
<div class="crm-accordion-body crm-search-display-subsearch-accordion-body">
<crm-search-display-table ng-if="colData.hasBeenOpened" search="colData.subsearch.search" display="colData.subsearch.display" api-entity="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].api_entity }}" settings="$ctrl.results.subsearch[colData.subsearch.search_and_display].settings" filters="colData.subsearch.filters"></crm-search-display-table>
<crm-search-display type="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].type }}" ng-if="colData.hasBeenOpened" search="colData.subsearch.search" display="colData.subsearch.display" api-entity="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].api_entity }}" settings="$ctrl.results.subsearch[colData.subsearch.search_and_display].settings" filters="colData.subsearch.filters"></crm-search-display>
</div>
</details>
<div ng-if="colData.subsearch.subsearch_mode === 'inline'" class="crm-search-display-subsearch-inline">
Expand All @@ -24,5 +24,5 @@
{{:: colData.val }}
<i ng-if=":: colData.icons.right[$index]" class="crm-i {{:: colData.icons.right[$index] }}" role="img" aria-hidden="true"></i>
</div>
<crm-search-display-table search="colData.subsearch.search" display="colData.subsearch.display" api-entity="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].api_entity }}" settings="$ctrl.results.subsearch[colData.subsearch.search_and_display].settings" filters="colData.subsearch.filters"></crm-search-display-table>
<crm-search-display type="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].type }}" search="colData.subsearch.search" display="colData.subsearch.display" api-entity="{{:: $ctrl.results.subsearch[colData.subsearch.search_and_display].api_entity }}" settings="$ctrl.results.subsearch[colData.subsearch.search_and_display].settings" filters="colData.subsearch.filters"></crm-search-display>
</div>
27 changes: 27 additions & 0 deletions ext/search_kit/ang/crmSearchDisplay/crmSearchDisplay.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
(function(angular, $, _) {
"use strict";

// Generic wrapper to render any display type
angular.module('crmSearchDisplay').component('crmSearchDisplay', {
bindings: {
type: '@',
apiEntity: '@',
search: '<',
display: '<',
settings: '<',
Comment on lines +7 to +11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@colemanw could we remove type and settings here and fetch them in the component based on search and display?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yea they're probably usually contained within those arrays.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ufundo actually no, they're not. search and display are strings not arrays. The only array passed in is the settings. So all this stuff needs to be passed thru.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So my thinking is they are logically dependent, rather than literally one includes the other.

My first though was something like:

this.$onInit = () => {
   this.display = crmApi4('SearchDisplay', 'get', {
     select: ['type', 'settings'],
     where: [['name', '=', this.display], ['saved_search_id.name', '=', this.search]]
   })[0];
   ...

It's an extra api request, and it wont work well in the editor, but it is much more convenient if you are writing html/smarty templates to not have to fetch those things in advance.

Maybe it could be a fallback/lazy option:

this.$onInit = () => {
   if (!this.type || !this.settings) {
     const savedDisplay = crmApi4('SearchDisplay', 'get', {
       select: ['type', 'settings'],
       where: [['name', '=', this.display], ['saved_search_id.name', '=', this.search]]
     })[0];
     this.type = this.type ? this.type : savedDisplay['type'];
     this.settings = this.settings ? this.settings : savedDisplay['type'];
     ...

That would be more of a follow-up than a blocker.

@colemanw colemanw Feb 23, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ufundo it sounds like a good idea, but I think there's 2 potential problems:

  1. In a Subsearch scenario, with 50 rows per page (default) that's 50 extra ajax requests per subsearch!
  2. Users might not be allowed to call the 'SearchDisplay', 'get' ajax api depending on their permission levels.

If we really wanted to solve both problems, the metadata about the search display could be returned as part of the first searchDisplay.run request as an extra SearchDisplayRunResult property... In theory, I haven't tried it, you could fetch metadata about the search AND run the search in the same ajax request.
But that's a potentially YAGNI project and we've got bigger priorities IMO.

The real downside to this PR is that it loads ALL searchDisplay code ALL the time (e.g. all of ChartKit, etc). But js is browser-cacheable so probably not worth wringing our hands about.

filters: '<',
},
template: function($element) {
let html = '';
const displayTypes = CRM.crmSearchDisplay.viewableDisplayTypes;
Object.entries(displayTypes).forEach(([type, directive]) => {
html += `<${directive} ng-if="$ctrl.type === '${type}'" api-entity="{{:: $ctrl.apiEntity }}" search="$ctrl.search" display="$ctrl.display" settings="$ctrl.settings" filters="$ctrl.filters"></${directive}>`;
});
return html;
},
controller: function($scope, $element) {
}

});

})(angular, CRM.$, CRM._);
2 changes: 1 addition & 1 deletion ext/search_kit/ang/crmSearchDisplayBatch.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
'ang/crmSearchDisplayBatch',
],
'css' => [],
'basePages' => ['civicrm/search', 'civicrm/admin/search'],
'basePages' => [],
'requires' => ['crmSearchDisplay', 'crmUi', 'ui.bootstrap', 'crmSearchTasks'],
'bundles' => ['bootstrap3'],
'permissions' => ['administer queues'],
Expand Down
2 changes: 1 addition & 1 deletion ext/search_kit/ang/crmSearchDisplayGrid.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'css' => [
'css/crmSearchDisplayGrid.css',
],
'basePages' => ['civicrm/search', 'civicrm/admin/search'],
'basePages' => [],
'requires' => ['crmSearchDisplay', 'crmUi', 'ui.bootstrap', 'crmSearchTasks'],
'bundles' => ['bootstrap3'],
'exports' => [
Expand Down
2 changes: 1 addition & 1 deletion ext/search_kit/ang/crmSearchDisplayList.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
'partials' => [
'ang/crmSearchDisplayList',
],
'basePages' => ['civicrm/search', 'civicrm/admin/search'],
'basePages' => [],
'requires' => ['crmSearchDisplay', 'crmUi', 'ui.bootstrap', 'crmSearchTasks'],
'bundles' => ['bootstrap3'],
'exports' => [
Expand Down
2 changes: 1 addition & 1 deletion ext/search_kit/ang/crmSearchDisplayTable.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'css' => [
'css/crmSearchDisplayTable.css',
],
'basePages' => ['civicrm/search', 'civicrm/admin/search'],
'basePages' => [],
'requires' => ['crmSearchDisplay', 'crmUi', 'crmSearchTasks', 'ui.bootstrap', 'ui.sortable'],
'bundles' => ['bootstrap3'],
'exports' => [
Expand Down
2 changes: 1 addition & 1 deletion ext/search_kit/ang/crmSearchDisplayTree.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'css' => [
'css/crmSearchDisplayTree.css',
],
'basePages' => ['civicrm/search', 'civicrm/admin/search'],
'basePages' => [],
'requires' => ['crmSearchDisplay', 'crmUi', 'crmSearchTasks', 'ui.bootstrap', 'ui.sortable'],
'bundles' => ['bootstrap3'],
'exports' => [
Expand Down
6 changes: 4 additions & 2 deletions ext/search_kit/ang/crmSearchPage.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
'ang/crmSearchPage/*.js',
'ang/crmSearchPage/*/*.js',
],
'partials' => [
'ang/crmSearchPage',
],
'basePages' => ['civicrm/search'],
'requires' => ['ngRoute', 'api4', 'crmUi'],
'partialsCallback' => ['\Civi\Search\Display', 'getPartials'],
'requires' => ['ngRoute', 'api4', 'crmUi', 'crmSearchDisplay'],
];
Loading