->execute();
}
+ /**
+ * Upgrade 1000 - install schema
+ * @return bool
+ */
public function upgrade_1000() {
$this->ctx->log->info('Applying update 1000 - install schema.');
// For early, early adopters who installed the extension pre-beta
return TRUE;
}
+ /**
+ * Upgrade 1001 - normalize search display column keys
+ * @return bool
+ */
+ public function upgrade_1001() {
+ $this->ctx->log->info('Applying update 1001 - normalize search display columns.');
+ $savedSearches = \Civi\Api4\SavedSearch::get(FALSE)
+ ->addWhere('api_params', 'IS NOT NULL')
+ ->addChain('displays', \Civi\Api4\SearchDisplay::get()->addWhere('saved_search_id', '=', '$id'))
+ ->execute();
+ foreach ($savedSearches as $savedSearch) {
+ $newAliases = [];
+ foreach ($savedSearch['api_params']['select'] ?? [] as $i => $select) {
+ if (strstr($select, '(') && !strstr($select, ' AS ')) {
+ $alias = CRM_Utils_String::munge(str_replace(')', '', $select), '_', 256);
+ $newAliases[$select] = $alias;
+ $savedSearch['api_params']['select'][$i] = $select . ' AS ' . $alias;
+ }
+ }
+ if ($newAliases) {
+ \Civi\Api4\SavedSearch::update(FALSE)
+ ->setValues(array_diff_key($savedSearch, ['displays' => 0]))
+ ->execute();
+ }
+ foreach ($savedSearch['displays'] ?? [] as $display) {
+ foreach ($display['settings']['columns'] ?? [] as $c => $column) {
+ $key = $newAliases[$column['expr']] ?? $column['expr'];
+ unset($display['settings']['columns'][$c]['expr']);
+ $display['settings']['columns'][$c]['key'] = explode(' AS ', $key)[1] ?? $key;
+ }
+ \Civi\Api4\SearchDisplay::update(FALSE)
+ ->setValues($display)
+ ->execute();
+ }
+ }
+ return TRUE;
+ }
+
}
return;
}
var splitAs = expr.split(' AS '),
- info = {fn: null, modifier: '', field: {}},
+ info = {fn: null, modifier: '', field: {}, alias: _.last(splitAs)},
fieldName = splitAs[0],
bracketPos = splitAs[0].indexOf('(');
if (bracketPos >= 0) {
info.suffix = !split[1] ? '' : ':' + split[1];
info.field = fieldAndJoin.field;
info.join = fieldAndJoin.join;
- info.alias = splitAs[1] || (info.fn ? info.fn.name + ':' + info.path : split[0]);
}
return info;
}
ctrl.hiddenColumns.splice(index, 1);
};
+ this.getExprFromSelect = function(key) {
+ var match;
+ _.each(ctrl.savedSearch.api_params.select, function(expr) {
+ var parts = expr.split(' AS ');
+ if (_.includes(parts, key)) {
+ match = parts[0];
+ return false;
+ }
+ });
+ return match;
+ };
+
+ this.getFieldLabel = function(key) {
+ var expr = ctrl.getExprFromSelect(key);
+ return searchMeta.getDefaultLabel(expr);
+ };
+
function fieldToColumn(fieldExpr) {
var info = searchMeta.parseExpr(fieldExpr);
return {
- expr: fieldExpr,
+ key: info.alias,
label: searchMeta.getDefaultLabel(fieldExpr),
dataType: (info.fn && info.fn.name === 'COUNT') ? 'Integer' : info.field.data_type
};
});
ctrl.hiddenColumns = [];
} else {
- var activeColumns = _.collect(ctrl.display.settings.columns, 'expr');
+ var activeColumns = _.collect(ctrl.display.settings.columns, 'key'),
+ selectAliases = _.map(ctrl.savedSearch.api_params.select, function(select) {
+ return _.last(select.split(' AS '));
+ });
ctrl.hiddenColumns = _.transform(ctrl.savedSearch.api_params.select, function(hiddenColumns, fieldExpr) {
- if (!_.includes(activeColumns, fieldExpr)) {
+ var key = _.last(fieldExpr.split(' AS '));
+ if (!_.includes(activeColumns, key)) {
hiddenColumns.push(fieldToColumn(fieldExpr));
}
});
- _.each(activeColumns, function(fieldExpr, index) {
- if (!_.includes(ctrl.savedSearch.api_params.select, fieldExpr)) {
+ _.eachRight(activeColumns, function(key, index) {
+ if (!_.includes(selectAliases, key)) {
ctrl.display.settings.columns.splice(index, 1);
}
});
parent: '^crmSearchAdminDisplay'
},
templateUrl: '~/crmSearchAdmin/displays/searchAdminDisplayList.html',
- controller: function($scope, searchMeta) {
+ controller: function($scope) {
var ts = $scope.ts = CRM.ts(),
ctrl = this;
- this.getFieldLabel = searchMeta.getDefaultLabel;
this.symbols = {
ul: [
<fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.display.settings.columns" ui-sortable="$ctrl.parent.sortableOptions">
<legend>{{:: ts('Fields') }}</legend>
<fieldset ng-repeat="col in $ctrl.display.settings.columns" class="crm-draggable">
- <legend>{{ $ctrl.getFieldLabel(col.expr) }}</legend>
+ <legend>{{ $ctrl.parent.getFieldLabel(col.key) }}</legend>
<div class="form-inline">
<label>{{:: ts('Label:') }}</label> <input class="form-control" type="text" ng-model="col.label" >
<div class="form-control checkbox-inline" ng-show="col.label.length" title="{{:: ts('Show label for every record even when this field is blank') }}">
<fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.parent.hiddenColumns" ui-sortable="$ctrl.parent.sortableOptions">
<legend>{{:: ts('Hidden Fields') }}</legend>
<fieldset ng-repeat="col in $ctrl.parent.hiddenColumns" class="crm-draggable">
- <legend>{{ $ctrl.getFieldLabel(col.expr) }}</legend>
+ <legend>{{ $ctrl.parent.getFieldLabel(col.key) }}</legend>
<div class="form-inline">
<label>{{:: ts('Label:') }}</label> <input disabled class="form-control" type="text" ng-model="col.label" />
<button class="btn-xs pull-right" ng-click="$ctrl.parent.restoreCol($index)" title="{{:: ts('Show') }}">
parent: '^crmSearchAdminDisplay'
},
templateUrl: '~/crmSearchAdmin/displays/searchAdminDisplayTable.html',
- controller: function($scope, searchMeta) {
+ controller: function($scope) {
var ts = $scope.ts = CRM.ts(),
ctrl = this;
- this.getFieldLabel = searchMeta.getDefaultLabel;
this.$onInit = function () {
if (!ctrl.display.settings) {
<fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.display.settings.columns" ui-sortable="$ctrl.parent.sortableOptions">
<legend>{{:: ts('Columns') }}</legend>
<fieldset ng-repeat="col in $ctrl.display.settings.columns" class="crm-draggable">
- <legend>{{ $ctrl.getFieldLabel(col.expr) }}</legend>
+ <legend>{{ $ctrl.parent.getFieldLabel(col.key) }}</legend>
<div class="form-inline">
<label>{{:: ts('Label:') }}</label> <input class="form-control" type="text" ng-model="col.label" />
<button class="btn-xs pull-right" ng-click="$ctrl.parent.removeCol($index)" title="{{:: ts('Hide') }}">
<fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.parent.hiddenColumns" ui-sortable="$ctrl.parent.sortableOptions">
<legend>{{:: ts('Hidden Columns') }}</legend>
<fieldset ng-repeat="col in $ctrl.parent.hiddenColumns" class="crm-draggable">
- <legend>{{ $ctrl.getFieldLabel(col.expr) }}</legend>
+ <legend>{{ $ctrl.parent.getFieldLabel(col.key) }}</legend>
<div class="form-inline">
<label>{{:: ts('Label:') }}</label> <input disabled class="form-control" type="text" ng-model="col.label" />
<button class="btn-xs pull-right" ng-click="$ctrl.parent.restoreCol($index)" title="{{:: ts('Show') }}">
return result;
}
- function prepareColumns(columns) {
- columns = _.cloneDeep(columns);
- _.each(columns, function(col) {
- col.key = _.last(col.expr.split(' AS '));
- });
- return columns;
- }
-
function getApiParams(ctrl, mode) {
return {
return: mode || 'page:' + ctrl.page,
return {
formatSearchValue: formatSearchValue,
- prepareColumns: prepareColumns,
getApiParams: getApiParams,
getResults: getResults,
replaceTokens: replaceTokens
this.rowCount = null;
this.$onInit = function() {
- this.columns = searchDisplayUtils.prepareColumns(this.settings.columns);
this.sort = this.settings.sort ? _.cloneDeep(this.settings.sort) : [];
$scope.displayUtils = searchDisplayUtils;
<li ng-repeat="row in $ctrl.results">
- <div ng-repeat="col in $ctrl.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.break ? '' : 'crm-inline-block' }}">
+ <div ng-repeat="col in $ctrl.settings.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.break ? '' : 'crm-inline-block' }}">
</div>
</li>
this.allRowsSelected = false;
this.$onInit = function() {
- this.columns = searchDisplayUtils.prepareColumns(this.settings.columns);
this.sort = this.settings.sort ? _.cloneDeep(this.settings.sort) : [];
$scope.displayUtils = searchDisplayUtils;
<th class="crm-search-result-select" ng-if="$ctrl.settings.actions">
<input type="checkbox" ng-checked="$ctrl.allRowsSelected" ng-click="selectAllRows()" >
</th>
- <th ng-repeat="col in $ctrl.columns" ng-click="setSort(col, $event)" title="{{:: ts('Click to sort results (shift-click to sort by multiple).') }}">
+ <th ng-repeat="col in $ctrl.settings.columns" ng-click="setSort(col, $event)" title="{{:: ts('Click to sort results (shift-click to sort by multiple).') }}">
<i class="crm-i {{ getSort(col) }}"></i>
<span>{{ col.label }}</span>
</th>
<td ng-if="$ctrl.settings.actions">
<input type="checkbox" ng-checked="isRowSelected(row)" ng-click="selectRow(row)" ng-disabled="!(!loadingAllRows && row.id)">
</td>
- <td ng-repeat="col in $ctrl.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.alignment }}">
+ <td ng-repeat="col in $ctrl.settings.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.alignment }}">
</td>
<td></td>
</tr>