Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REF] Centralize logic for resolving settings pseudoconstants #14264

Merged
merged 1 commit into from
May 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions CRM/Admin/Form/SettingTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ public function getDefaultEntity() {
*/
protected function getSettingsMetaData() {
if (empty($this->settingsMetadata)) {
$allSettingMetaData = civicrm_api3('setting', 'getfields', []);
$this->settingsMetadata = array_intersect_key($allSettingMetaData['values'], $this->_settings);
$this->settingsMetadata = \Civi\Core\SettingsMetadata::getMetadata(['name' => array_keys($this->_settings)], NULL, TRUE);
// This array_merge re-orders to the key order of $this->_settings.
$this->settingsMetadata = array_merge($this->_settings, $this->settingsMetadata);
}
Expand Down Expand Up @@ -174,10 +173,7 @@ protected function addFieldsDefinedInSettingsMetadata() {
$quickFormType = $this->getQuickFormType($props);
if (isset($quickFormType)) {
$options = CRM_Utils_Array::value('options', $props);
if (isset($props['pseudoconstant'])) {
$options = civicrm_api3('Setting', 'getoptions', [
'field' => $setting,
])['values'];
if ($options) {
if ($props['html_type'] === 'Select' && isset($props['is_required']) && $props['is_required'] === FALSE && !isset($options[''])) {
// If the spec specifies the field is not required add a null option.
// Why not if empty($props['is_required']) - basically this has been added to the spec & might not be set to TRUE
Expand Down
45 changes: 36 additions & 9 deletions Civi/Core/SettingsMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class SettingsMetadata {
*
* @param array $filters
* @param int $domainID
* @param bool $loadOptions
*
* @return array
* the following information as appropriate for each setting
Expand All @@ -64,8 +65,10 @@ class SettingsMetadata {
* - is_contact
* - description
* - help_text
* - options
* - pseudoconstant
*/
public static function getMetadata($filters = [], $domainID = NULL) {
public static function getMetadata($filters = [], $domainID = NULL, $loadOptions = FALSE) {
if ($domainID === NULL) {
$domainID = \CRM_Core_Config::domainID();
}
Expand Down Expand Up @@ -95,6 +98,9 @@ public static function getMetadata($filters = [], $domainID = NULL) {
}

self::_filterSettingsSpecification($filters, $settingsMetadata);
if ($loadOptions) {
self::loadOptions($settingsMetadata);
}

return $settingsMetadata;
}
Expand Down Expand Up @@ -145,20 +151,41 @@ protected static function loadSettingsMetadata($metaDataFolder) {
* Metadata to filter.
*/
protected static function _filterSettingsSpecification($filters, &$settingSpec) {
if (empty($filters)) {
return;
}
elseif (array_keys($filters) == ['name']) {
$settingSpec = [$filters['name'] => \CRM_Utils_Array::value($filters['name'], $settingSpec, '')];
return;
if (!empty($filters['name'])) {
$settingSpec = array_intersect_key($settingSpec, array_flip((array) $filters['name']));
// FIXME: This is a workaround for settingsBag::setDb() called by unit tests with settings names that don't exist
$settingSpec += array_fill_keys((array) $filters['name'], []);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eileenmcnaughton I had to add this line to mimic the old behavior.
Settings code is kind of a mess here. Settings::set() calls a validate function that doesn't actually work, and unit tests are counting on that so they can test make-believe settings :/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this is why the validate function doesn't work: If you pass in ['name' => 'foo'] as the $filters to this function then it will spit back out an array with the key foo even if there is no such setting!. IMO that's wrong, but out of scope for this PR so I left the errant behavior as-is.

unset($filters['name']);
}
else {
if (!empty($filters)) {
foreach ($settingSpec as $field => $fieldValues) {
if (array_intersect_assoc($fieldValues, $filters) != $filters) {
unset($settingSpec[$field]);
}
}
return;
}
}

/**
* Retrieve options from settings metadata
*
* @param array $settingSpec
*/
protected static function loadOptions(&$settingSpec) {
foreach ($settingSpec as &$spec) {
if (empty($spec['pseudoconstant'])) {
continue;
}
// It would be nice if we could leverage CRM_Core_PseudoConstant::get() somehow,
// but it's tightly coupled to DAO/field. However, if you really need to support
// more pseudoconstant types, then probably best to refactor it. For now, KISS.
if (!empty($spec['pseudoconstant']['callback'])) {
$spec['options'] = Resolver::singleton()->call($spec['pseudoconstant']['callback'], []);
}
elseif (!empty($spec['pseudoconstant']['optionGroupName'])) {
$keyColumn = \CRM_Utils_Array::value('keyColumn', $spec['pseudoconstant'], 'value');
$spec['options'] = \CRM_Core_OptionGroup::values($spec['pseudoconstant']['optionGroupName'], FALSE, FALSE, TRUE, NULL, 'label', TRUE, FALSE, $keyColumn);
}
}
}

Expand Down
27 changes: 4 additions & 23 deletions api/v3/Setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,33 +148,14 @@ function _civicrm_api3_setting_getdefaults_spec(&$params) {
* @throws \API_Exception
*/
function civicrm_api3_setting_getoptions($params) {
$specs = CRM_Core_BAO_Setting::getSettingSpecification();
$domainId = CRM_Utils_Array::value('domain_id', $params);
$specs = \Civi\Core\SettingsMetadata::getMetadata(['name' => $params['field']], $domainId, TRUE);

if (empty($specs[$params['field']]) || empty($specs[$params['field']]['pseudoconstant'])) {
if (empty($specs[$params['field']]) || !is_array(CRM_Utils_Array::value('options', $specs[$params['field']]))) {
throw new API_Exception("The field '" . $params['field'] . "' has no associated option list.");
}

$pseudoconstant = $specs[$params['field']]['pseudoconstant'];

// It would be nice if we could leverage CRM_Core_PseudoConstant::get() somehow,
// but it's tightly coupled to DAO/field. However, if you really need to support
// more pseudoconstant types, then probably best to refactor it. For now, KISS.
if (!empty($pseudoconstant['callback'])) {
$values = Civi\Core\Resolver::singleton()->call($pseudoconstant['callback'], []);
return civicrm_api3_create_success($values, $params, 'Setting', 'getoptions');
}
elseif (!empty($pseudoconstant['optionGroupName'])) {
$keyColumn = 'value';
if (!empty($pseudoconstant['keyColumn'])) {
$keyColumn = $pseudoconstant['keyColumn'];
}
return civicrm_api3_create_success(
CRM_Core_OptionGroup::values($pseudoconstant['optionGroupName'], FALSE, FALSE, TRUE, NULL, 'label', TRUE, FALSE, $keyColumn),
$params, 'Setting', 'getoptions'
);
}

throw new API_Exception("The field '" . $params['field'] . "' uses an unsupported option list.");
return civicrm_api3_create_success($specs[$params['field']]['options'], $params, 'Setting', 'getoptions');
}

/**
Expand Down