From 415f0f472a73c62c95a1ba83a67bfcd4af610a6a Mon Sep 17 00:00:00 2001 From: eileen Date: Tue, 14 Nov 2017 17:30:14 +1300 Subject: [PATCH] CRM-20545 - port extended reports metadata approach to adding columns --- CRM/Report/Form.php | 276 ++++++++++++++++++ CRM/Report/Form/Contribute/Detail.php | 68 +---- .../Form/Contribute/HouseholdSummary.php | 9 +- .../Form/Contribute/OrganizationSummary.php | 9 +- 4 files changed, 301 insertions(+), 61 deletions(-) diff --git a/CRM/Report/Form.php b/CRM/Report/Form.php index 19d61a1143e0..a56a25a46171 100644 --- a/CRM/Report/Form.php +++ b/CRM/Report/Form.php @@ -4928,4 +4928,280 @@ protected function setTableAlias($table, $tableName) { return $this->_aliases[$tableName]; } + /** + * Function to add columns to reports. + * + * This is ported from extended reports, which also adds join filters to the options. + * + * @param string $type + * @param array $options + * - prefix - A string to prepend to the table name + * - prefix_label A string to prepend to the fields + * - fields (bool) - should the fields for this table be made available + * - group_by (bool) - should the group bys for this table be made available. + * - order_by (bool) - should the group bys for this table be made available. + * - filters (bool) - should the filters for this table by made available. + * - fields_defaults (array) array of fields that should be displayed by default. + * - filters_defaults (array) array of fields that should be filtered by default. + * - join_filters (array) fields available for filtering joins (requires additional custom code). + * - join_fields (array) fields available from join (requires additional custom code). + * - group_by_defaults (array) array of group bys that should be applied by default. + * - order_by_defaults (array) array of order bys that should be applied by default. + * - custom_fields (array) array of entity types for custom fields (not usually required). + * - contact_type (string) optional restriction on contact type for some tables. + * - fields_excluded (array) fields that are in the generic set for the table but not in the report. + * + * @return array + */ + protected function getColumns($type, $options = array()) { + $defaultOptions = array( + 'prefix' => '', + 'prefix_label' => '', + 'fields' => TRUE, + 'group_bys' => FALSE, + 'order_bys' => TRUE, + 'filters' => TRUE, + 'join_filters' => FALSE, + 'fields_defaults' => array(), + 'filters_defaults' => array(), + 'group_bys_defaults' => array(), + 'order_bys_defaults' => array(), + ); + $options = array_merge($defaultOptions, $options); + + $fn = 'get' . $type . 'Columns'; + return $this->$fn($options); + } + + /** + * Get columns for contact table. + * + * @param array $options + * + * @return array + */ + protected function getContactColumns($options = array()) { + $defaultOptions = array( + 'custom_fields' => array('Individual', 'Contact', 'Organization'), + 'fields_defaults' => array('display_name', 'id'), + 'order_bys_defaults' => array('sort_name ASC'), + 'contact_type' => NULL, + ); + + $options = array_merge($defaultOptions, $options); + + $tableAlias = $options['prefix'] . 'contact'; + + $spec = array( + $options['prefix'] . 'display_name' => array( + 'name' => 'display_name', + 'title' => ts($options['prefix_label'] . 'Contact Name'), + 'is_fields' => TRUE, + ), + $options['prefix'] . 'sort_name' => array( + 'name' => 'sort_name', + 'title' => ts($options['prefix_label'] . 'Contact Name (in sort format)'), + 'is_fields' => TRUE, + 'is_filters' => TRUE, + 'is_order_bys' => TRUE, + ), + $options['prefix'] . 'id' => array( + 'name' => 'id', + 'title' => ts($options['prefix_label'] . 'Contact ID'), + 'alter_display' => 'alterContactID', + 'type' => CRM_Utils_Type::T_INT, + 'is_order_bys' => TRUE, + 'is_group_bys' => TRUE, + 'is_fields' => TRUE, + ), + $options['prefix'] . 'external_identifier' => array( + 'name' => 'external_identifier', + 'title' => ts($options['prefix_label'] . 'External ID'), + 'type' => CRM_Utils_Type::T_INT, + 'is_fields' => TRUE, + ), + $options['prefix'] . 'contact_type' => array( + 'title' => ts($options['prefix_label'] . 'Contact Type'), + 'name' => 'contact_type', + 'operatorType' => CRM_Report_Form::OP_MULTISELECT, + 'options' => CRM_Contact_BAO_Contact::buildOptions('contact_type'), + 'is_fields' => TRUE, + 'is_filters' => TRUE, + 'is_group_bys' => TRUE, + ), + $options['prefix'] . 'contact_sub_type' => array( + 'title' => ts($options['prefix_label'] . 'Contact Sub Type'), + 'name' => 'contact_sub_type', + 'operatorType' => CRM_Report_Form::OP_MULTISELECT, + 'options' => CRM_Contact_BAO_Contact::buildOptions('contact_sub_type'), + 'is_fields' => TRUE, + 'is_filters' => TRUE, + 'is_group_bys' => TRUE, + ), + $options['prefix'] . 'is_deleted' => array( + 'title' => ts($options['prefix_label'] . 'Is deleted'), + 'name' => 'is_deleted', + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'is_fields' => FALSE, + 'is_filters' => TRUE, + 'is_group_bys' => FALSE, + ), + ); + $individualFields = array( + $options['prefix'] . 'first_name' => array( + 'name' => 'first_name', + 'title' => ts($options['prefix_label'] . 'First Name'), + 'is_fields' => TRUE, + 'is_filters' => TRUE, + 'is_order_bys' => TRUE, + ), + $options['prefix'] . 'middle_name' => array( + 'name' => 'middle_name', + 'title' => ts($options['prefix_label'] . 'Middle Name'), + 'is_fields' => TRUE, + ), + $options['prefix'] . 'last_name' => array( + 'name' => 'last_name', + 'title' => ts($options['prefix_label'] . 'Last Name'), + 'default_order' => 'ASC', + 'is_fields' => TRUE, + ), + $options['prefix'] . 'nick_name' => array( + 'name' => 'nick_name', + 'title' => ts($options['prefix_label'] . 'Nick Name'), + 'is_fields' => TRUE, + ), + $options['prefix'] . 'gender_id' => array( + 'name' => 'gender_id', + 'title' => ts($options['prefix_label'] . 'Gender'), + 'options' => CRM_Contact_BAO_Contact::buildOptions('gender_id'), + 'operatorType' => CRM_Report_Form::OP_MULTISELECT, + 'alter_display' => 'alterGenderID', + 'is_fields' => TRUE, + 'is_filters' => TRUE, + ), + 'birth_date' => array( + 'title' => ts($options['prefix_label'] . 'Birth Date'), + 'operatorType' => CRM_Report_Form::OP_DATE, + 'type' => CRM_Utils_Type::T_DATE, + 'is_fields' => TRUE, + 'is_filters' => TRUE, + ), + 'age' => array( + 'title' => ts($options['prefix_label'] . 'Age'), + 'dbAlias' => 'TIMESTAMPDIFF(YEAR, ' . $tableAlias . '.birth_date, CURDATE())', + 'type' => CRM_Utils_Type::T_INT, + 'is_fields' => TRUE, + ), + $options['prefix'] . 'is_deceased' => array( + 'title' => ts($options['prefix_label'] . 'Is deceased'), + 'name' => 'is_deceased', + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'is_fields' => FALSE, + 'is_filters' => TRUE, + 'is_group_bys' => FALSE, + ), + ); + if (!$options['contact_type'] || $options['contact_type'] === 'Individual') { + $spec = array_merge($spec, $individualFields); + } + + if (!empty($options['custom_fields'])) { + $this->_customGroupExtended[$options['prefix'] . 'civicrm_contact'] = array( + 'extends' => $options['custom_fields'], + 'title' => $options['prefix_label'], + 'filters' => $options['filters'], + 'prefix' => $options['prefix'], + 'prefix_label' => $options['prefix_label'], + ); + } + + return $this->buildColumns($spec, $options['prefix'] . 'civicrm_contact', 'CRM_Contact_DAO_Contact', $tableAlias, $this->getDefaultsFromOptions($options), $options); + } + + /** + * Build the columns. + * + * The normal report class needs you to remember to do a few things that are often erratic + * + * 1) use a unique key for any field that might not be unique (e.g. start date, label) + * - this class will prepend an alias to the key & set the 'name' if you don't set it yourself. + * You can suppress the alias with 'no_field_disambiguation' if transitioning existing reports. This + * means any saved filters / fields on saved report instances. This will mean that matching names from + * different tables may be ambigious, but it will smooth any code transition. + * - note that it assumes the value being passed in is the actual table field name + * + * 2) set the field & set it to no display if you don't want the field but you might want to use the field in other + * contexts - the code looks up the fields array for data - so it both defines the field spec & the fields you want to show + * + * 3) this function also sets the 'metadata' array - the extended report class now uses this in place + * of the fields array to reduce the issues caused when metadata is needed but 'fields' are not defined. Code in + * the core classes can start to move towards that. + * + * @param array $specs + * @param string $tableName + * @param string $daoName + * @param string $tableAlias + * @param array $defaults + * @param array $options + * + * @return array + */ + protected function buildColumns($specs, $tableName, $daoName = NULL, $tableAlias = NULL, $defaults = array(), $options = array()) { + if (!$tableAlias) { + $tableAlias = str_replace('civicrm_', '', $tableName); + } + $types = array('filters', 'group_bys', 'order_bys', 'join_filters'); + $columns = array($tableName => array_fill_keys($types, array())); + // The code that uses this no longer cares if it is a DAO or BAO so just call it a DAO. + $columns[$tableName]['dao'] = $daoName; + $columns[$tableName]['alias'] = $tableAlias; + + foreach ($specs as $specName => $spec) { + if (empty($spec['name'])) { + $spec['name'] = $specName; + } + + $fieldAlias = (empty($options['no_field_disambiguation']) ? $tableAlias . '_' : '') . $specName; + $columns[$tableName]['metadata'][$fieldAlias] = $spec; + $columns[$tableName]['fields'][$fieldAlias] = $spec; + if (isset($defaults['fields_defaults']) && in_array($spec['name'], $defaults['fields_defaults'])) { + $columns[$tableName]['fields'][$fieldAlias]['default'] = TRUE; + } + + if (!$spec['is_fields'] || (isset($options['fields_excluded']) && in_array($specName, $options['fields_excluded']))) { + $columns[$tableName]['fields'][$fieldAlias]['no_display'] = TRUE; + } + + if (isset($options['fields_required']) && in_array($specName, $options['fields_required'])) { + $columns[$tableName]['fields'][$fieldAlias]['required'] = TRUE; + } + + foreach ($types as $type) { + if ($options[$type] && !empty($spec['is_' . $type])) { + $columns[$tableName][$type][$fieldAlias] = $spec; + if (isset($defaults[$type . '_defaults']) && isset($defaults[$type . '_defaults'][$spec['name']])) { + $columns[$tableName][$type][$fieldAlias]['default'] = $defaults[$type . '_defaults'][$spec['name']]; + } + } + } + } + return $columns; + } + + /** + * @param $options + * + * @return array + */ + protected function getDefaultsFromOptions($options) { + $defaults = array( + 'fields_defaults' => $options['fields_defaults'], + 'filters_defaults' => $options['filters_defaults'], + 'group_bys_defaults' => $options['group_bys_defaults'], + 'order_bys_defaults' => $options['order_bys_defaults'], + ); + return $defaults; + } + } diff --git a/CRM/Report/Form/Contribute/Detail.php b/CRM/Report/Form/Contribute/Detail.php index 81f72ac16e56..e67e0d0b4424 100644 --- a/CRM/Report/Form/Contribute/Detail.php +++ b/CRM/Report/Form/Contribute/Detail.php @@ -67,65 +67,15 @@ public function __construct() { $this->activeCampaigns = $getCampaigns['campaigns']; asort($this->activeCampaigns); } - $this->_columns = array( - 'civicrm_contact' => array( - 'dao' => 'CRM_Contact_DAO_Contact', - 'fields' => $this->getBasicContactFields(), - 'filters' => array( - 'sort_name' => array( - 'title' => ts('Donor Name'), - 'operator' => 'like', - ), - 'id' => array( - 'title' => ts('Contact ID'), - 'no_display' => TRUE, - 'type' => CRM_Utils_Type::T_INT, - ), - 'gender_id' => array( - 'title' => ts('Gender'), - 'operatorType' => CRM_Report_Form::OP_MULTISELECT, - 'options' => CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'), - ), - 'birth_date' => array( - 'title' => ts('Birth Date'), - 'operatorType' => CRM_Report_Form::OP_DATE, - ), - 'contact_type' => array( - 'title' => ts('Contact Type'), - ), - 'contact_sub_type' => array( - 'title' => ts('Contact Subtype'), - ), - ), - 'grouping' => 'contact-fields', - 'order_bys' => array( - 'sort_name' => array( - 'title' => ts('Last Name, First Name'), - 'default' => '1', - 'default_weight' => '0', - 'default_order' => 'ASC', - ), - 'first_name' => array( - 'name' => 'first_name', - 'title' => ts('First Name'), - ), - 'gender_id' => array( - 'name' => 'gender_id', - 'title' => ts('Gender'), - ), - 'birth_date' => array( - 'name' => 'birth_date', - 'title' => ts('Birth Date'), - ), - 'contact_type' => array( - 'title' => ts('Contact Type'), - ), - 'contact_sub_type' => array( - 'title' => ts('Contact Subtype'), - ), - ), - ), + $this->_columns = array_merge($this->getColumns('Contact', array( + 'order_bys_defaults' => array('sort_name' => 'ASC '), + 'fields_defaults' => array('sort_name'), + 'fields_excluded' => array('id'), + 'fields_required' => array('id'), + 'filters_defaults' => array('is_deleted' => FALSE), + 'no_field_disambiguation' => TRUE, + )), array( 'civicrm_email' => array( 'dao' => 'CRM_Core_DAO_Email', 'fields' => array( @@ -358,7 +308,7 @@ public function __construct() { ), ), ), - ) + $this->addAddressFields(FALSE); + )) + $this->addAddressFields(FALSE); // The tests test for this variation of the sort_name field. Don't argue with the tests :-). $this->_columns['civicrm_contact']['fields']['sort_name']['title'] = ts('Donor Name'); $this->_groupFilter = TRUE; diff --git a/CRM/Report/Form/Contribute/HouseholdSummary.php b/CRM/Report/Form/Contribute/HouseholdSummary.php index 1c82dbc92cea..5c31dfde91b0 100644 --- a/CRM/Report/Form/Contribute/HouseholdSummary.php +++ b/CRM/Report/Form/Contribute/HouseholdSummary.php @@ -77,7 +77,14 @@ public function __construct() { ), ), 'filters' => array( - 'household_name' => array('title' => ts('Household Name')), + 'household_name' => array( + 'title' => ts('Household Name'), + ), + 'is_deleted' => array( + 'no_display' => TRUE, + 'default' => 0, + 'type' => CRM_Utils_Type::T_BOOLEAN, + ), ), 'grouping' => 'household-fields', ), diff --git a/CRM/Report/Form/Contribute/OrganizationSummary.php b/CRM/Report/Form/Contribute/OrganizationSummary.php index 9a310fe91d87..c7dac57dffbf 100644 --- a/CRM/Report/Form/Contribute/OrganizationSummary.php +++ b/CRM/Report/Form/Contribute/OrganizationSummary.php @@ -89,7 +89,14 @@ public function __construct() { ), ), 'filters' => array( - 'organization_name' => array('title' => ts('Organization Name')), + 'organization_name' => array( + 'title' => ts('Organization Name'), + ), + 'is_deleted' => array( + 'no_display' => TRUE, + 'default' => 0, + 'type' => CRM_Utils_Type::T_BOOLEAN, + ), ), 'grouping' => 'organization-fields', ),