From 22601c92aac43d1afd0d5c6a6b93dce72095b9dd Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 13 Oct 2020 17:47:38 -0400 Subject: [PATCH] Search ext: Add settingsCallback to searchAdmin module --- ext/search/CRM/Search/Page/Admin.php | 100 +---------------- ext/search/Civi/Search/Admin.php | 105 ++++++++++++++++++ ext/search/ang/searchAdmin.ang.php | 1 + ext/search/ang/searchAdmin.module.js | 2 +- .../ang/searchAdmin/crmSearch.component.js | 2 +- .../searchAdmin/crmSearchClause.component.js | 2 +- .../crmSearchFunction.component.js | 4 +- 7 files changed, 116 insertions(+), 100 deletions(-) create mode 100644 ext/search/Civi/Search/Admin.php diff --git a/ext/search/CRM/Search/Page/Admin.php b/ext/search/CRM/Search/Page/Admin.php index c1d06ab0f8..cfded724aa 100644 --- a/ext/search/CRM/Search/Page/Admin.php +++ b/ext/search/CRM/Search/Page/Admin.php @@ -13,15 +13,6 @@ * Angular base page for search admin */ class CRM_Search_Page_Admin extends CRM_Core_Page { - /** - * @var string[] - */ - private $loadOptions = ['id', 'name', 'label', 'description', 'color', 'icon']; - - /** - * @var array - */ - private $schema = []; /** * @var string[] @@ -30,25 +21,22 @@ class CRM_Search_Page_Admin extends CRM_Core_Page { public function run() { $breadCrumb = [ - 'title' => ts('Search'), + 'title' => ts('Search Kit'), 'url' => CRM_Utils_System::url('civicrm/search'), ]; CRM_Utils_System::appendBreadCrumb([$breadCrumb]); - $this->getSchema(); + $schema = \Civi\Search\Admin::getSchema(); // If user does not have permission to search any entity, bye bye. - if (!$this->allowedEntities) { + if (!$schema) { CRM_Utils_System::permissionDenied(); } // Add client-side vars for the search UI $vars = [ - 'operators' => CRM_Utils_Array::makeNonAssociative($this->getOperators()), - 'schema' => $this->schema, - 'links' => $this->getLinks(), - 'loadOptions' => $this->loadOptions, - 'functions' => CRM_Api4_Page_Api4Explorer::getSqlFunctions(), + 'schema' => $schema, + 'links' => \Civi\Search\Admin::getLinks(array_column($schema, 'name')), ]; Civi::resources() @@ -66,82 +54,4 @@ class CRM_Search_Page_Admin extends CRM_Core_Page { parent::run(); } - /** - * @return string[] - */ - private function getOperators() { - return [ - '=' => '=', - '!=' => '≠', - '>' => '>', - '<' => '<', - '>=' => '≥', - '<=' => '≤', - 'CONTAINS' => ts('Contains'), - 'IN' => ts('Is In'), - 'NOT IN' => ts('Not In'), - 'LIKE' => ts('Is Like'), - 'NOT LIKE' => ts('Not Like'), - 'BETWEEN' => ts('Is Between'), - 'NOT BETWEEN' => ts('Not Between'), - 'IS NULL' => ts('Is Null'), - 'IS NOT NULL' => ts('Not Null'), - ]; - } - - /** - * Populates $this->schema & $this->allowedEntities - */ - private function getSchema() { - $schema = \Civi\Api4\Entity::get() - ->addSelect('name', 'title', 'titlePlural', 'description', 'icon') - ->addWhere('name', '!=', 'Entity') - ->addOrderBy('titlePlural') - ->setChain([ - 'get' => ['$name', 'getActions', ['where' => [['name', '=', 'get']]], ['params']], - ])->execute(); - $getFields = ['name', 'label', 'description', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize', 'fk_entity']; - foreach ($schema as $entity) { - // Skip if entity doesn't have a 'get' action or the user doesn't have permission to use get - if ($entity['get']) { - // Get fields and pre-load options for certain prominent entities - $loadOptions = in_array($entity['name'], ['Contact', 'Group']) ? $this->loadOptions : FALSE; - if ($loadOptions) { - $entity['optionsLoaded'] = TRUE; - } - $entity['fields'] = civicrm_api4($entity['name'], 'getFields', [ - 'select' => $getFields, - 'where' => [['permission', 'IS NULL']], - 'orderBy' => ['label'], - 'loadOptions' => $loadOptions, - ]); - // Get the names of params this entity supports (minus some obvious ones) - $params = $entity['get'][0]; - CRM_Utils_Array::remove($params, 'checkPermissions', 'debug', 'chain', 'language'); - unset($entity['get']); - $this->schema[] = ['params' => array_keys($params)] + array_filter($entity); - $this->allowedEntities[] = $entity['name']; - } - } - } - - /** - * @return array - */ - private function getLinks() { - $results = []; - $keys = array_flip(['alias', 'entity', 'joinType']); - foreach (civicrm_api4('Entity', 'getLinks', ['where' => [['entity', 'IN', $this->allowedEntities]]], ['entity' => 'links']) as $entity => $links) { - $entityLinks = []; - foreach ($links as $link) { - if (!empty($link['entity']) && in_array($link['entity'], $this->allowedEntities)) { - // Use entity.alias as array key to avoid duplicates - $entityLinks[$link['entity'] . $link['alias']] = array_intersect_key($link, $keys); - } - } - $results[$entity] = array_values($entityLinks); - } - return array_filter($results); - } - } diff --git a/ext/search/Civi/Search/Admin.php b/ext/search/Civi/Search/Admin.php new file mode 100644 index 0000000000..6ddc792a0f --- /dev/null +++ b/ext/search/Civi/Search/Admin.php @@ -0,0 +1,105 @@ + \CRM_Utils_Array::makeNonAssociative(self::getOperators()), + 'functions' => \CRM_Api4_Page_Api4Explorer::getSqlFunctions(), + ]; + } + + /** + * @return string[] + */ + public static function getOperators():array { + return [ + '=' => '=', + '!=' => '≠', + '>' => '>', + '<' => '<', + '>=' => '≥', + '<=' => '≤', + 'CONTAINS' => ts('Contains'), + 'IN' => ts('Is In'), + 'NOT IN' => ts('Not In'), + 'LIKE' => ts('Is Like'), + 'NOT LIKE' => ts('Not Like'), + 'BETWEEN' => ts('Is Between'), + 'NOT BETWEEN' => ts('Not Between'), + 'IS NULL' => ts('Is Null'), + 'IS NOT NULL' => ts('Not Null'), + ]; + } + + /** + * Fetch all entities the current user has permission to `get` + */ + public static function getSchema() { + $schema = []; + $entities = \Civi\Api4\Entity::get() + ->addSelect('name', 'title', 'titlePlural', 'description', 'icon') + ->addWhere('name', '!=', 'Entity') + ->addOrderBy('titlePlural') + ->setChain([ + 'get' => ['$name', 'getActions', ['where' => [['name', '=', 'get']]], ['params']], + ])->execute(); + $getFields = ['name', 'label', 'description', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize', 'fk_entity']; + foreach ($entities as $entity) { + // Skip if entity doesn't have a 'get' action or the user doesn't have permission to use get + if ($entity['get']) { + $entity['fields'] = civicrm_api4($entity['name'], 'getFields', [ + 'select' => $getFields, + 'where' => [['permission', 'IS NULL']], + 'orderBy' => ['label'], + ]); + $params = $entity['get'][0]; + // Entity must support at least these params or it is too weird for search kit + if (!array_diff(['select', 'where', 'orderBy', 'limit', 'offset'], array_keys($params))) { + \CRM_Utils_Array::remove($params, 'checkPermissions', 'debug', 'chain', 'language', 'select', 'where', 'orderBy', 'limit', 'offset'); + unset($entity['get']); + $schema[] = ['params' => array_keys($params)] + array_filter($entity); + } + } + } + return $schema; + } + + /** + * @return array + */ + public static function getLinks($allowedEntities) { + $results = []; + $keys = array_flip(['alias', 'entity', 'joinType']); + foreach (civicrm_api4('Entity', 'getLinks', ['where' => [['entity', 'IN', $allowedEntities]]], ['entity' => 'links']) as $entity => $links) { + $entityLinks = []; + foreach ($links as $link) { + if (!empty($link['entity']) && in_array($link['entity'], $allowedEntities)) { + // Use entity.alias as array key to avoid duplicates + $entityLinks[$link['entity'] . $link['alias']] = array_intersect_key($link, $keys); + } + } + $results[$entity] = array_values($entityLinks); + } + return array_filter($results); + } + +} diff --git a/ext/search/ang/searchAdmin.ang.php b/ext/search/ang/searchAdmin.ang.php index 1c60e31ee5..f07fc61de2 100644 --- a/ext/search/ang/searchAdmin.ang.php +++ b/ext/search/ang/searchAdmin.ang.php @@ -14,4 +14,5 @@ return [ ], 'basePages' => [], 'requires' => ['crmUi', 'crmUtil', 'ngRoute', 'crmRouteBinder', 'ui.sortable', 'ui.bootstrap', 'dialogService', 'api4', 'searchActions'], + 'settingsFactory' => ['\Civi\Search\Admin', 'getAdminSettings'], ]; diff --git a/ext/search/ang/searchAdmin.module.js b/ext/search/ang/searchAdmin.module.js index 7132e7eb6f..cdb1562baa 100644 --- a/ext/search/ang/searchAdmin.module.js +++ b/ext/search/ang/searchAdmin.module.js @@ -115,7 +115,7 @@ if (bracketPos >= 0) { var parsed = expr.substr(bracketPos).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/); fieldName = parsed[2]; - result.fn = _.find(CRM.vars.search.functions, {name: expr.substring(0, bracketPos)}); + result.fn = _.find(CRM.searchAdmin.functions, {name: expr.substring(0, bracketPos)}); result.modifier = _.trim(parsed[1]); } result.field = expr ? getField(fieldName, searchEntity) : undefined; diff --git a/ext/search/ang/searchAdmin/crmSearch.component.js b/ext/search/ang/searchAdmin/crmSearch.component.js index 7ebea92db2..19e049ee81 100644 --- a/ext/search/ang/searchAdmin/crmSearch.component.js +++ b/ext/search/ang/searchAdmin/crmSearch.component.js @@ -456,7 +456,7 @@ function enqueue(entity) { entity.optionsLoaded = false; entities[entity.name] = [entity.name, 'getFields', { - loadOptions: CRM.vars.search.loadOptions, + loadOptions: ['id', 'name', 'label', 'description', 'color', 'icon'], where: [['options', '!=', false]], select: ['options'] }, {name: 'options'}]; diff --git a/ext/search/ang/searchAdmin/crmSearchClause.component.js b/ext/search/ang/searchAdmin/crmSearchClause.component.js index 3258d57bfc..884eae6bf5 100644 --- a/ext/search/ang/searchAdmin/crmSearchClause.component.js +++ b/ext/search/ang/searchAdmin/crmSearchClause.component.js @@ -16,7 +16,7 @@ var ts = $scope.ts = CRM.ts(), ctrl = this; this.conjunctions = {AND: ts('And'), OR: ts('Or'), NOT: ts('Not')}; - this.operators = CRM.vars.search.operators; + this.operators = CRM.searchAdmin.operators; this.sortOptions = { axis: 'y', connectWith: '.api4-clause-group-sortable', diff --git a/ext/search/ang/searchAdmin/crmSearchFunction.component.js b/ext/search/ang/searchAdmin/crmSearchFunction.component.js index 2b8448b55d..4ac5870576 100644 --- a/ext/search/ang/searchAdmin/crmSearchFunction.component.js +++ b/ext/search/ang/searchAdmin/crmSearchFunction.component.js @@ -12,7 +12,7 @@ ctrl = this; this.$onInit = function() { - ctrl.functions = formatForSelect2(_.where(CRM.vars.search.functions, {category: ctrl.cat}), 'name', 'title'); + ctrl.functions = formatForSelect2(_.where(CRM.searchAdmin.functions, {category: ctrl.cat}), 'name', 'title'); var fieldInfo = searchMeta.parseExpr(ctrl.expr); ctrl.path = fieldInfo.path + fieldInfo.suffix; ctrl.field = fieldInfo.field; @@ -22,7 +22,7 @@ }; function initFunction() { - ctrl.fnInfo = _.find(CRM.vars.search.functions, {name: ctrl.fn}); + ctrl.fnInfo = _.find(CRM.searchAdmin.functions, {name: ctrl.fn}); if (ctrl.fnInfo && _.includes(ctrl.fnInfo.params[0].prefix, 'DISTINCT')) { ctrl.modifierAllowed = true; } -- 2.25.1