Skip to content
Draft
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
6 changes: 3 additions & 3 deletions CRM/Core/BAO/CustomField.php
Original file line number Diff line number Diff line change
Expand Up @@ -2064,9 +2064,9 @@ protected static function prepareCreate($params) {
// An option_type of 2 would be a 'message' from the form layer not to handle
// the option_values key. If not set then it is not ignored.
$optionsType = (int) ($params['option_type'] ?? 0);
if (($optionsType !== 2 && empty($params['id']))
&& (empty($params['option_group_id']) && !empty($params['option_value'])
)
if ($optionsType === 1 &&
empty($params['option_group_id']) &&
!empty($params['option_value'])
) {
// first create an option group for this custom group
$customGroupTitle = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $params['custom_group_id'], 'title');
Expand Down
252 changes: 41 additions & 211 deletions CRM/Custom/Form/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ public function preProcess() {
CRM_Core_Error::statusBounce("You cannot add or edit fields in a reserved custom field-set.");
}

// Add crm-options-repeat web component. FIXME: need an autoloader for web components.
\Civi::resources()->addScriptFile('civicrm', 'js/CrmOptionsRepeat.js');

if ($this->_gid) {
$url = CRM_Utils_System::url('civicrm/admin/custom/group/field',
"reset=1&gid={$this->_gid}"
Expand Down Expand Up @@ -185,13 +188,6 @@ public function setDefaultValues() {
}
}

// Set defaults for option values.
for ($i = 1; $i <= self::NUM_OPTION; $i++) {
$defaults['option_status[' . $i . ']'] = 1;
$defaults['option_weight[' . $i . ']'] = $i;
$defaults['option_value[' . $i . ']'] = $i;
}

return $defaults;
}

Expand Down Expand Up @@ -291,9 +287,7 @@ public function buildQuickForm() {
$element = &$this->addRadio('option_type',
ts('Option Type'),
$optionTypes,
[
'onclick' => "showOptionSelect();",
], '<br/>'
[], '<br/>'
);
// if empty option group freeze the option type.
if ($emptyOptGroup) {
Expand All @@ -319,52 +313,8 @@ public function buildQuickForm() {

$this->add('hidden', 'filter_selected', 'Group', ['id' => 'filter_selected']);

// form fields of Custom Option rows
$defaultOption = [];
$_showHide = new CRM_Core_ShowHideBlocks();
for ($i = 1; $i <= self::NUM_OPTION; $i++) {

//the show hide blocks
$showBlocks = 'optionField_' . $i;
if ($i > 2) {
$_showHide->addHide($showBlocks);
if ($i == self::NUM_OPTION) {
$_showHide->addHide('additionalOption');
}
}
else {
$_showHide->addShow($showBlocks);
}

$optionAttributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue');
// label
$this->add('text', 'option_label[' . $i . ']', ts('Label'),
$optionAttributes['label']
);

// value
$this->add('text', 'option_value[' . $i . ']', ts('Value'),
$optionAttributes['value']
);

// weight
$this->add('number', "option_weight[$i]", ts('Order'),
$optionAttributes['weight']
);

// is active ?
$this->add('checkbox', "option_status[$i]", ts('Active?'));

$defaultOption[$i] = NULL;

//for checkbox handling of default option
$this->add('checkbox', "default_checkbox_option[$i]", NULL);
}

//default option selection
$this->addRadio('default_option', NULL, $defaultOption);

$_showHide->addToTemplate();
// Receives json from CrmOptionsRepeat element
$this->add('text', 'option_values');

// text length for alpha numeric data types
$this->add('number',
Expand Down Expand Up @@ -512,8 +462,6 @@ public static function formRule($fields, $files, $self) {

$errors = [];

self::clearEmptyOptions($fields);

//validate field label as well as name.
$title = $fields['label'];
$name = CRM_Utils_String::munge($title, '_', 64);
Expand Down Expand Up @@ -633,124 +581,46 @@ public static function formRule($fields, $files, $self) {
}
}

/** Check the option values entered
* Appropriate values are required for the selected datatype
* Incomplete row checking is also required.
*/
$_flagOption = $_rowError = 0;
$_showHide = new CRM_Core_ShowHideBlocks();
$htmlType = $fields['html_type'];

if (isset($fields['option_type']) && $fields['option_type'] == 1) {
//capture duplicate Custom option values
if (!empty($fields['option_value'])) {
$countValue = count($fields['option_value']);
$uniqueCount = count(array_unique($fields['option_value']));
if (!empty($fields['option_values'])) {
$optionValues = json_decode($fields['option_values'], TRUE);

if ($countValue > $uniqueCount) {
// Check for duplicate option values
$countValue = count($optionValues);

$start = 1;
while ($start < self::NUM_OPTION) {
$nextIndex = $start + 1;
while ($nextIndex <= self::NUM_OPTION) {
if ($fields['option_value'][$start] == $fields['option_value'][$nextIndex] &&
strlen($fields['option_value'][$nextIndex])
) {
$errors['option_value[' . $start . ']'] = ts('Duplicate Option values');
$errors['option_value[' . $nextIndex . ']'] = ts('Duplicate Option values');
$_flagOption = 1;
}
$nextIndex++;
}
$start++;
}
$uniqueCount = count(array_unique(array_column($optionValues, 'value')));
if ($countValue > $uniqueCount) {
$errors['option_values'] = ts('Duplicate Option values');
}
}

//capture duplicate Custom Option label
if (!empty($fields['option_label'])) {
$countValue = count($fields['option_label']);
$uniqueCount = count(array_unique($fields['option_label']));

$uniqueCount = count(array_unique(array_column($optionValues, 'label')));
if ($countValue > $uniqueCount) {
$start = 1;
while ($start < self::NUM_OPTION) {
$nextIndex = $start + 1;
while ($nextIndex <= self::NUM_OPTION) {
if ($fields['option_label'][$start] == $fields['option_label'][$nextIndex] &&
!empty($fields['option_label'][$nextIndex])
) {
$errors['option_label[' . $start . ']'] = ts('Duplicate Option label');
$errors['option_label[' . $nextIndex . ']'] = ts('Duplicate Option label');
$_flagOption = 1;
}
$nextIndex++;
}
$start++;
}
$errors['option_values'] = ts('Duplicate Option labels');
}
}

for ($i = 1; $i <= self::NUM_OPTION; $i++) {
if (!$fields['option_label'][$i]) {
if ($fields['option_value'][$i]) {
$errors['option_label[' . $i . ']'] = ts('Option label cannot be empty');
$_flagOption = 1;
foreach ($optionValues as $optionValue) {
if (empty($optionValue['label'])) {
$errors['option_values'] = ts('Option label cannot be empty');
}
else {
$_emptyRow = 1;
}
}
else {
if (!strlen(trim($fields['option_value'][$i]))) {
if (!$fields['option_value'][$i]) {
$errors['option_value[' . $i . ']'] = ts('Option value cannot be empty');
$_flagOption = 1;
}
if (empty($optionValue['value'])) {
$errors['option_values'] = ts('Option value cannot be empty');
}
}

if ($fields['option_value'][$i] && $dataType != 'String') {
if ($dataType == 'Int') {
if (!CRM_Utils_Rule::integer($fields['option_value'][$i])) {
$_flagOption = 1;
$errors['option_value[' . $i . ']'] = ts('Please enter a valid integer.');
}
if ($dataType === 'Int' && !CRM_Utils_Rule::integer($optionValue['value'])) {
$errors['option_values'] = ts('Please enter a valid integer.');
}
elseif ($dataType == 'Money') {
if (!CRM_Utils_Rule::money($fields['option_value'][$i])) {
$_flagOption = 1;
$errors['option_value[' . $i . ']'] = ts('Please enter a valid money value.');
}
elseif ($dataType === 'Money' && !CRM_Utils_Rule::money($optionValue['value'])) {
$errors['option_values'] = ts('Please enter a valid money value.');
}
else {
if (!CRM_Utils_Rule::numeric($fields['option_value'][$i])) {
$_flagOption = 1;
$errors['option_value[' . $i . ']'] = ts('Please enter a valid number.');
}
elseif (!CRM_Utils_Rule::numeric($optionValue['value'])) {
$errors['option_values'] = ts('Please enter a valid number.');
}
}

$showBlocks = 'optionField_' . $i;
if ($_flagOption) {
$_showHide->addShow($showBlocks);
$_rowError = 1;
}

if (!empty($_emptyRow)) {
$_showHide->addHide($showBlocks);
}
else {
$_showHide->addShow($showBlocks);
}
if ($i == self::NUM_OPTION) {
$hideBlock = 'additionalOption';
$_showHide->addHide($hideBlock);
}

$_flagOption = $_emptyRow = 0;
}
}

elseif (in_array($htmlType, self::$htmlTypesWithOptions) &&
!in_array($dataType, ['Boolean', 'Country', 'StateProvince', 'ContactReference', 'EntityReference'])
) {
Expand All @@ -774,39 +644,6 @@ public static function formRule($fields, $files, $self) {
}
}

$assignError = new CRM_Core_Page();
if ($_rowError) {
$_showHide->addToTemplate();
$assignError->assign('optionRowError', $_rowError);
}
else {
if (isset($htmlType)) {
switch ($htmlType) {
case 'Radio':
case 'CheckBox':
case 'Select':
$_fieldError = 1;
$assignError->assign('fieldError', $_fieldError);
break;

default:
$_fieldError = 0;
$assignError->assign('fieldError', $_fieldError);
}
}

for ($idx = 1; $idx <= self::NUM_OPTION; $idx++) {
$showBlocks = 'optionField_' . $idx;
if (!empty($fields['option_label'][$idx])) {
$_showHide->addShow($showBlocks);
}
else {
$_showHide->addHide($showBlocks);
}
}
$_showHide->addToTemplate();
}

// we can not set require and view at the same time.
if (!empty($fields['is_required']) && !empty($fields['is_view'])) {
$errors['is_view'] = ts('Can not set this field Required and View Only at the same time.');
Expand All @@ -822,7 +659,7 @@ public static function formRule($fields, $files, $self) {
$optionQuery = "SELECT value FROM civicrm_option_value WHERE option_group_id = " . (int) $fields['option_group_id'];
}
else {
$options = array_map(['CRM_Core_DAO', 'escapeString'], array_filter($fields['option_value'], 'strlen'));
$options = array_map(['CRM_Core_DAO', 'escapeString'], array_column($optionValues, 'value'));
$optionQuery = '"' . implode('","', $options) . '"';
}
$table = CRM_Core_BAO_CustomGroup::getGroup(['id' => $self->_gid])['table_name'];
Expand All @@ -845,7 +682,13 @@ public static function formRule($fields, $files, $self) {
public function postProcess() {
// store the submitted values in an array
$params = $this->controller->exportValues($this->_name);
self::clearEmptyOptions($params);
if (!empty($params['option_values']) && $params['option_type'] == 1) {
$params['option_values'] = json_decode($params['option_values'], TRUE);
$params['option_group_id'] = NULL;
}
else {
$params['option_values'] = NULL;
}

// Automatically disable 'is_search_range' if the field does not support it
if (in_array($params['data_type'], ['Int', 'Float', 'Money', 'Date'])) {
Expand Down Expand Up @@ -902,13 +745,15 @@ public function postProcess() {
if ($this->_action & CRM_Core_Action::UPDATE) {
$params['id'] = $this->_id;
}
$customField = CRM_Core_BAO_CustomField::create($params);
$this->_id = $customField->id;
$customField = civicrm_api4('CustomField', 'save', [
'records' => [$params],
])->single();
$this->_id = $customField['id'];

// reset the cache
Civi::cache('fields')->flush();

$msg = '<p>' . ts("Custom field '%1' has been saved.", [1 => $customField->label]) . '</p>';
$msg = '<p>' . ts("Custom field '%1' has been saved.", [1 => $customField['label']]) . '</p>';

$buttonName = $this->controller->getButtonName();
$session = CRM_Core_Session::singleton();
Expand All @@ -926,22 +771,7 @@ public function postProcess() {
$session->setStatus($msg, ts('Saved'), 'success');

// Add data when in ajax contect
$this->ajaxResponse['customField'] = $customField->toArray();
}

/**
* Removes value from fields with no label.
*
* This allows default values to be set in the form, but ignored in post-processing.
*
* @param array $fields
*/
public static function clearEmptyOptions(&$fields) {
foreach ($fields['option_label'] as $i => $label) {
if (!strlen(trim($label))) {
$fields['option_value'][$i] = '';
}
}
$this->ajaxResponse['customField'] = $customField;
}

/**
Expand Down
8 changes: 3 additions & 5 deletions Civi/Api4/Action/CustomField/CustomFieldSaveTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ trait CustomFieldSaveTrait {
*/
protected function write(array $items) {
foreach ($items as &$field) {
if (empty($field['id'])) {
self::formatOptionValues($field);
}
self::formatOptionValues($field);
}
return parent::write($items);
}
Expand All @@ -35,7 +33,7 @@ protected function write(array $items) {
* @param array $field
*/
private static function formatOptionValues(array &$field): void {
$field['option_type'] = !empty($field['option_values']);
$field['option_type'] = (int) !empty($field['option_values']);
if (!empty($field['option_values'])) {
$weight = 0;
$field['option_label'] = $field['option_value'] = $field['option_status'] = $field['option_weight'] =
Expand All @@ -50,7 +48,7 @@ private static function formatOptionValues(array &$field): void {
}
$field['option_label'][] = $value['label'] ?? $value['name'];
$field['option_name'][] = $value['name'] ?? NULL;
$field['option_value'][] = $value['id'];
$field['option_value'][] = $value['value'] ?? $value['id'];
$field['option_status'][] = $value['is_active'] ?? 1;
$field['option_weight'][] = $value['weight'] ?? ++$weight;
$field['option_color'][] = $value['color'] ?? NULL;
Expand Down
Loading