Skip to content
Open
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: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Fixed

- Fix item creation failing when a Yes/No field has a non boolean default value

## [1.24.6] - 2026-10-06

### Added
Expand Down
5 changes: 5 additions & 0 deletions ajax/field_specific_fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@
'rand' => $rand,
],
);
} elseif ($type === 'yesno') {
Dropdown::showYesNo(
'default_value',
PluginFieldsField::normalizeYesNoValue($field->fields['default_value']),
);
} else {
echo Html::input(
'default_value',
Expand Down
41 changes: 40 additions & 1 deletion inc/field.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,25 @@ public static function installBaseData(Migration $migration, $version)
// change default_value from varchar to longtext
$migration->changeField($table, 'default_value', 'default_value', 'longtext');

// Yes/No default values were typed as free text, normalize them to 0/1
$yesno_fields = $DB->request([
'SELECT' => ['id', 'default_value'],
'FROM' => $table,
'WHERE' => [
'type' => 'yesno',
'NOT' => ['default_value' => ['0', '1']],
],
]);
foreach ($yesno_fields as $yesno_field) {
$migration->addPostQuery(
$DB->buildUpdate(
$table,
['default_value' => (string) self::normalizeYesNoValue($yesno_field['default_value'])],
['id' => $yesno_field['id']],
),
);
}

$toolbox = new PluginFieldsToolbox();
$toolbox->fixFieldsNames($migration, ['NOT' => ['type' => 'dropdown']]);

Expand Down Expand Up @@ -288,6 +307,8 @@ public function prepareInputForAdd($input)

if ($input['multiple'] ?? false) {
$input['default_value'] = json_encode($input['default_value'] ?: []);
} elseif (($input['type'] ?? '') === 'yesno' && array_key_exists('default_value', $input)) {
$input['default_value'] = (string) self::normalizeYesNoValue($input['default_value']);
}

//reject adding when field name is too long for mysql
Expand Down Expand Up @@ -368,6 +389,11 @@ public function prepareInputForUpdate($input)
&& $this->fields['multiple']
) {
$input['default_value'] = json_encode($input['default_value'] ?: []);
} elseif (
array_key_exists('default_value', $input)
&& $this->fields['type'] === 'yesno'
) {
$input['default_value'] = (string) self::normalizeYesNoValue($input['default_value']);
}

return $input;
Expand Down Expand Up @@ -744,6 +770,8 @@ public function showSummary($container)
} else {
echo Dropdown::getDropdownName($table, $this->fields['default_value']);
}
} elseif ($this->fields['type'] === 'yesno') {
echo Dropdown::getYesNo(self::getDefaultValue($this->fields));
} else {
echo htmlspecialchars((string) $this->fields['default_value']);
}
Expand Down Expand Up @@ -1170,7 +1198,10 @@ public static function getDefaultValue(array $field)
{
$value = null;

if (in_array($field['type'], ['dropdown', 'yesno']) && $field['default_value'] === '') {
if ($field['type'] === 'yesno') {
// Yes/No values are stored in an integer column, ignore any non boolean default value
$value = self::normalizeYesNoValue($field['default_value']);
} elseif ($field['type'] === 'dropdown' && $field['default_value'] === '') {
$value = 0;
} elseif ($field['default_value'] !== '') {
$value = $field['default_value'];
Expand All @@ -1187,6 +1218,14 @@ public static function getDefaultValue(array $field)
return $value;
}

/**
* Normalize a Yes/No value to 0 or 1.
*/
public static function normalizeYesNoValue(mixed $value): int
{
return filter_var($value, FILTER_VALIDATE_BOOL) ? 1 : 0;
}

public static function prepareHtmlFields(
$fields,
$item,
Expand Down
46 changes: 46 additions & 0 deletions tests/Units/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,52 @@ public function testDefaultValueIsAppliedToExistingItemsOnlyAtFieldCreation(
$this->assertEquals($updated_default, $readValue($ticket3));
}

public function testYesNoDefaultValueIsNormalized(): void
{
/** @var DBmysql $DB */
global $DB;

$this->login();

$container = $this->createFieldContainer([
'label' => 'YesNo Default Container',
'type' => 'dom',
'itemtypes' => [Ticket::class],
'is_active' => 1,
'entities_id' => 0,
'is_recursive' => 1,
]);

$field = $this->createField(
[
'label' => 'YesNo Field',
'type' => 'yesno',
PluginFieldsContainer::getForeignKeyField() => $container->getID(),
'ranking' => 1,
'is_active' => 1,
'is_readonly' => 0,
'default_value' => 'yes',
],
['default_value'],
);
$this->assertSame('1', $field->fields['default_value']);

$this->updateItem(PluginFieldsField::class, $field->getID(), ['default_value' => 'Non'], ['default_value']);
$this->assertTrue($field->getFromDB($field->getID()));
$this->assertSame('0', $field->fields['default_value']);

// Legacy data: default value typed as free text before the Yes/No selector was used
$DB->update(PluginFieldsField::getTable(), ['default_value' => 'Non'], ['id' => $field->getID()]);

$ticket = $this->createItem(Ticket::class, [
'name' => 'YesNo ticket ' . $this->getUniqueString(),
'content' => 'Test',
'entities_id' => 0,
]);

$this->assertEquals(0, $this->getDefaultValueStored($ticket, $container, $field->fields['name']));
}

public static function provideFieldTypesForSearchDefaultValue(): iterable
{
yield 'text' => ['type' => 'text', 'default_value' => 'search default text'];
Expand Down
Loading