return searchMeta.getDefaultLabel(expr);
};
- function fieldToColumn(fieldExpr) {
- var info = searchMeta.parseExpr(fieldExpr);
- return {
- key: info.alias,
- label: searchMeta.getDefaultLabel(fieldExpr),
- dataType: (info.fn && info.fn.name === 'COUNT') ? 'Integer' : info.field.data_type
- };
+ function fieldToColumn(fieldExpr, defaults) {
+ var info = searchMeta.parseExpr(fieldExpr),
+ values = _.cloneDeep(defaults);
+ if (defaults.key) {
+ values.key = info.alias;
+ }
+ if (defaults.label) {
+ values.label = searchMeta.getDefaultLabel(fieldExpr);
+ }
+ if (defaults.dataType) {
+ values.dataType = (info.fn && info.fn.name === 'COUNT') ? 'Integer' : info.field && info.field.data_type;
+ }
+ return values;
}
// Helper function to sort active from hidden columns and initialize each column with defaults
- this.initColumns = function() {
+ this.initColumns = function(defaults) {
if (!ctrl.display.settings.columns) {
ctrl.display.settings.columns = _.transform(ctrl.savedSearch.api_params.select, function(columns, fieldExpr) {
- columns.push(fieldToColumn(fieldExpr));
+ columns.push(fieldToColumn(fieldExpr, defaults));
});
ctrl.hiddenColumns = [];
} else {
ctrl.hiddenColumns = _.transform(ctrl.savedSearch.api_params.select, function(hiddenColumns, fieldExpr) {
var key = _.last(fieldExpr.split(' AS '));
if (!_.includes(activeColumns, key)) {
- hiddenColumns.push(fieldToColumn(fieldExpr));
+ hiddenColumns.push(fieldToColumn(fieldExpr, defaults));
}
});
_.eachRight(activeColumns, function(key, index) {
<input class="form-control" type="text" ng-model="col.title" ng-if="col.title" ng-model-options="{updateOn: 'blur'}" />
<crm-search-admin-token-select ng-if="col.title" api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams" model="col" field="title"></crm-search-admin-token-select>
</div>
+<div class="form-inline crm-search-admin-flex-row">
+ <label title="{{ ts('Change the contents of this field, or combine multiple field values.') }}">
+ <input type="checkbox" ng-checked="col.rewrite" ng-click="col.rewrite = col.rewrite ? null : '['+col.key+']'" >
+ {{ col.rewrite ? ts('Rewrite:') : ts('Rewrite') }}
+ </label>
+ <input type="text" class="form-control" ng-if="col.rewrite" ng-model="col.rewrite" ng-model-options="{updateOn: 'blur'}">
+ <crm-search-admin-token-select ng-if="col.rewrite" api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams" model="col" field="rewrite"></crm-search-admin-token-select>
+</div>
-<fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.parent.hiddenColumns" ui-sortable="$ctrl.parent.sortableOptions">
+<fieldset class="crm-search-admin-edit-columns crm-search-admin-unused-columns" ng-model="$ctrl.parent.hiddenColumns" ui-sortable="$ctrl.parent.sortableOptions">
<legend>{{:: ts('Unused') }}</legend>
<fieldset ng-repeat="col in $ctrl.parent.hiddenColumns" class="crm-draggable">
<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 type="button" class="btn-xs pull-right" ng-click="$ctrl.parent.restoreCol($index)" title="{{:: ts('Show') }}">
<i class="crm-i fa-undo"></i>
</button>
pager: true
};
}
- ctrl.parent.initColumns();
+ ctrl.parent.initColumns({key: true, dataType: true});
};
}
<legend>{{:: ts('Fields') }}</legend>
<fieldset ng-repeat="col in $ctrl.display.settings.columns" class="crm-draggable">
<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') }}">
- <label><input type="checkbox" ng-model="col.forceLabel"> <span>{{:: ts('Always show') }}</span></label>
- </div>
+ <div class="form-inline" title="{{ ts('Should this item display on its own line or inline with other items?') }}">
+ <label><input type="checkbox" ng-model="col.break"> {{:: ts('Display on new line') }}</label>
<button type="button" class="btn-xs pull-right" ng-click="$ctrl.parent.removeCol($index)" title="{{:: ts('Hide') }}">
<i class="crm-i fa-ban"></i>
</button>
</div>
- <div class="form-inline">
- <label>{{:: ts('Prefix:') }}</label>
- <input class="form-control" ng-model="col.prefix" size="4">
- <label>{{:: ts('Suffix:') }}</label>
- <input class="form-control" ng-model="col.suffix" size="4">
- <div class="form-control checkbox-inline">
- <label><input type="checkbox" ng-model="col.break"> <span>{{:: ts('New line') }}</span></label>
+ <div class="form-inline crm-search-admin-flex-row">
+ <label>
+ <input type="checkbox" ng-checked="col.label" ng-click="col.label = col.label ? null : $ctrl.parent.getFieldLabel(col.key)" >
+ {{ col.label ? ts('Label:') : ts('Label') }}
+ </label>
+ <input ng-if="col.label" class="form-control" type="text" ng-model="col.label" ng-model-options="{updateOn: 'blur'}">
+ <crm-search-admin-token-select ng-if="col.label" api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams" model="col" field="label"></crm-search-admin-token-select>
+ </div>
+ <div class="form-inline" ng-if="col.label">
+ <label style="visibility: hidden"><input type="checkbox" disabled></label>
+ <div class="checkbox">
+ <label><input type="checkbox" ng-model="col.forceLabel"> {{:: ts('Show label even when field is blank') }}</label>
</div>
</div>
<div ng-include="'~/crmSearchAdmin/displays/common/fieldOptions.html'"></div>
pager: true
};
}
- ctrl.parent.initColumns();
+ ctrl.parent.initColumns({key: true, label: true, dataType: true});
};
}
<legend>{{:: ts('Columns') }}</legend>
<fieldset ng-repeat="col in $ctrl.display.settings.columns" class="crm-draggable">
<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 type="button" class="btn-xs pull-right" ng-click="$ctrl.parent.removeCol($index)" title="{{:: ts('Hide') }}">
+ <div class="form-inline crm-search-admin-flex-row">
+ <label for="crm-search-admin-edit-col-{{ $index }}">{{:: ts('Header:') }}</label>
+ <input id="crm-search-admin-edit-col-{{ $index }}" class="form-control" type="text" ng-model="col.label" >
+
+ <button type="button" class="btn-xs" ng-click="$ctrl.parent.removeCol($index)" title="{{:: ts('Hide') }}">
<i class="crm-i fa-ban"></i>
</button>
</div>
.factory('searchDisplayUtils', function(crmApi4) {
- function replaceTokens(str, data) {
+ // Replace tokens keyed to rowData.
+ // If rowMeta is provided, values will be formatted; if omiited, raw values will be provided.
+ function replaceTokens(str, rowData, rowMeta) {
if (!str) {
return '';
}
- _.each(data, function(value, key) {
- str = str.replace('[' + key + ']', value);
+ _.each(rowData, function(value, key) {
+ if (str.indexOf('[' + key + ']') >= 0) {
+ var column = rowMeta && _.findWhere(rowMeta, {key: key}),
+ replacement = column ? formatRawValue(column, value) : value;
+ str = str.replace(new RegExp(_.escapeRegExp('[' + key + ']', 'g')), replacement);
+ }
});
return str;
}
- function getUrl(link, row) {
- var url = replaceTokens(link, row);
+ function getUrl(link, rowData) {
+ var url = replaceTokens(link, rowData);
if (url.slice(0, 1) !== '/' && url.slice(0, 4) !== 'http') {
url = CRM.url(url);
}
return _.escape(url);
}
- function formatSearchValue(row, col, value) {
- var type = col.dataType,
+ // Returns html-escaped display value for a single column in a row
+ function formatDisplayValue(rowData, key, rowMeta) {
+ var column = _.findWhere(rowMeta, {key: key}),
+ displayValue = column.rewrite ? replaceTokens(column.rewrite, rowData, rowMeta) : formatRawValue(column, rowData[key]),
+ result = _.escape(displayValue);
+ if (column.link) {
+ result = '<a href="' + getUrl(column.link, rowData) + '">' + result + '</a>';
+ }
+ return result;
+ }
+
+ // Formats raw field value according to data type
+ function formatRawValue(column, value) {
+ var type = column && column.dataType,
result = value;
if (_.isArray(value)) {
return _.map(value, function(val) {
- return formatSearchValue(row, col, val);
+ return formatRawValue(column, val);
}).join(', ');
}
if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) {
else if (type === 'Money' && typeof value === 'number') {
result = CRM.formatMoney(value);
}
- result = _.escape(result);
- if (col.link) {
- result = '<a href="' + getUrl(col.link, row) + '">' + result + '</a>';
- }
return result;
}
}
return {
- formatSearchValue: formatSearchValue,
+ formatDisplayValue: formatDisplayValue,
getApiParams: getApiParams,
getResults: getResults,
replaceTokens: replaceTokens
ctrl.getResults();
}
- $scope.formatResult = function(row, col) {
- var value = row[col.key],
- formatted = searchDisplayUtils.formatSearchValue(row, col, value),
- output = '';
- if (formatted.length || (col.label && col.forceLabel)) {
- if (col.label && (formatted.length || col.forceLabel)) {
- output += '<label>' + _.escape(col.label) + '</label> ';
- }
- if (formatted.length) {
- output += (col.prefix || '') + formatted + (col.suffix || '');
- }
+ $scope.formatResult = function(rowData, col) {
+ var formatted = searchDisplayUtils.formatDisplayValue(rowData, col.key, ctrl.settings.columns);
+ if (col.label && (formatted.length || col.forceLabel)) {
+ var label = searchDisplayUtils.replaceTokens(col.label, rowData, ctrl.settings.columns);
+ formatted = '<label>' + _.escape(label) + '</label> ' + formatted;
}
- return output;
+ return formatted;
};
}
<li ng-repeat="row in $ctrl.results">
- <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 ng-repeat="col in $ctrl.settings.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row, $ctrl.settings.columns) }}" class="{{:: col.break ? '' : 'crm-inline-block' }}">
</div>
</li>
ctrl.getResults();
};
- $scope.formatResult = function(row, col) {
- var value = row[col.key];
- return searchDisplayUtils.formatSearchValue(row, col, value);
+ $scope.formatResult = function(rowData, col) {
+ return searchDisplayUtils.formatDisplayValue(rowData, col.key, ctrl.settings.columns);
};
$scope.selectAllRows = function() {
<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.settings.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, $ctrl.settings.columns) }}" class="{{:: col.alignment }}">
</td>
<td></td>
</tr>
flex: 1;
}
+#bootstrap-theme .crm-search-admin-unused-columns fieldset {
+ min-height: 60px;
+}
+
#bootstrap-theme input[type=search]::placeholder {
font-family: FontAwesome;
text-align: right;