From ab4e6f8a7b97db5cc66d4820214e7d55a05db0d1 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Thu, 5 Aug 2021 12:41:46 -0400 Subject: [PATCH] SearchKit - Refactor task-related functions into a trait and move base trait to its own file --- ext/search_kit/ang/crmSearchDisplay.module.js | 162 +---------------- .../traits/searchDisplayBaseTrait.service.js | 164 ++++++++++++++++++ .../crmSearchDisplayTable.component.js | 51 +----- .../crmSearchDisplayTable.html | 4 +- .../traits/searchDisplayTasksTrait.service.js | 64 +++++++ 5 files changed, 234 insertions(+), 211 deletions(-) create mode 100644 ext/search_kit/ang/crmSearchDisplay/traits/searchDisplayBaseTrait.service.js create mode 100644 ext/search_kit/ang/crmSearchTasks/traits/searchDisplayTasksTrait.service.js diff --git a/ext/search_kit/ang/crmSearchDisplay.module.js b/ext/search_kit/ang/crmSearchDisplay.module.js index 14b863f67b..beae3e52e1 100644 --- a/ext/search_kit/ang/crmSearchDisplay.module.js +++ b/ext/search_kit/ang/crmSearchDisplay.module.js @@ -2,166 +2,6 @@ "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._); diff --git a/ext/search_kit/ang/crmSearchDisplay/traits/searchDisplayBaseTrait.service.js b/ext/search_kit/ang/crmSearchDisplay/traits/searchDisplayBaseTrait.service.js new file mode 100644 index 0000000000..68d45d5029 --- /dev/null +++ b/ext/search_kit/ang/crmSearchDisplay/traits/searchDisplayBaseTrait.service.js @@ -0,0 +1,164 @@ +(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._); diff --git a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js index c4ed51c7d0..7801a41b0b 100644 --- a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js +++ b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js @@ -13,13 +13,10 @@ 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); @@ -38,11 +35,6 @@ }); }; - this.onChangeFilters = function() { - ctrl.selectedRows.legth = 0; - ctrl.allRowsSelected = false; - }; - /** * Returns crm-i icon class for a sortable column * @param col @@ -80,43 +72,6 @@ $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); - }; - } }); diff --git a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html index ba1eed9e13..9917eb082f 100644 --- a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html +++ b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html @@ -6,7 +6,7 @@ - + @@ -17,7 +17,7 @@ - + diff --git a/ext/search_kit/ang/crmSearchTasks/traits/searchDisplayTasksTrait.service.js b/ext/search_kit/ang/crmSearchTasks/traits/searchDisplayTasksTrait.service.js new file mode 100644 index 0000000000..bfbb4f9aa9 --- /dev/null +++ b/ext/search_kit/ang/crmSearchTasks/traits/searchDisplayTasksTrait.service.js @@ -0,0 +1,64 @@ +(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._); -- 2.25.1