Skip to content

Commit

Permalink
Merge pull request #11819 from compucorp/CRM-21840-show-tool-icon-for…
Browse files Browse the repository at this point in the history
…-radio-groups

CRM-21840: Show Options Edit Link for Radio and Checkbox Groups
  • Loading branch information
colemanw authored Mar 20, 2018
2 parents 9ac4f69 + 3ef9334 commit d69014e
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 23 deletions.
37 changes: 28 additions & 9 deletions CRM/Core/BAO/CustomField.php
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,7 @@ public static function addQuickFormElement(
$field = self::getFieldObject($fieldId);
$widget = $field->html_type;
$element = NULL;
$customFieldAttributes = array();

// Custom field HTML should indicate group+field name
$groupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $field->custom_group_id);
Expand Down Expand Up @@ -837,23 +838,27 @@ public static function addQuickFormElement(
if ($search || ($widget !== 'AdvMulti-Select' && strpos($widget, 'Select') !== FALSE)) {
$widget = 'Select';
}
$selectAttributes = array(
'data-crm-custom' => $dataCrmCustomVal,
'class' => 'crm-select2',
);

$customFieldAttributes['data-crm-custom'] = $dataCrmCustomVal;
$selectAttributes = array('class' => 'crm-select2');

// Search field is always multi-select
if ($search || strpos($field->html_type, 'Multi') !== FALSE) {
$selectAttributes['class'] .= ' huge';
$selectAttributes['multiple'] = 'multiple';
$selectAttributes['placeholder'] = $placeholder;
}

// Add data for popup link. Normally this is handled by CRM_Core_Form->addSelect
if ($field->option_group_id && !$search && $widget == 'Select' && CRM_Core_Permission::check('administer CiviCRM')) {
$selectAttributes += array(
$isSupportedWidget = in_array($widget, ['Select', 'Radio']);
$canEditOptions = CRM_Core_Permission::check('administer CiviCRM');
if ($field->option_group_id && !$search && $isSelect && $canEditOptions) {
$customFieldAttributes += array(
'data-api-entity' => $field->getEntity(),
'data-api-field' => 'custom_' . $field->id,
'data-option-edit-path' => 'civicrm/admin/options/' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $field->option_group_id),
);
$selectAttributes += $customFieldAttributes;
}
}

Expand Down Expand Up @@ -933,10 +938,18 @@ public static function addQuickFormElement(

case 'Radio':
$choice = array();
parse_str($field->attributes, $radioAttributes);
$radioAttributes = array_merge($radioAttributes, $customFieldAttributes);

foreach ($options as $v => $l) {
$choice[] = $qf->createElement('radio', NULL, '', $l, (string) $v, $field->attributes);
$choice[] = $qf->createElement('radio', NULL, '', $l, (string) $v, $radioAttributes);
}
$element = $qf->addGroup($choice, $elementName, $label);
$optionEditKey = 'data-option-edit-path';
if (isset($selectAttributes[$optionEditKey])) {
$element->setAttribute($optionEditKey, $selectAttributes[$optionEditKey]);
}

if ($useRequired && !$search) {
$qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required');
}
Expand Down Expand Up @@ -988,9 +1001,15 @@ public static function addQuickFormElement(
case 'CheckBox':
$check = array();
foreach ($options as $v => $l) {
$check[] = &$qf->addElement('advcheckbox', $v, NULL, $l, array('data-crm-custom' => $dataCrmCustomVal));
$check[] = &$qf->addElement('advcheckbox', $v, NULL, $l, $customFieldAttributes);
}
$element = $qf->addGroup($check, $elementName, $label);

$group = $element = $qf->addGroup($check, $elementName, $label);
$optionEditKey = 'data-option-edit-path';
if (isset($customFieldAttributes[$optionEditKey])) {
$group->setAttribute($optionEditKey, $customFieldAttributes[$optionEditKey]);
}

if ($useRequired && !$search) {
$qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required');
}
Expand Down
32 changes: 25 additions & 7 deletions CRM/Core/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,12 @@ public function &addRadio($name, $title, $values, $attributes = array(), $separa
$options[] = $this->createElement('radio', NULL, NULL, $var, $key, $attributes);
}
$group = $this->addGroup($options, $name, $title, $separator);

$optionEditKey = 'data-option-edit-path';
if (!empty($attributes[$optionEditKey])) {
$group->setAttribute($optionEditKey, $attributes[$optionEditKey]);
}

if ($required) {
$this->addRule($name, ts('%1 is a required field.', array(1 => $title)), 'required');
}
Expand Down Expand Up @@ -1144,25 +1150,29 @@ public function addCheckBox(
if ($javascriptMethod) {
foreach ($values as $key => $var) {
if (!$flipValues) {
$options[] = $this->createElement('checkbox', $var, NULL, $key, $javascriptMethod);
$options[] = $this->createElement('checkbox', $var, NULL, $key, $javascriptMethod, $attributes);
}
else {
$options[] = $this->createElement('checkbox', $key, NULL, $var, $javascriptMethod);
$options[] = $this->createElement('checkbox', $key, NULL, $var, $javascriptMethod, $attributes);
}
}
}
else {
foreach ($values as $key => $var) {
if (!$flipValues) {
$options[] = $this->createElement('checkbox', $var, NULL, $key);
$options[] = $this->createElement('checkbox', $var, NULL, $key, $attributes);
}
else {
$options[] = $this->createElement('checkbox', $key, NULL, $var);
$options[] = $this->createElement('checkbox', $key, NULL, $var, $attributes);
}
}
}

$this->addGroup($options, $id, $title, $separator);
$group = $this->addGroup($options, $id, $title, $separator);
$optionEditKey = 'data-option-edit-path';
if (!empty($attributes[$optionEditKey])) {
$group->setAttribute($optionEditKey, $attributes[$optionEditKey]);
}

if ($other) {
$this->addElement('text', $id . '_other', ts('Other'), $attributes[$id . '_other']);
Expand Down Expand Up @@ -1425,8 +1435,16 @@ public function addField($name, $props = array(), $required = FALSE, $legacyDate
}

// Add data for popup link.
if ((!empty($props['option_url']) || !array_key_exists('option_url', $props)) && ($context != 'search' && $widget == 'Select' && CRM_Core_Permission::check('administer CiviCRM'))) {
$props['data-option-edit-path'] = !empty($props['option_url']) ? $props['option_url'] : CRM_Core_PseudoConstant::getOptionEditUrl($fieldSpec);
$canEditOptions = CRM_Core_Permission::check('administer CiviCRM');
$hasOptionUrl = !empty($props['option_url']);
$optionUrlKeyIsSet = array_key_exists('option_url', $props);
$shouldAdd = $context !== 'search' && $isSelect && $canEditOptions;

// Only add if key is not set, or if non-empty option url is provided
if (($hasOptionUrl || !$optionUrlKeyIsSet) && $shouldAdd) {
$optionUrl = $hasOptionUrl ? $props['option_url'] :
CRM_Core_PseudoConstant::getOptionEditUrl($fieldSpec);
$props['data-option-edit-path'] = $optionUrl;
$props['data-api-entity'] = $props['entity'];
$props['data-api-field'] = $props['name'];
}
Expand Down
5 changes: 4 additions & 1 deletion CRM/Core/Form/Renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ public function _elementToArray(&$element, $required, $error) {
}
// Active form elements
else {
if ($element->getType() == 'select' && $element->getAttribute('data-option-edit-path')) {
$typesToShowEditLink = array('select', 'group');
$hasEditPath = NULL !== $element->getAttribute('data-option-edit-path');

if (in_array($element->getType(), $typesToShowEditLink) && $hasEditPath) {
$this->addOptionsEditLink($el, $element);
}

Expand Down
162 changes: 156 additions & 6 deletions js/crm.optionEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,162 @@ jQuery(function($) {
.on('click', 'a.crm-option-edit-link', CRM.popup)
.on('crmPopupFormSuccess', 'a.crm-option-edit-link', function() {
$(this).trigger('crmOptionsEdited');
var $elects = $('select[data-option-edit-path="' + $(this).data('option-edit-path') + '"]');
if ($elects.data('api-entity') && $elects.data('api-field')) {
CRM.api3($elects.data('api-entity'), 'getoptions', {sequential: 1, field: $elects.data('api-field')})
.done(function (data) {
CRM.utils.setOptions($elects, data.values);
});
var optionEditPath = $(this).data('option-edit-path');
var $selects = $('select[data-option-edit-path="' + optionEditPath + '"]');
var $inputs = $('input[data-option-edit-path="' + optionEditPath + '"]');
var $radios = $inputs.filter('[type=radio]');
var $checkboxes = $inputs.filter('[type=checkbox]');

if ($selects.length > 0) {
rebuildOptions($selects, CRM.utils.setOptions);
}
else if ($radios.length > 0) {
rebuildOptions($radios, rebuildRadioOptions);
}
else if ($checkboxes.length > 0) {
rebuildOptions($checkboxes, rebuildCheckboxOptions);
}
});

/**
* Fetches options using metadata from the existing ones and calls the
* function to rebuild them
* @param $existing {object} The existing options, used as metadata store
* @param rebuilder {function} Function to be called to rebuild the options
*/
function rebuildOptions($existing, rebuilder) {
if ($existing.data('api-entity') && $existing.data('api-field')) {
CRM.api3($existing.data('api-entity'), 'getoptions', {
sequential: 1,
field: $existing.data('api-field')
})
.done(function(data) {
rebuilder($existing, data.values);
});
}
}

/**
* Rebuild checkbox input options, overwriting the existing options
*
* @param $existing {object} the existing checkbox options
* @param newOptions {array} in format returned by api.getoptions
*/
function rebuildCheckboxOptions($existing, newOptions) {
var $parent = $existing.first().parent(),
$firstExisting = $existing.first(),
optionName = $firstExisting.attr('name'),
optionAttributes =
'data-option-edit-path =' + $firstExisting.data('option-edit-path') +
' data-api-entity = ' + $firstExisting.data('api-entity') +
' data-api-field = ' + $firstExisting.data('api-field');

var prefix = optionName.substr(0, optionName.lastIndexOf("["));

var checkedBoxes = [];
$parent.find('input:checked').each(function() {
checkedBoxes.push($(this).attr('id'));
});

// remove existing checkboxes
$parent.find('input[type=checkbox]').remove();

// find existing labels for the checkboxes
var $checkboxLabels = $parent.find('label').filter(function() {
var forAttr = $(this).attr('for') || '';

return forAttr.indexOf(prefix) !== -1;
});

// find what is used to separate the elements; spaces or linebreaks
var $elementAfterLabel = $checkboxLabels.first().next();
var separator = $elementAfterLabel.is('br') ? '<br/>' : '&nbsp;';

// remove existing labels
$checkboxLabels.remove();

// remove linebreaks in container
$parent.find('br').remove();

// remove separator whitespace in container
$parent.html(function (i, html) {
return html.replace(/&nbsp;/g, '');
});

var renderedOptions = '';
// replace missing br at start of element
if (separator === '<br/>') {
$parent.prepend(separator);
renderedOptions = separator;
}

newOptions.forEach(function(option) {
var optionId = prefix + '_' + option.key,
checked = '';

if ($.inArray(optionId, checkedBoxes) !== -1) {
checked = ' checked="checked"';
}

renderedOptions += '<input type="checkbox" ' +
' value="1"' +
' id="' + optionId + '"' +
' name="' + prefix + '[' + option.key +']' + '"' +
checked +
' class="crm-form-checkbox"' +
optionAttributes +
'><label for="' + optionId + '">' + option.value + '</label>' +
separator;
});

// remove final separator
renderedOptions = renderedOptions.substring(0, renderedOptions.lastIndexOf(separator));

var $editLink = $parent.find('.crm-option-edit-link');

// try to insert before the edit link to maintain structure
if ($editLink.length > 0) {
$(renderedOptions).insertBefore($editLink);
}
else {
$parent.append(renderedOptions);
}
}

/**
* Rebuild radio input options, overwriting the existing options
*
* @param $existing {object} the existing input options
* @param newOptions {array} in format returned by api.getoptions
*/
function rebuildRadioOptions($existing, newOptions) {
var $parent = $existing.first().parent(),
$firstExisting = $existing.first(),
optionName = $firstExisting.attr('name'),
renderedOptions = '',
checkedValue = parseInt($parent.find('input:checked').attr('value')),
optionAttributes =
'data-option-edit-path =' + $firstExisting.attr('data-option-edit-path') +
' data-api-entity = ' + $firstExisting.attr('data-api-entity') +
' data-api-field = ' + $firstExisting.attr('data-api-field');

// remove existing radio inputs and labels
$parent.find('input, label').remove();

newOptions.forEach(function(option) {
var optionId = 'CIVICRM_QFID_' + option.key + '_' + optionName,
checked = (option.key === checkedValue) ? ' checked="checked"' : '';

renderedOptions += '<input type="radio" ' +
' value=' + option.key +
' id="' + optionId +'"' +
' name="' + optionName + '"' +
checked +
' class="crm-form-radio"' +
optionAttributes +
'><label for="' + optionId + '">' + option.value + '</label> ';
});

$parent.prepend(renderedOptions);
}
});

0 comments on commit d69014e

Please sign in to comment.