diff --git a/administrator/components/com_fields/src/Helper/FieldsHelper.php b/administrator/components/com_fields/src/Helper/FieldsHelper.php index 7193de45bf694..833543765ce39 100644 --- a/administrator/components/com_fields/src/Helper/FieldsHelper.php +++ b/administrator/components/com_fields/src/Helper/FieldsHelper.php @@ -716,4 +716,123 @@ public static function clearFieldsCache() self::$fieldCache = null; self::$fieldsCache = null; } + + /** + * Checks the showon attribute and determines if any field matches the condition or not + * + * @param string $showOn the showon attribute value + * @param array $fields the list of custom fields present in the form + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + public static function matchShowon($showOn, $fields) + { + $isAMatch = false; + + // Separate the conditions if there is a [OR] value + $orConditions = explode('[OR]', $showOn); + + foreach ($orConditions as $orCondition) { + // Separate all [AND] conditions + $andConditions = explode('[AND]', $orCondition); + + // All AND conditions must be met for the field to show + $allAndConditionsAreMet = true; + + foreach ($andConditions as $andCondition) { + $condition = explode(':', $andCondition, 2); + + // Prevent bad entries (values such as field:1:3 are allowed) + if (count($condition) < 2) { + break; + } + + $fieldName = $condition[0]; + $fieldValues = $condition[1]; + + // The field name can contain ! in the end, 'does not equal' + $notEqual = false; + if (strpos($fieldName, '!') !== false) { + $fieldName = rtrim($fieldName, '!'); + $notEqual = true; + } + + // Check if a match can be found between values set and the showon condition + $foundMatch = self::matchFieldValues($fieldName, explode(',', $fieldValues), $fields); + + if (!$notEqual) { + // The possible values to have + // field:3,7 => we can have a value of 3 or 7 + + if (!$foundMatch) { + // Did not find a value the field should match, no need to continue looking in the [AND] + $allAndConditionsAreMet = false; + break; + } + } else { + // The possible values NOT to have + // field!:3,7 => we cannot have a value of 3 nor a value of 7 + // field!: => we cannot have an empty value + + if ($foundMatch) { + // Found a value the field should not have, no need to continue looking in the [AND] + $allAndConditionsAreMet = false; + break; + } + } + } + + if ($allAndConditionsAreMet) { + // One condition is met, no need to continue looking in [OR] + $isAMatch = true; + break; + } + } + + return $isAMatch; + } + + /** + * Checks if any of a field's values match a certain set of values + * + * @param string $fieldName the field name + * @param array $valuesToMatch an array of values to match + * @param array $fields a list of prepared custom fields + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + public static function matchFieldValues($fieldName, $valuesToMatch, $fields) + { + $foundMatch = false; + + foreach ($valuesToMatch as $valueToMatch) { + foreach ($fields as $field) { + if ($field->name === $fieldName) { + if (is_array($field->rawvalue)) { + foreach ($field->rawvalue as $rawValue) { + if ($rawValue === $valueToMatch) { + $foundMatch = true; + break; + } + } + } elseif ($field->rawvalue === $valueToMatch) { + $foundMatch = true; + } + + break; + } + } + + if ($foundMatch) { + // Found a match, no need to keep going + break; + } + } + + return $foundMatch; + } } diff --git a/components/com_contact/layouts/fields/render.php b/components/com_contact/layouts/fields/render.php index a5c963ff5e05b..ccc799f87ac40 100644 --- a/components/com_contact/layouts/fields/render.php +++ b/components/com_contact/layouts/fields/render.php @@ -52,6 +52,12 @@ continue; } + // Check conditions on fields + $showOn = $field->params->get('showon', ''); + if (!empty($showOn) && !FieldsHelper::matchShowon($showOn, $fields)) { + continue; + } + $layout = $field->params->get('layout', 'render'); echo FieldsHelper::render($context, 'field.' . $layout, ['field' => $field]); } diff --git a/libraries/src/Form/FormHelper.php b/libraries/src/Form/FormHelper.php index 26ed8fddaf990..dd5f200c751d7 100644 --- a/libraries/src/Form/FormHelper.php +++ b/libraries/src/Form/FormHelper.php @@ -512,6 +512,10 @@ public static function parseShowOnConditions($showOn, $formControl = null, $grou $compareEqual = strpos($showOnPart, '!:') === false; $showOnPartBlocks = explode(($compareEqual ? ':' : '!:'), $showOnPart, 2); + if (count($showOnPartBlocks) !== 2) { + continue; + } + $dotPos = strpos($showOnPartBlocks[0], '.'); if ($dotPos === false) { diff --git a/plugins/fields/subform/src/Extension/Subform.php b/plugins/fields/subform/src/Extension/Subform.php index b90ccdf603b32..7a1fd1416dfae 100644 --- a/plugins/fields/subform/src/Extension/Subform.php +++ b/plugins/fields/subform/src/Extension/Subform.php @@ -317,6 +317,73 @@ public function onCustomFieldsPrepareDom($field, \DOMElement $parent, Form $form return $parent_field; } + /** + * Modify the raw value of a field before saving it + * + * @param \stdClass $field The field + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function onCustomFieldsBeforeSave($field) + { + // Check if the field should be processed by us + if (!$this->isTypeSupported($field->type)) { + return; + } + + if (empty($field->rawvalue)) { + return; + } + + if (is_array($field->rawvalue)) { + return; + } + + $decoded_value = json_decode($field->rawvalue, true); + + if (!$decoded_value || !is_array($decoded_value)) { + return; + } + + $subform_rows = []; + + // Iterate over each row of the data + foreach ($decoded_value as $key => $row) { + $row_subfields = []; + + // For each row, iterate over all the subfields + foreach ($this->getSubfieldsFromField($field) as $subfield) { + foreach ($row as $fieldId => $rawValue) { + if ($subfield->id === (int)str_replace('field', '', $fieldId)) { + $subfield->rawvalue = $rawValue; + $row_subfields[$subfield->fieldname] = $subfield; + } + } + } + + $subform_rows[$key] = []; + + // Check conditions on fields (through the showon attribute) + foreach ($row_subfields as $subfield) { + $showOn = $subfield->params->get('showon', ''); + + $subform_rows[$key]['field' . $subfield->id] = $subfield->rawvalue; + + if (empty($showOn)) { + continue; + } + + if (!FieldsHelper::matchShowon($showOn, $row_subfields)) { + unset($subform_rows[$key]['field' . $subfield->id]); + } + } + } + + $field->rawvalue = json_encode($subform_rows); + } + /** * Returns an array of all options configured for this field. * diff --git a/plugins/system/fields/src/Extension/Fields.php b/plugins/system/fields/src/Extension/Fields.php index 957674b431f73..0e0e899c4377f 100644 --- a/plugins/system/fields/src/Extension/Fields.php +++ b/plugins/system/fields/src/Extension/Fields.php @@ -12,6 +12,7 @@ use Joomla\CMS\Event\Content; use Joomla\CMS\Event\Model; +use Joomla\CMS\Factory; use Joomla\CMS\Language\Multilanguage; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\User\UserFactoryAwareTrait; @@ -137,6 +138,14 @@ public function onContentAfterSave($context, $item, $isNew, $data = []): void // Loop over the fields foreach ($fields as $field) { + // Empty fields that are hidden so that their values will be removed if they exist + $showOn = $field->params->get('showon', ''); + if (!empty($showOn) && !FieldsHelper::matchShowon($showOn, $fields)) { + // Remove value from database + $model->setFieldValue($field->id, $item->id, null); + continue; + } + // Determine the value if it is (un)available from the data if (array_key_exists($field->name, $data['com_fields'])) { $value = $data['com_fields'][$field->name] === false ? null : $data['com_fields'][$field->name]; @@ -155,8 +164,11 @@ public function onContentAfterSave($context, $item, $isNew, $data = []): void $value = json_encode($value); } + $field->rawvalue = $value; + Factory::getApplication()->triggerEvent('onCustomFieldsBeforeSave', [&$field]); + // Setting the value for the field and the item - $model->setFieldValue($field->id, $item->id, $value); + $model->setFieldValue($field->id, $item->id, $field->rawvalue); } }