[TikiWiki-commits] [Git][tikiwiki/tiki][30.x] [FIX] Plugin listexecute - tracker_item_modify: split add/remove/method/assign flows
"Domeshow Emmanuel \(@Domeshow\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <6a04be4b54895_381912708085@gitlab-sidekiq-low-urgency-cpu-bound-v2-76fb78779f-4v8tb.mail> |
Domeshow Emmanuel pushed to branch 30.x at Tiki Wiki CMS Groupware / Tiki Commits: 461405a3 by Domeshow Emmanuel at 2026-05-13T21:01:59+03:00 [FIX] Plugin listexecute - tracker_item_modify: split add/remove/method/assign flows --- * [FIX] Plugin listexecute - tracker_item_modify: split add/remove/method/assign flows --- * [ENH] Add test for tracker dropdown empty values * [FIX] Plugin listexecute - tracker_item_modify: preserve incremental add/remove updates * [REF] Plugin listexecute: simplify add/remove/method/assign flows * [FIX] Plugin listexecute - Tracker item modify: preserve existing values for add/remove * [FIX] Tracker Field: dropdown validation with leading comma values See merge request tikiwiki/tiki!9637 (cherry picked from commit 252f5929091deba3fcd44597b1dee458eb1f5643) 16f6502d [FIX] Tracker Field: dropdown validation with leading comma values 0e47be99 [FIX] Plugin listexecute - Tracker item modify: preserve existing values for add/remove e2ded951 [REF] Plugin listexecute: simplify add/remove/method/assign flows ab0d2daa [FIX] Plugin listexecute - tracker_item_modify: preserve incremental add/remove updates 08bf6889 [ENH] Add test for tracker dropdown empty values Co-authored-by: Domeshow Emmanuel <[email protected]> See merge request tikiwiki/tiki!10215 - - - - - 3 changed files: - lib/core/Search/Action/TrackerItemModify.php - lib/core/Tracker/Field/Dropdown.php - + lib/test/IntegrationTests/TrackerDropdownEmptyValueUpdateTest.php Changes: ===================================== lib/core/Search/Action/TrackerItemModify.php ===================================== @@ -60,7 +60,7 @@ class Search_Action_TrackerItemModify implements Search_Action_Action } } - if (empty($value) && empty($calc) && empty($add) && empty($remove) && empty($method)) { + if (! isset($value) && empty($calc) && empty($add) && empty($remove) && empty($method)) { throw new Search_Action_Exception(tr('tracker_item_modify action missing value, calc, add or remove parameter.')); } @@ -142,77 +142,64 @@ class Search_Action_TrackerItemModify implements Search_Action_Action $trklib = TikiLib::lib('trk'); - $value = $this->stripNp($value); + if (is_string($value)) { + $value = $this->stripNp($value); + } $info = $trklib->get_tracker_item($object_id); - $definition = Tracker_Definition::get($info['trackerId']); - - if (! empty($calc)) { - $runner = new Math_Formula_Runner( - [ - 'Math_Formula_Function_' => '', - 'Tiki_Formula_Function_' => '', - ] - ); - try { - $runner->setFormula($calc); - $data = ['itemId' => $object_id]; - foreach ($runner->inspect() as $fieldName) { - if (is_string($fieldName) || is_numeric($fieldName)) { - $tField = $definition->getField($fieldName); - if ($tField && isset($info[$tField['fieldId']])) { - $data[$fieldName] = $info[$tField['fieldId']]; - } - } - } - $item = Tracker_Item::fromInfo($info); - $item->prepareFieldValues($data); - $runner->setVariables($data); - $value = $runner->evaluate(); - } catch (Math_Formula_Exception $e) { - throw new Search_Action_Exception(tr('Error applying tracker_item_modify calc formula to item %0: %1', $object_id, $e->getMessage())); - } + if (! $info) { + throw new Search_Action_Exception(tr('Tracker item %0 not found.', $object_id)); } + $definition = Tracker_Definition::get($info['trackerId']); $fieldInfo = $definition->getField($field); - $info[$fieldInfo['fieldId']] = $value; $handler = $definition->getFieldFactory()->getHandler($fieldInfo, $info); - - if (! empty($add)) { + // Ref: https://doc.tiki.org/PluginListExecute#tracker_item_modify + if ($add) { $value = $handler->addValue($add); - } - - if (! empty($remove)) { + } elseif ($remove) { $value = $handler->removeValue($remove); - } - - if (empty($add) && empty($remove)) { - if (is_scalar($value)) { - $value = ['ins_' . $fieldInfo['fieldId'] => $value]; + } elseif (in_array($method, ['add', 'remove'], true)) { + $value = $this->normalizeAssignedValue($handler, $value, $fieldInfo['fieldId']); + $values = explode(',', $value); + $computed = $handler->getValue(); + + foreach ($values as $val) { + $currentInfo = $info; + $currentInfo[$fieldInfo['fieldId']] = $computed; + $currentHandler = $definition->getFieldFactory()->getHandler($fieldInfo, $currentInfo); + $computed = $method === 'add' + ? $currentHandler->addValue($val) + : $currentHandler->removeValue($val); } - $data = $handler->getFieldData($value); - $value = $data['value']; - - switch ($method) { - case 'add': - $values = explode(',', $value); - $value = ''; - foreach ($values as $val) { - $value .= $handler->addValue($val) . ','; - } - $values = explode(',', $value); - $value = implode(',', array_unique(array_filter($values))); - break; - case 'remove': - $values = explode(',', $value); - $value = ''; - foreach ($values as $val) { - // FIXME only the last value gets removed - $value = $handler->removeValue($val) . ','; + $value = $computed; + } else { + if (! empty($calc)) { + $runner = new Math_Formula_Runner( + [ + 'Math_Formula_Function_' => '', + 'Tiki_Formula_Function_' => '', + ] + ); + try { + $runner->setFormula($calc); + $data = ['itemId' => $object_id]; + foreach ($runner->inspect() as $fieldName) { + if (is_string($fieldName) || is_numeric($fieldName)) { + $tField = $definition->getField($fieldName); + if ($tField && isset($info[$tField['fieldId']])) { + $data[$fieldName] = $info[$tField['fieldId']]; + } + } } - $values = explode(',', $value); - $value = implode(',', array_unique(array_filter($values))); - break; + $item = Tracker_Item::fromInfo($info); + $item->prepareFieldValues($data); + $runner->setVariables($data); + $value = $runner->evaluate(); + } catch (Math_Formula_Exception $e) { + throw new Search_Action_Exception(tr('Error applying tracker_item_modify calc formula to item %0: %1', $object_id, $e->getMessage())); + } } + $value = $this->normalizeAssignedValue($handler, $value, $fieldInfo['fieldId']); } $utilities = new Services_Tracker_Utilities(); @@ -233,4 +220,14 @@ class Search_Action_TrackerItemModify implements Search_Action_Action { return str_replace(['~np~', '~/np~'], '', $value); } + + private function normalizeAssignedValue($handler, $value, int $fieldId): string + { + if (is_scalar($value)) { + $value = ['ins_' . $fieldId => $value]; + } + + $data = $handler->getFieldData($value); + return $data['value']; + } } ===================================== lib/core/Tracker/Field/Dropdown.php ===================================== @@ -487,7 +487,13 @@ class Tracker_Field_Dropdown extends \Tracker\Field\AbstractItemField implements { if ($this->getConfiguration('type') !== 'D') { $value = $this->getValue($this->getDefaultValue()); - $allValues = $value === '' ? [] : explode(',', $value); + $allValues = $value === '' ? [] + : array_filter( + array_map('trim', explode(',', $value)), + static function ($val) { + return $val !== ''; + } + ); if (! empty($allValues)) { foreach ($allValues as $val) { ===================================== lib/test/IntegrationTests/TrackerDropdownEmptyValueUpdateTest.php ===================================== @@ -0,0 +1,151 @@ +<?php + +// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project +// +// All Rights Reserved. See copyright.txt for details and a complete list of authors. +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. + +namespace Tiki\Lib\Test\IntegrationTests; + +use Services_Tracker_Utilities; +use TikiLib; +use TikiTestCase; +use Tracker_Definition; + +/** + * @group integration + */ +class TrackerDropdownEmptyValueUpdateTest extends TikiTestCase +{ + protected static $trklib; + protected static $trackerId; + protected static $oldPrefs; + + public static function setUpBeforeClass(): void + { + global $prefs; + + self::$oldPrefs = $prefs; + $prefs['feature_trackers'] = 'y'; + + parent::setUpBeforeClass(); + self::$trklib = TikiLib::lib('trk'); + self::$trackerId = self::$trklib->replace_tracker(null, 'Dropdown Empty Value Test ' . uniqid(), '', [], 'n'); + + $fields = [ + [ + 'name' => 'Title', + 'type' => 't', + 'isHidden' => 'n', + 'isMandatory' => 'y', + 'permName' => 'test_title', + ], + [ + 'name' => 'Statuses', + 'type' => 'M', + 'isHidden' => 'n', + 'isMandatory' => 'n', + 'permName' => 'test_statuses', + 'options' => json_encode([ + 'options' => ['=Default', '1=Active', '2=Delayed', '3=Closed'], + ]), + ], + ]; + + foreach ($fields as $i => $field) { + self::$trklib->replace_tracker_field( + self::$trackerId, + 0, + $field['name'], + $field['type'], + 'y', + 'y', + 'y', + 'y', + $field['isHidden'], + $field['isMandatory'], + ($i + 1) * 10, + $field['options'] ?? '', + '', + '', + null, + '', + null, + null, + 'n', + '', + '', + '', + $field['permName'] + ); + } + } + + public static function tearDownAfterClass(): void + { + global $prefs; + + if (! empty(self::$trackerId)) { + self::$trklib->remove_tracker(self::$trackerId); + } + + $prefs = self::$oldPrefs; + parent::tearDownAfterClass(); + } + + public function testUpdateItemAllowsSavingValidEmptyDropdownValue(): void + { + $itemId = $this->createItem('1'); + $definition = Tracker_Definition::get(self::$trackerId); + $fieldInfo = $definition->getFieldFromPermName('test_statuses'); + $itemInfo = self::$trklib->get_tracker_item($itemId); + $handler = $definition->getFieldFactory()->getHandler($fieldInfo, $itemInfo); + + $fieldData = $handler->getFieldData([ + 'ins_' . $fieldInfo['fieldId'] => ['', '1', '2'], + ]); + + $this->assertSame(',1,2', $fieldData['value']); + $this->assertSame(['', '1', '2'], $fieldData['selected']); + + $utilities = new Services_Tracker_Utilities(); + $result = $utilities->updateItem( + $definition, + [ + 'itemId' => $itemId, + 'status' => 'o', + 'fields' => [ + 'test_statuses' => $fieldData['value'], + ], + 'validate' => true, + 'notify_watchers' => null, + ] + ); + + $this->assertNotFalse($result); + + $updatedItem = self::$trklib->get_tracker_item($itemId); + $this->assertSame(',1,2', $updatedItem[$fieldInfo['fieldId']]); + + $updatedHandler = $definition->getFieldFactory()->getHandler($fieldInfo, $updatedItem); + $this->assertTrue($updatedHandler->isValid() === true); + } + + private function createItem(string $statusesValue): int + { + $definition = Tracker_Definition::get(self::$trackerId); + $fields = $definition->getFields(); + + foreach ($fields as &$field) { + if ($field['permName'] === 'test_title') { + $field['value'] = 'Test item'; + } + + if ($field['permName'] === 'test_statuses') { + $field['value'] = $statusesValue; + } + } + + return self::$trklib->replace_item(self::$trackerId, 0, ['data' => $fields], 'o'); + } +} View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/461405a3b49710aaac196172c4b305a3bcdfbd08 -- View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/461405a3b49710aaac196172c4b305a3bcdfbd08 You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help _______________________________________________ TikiWiki-cvs mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs