Skip to content

Commit

Permalink
Fix rendering of user-entered values in editable listbox-multiple TVs (
Browse files Browse the repository at this point in the history
…#16640)

### What does it do?
Back ports fixes already made and merged into 3.x. See the referenced
PRs below for more info.

### Related issue(s)/PR(s)
Alternate to PR #16241; includes back ports of #16242 and #16561
  • Loading branch information
smg6511 authored Nov 6, 2024
1 parent 6ed6692 commit c2c62d1
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 27 deletions.
4 changes: 4 additions & 0 deletions core/lexicon/en/tv_widget.inc.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<?php

/**
* TV Widget English lexicon topic
*
* @language en
* @package modx
* @subpackage lexicon
*/

$_lang['attributes'] = 'Attributes';
$_lang['capitalize'] = 'Capitalize';
$_lang['checkbox'] = 'Check Box';
Expand All @@ -25,6 +27,8 @@
$_lang['combo_listwidth_desc'] = 'The width, in pixels, of the dropdown list itself. Defaults to the width of the combobox.';
$_lang['combo_maxheight'] = 'Max Height';
$_lang['combo_maxheight_desc'] = 'The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300).';
$_lang['combo_preserve_selectionorder'] = 'Preserve Entry Order';
$_lang['combo_preserve_selectionorder_desc'] = 'When set to Yes, saved items display in the order they were originally selected. Otherwise, items follow the order defined in the list options. (Default: Yes)';
$_lang['combo_stackitems'] = 'Stack Selected Items';
$_lang['combo_stackitems_desc'] = 'When set to Yes, the items will be stacked 1 per line. Defaults to No, which displays the items inline.';
$_lang['combo_title'] = 'List Header';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

/*
* This file is part of MODX Revolution.
*
Expand All @@ -12,43 +13,88 @@
* @package modx
* @subpackage processors.element.tv.renders.mgr.input
*/
class modTemplateVarInputRenderListboxMultiple extends modTemplateVarInputRender {
public function getTemplate() {
class modTemplateVarInputRenderListboxMultiple extends modTemplateVarInputRender
{
public function getTemplate()
{
return 'element/tv/renders/input/listbox-multiple.tpl';
}
public function process($value,array $params = array()) {
$value = explode("||",$value);

public function process($value, array $params = [])
{
$savedValues = explode('||', $value);
$options = $this->getInputOptions();
$items = array();

$items = [];
$selections = [];
$optsValues = [];

foreach ($options as $option) {
$opt = explode("==",$option);
if (!isset($opt[1])) $opt[1] = $opt[0];
$items[] = array(
'text' => htmlspecialchars($opt[0],ENT_COMPAT,'UTF-8'),
'value' => htmlspecialchars($opt[1],ENT_COMPAT,'UTF-8'),
'selected' => in_array($opt[1],$value),
);
$opt = explode('==', $option);
if (!isset($opt[1])) {
$opt[1] = $opt[0];
}
$optLabel = htmlspecialchars($opt[0], ENT_COMPAT, 'UTF-8');
$optValue = htmlspecialchars($opt[1], ENT_COMPAT, 'UTF-8');

/*
Collect defined options values for later comparison to savedValues
to determine if any custom user-entered values need to be accounted for.
*/
$optsValues[] = $optValue;

if (in_array($opt[1], $savedValues)) {
$selections[] = [
'text' => $optLabel,
'value' => $optValue,
'selected' => 1
];
} else {
$items[] = [
'text' => $optLabel,
'value' => $optValue,
'selected' => 0
];
}
}

// preserve the order of selected values
$orderedItems = array();
// loop trough the selected values
foreach ($value as $val) {
// find the corresponding option in the items array
foreach ($items as $item => $values) {
// if found, add it in the right order to the $orderItems array
if ($values['value'] == $val) {
$orderedItems[] = $values;
// and remove it from the original $items array
unset($items[$item]);
// Ensure custom values are displayed when the listbox is editable
if (isset($params['forceSelection']) && empty($params['forceSelection'])) {
$customValues = array_diff($savedValues, $optsValues);
if (!empty($customValues)) {
$customData = [];
foreach ($customValues as $customValue) {
$customValue = htmlspecialchars($customValue, ENT_COMPAT, 'UTF-8');
$customData[] = [
'text' => $customValue,
'value' => $customValue,
'selected' => 1
];
}
$selections = array_merge($selections, $customData);
}
}
// merge the correctly ordered items with the unselected remaining ones
$items = array_merge($orderedItems, $items);
$items = array_merge($selections, $items);

$this->setPlaceholder('opts',$items);
if ($params['preserveSelectionOrder']) {
$this->reorderBySelectionOrder($savedValues, $items);
}

$this->setPlaceholder('opts', $items);
}

private function reorderBySelectionOrder(array $selections, array &$items)
{
$itemsValues = array_column($items, 'value');
$orderedSelections = [];

foreach ($selections as $selection) {
$index = array_search($selection, $itemsValues);
if ($index !== false) {
$orderedSelections[] = $items[$index];
unset($items[$index]);
}
}
$items = array_merge($orderedSelections, $items);
}
}
return 'modTemplateVarInputRenderListboxMultiple';
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,19 @@ MODx.load({
,forId: 'inopt_stackItems{/literal}{$tv|default}{literal}'
,html: _('combo_stackitems_desc')
,cls: 'desc-under'
},{
xtype: 'combo-boolean',
fieldLabel: _('combo_preserve_selectionorder'),
description: MODx.expandHelp ? '' : _('combo_preserve_selectionorder_desc'),
name: 'inopt_preserveSelectionOrder',
hiddenName: 'inopt_preserveSelectionOrder',
width: 200,
value: (params['preserveSelectionOrder']) ? !(params['preserveSelectionOrder'] === 0 || params['preserveSelectionOrder'] === 'false') : true
},{
xtype: 'box',
hidden: !MODx.expandHelp,
html: _('combo_preserve_selectionorder_desc'),
cls: 'desc-under'
}]
,renderTo: 'tv-input-properties-form{/literal}{$tv|default}{literal}'
});
Expand Down

0 comments on commit c2c62d1

Please sign in to comment.