Skip to content

Commit

Permalink
Add upgrade-safe DAO::getSupportedFields method
Browse files Browse the repository at this point in the history
Switches api v3 and v4 to use that method so they are upgrade-safe by default.
  • Loading branch information
colemanw authored and seamuslee001 committed Jul 21, 2020
1 parent 71d7064 commit 2421c6d
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 8 deletions.
37 changes: 37 additions & 0 deletions CRM/Core/DAO.php
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,43 @@ public static function &fields() {
return $result;
}

/**
* Returns all usable fields, indexed by name.
*
* This function differs from fields() in that it indexes by name rather than unique_name.
*
* It excludes fields not added yet by pending upgrades.
* This avoids problems with trying to SELECT a field that exists in code but has not yet been added to the db.
*
* @param bool $checkPermissions
* Filter by field permissions.
* @return array
*/
public static function getSupportedFields($checkPermissions = FALSE) {
$fields = array_column((array) static::fields(), NULL, 'name');

// Exclude fields yet not added by pending upgrades
$dbVer = \CRM_Core_BAO_Domain::version();
if ($fields && version_compare($dbVer, \CRM_Utils_System::version()) < 0) {
$fields = array_filter($fields, function($field) use ($dbVer) {
$add = $field['add'] ?? '1.0.0';
if (substr_count($add, '.') < 2) {
$add .= '.alpha1';
}
return version_compare($dbVer, $add, '>=');
});
}

// Exclude fields the user does not have permission for
if ($checkPermissions) {
$fields = array_filter($fields, function($field) {
return empty($field['permission']) || CRM_Core_Permission::check($field['permission']);
});
}

return $fields;
}

/**
* Get/set an associative array of table columns
*
Expand Down
2 changes: 1 addition & 1 deletion Civi/Api4/Service/Spec/SpecGatherer.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ private function getCustomGroupFields($customGroup, RequestSpec $specification)
private function getDAOFields($entityName) {
$bao = CoreUtil::getBAOFromApiName($entityName);

return $bao::fields();
return $bao::getSupportedFields();
}

}
16 changes: 10 additions & 6 deletions api/v3/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ function _civicrm_api3_load_DAO($entity) {
* return the DAO name to manipulate this function
* eg. "civicrm_api3_contact_create" or "Contact" will return "CRM_Contact_BAO_Contact"
*
* @return mixed|string
* @return CRM_Core_DAO|string
*/
function _civicrm_api3_get_DAO($name) {
if (strpos($name, 'civicrm_api3') !== FALSE) {
Expand Down Expand Up @@ -1878,15 +1878,19 @@ function _civicrm_api_get_fields($entity, $unique = FALSE, &$params = []) {
if (empty($dao)) {
return [];
}
$d = new $dao();
$fields = $d->fields();
$fields = $dao::fields();
$supportedFields = $dao::getSupportedFields();

foreach ($fields as $name => &$field) {
foreach ($fields as $name => $field) {
// Denote as core field
$field['is_core_field'] = TRUE;
$fields[$name]['is_core_field'] = TRUE;
// Set html attributes for text fields
if (isset($field['html'])) {
$field['html'] += (array) $d::makeAttribute($field);
$fields[$name]['html'] += (array) $dao::makeAttribute($field);
}
// Delete field if not supported by current db schema (prevents errors when there are pending db updates)
if (!isset($supportedFields[$field['name']])) {
unset($fields[$name]);
}
}

Expand Down
2 changes: 1 addition & 1 deletion sql/test_data_second_domain.mysql
Original file line number Diff line number Diff line change
Expand Up @@ -963,4 +963,4 @@ INSERT INTO civicrm_navigation
VALUES
( @domainID, CONCAT('civicrm/report/instance/', @instanceID,'&reset=1'), 'Mailing Detail Report', 'Mailing Detail Report', 'administer CiviMail', 'OR', @reportlastID, '1', NULL, @instanceID+2 );
UPDATE civicrm_report_instance SET navigation_id = LAST_INSERT_ID() WHERE id = @instanceID;
UPDATE civicrm_domain SET version = '5.27.alpha1';
UPDATE civicrm_domain SET version = '5.28.alpha1';
26 changes: 26 additions & 0 deletions tests/phpunit/CRM/Core/DAOTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -516,4 +516,30 @@ public function testModifyAndBreakQuery() {
$this->fail('String not altered');
}

public function testSupportedFields() {
// Hack a different db version which will trigger getSupportedFields to filter out newer fields
\CRM_Core_DAO::$_dbColumnValueCache['CRM_Core_DAO_Domain']['id'][1]['version'] = '5.26.0';

$customGroupFields = CRM_Core_DAO_CustomGroup::getSupportedFields();
// 'icon' was added in 5.28
$this->assertArrayNotHasKey('icon', $customGroupFields);

// Remove domain version override:
\CRM_Core_DAO::$_dbColumnValueCache = NULL;

$activityFields = CRM_Activity_DAO_Activity::getSupportedFields();
// Fields should be indexed by name not unique_name (which is "activity_id")
$this->assertEquals('id', $activityFields['id']['name']);

$customGroupFields = CRM_Core_DAO_CustomGroup::getSupportedFields();
$this->assertArrayHasKey('icon', $customGroupFields);

\CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'view all contacts'];
$contactFields = CRM_Contact_DAO_Contact::getSupportedFields();
$this->assertArrayHasKey('api_key', $contactFields);

$permissionedContactFields = CRM_Contact_DAO_Contact::getSupportedFields(TRUE);
$this->assertArrayNotHasKey('api_key', $permissionedContactFields);
}

}

0 comments on commit 2421c6d

Please sign in to comment.