diff --git a/CRM/Contribute/Form/Contribution/Main.php b/CRM/Contribute/Form/Contribution/Main.php index 9a94d17b895f..8b78716d9333 100644 --- a/CRM/Contribute/Form/Contribution/Main.php +++ b/CRM/Contribute/Form/Contribution/Main.php @@ -491,8 +491,8 @@ private function buildPriceSet(&$form, $component = NULL) { $adminFieldVisible = CRM_Core_Permission::check('administer CiviCRM'); $checklifetime = FALSE; foreach ($this->getPriceFieldMetaData() as $id => $field) { - if ($field['visibility'] === 'public' || - ($field['visibility'] === 'admin' && $adminFieldVisible) + if ($field['visibility_id:name'] === 'public' || + ($field['visibility_id:name'] === 'admin' && $adminFieldVisible) ) { $options = $field['options'] ?? NULL; if ($this->_membershipContactID && $options) { @@ -508,7 +508,7 @@ private function buildPriceSet(&$form, $component = NULL) { } if (!CRM_Core_Permission::check('edit contributions')) { foreach ($options as $key => $currentOption) { - if ($currentOption['visibility_id'] == CRM_Price_BAO_PriceField::getVisibilityOptionID('admin')) { + if ($currentOption['visibility_id:name'] === 'admin') { unset($options[$key]); } } diff --git a/CRM/Contribute/Form/ContributionBase.php b/CRM/Contribute/Form/ContributionBase.php index e1d433ff8aad..1b0b80b78694 100644 --- a/CRM/Contribute/Form/ContributionBase.php +++ b/CRM/Contribute/Form/ContributionBase.php @@ -556,7 +556,7 @@ private function initSet($form) { if ($form->_action & CRM_Core_Action::UPDATE) { $form->_values['line_items'] = CRM_Price_BAO_LineItem::getLineItems($form->_id, 'contribution'); } - $form->_priceSet = [$this->getPriceSetID() => $this->order->getPriceSetMetadata()]; + $form->_priceSet = $this->order->getPriceSetMetadata(); $this->setPriceFieldMetaData($this->order->getPriceFieldsMetadata()); $form->set('priceSet', $form->_priceSet); } diff --git a/CRM/Financial/BAO/Order.php b/CRM/Financial/BAO/Order.php index d422c7865f34..7f05c741c3fc 100644 --- a/CRM/Financial/BAO/Order.php +++ b/CRM/Financial/BAO/Order.php @@ -711,8 +711,6 @@ public function getPriceSetMetadata(): array { if (empty($this->priceSetMetadata)) { $this->priceSetMetadata = CRM_Price_BAO_PriceSet::getCachedPriceSetDetail($this->getPriceSetID()); $this->priceSetMetadata['id'] = $this->getPriceSetID(); - // @todo - make sure this is an array - commented out for now as this PR is against the rc. - // $priceSetMetadata['extends'] = explode(CRM_Core_DAO::VALUE_SEPARATOR, $priceSetMetadata['extends']); $this->setPriceFieldMetadata($this->priceSetMetadata['fields']); unset($this->priceSetMetadata['fields']); } @@ -725,8 +723,7 @@ public function isMembershipPriceSet() { } // Access the property if set, to avoid a potential loop when the hook is called. $priceSetMetadata = $this->priceSetMetadata ?: $this->getPriceSetMetadata(); - $extends = explode(CRM_Core_DAO::VALUE_SEPARATOR, $priceSetMetadata['extends']); - return in_array(CRM_Core_Component::getComponentID('CiviMember'), $extends, FALSE); + return in_array(CRM_Core_Component::getComponentID('CiviMember'), $priceSetMetadata['extends'], FALSE); } /** diff --git a/CRM/Price/BAO/PriceField.php b/CRM/Price/BAO/PriceField.php index cb5903a138a3..1b19622a54e2 100644 --- a/CRM/Price/BAO/PriceField.php +++ b/CRM/Price/BAO/PriceField.php @@ -313,7 +313,7 @@ public static function addQuickFormElement( if (!empty($fieldOptions[$optionKey]['label'])) { //check for label. - $label = $fieldOptions[$optionKey]['label']; + $label = CRM_Utils_String::purifyHTML($fieldOptions[$optionKey]['label']); } // @todo - move this back to the only calling function on Contribution_Form_Main.php if ($isQuickConfig && $field->name === 'other_amount') { @@ -383,7 +383,7 @@ public static function addQuickFormElement( $qf->assign('membershipFieldID', $field->id); } - $choice[$opt['id']] = $priceOptionText['label']; + $choice[$opt['id']] = CRM_Utils_String::purifyHTML($priceOptionText['label']); $choiceAttrs[$opt['id']] = $extra; if ($is_pay_later) { $qf->add('text', 'txt-' . $elementName, $label, ['size' => '4']); @@ -771,12 +771,12 @@ public static function priceSetValidation($priceSetId, $fields, &$error, $allowN */ public static function buildPriceOptionText($opt, $isDisplayAmounts, $valueFieldName) { $preHelpText = $postHelpText = ''; - $optionLabel = !empty($opt['label']) ? '' . $opt['label'] . '' : ''; - if (!empty($opt['help_pre'])) { - $preHelpText = '' . $opt['help_pre'] . ': '; + $optionLabel = !empty($opt['label']) ? '' . CRM_Utils_String::purifyHTML($opt['label']) . '' : ''; + if (CRM_Utils_String::purifyHTML($opt['help_pre'] ?? '')) { + $preHelpText = '' . CRM_Utils_String::purifyHTML($opt['help_pre']) . ': '; } - if (!empty($opt['help_post'])) { - $postHelpText = ': ' . $opt['help_post'] . ''; + if (CRM_Utils_String::purifyHTML($opt['help_post'] ?? '')) { + $postHelpText = ': ' . CRM_Utils_String::purifyHTML($opt['help_post']) . ''; } $invoicing = Civi::settings()->get('invoicing'); diff --git a/CRM/Price/BAO/PriceSet.php b/CRM/Price/BAO/PriceSet.php index 8e6048016b4c..8d5661c9216d 100644 --- a/CRM/Price/BAO/PriceSet.php +++ b/CRM/Price/BAO/PriceSet.php @@ -15,6 +15,10 @@ * @copyright CiviCRM LLC https://civicrm.org/licensing */ +use Civi\Api4\PriceField; +use Civi\Api4\PriceFieldValue; +use Civi\Api4\PriceSet; + /** * Business object for managing price sets. * @@ -757,30 +761,50 @@ public static function filterPriceFieldsFromParams($priceSetID, $params) { } /** - * Wrapper for getSetDetail with caching. + * Get PriceSet + Fields + FieldValues nested, with caching. + * + * This gets the same values as getSet but uses apiv4 for more + * predictability & better variable typing. * * We seem to be passing this array around in a painful way - presumably to avoid the hit * of loading it - so lets make it callable with caching. * - * Why not just add caching to the other function? We could do - it just seemed a bit unclear the best caching pattern - * & the function was already pretty fugly. Also, I feel like we need to migrate the interaction with price-sets into - * a more granular interaction - ie. retrieve specific data using specific functions on this class & have the form - * think less about the price sets. - * * @param int $priceSetID * * @return array + * + * @noinspection PhpUnhandledExceptionInspection */ - public static function getCachedPriceSetDetail($priceSetID) { + public static function getCachedPriceSetDetail(int $priceSetID): array { $cacheKey = __CLASS__ . __FUNCTION__ . '_' . $priceSetID; $cache = CRM_Utils_Cache::singleton(); - $values = $cache->get($cacheKey); - if (empty($values)) { - $data = self::getSetDetail($priceSetID); - $values = $data[$priceSetID]; - $cache->set($cacheKey, $values); + $data = $cache->get($cacheKey); + if (empty($data)) { + $data = PriceSet::get(FALSE) + ->addWhere('id', '=', $priceSetID) + ->addSelect('*', 'visibility_id:name', 'extends:name') + ->execute()->first(); + $data['fields'] = (array) PriceField::get(FALSE) + ->addWhere('price_set_id', '=', $priceSetID) + ->addSelect('*', 'visibility_id:name') + ->execute()->indexBy('id'); + foreach ($data['fields'] as &$field) { + $field['options'] = []; + // Add in visibility because Smarty templates expect it and it is hard to adjust them to colon format. + $field['visibility'] = $field['visibility_id:name']; + } + $options = PriceFieldValue::get(FALSE) + ->addWhere('price_field_id', 'IN', array_keys($data['fields'])) + ->addSelect('*', 'membership_type_id.name', 'visibility_id:name') + ->execute(); + foreach ($options as $option) { + // Add in visibility because Smarty templates expect it and it is hard to adjust them to colon format. + $option['visibility'] = $option['visibility_id:name']; + $data['fields'][$option['price_field_id']]['options'][$option['id']] = $option; + } + $cache->set($cacheKey, $data); } - return $values; + return $data; } /** diff --git a/templates/CRM/Price/Form/Field.tpl b/templates/CRM/Price/Form/Field.tpl index aee89a9db979..6753cb935469 100644 --- a/templates/CRM/Price/Form/Field.tpl +++ b/templates/CRM/Price/Form/Field.tpl @@ -81,12 +81,12 @@
{$form.label.label} | +{$form.label.label|escape} | {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_price_field' field='label' id=$fid}{/if}{$form.label.html} | |||||||
{$form.html_type.label} | +{$form.html_type.label|escape} | {$form.html_type.html} |
{$form.price.label} * | +{$form.price.label|escape} * | {$form.price.html}
{if $action neq 4}
{ts}Unit price.{/ts} {help id="id-negative"} @@ -111,25 +111,25 @@ |
|||||||
{$form.non_deductible_amount.label} | +{$form.non_deductible_amount.label|escape} | {$form.non_deductible_amount.html} | |||||||
{$form.count.label} | +{$form.count.label|escape} | {$form.count.html} {ts}Enter a value here if you want to increment the number of registered participants per unit against the maximum number of participants allowed for this event.{/ts} {help id="id-participant-count"} |
|||||||
{$form.max_value.label} | +{$form.max_value.label|escape} | {$form.max_value.html} | |||||||
{$form.financial_type_id.label}* | +{$form.financial_type_id.label|escape}* | {if !$financialType} {capture assign=ftUrl}{crmURL p='civicrm/admin/financial/financialType' q="reset=1"}{/capture} @@ -169,7 +169,7 @@ | |||||||
{$form.help_pre.label} | +{$form.help_pre.label|escape} | {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_price_field' field='help_pre' id=$fid}{/if}{$form.help_pre.html|crmAddClass:huge}
{if $action neq 4}
{ts}Explanatory text displayed to users at the beginning of this field.{/ts}
@@ -178,7 +178,7 @@
| |||||||
{$form.help_post.label} | +{$form.help_post.label|escape} | {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_price_field' field='help_post' id=$fid}{/if}{$form.help_post.html|crmAddClass:huge}
{if $action neq 4}
{ts}Explanatory text displayed to users below this field.{/ts}
diff --git a/templates/CRM/Price/Form/Option.tpl b/templates/CRM/Price/Form/Option.tpl
index 52b8ad7a60b5..f64d2b7df087 100644
--- a/templates/CRM/Price/Form/Option.tpl
+++ b/templates/CRM/Price/Form/Option.tpl
@@ -28,11 +28,11 @@
| |||||||
{$form.label.label} | +{$form.label.label|escape} | {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_price_field_value' field='label' id=$optionId}{/if}{$form.label.html} | |||||||
{$form.amount.label} | +{$form.amount.label|escape} | {$form.amount.html} | |||||||
{if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_price_field_value' field='description' id=$optionId}{/if}{$form.description.html} | |||||||||
{$form.help_pre.label} | +{$form.help_pre.label|escape} | {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_price_field_value' field='help_pre' id=$optionId}{/if}{$form.help_pre.html} | |||||||
{$form.help_post.label} | +{$form.help_post.label|escape} | {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_price_field_value' field='help_post' id=$optionId}{/if}{$form.help_post.html} | |||||||
{$row.label} | +{$row.label|escape} | {$row.amount|crmMoney} | {$row.non_deductible_amount|crmMoney} | -{$row.help_pre} | -{$row.help_post} | +{$row.help_pre|escape} | +{$row.help_post|escape} | {if $isEvent}{$row.count} | {$row.max_value} | diff --git a/tests/phpunit/CRM/Contribute/Form/Contribution/MainTest.php b/tests/phpunit/CRM/Contribute/Form/Contribution/MainTest.php index abd3fedfe6e3..4c8fc4b34d8b 100644 --- a/tests/phpunit/CRM/Contribute/Form/Contribution/MainTest.php +++ b/tests/phpunit/CRM/Contribute/Form/Contribution/MainTest.php @@ -214,7 +214,44 @@ public function testExpiredPriceSet(): void { $allFields = $form->getPriceFieldMetadata(); $this->assertCount(1, $allFields); $field = current($allFields); - $this->assertEquals('test_valid_price_field', $field['name']); + $expectedValues = [ + 'name' => 'test_valid_price_field', + 'label' => 'test valid price field', + 'html_type' => 'Radio', + 'is_enter_qty' => 1, + 'weight' => TRUE, + 'is_display_amounts' => TRUE, + 'options_per_line' => 1, + 'is_active' => TRUE, + 'active_on' => NULL, + 'expire_on' => NULL, + 'javascript' => NULL, + 'visibility_id:name' => 'public', + 'visibility_id' => 1, + 'is_required' => TRUE, + ]; + foreach ($expectedValues as $key => $value) { + $this->assertEquals($value, $field[$key]); + } + + $option = reset($field['options']); + $expectedValues = [ + 'name' => 'rye grass', + 'label' => 'juicy and healthy', + 'amount' => 1, + 'weight' => 1, + 'membership_type_id' => $this->ids['MembershipType']['test'], + 'membership_num_terms' => 2, + 'is_default' => FALSE, + 'is_active' => TRUE, + 'financial_type_id' => '1', + 'non_deductible_amount' => 0, + 'visibility_id' => '1', + ]; + foreach ($expectedValues as $key => $value) { + $this->assertEquals($value, $option[$key]); + } + } /**