"use strict";
// Declare module
- angular.module('crmSearchDisplay', CRM.angRequires('crmSearchDisplay'))
-
- // Provides base methods and properties common to all search display types
- .factory('searchDisplayBaseTrait', function(crmApi4) {
- var ts = CRM.ts('org.civicrm.search_kit');
-
- // Replace tokens keyed to rowData.
- // If rowMeta is provided, values will be formatted; if omitted, raw values will be provided.
- function replaceTokens(str, rowData, rowMeta, index) {
- if (!str) {
- return '';
- }
- _.each(rowData, function(value, key) {
- if (str.indexOf('[' + key + ']') >= 0) {
- var column = rowMeta && _.findWhere(rowMeta, {key: key}),
- val = column ? formatRawValue(column, value) : value,
- replacement = angular.isArray(val) ? val[index || 0] : val;
- str = str.replace(new RegExp(_.escapeRegExp('[' + key + ']', 'g')), replacement);
- }
- });
- return str;
- }
-
- function getUrl(link, rowData, index) {
- var url = replaceTokens(link, rowData, null, index);
- if (url.slice(0, 1) !== '/' && url.slice(0, 4) !== 'http') {
- url = CRM.url(url);
- }
- return url;
- }
-
- // Returns display value for a single column in a row
- function formatDisplayValue(rowData, key, columns) {
- var column = _.findWhere(columns, {key: key}),
- displayValue = column.rewrite ? replaceTokens(column.rewrite, rowData, columns) : formatRawValue(column, rowData[key]);
- return angular.isArray(displayValue) ? displayValue.join(', ') : displayValue;
- }
-
- // Returns value and url for a column formatted as link(s)
- function formatLinks(rowData, key, columns) {
- var column = _.findWhere(columns, {key: key}),
- value = formatRawValue(column, rowData[key]),
- values = angular.isArray(value) ? value : [value],
- links = [];
- _.each(values, function(value, index) {
- links.push({
- value: value,
- url: getUrl(column.link.path, rowData, index)
- });
- });
- return links;
- }
-
- // 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 formatRawValue(column, val);
- });
- }
- if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) {
- result = CRM.utils.formatDate(value, null, type === 'Timestamp');
- }
- else if (type === 'Boolean' && typeof value === 'boolean') {
- result = value ? ts('Yes') : ts('No');
- }
- else if (type === 'Money' && typeof value === 'number') {
- result = CRM.formatMoney(value);
- }
- return result;
- }
-
- // Return a base trait shared by all search display controllers
- // Gets mixed in using angular.extend()
- return {
- page: 1,
- rowCount: null,
- getUrl: getUrl,
-
- // Called by the controller's $onInit function
- initializeDisplay: function($scope, $element) {
- var ctrl = this;
- this.sort = this.settings.sort ? _.cloneDeep(this.settings.sort) : [];
-
- $scope.getResults = _.debounce(function() {
- ctrl.getResults();
- }, 100);
-
- // If search is embedded in contact summary tab, display count in tab-header
- var contactTab = $element.closest('.crm-contact-page .ui-tabs-panel').attr('id');
- if (contactTab) {
- var unwatchCount = $scope.$watch('$ctrl.rowCount', function(rowCount) {
- if (typeof rowCount === 'number') {
- unwatchCount();
- CRM.tabHeader.updateCount(contactTab.replace('contact-', '#tab_'), rowCount);
- }
- });
- }
-
- function onChangeFilters() {
- ctrl.page = 1;
- ctrl.rowCount = null;
- if (ctrl.onChangeFilters) {
- ctrl.onChangeFilters();
- }
- $scope.getResults();
- }
-
- if (this.afFieldset) {
- $scope.$watch(this.afFieldset.getFieldData, onChangeFilters, true);
- }
- $scope.$watch('$ctrl.filters', onChangeFilters, true);
- },
-
- // Generate params for the SearchDisplay.run api
- getApiParams: function(mode) {
- return {
- return: mode || 'page:' + this.page,
- savedSearch: this.search,
- display: this.display,
- sort: this.sort,
- filters: _.assign({}, (this.afFieldset ? this.afFieldset.getFieldData() : {}), this.filters),
- afform: this.afFieldset ? this.afFieldset.getFormName() : null
- };
- },
-
- // Call SearchDisplay.run and update ctrl.results and ctrl.rowCount
- getResults: function() {
- var ctrl = this;
- return crmApi4('SearchDisplay', 'run', ctrl.getApiParams()).then(function(results) {
- ctrl.results = results;
- ctrl.editing = false;
- if (!ctrl.rowCount) {
- if (!ctrl.settings.limit || results.length < ctrl.settings.limit) {
- ctrl.rowCount = results.length;
- } else if (ctrl.settings.pager) {
- var params = ctrl.getApiParams('row_count');
- crmApi4('SearchDisplay', 'run', params).then(function(result) {
- ctrl.rowCount = result.count;
- });
- }
- }
- });
- },
- replaceTokens: function(value, row) {
- return replaceTokens(value, row, this.settings.columns);
- },
- getLinks: function(rowData, col) {
- rowData._links = rowData._links || {};
- if (!(col.key in rowData._links)) {
- rowData._links[col.key] = formatLinks(rowData, col.key, this.settings.columns);
- }
- return rowData._links[col.key];
- },
- formatFieldValue: function(rowData, col) {
- return formatDisplayValue(rowData, col.key, this.settings.columns);
- }
- };
- });
+ angular.module('crmSearchDisplay', CRM.angRequires('crmSearchDisplay'));
})(angular, CRM.$, CRM._);
--- /dev/null
+(function(angular, $, _) {
+ "use strict";
+
+ // Trait provides base methods and properties common to all search display types
+ angular.module('crmSearchDisplay').factory('searchDisplayBaseTrait', function(crmApi4) {
+ var ts = CRM.ts('org.civicrm.search_kit');
+
+ // Replace tokens keyed to rowData.
+ // If rowMeta is provided, values will be formatted; if omitted, raw values will be provided.
+ function replaceTokens(str, rowData, rowMeta, index) {
+ if (!str) {
+ return '';
+ }
+ _.each(rowData, function(value, key) {
+ if (str.indexOf('[' + key + ']') >= 0) {
+ var column = rowMeta && _.findWhere(rowMeta, {key: key}),
+ val = column ? formatRawValue(column, value) : value,
+ replacement = angular.isArray(val) ? val[index || 0] : val;
+ str = str.replace(new RegExp(_.escapeRegExp('[' + key + ']', 'g')), replacement);
+ }
+ });
+ return str;
+ }
+
+ function getUrl(link, rowData, index) {
+ var url = replaceTokens(link, rowData, null, index);
+ if (url.slice(0, 1) !== '/' && url.slice(0, 4) !== 'http') {
+ url = CRM.url(url);
+ }
+ return url;
+ }
+
+ // Returns display value for a single column in a row
+ function formatDisplayValue(rowData, key, columns) {
+ var column = _.findWhere(columns, {key: key}),
+ displayValue = column.rewrite ? replaceTokens(column.rewrite, rowData, columns) : formatRawValue(column, rowData[key]);
+ return angular.isArray(displayValue) ? displayValue.join(', ') : displayValue;
+ }
+
+ // Returns value and url for a column formatted as link(s)
+ function formatLinks(rowData, key, columns) {
+ var column = _.findWhere(columns, {key: key}),
+ value = formatRawValue(column, rowData[key]),
+ values = angular.isArray(value) ? value : [value],
+ links = [];
+ _.each(values, function(value, index) {
+ links.push({
+ value: value,
+ url: getUrl(column.link.path, rowData, index)
+ });
+ });
+ return links;
+ }
+
+ // 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 formatRawValue(column, val);
+ });
+ }
+ if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) {
+ result = CRM.utils.formatDate(value, null, type === 'Timestamp');
+ }
+ else if (type === 'Boolean' && typeof value === 'boolean') {
+ result = value ? ts('Yes') : ts('No');
+ }
+ else if (type === 'Money' && typeof value === 'number') {
+ result = CRM.formatMoney(value);
+ }
+ return result;
+ }
+
+ // Return a base trait shared by all search display controllers
+ // Gets mixed in using angular.extend()
+ return {
+ page: 1,
+ rowCount: null,
+ getUrl: getUrl,
+
+ // Called by the controller's $onInit function
+ initializeDisplay: function($scope, $element) {
+ var ctrl = this;
+ this.sort = this.settings.sort ? _.cloneDeep(this.settings.sort) : [];
+
+ $scope.getResults = _.debounce(function() {
+ ctrl.getResults();
+ }, 100);
+
+ // If search is embedded in contact summary tab, display count in tab-header
+ var contactTab = $element.closest('.crm-contact-page .ui-tabs-panel').attr('id');
+ if (contactTab) {
+ var unwatchCount = $scope.$watch('$ctrl.rowCount', function(rowCount) {
+ if (typeof rowCount === 'number') {
+ unwatchCount();
+ CRM.tabHeader.updateCount(contactTab.replace('contact-', '#tab_'), rowCount);
+ }
+ });
+ }
+
+ function onChangeFilters() {
+ ctrl.page = 1;
+ ctrl.rowCount = null;
+ if (ctrl.onChangeFilters) {
+ ctrl.onChangeFilters();
+ }
+ $scope.getResults();
+ }
+
+ if (this.afFieldset) {
+ $scope.$watch(this.afFieldset.getFieldData, onChangeFilters, true);
+ }
+ $scope.$watch('$ctrl.filters', onChangeFilters, true);
+ },
+
+ // Generate params for the SearchDisplay.run api
+ getApiParams: function(mode) {
+ return {
+ return: mode || 'page:' + this.page,
+ savedSearch: this.search,
+ display: this.display,
+ sort: this.sort,
+ filters: _.assign({}, (this.afFieldset ? this.afFieldset.getFieldData() : {}), this.filters),
+ afform: this.afFieldset ? this.afFieldset.getFormName() : null
+ };
+ },
+
+ // Call SearchDisplay.run and update ctrl.results and ctrl.rowCount
+ getResults: function() {
+ var ctrl = this;
+ return crmApi4('SearchDisplay', 'run', ctrl.getApiParams()).then(function(results) {
+ ctrl.results = results;
+ ctrl.editing = false;
+ if (!ctrl.rowCount) {
+ if (!ctrl.settings.limit || results.length < ctrl.settings.limit) {
+ ctrl.rowCount = results.length;
+ } else if (ctrl.settings.pager) {
+ var params = ctrl.getApiParams('row_count');
+ crmApi4('SearchDisplay', 'run', params).then(function(result) {
+ ctrl.rowCount = result.count;
+ });
+ }
+ }
+ });
+ },
+ replaceTokens: function(value, row) {
+ return replaceTokens(value, row, this.settings.columns);
+ },
+ getLinks: function(rowData, col) {
+ rowData._links = rowData._links || {};
+ if (!(col.key in rowData._links)) {
+ rowData._links[col.key] = formatLinks(rowData, col.key, this.settings.columns);
+ }
+ return rowData._links[col.key];
+ },
+ formatFieldValue: function(rowData, col) {
+ return formatDisplayValue(rowData, col.key, this.settings.columns);
+ }
+ };
+ });
+
+})(angular, CRM.$, CRM._);
afFieldset: '?^^afFieldset'
},
templateUrl: '~/crmSearchDisplayTable/crmSearchDisplayTable.html',
- controller: function($scope, $element, crmApi4, searchDisplayBaseTrait) {
+ controller: function($scope, $element, crmApi4, searchDisplayBaseTrait, searchDisplayTasksTrait) {
var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
- // Mix in properties of searchDisplayBaseTrait
- ctrl = angular.extend(this, searchDisplayBaseTrait);
-
- this.selectedRows = [];
- this.allRowsSelected = false;
+ // Mix in traits to this controller
+ ctrl = angular.extend(this, searchDisplayBaseTrait, searchDisplayTasksTrait);
this.$onInit = function() {
this.initializeDisplay($scope, $element);
});
};
- this.onChangeFilters = function() {
- ctrl.selectedRows.legth = 0;
- ctrl.allRowsSelected = false;
- };
-
/**
* Returns crm-i icon class for a sortable column
* @param col
$scope.getResults();
};
- $scope.selectAllRows = function() {
- // Deselect all
- if (ctrl.allRowsSelected) {
- ctrl.allRowsSelected = false;
- ctrl.selectedRows.length = 0;
- return;
- }
- // Select all
- ctrl.allRowsSelected = true;
- if (ctrl.page === 1 && ctrl.results.length < ctrl.settings.limit) {
- ctrl.selectedRows = _.pluck(ctrl.results, 'id');
- return;
- }
- // If more than one page of results, use ajax to fetch all ids
- $scope.loadingAllRows = true;
- var params = ctrl.getApiParams('id');
- crmApi4('SearchDisplay', 'run', params, ['id']).then(function(ids) {
- $scope.loadingAllRows = false;
- ctrl.selectedRows = _.toArray(ids);
- });
- };
-
- $scope.selectRow = function(row) {
- var index = ctrl.selectedRows.indexOf(row.id);
- if (index < 0) {
- ctrl.selectedRows.push(row.id);
- ctrl.allRowsSelected = (ctrl.rowCount === ctrl.selectedRows.length);
- } else {
- ctrl.allRowsSelected = false;
- ctrl.selectedRows.splice(index, 1);
- }
- };
-
- $scope.isRowSelected = function(row) {
- return ctrl.allRowsSelected || _.includes(ctrl.selectedRows, row.id);
- };
-
}
});
<thead>
<tr>
<th class="crm-search-result-select" ng-if=":: $ctrl.settings.actions">
- <input type="checkbox" ng-checked="$ctrl.allRowsSelected" ng-click="selectAllRows()" >
+ <input type="checkbox" ng-checked="$ctrl.allRowsSelected" ng-click="$ctrl.selectAllRows()" >
</th>
<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 ng-if="col.type === 'field'" class="crm-i {{ getSort(col) }}"></i>
<tbody>
<tr ng-repeat="(rowIndex, row) in $ctrl.results">
<td ng-if=":: $ctrl.settings.actions">
- <input type="checkbox" ng-checked="isRowSelected(row)" ng-click="selectRow(row)" ng-disabled="!(!loadingAllRows && row.id)">
+ <input type="checkbox" ng-checked="$ctrl.isRowSelected(row)" ng-click="$ctrl.selectRow(row)" ng-disabled="!(!$ctrl.loadingAllRows && row.id)">
</td>
<td ng-repeat="col in $ctrl.settings.columns" ng-include="'~/crmSearchDisplay/colType/' + col.type + '.html'" title="{{:: $ctrl.replaceTokens(col.title, row) }}" class="{{:: col.alignment }}">
</td>
--- /dev/null
+(function(angular, $, _) {
+ "use strict";
+
+ // Trait shared by any search display controllers which use tasks
+ angular.module('crmSearchDisplay').factory('searchDisplayTasksTrait', function(crmApi4) {
+ var ts = CRM.ts('org.civicrm.search_kit');
+
+ // Trait properties get mixed into display controller using angular.extend()
+ return {
+
+ selectedRows: [],
+ allRowsSelected: false,
+
+ // Toggle the "select all" checkbox
+ selectAllRows: function() {
+ var ctrl = this;
+ // Deselect all
+ if (ctrl.allRowsSelected) {
+ ctrl.allRowsSelected = false;
+ ctrl.selectedRows.length = 0;
+ return;
+ }
+ // Select all
+ ctrl.allRowsSelected = true;
+ if (ctrl.page === 1 && ctrl.results.length < ctrl.settings.limit) {
+ ctrl.selectedRows = _.pluck(ctrl.results, 'id');
+ return;
+ }
+ // If more than one page of results, use ajax to fetch all ids
+ ctrl.loadingAllRows = true;
+ var params = ctrl.getApiParams('id');
+ crmApi4('SearchDisplay', 'run', params, ['id']).then(function(ids) {
+ ctrl.loadingAllRows = false;
+ ctrl.selectedRows = _.toArray(ids);
+ });
+ },
+
+ // Toggle row selection
+ selectRow: function(row) {
+ var index = this.selectedRows.indexOf(row.id);
+ if (index < 0) {
+ this.selectedRows.push(row.id);
+ this.allRowsSelected = (this.rowCount === this.selectedRows.length);
+ } else {
+ this.allRowsSelected = false;
+ this.selectedRows.splice(index, 1);
+ }
+ },
+
+ // @return bool
+ isRowSelected: function(row) {
+ return this.allRowsSelected || _.includes(this.selectedRows, row.id);
+ },
+
+ // Reset selection when filters are changed
+ onChangeFilters: function() {
+ this.selectedRows.length = 0;
+ this.allRowsSelected = false;
+ }
+
+ };
+ });
+
+})(angular, CRM.$, CRM._);