/** * Calculate and return the current type value of a record * * @param string $table The table name. MUST be in $GLOBALS['TCA'] * @param array $row The row from the table, should contain at least the "type" field, if applicable. * @return string Return the "type" value for this record, ready to pick a "types" configuration from the $GLOBALS['TCA'] array. * @throws \RuntimeException */ protected function getRecordTypeValue($table, array $row) { $typeNum = 0; $field = $GLOBALS['TCA'][$table]['ctrl']['type']; if ($field) { if (strpos($field, ':') !== FALSE) { list($pointerField, $foreignTypeField) = explode(':', $field); $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$pointerField]['config']; $relationType = $fieldConfig['type']; if ($relationType === 'select') { $foreignUid = $row[$pointerField]; $foreignTable = $fieldConfig['foreign_table']; } elseif ($relationType === 'group') { $values = FormEngineUtility::extractValuesOnlyFromValueLabelList($row[$pointerField]); list(, $foreignUid) = GeneralUtility::revExplode('_', $values[0], 2); $allowedTables = explode(',', $fieldConfig['allowed']); // Always take the first configured table. $foreignTable = $allowedTables[0]; } else { throw new \RuntimeException('TCA Foreign field pointer fields are only allowed to be used with group or select field types.', 1325861239); } if ($foreignUid) { $foreignRow = BackendUtility::getRecord($foreignTable, $foreignUid, $foreignTypeField); $this->registerDefaultLanguageData($foreignTable, $foreignRow); if ($foreignRow[$foreignTypeField]) { $foreignTypeFieldConfig = $GLOBALS['TCA'][$table]['columns'][$field]; $typeNum = $this->overrideTypeWithValueFromDefaultLanguageRecord($foreignTable, $foreignRow, $foreignTypeField, $foreignTypeFieldConfig); } } } else { $typeFieldConfig = $GLOBALS['TCA'][$table]['columns'][$field]; $typeNum = $this->overrideTypeWithValueFromDefaultLanguageRecord($table, $row, $field, $typeFieldConfig); } } if (empty($typeNum)) { // If that value is an empty string, set it to "0" (zero) $typeNum = 0; } // If current typeNum doesn't exist, set it to 0 (or to 1 for historical reasons, if 0 doesn't exist) if (!$GLOBALS['TCA'][$table]['types'][$typeNum]) { $typeNum = $GLOBALS['TCA'][$table]['types']['0'] ? 0 : 1; } // Force to string. Necessary for eg '-1' to be recognized as a type value. return (string)$typeNum; }
/** * Creates a checkbox list (renderMode = "checkbox") * * @param string $table See getSingleField_typeSelect() * @param string $field See getSingleField_typeSelect() * @param array $row See getSingleField_typeSelect() * @param array $parameterArray See getSingleField_typeSelect() * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience) * @param array $selItems Items available for selection * @param string $noMatchingLabel Label for no-matching-value * @return string The HTML code for the item */ protected function getSingleField_typeSelect_checkbox($table, $field, $row, $parameterArray, $config, $selItems, $noMatchingLabel) { if (empty($selItems)) { return ''; } // Get values in an array (and make unique, which is fine because there can be no duplicates anyway): $itemArray = array_flip(FormEngineUtility::extractValuesOnlyFromValueLabelList($parameterArray['itemFormElValue'])); $output = ''; // Disabled $disabled = 0; if ($this->isGlobalReadonly() || $config['readOnly']) { $disabled = 1; } // Traverse the Array of selector box items: $groups = array(); $currentGroup = 0; $c = 0; $sOnChange = ''; if (!$disabled) { $sOnChange = implode('', $parameterArray['fieldChangeFunc']); // Used to accumulate the JS needed to restore the original selection. foreach ($selItems as $p) { // Non-selectable element: if ($p[1] === '--div--') { $selIcon = ''; if (isset($p[2]) && $p[2] != 'empty-empty') { $selIcon = FormEngineUtility::getIconHtml($p[2]); } $currentGroup++; $groups[$currentGroup]['header'] = array('icon' => $selIcon, 'title' => htmlspecialchars($p[0])); } else { // Check if some help text is available // Since TYPO3 4.5 help text is expected to be an associative array // with two key, "title" and "description" // For the sake of backwards compatibility, we test if the help text // is a string and use it as a description (this could happen if items // are modified with an itemProcFunc) $hasHelp = FALSE; $help = ''; $helpArray = array(); if (!empty($p[3])) { $hasHelp = TRUE; if (is_array($p[3])) { $helpArray = $p[3]; } else { $helpArray['description'] = $p[3]; } } if ($hasHelp) { $help = BackendUtility::wrapInHelp('', '', '', $helpArray); } // Selected or not by default: $checked = 0; if (isset($itemArray[$p[1]])) { $checked = 1; unset($itemArray[$p[1]]); } // Build item array $groups[$currentGroup]['items'][] = array('id' => str_replace('.', '', uniqid('select_checkbox_row_', TRUE)), 'name' => $parameterArray['itemFormElName'] . '[' . $c . ']', 'value' => $p[1], 'checked' => $checked, 'disabled' => $disabled, 'class' => '', 'icon' => !empty($p[2]) ? FormEngineUtility::getIconHtml($p[2]) : IconUtility::getSpriteIcon('empty-empty'), 'title' => htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', FALSE), 'help' => $help); $c++; } } } // Remaining values (invalid): if (!empty($itemArray) && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) { $currentGroup++; foreach ($itemArray as $theNoMatchValue => $temp) { // Build item array $groups[$currentGroup]['items'][] = array('id' => str_replace('.', '', uniqid('select_checkbox_row_', TRUE)), 'name' => $parameterArray['itemFormElName'] . '[' . $c . ']', 'value' => $theNoMatchValue, 'checked' => 1, 'disabled' => $disabled, 'class' => 'danger', 'icon' => '', 'title' => htmlspecialchars(@sprintf($noMatchingLabel, $theNoMatchValue), ENT_COMPAT, 'UTF-8', FALSE), 'help' => ''); $c++; } } // Add an empty hidden field which will send a blank value if all items are unselected. $output .= '<input type="hidden" class="select-checkbox" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="" />'; // Building the checkboxes foreach ($groups as $groupKey => $group) { $groupId = htmlspecialchars($parameterArray['itemFormElID']) . '-group-' . $groupKey; $output .= '<div class="panel panel-default">'; if (is_array($group['header'])) { $output .= ' <div class="panel-heading"> <a data-toggle="collapse" href="#' . $groupId . '" aria-expanded="true" aria-controls="' . $groupId . '"> ' . $group['header']['icon'] . ' ' . $group['header']['title'] . ' </a> </div> '; } if (is_array($group['items']) && !empty($group['items'])) { $tableRows = ''; $checkGroup = array(); $uncheckGroup = array(); $resetGroup = array(); // Render rows foreach ($group['items'] as $item) { $tableRows .= ' <tr class="' . $item['class'] . '"> <td class="col-checkbox"> <input type="checkbox" id="' . $item['id'] . '" name="' . htmlspecialchars($item['name']) . '" value="' . htmlspecialchars($item['value']) . '" onclick="' . htmlspecialchars($sOnChange) . '" ' . ($item['checked'] ? ' checked=checked' : '') . ' ' . ($item['disabled'] ? ' disabled=disabled' : '') . ' ' . $parameterArray['onFocus'] . ' /> </td> <td class="col-icon"> <label class="label-block" for="' . $item['id'] . '">' . $item['icon'] . '</label> </td> <td class="col-title"> <label class="label-block" for="' . $item['id'] . '">' . $item['title'] . '</label> </td> <td>' . $item['help'] . '</td> </tr> '; $checkGroup[] = 'document.editform[' . GeneralUtility::quoteJSvalue($item['name']) . '].checked=1;'; $uncheckGroup[] = 'document.editform[' . GeneralUtility::quoteJSvalue($item['name']) . '].checked=0;'; $resetGroup[] = 'document.editform[' . GeneralUtility::quoteJSvalue($item['name']) . '].checked=' . $item['checked'] . ';'; } // Build toggle group checkbox $toggleGroupCheckbox = ''; if (!empty($resetGroup)) { $toggleGroupCheckbox = ' <input type="checkbox" class="checkbox" onclick="if (checked) {' . htmlspecialchars(implode('', $checkGroup) . '} else {' . implode('', $uncheckGroup)) . '}"> '; } // Build reset group button $resetGroupBtn = ''; if (!empty($resetGroup)) { $resetGroupBtn = ' <a href="#" class="btn btn-default" onclick="' . implode('', $resetGroup) . ' return false;' . '"> ' . IconUtility::getSpriteIcon('actions-edit-undo', array('title' => htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.revertSelection')))) . ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.revertSelection') . ' </a> '; } $output .= ' <div id="' . $groupId . '" class="panel-collapse collapse in" role="tabpanel"> <div class="table-fit"> <table class="table table-transparent table-hover"> <thead> <tr> <th class="col-checkbox">' . $toggleGroupCheckbox . '</th> <th class="col-icon"></th> <th class="text-right" colspan="2">' . $resetGroupBtn . '</th> </tr> </thead> <tbody>' . $tableRows . '</tbody> </table> </div> </div> '; } $output .= '</div>'; } return $output; }
/** * Creates a selectorbox list (renderMode = "singlebox") * * @param string $table See getSingleField_typeSelect() * @param string $field See getSingleField_typeSelect() * @param array $row See getSingleField_typeSelect() * @param array $parameterArray See getSingleField_typeSelect() * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience) * @param array $selItems Items available for selection * @param string $noMatchingLabel Label for no-matching-value * @return string The HTML code for the item */ protected function getSingleField_typeSelect_singlebox($table, $field, $row, $parameterArray, $config, $selItems, $noMatchingLabel) { $languageService = $this->getLanguageService(); // Get values in an array (and make unique, which is fine because there can be no duplicates anyway): $itemArray = array_flip(FormEngineUtility::extractValuesOnlyFromValueLabelList($parameterArray['itemFormElValue'])); $item = ''; $disabled = ''; if ($this->isGlobalReadonly() || $config['readOnly']) { $disabled = ' disabled="disabled"'; } // Traverse the Array of selector box items: $opt = array(); // Used to accumulate the JS needed to restore the original selection. $restoreCmd = array(); $c = 0; foreach ($selItems as $p) { // Selected or not by default: $sM = ''; if (isset($itemArray[$p[1]])) { $sM = ' selected="selected"'; $restoreCmd[] = 'document.editform[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'] . '[]') . '].options[' . $c . '].selected=1;'; unset($itemArray[$p[1]]); } // Non-selectable element: $nonSel = ''; if ((string) $p[1] === '--div--') { $nonSel = ' onclick="this.selected=0;" class="formcontrol-select-divider"'; } // Icon style for option tag: $styleAttrValue = ''; if ($config['iconsInOptionTags']) { $styleAttrValue = FormEngineUtility::optionTagStyle($p[2]); } // Compile <option> tag: $opt[] = '<option value="' . htmlspecialchars($p[1]) . '"' . $sM . $nonSel . ($styleAttrValue ? ' style="' . htmlspecialchars($styleAttrValue) . '"' : '') . '>' . htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', FALSE) . '</option>'; $c++; } // Remaining values: if (!empty($itemArray) && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) { foreach ($itemArray as $theNoMatchValue => $temp) { // Compile <option> tag: array_unshift($opt, '<option value="' . htmlspecialchars($theNoMatchValue) . '" selected="selected">' . htmlspecialchars(@sprintf($noMatchingLabel, $theNoMatchValue), ENT_COMPAT, 'UTF-8', FALSE) . '</option>'); } } // Compile selector box: $sOnChange = implode('', $parameterArray['fieldChangeFunc']); $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="' . htmlspecialchars($config['itemListStyle']) . '"' : ''; $size = (int) $config['size']; $cssPrefix = $size === 1 ? 'tceforms-select' : 'tceforms-multiselect'; $size = $config['autoSizeMax'] ? MathUtility::forceIntegerInRange(count($selItems) + 1, MathUtility::forceIntegerInRange($size, 1), $config['autoSizeMax']) : $size; $selectBox = '<select id="' . str_replace('.', '', uniqid($cssPrefix, TRUE)) . '" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '[]" ' . 'class="form-control ' . $cssPrefix . '"' . ($size ? ' size="' . $size . '" ' : '') . ' multiple="multiple" onchange="' . htmlspecialchars($sOnChange) . '"' . $parameterArray['onFocus'] . ' ' . $this->getValidationDataAsDataAttribute($config) . $selector_itemListStyle . $disabled . '> ' . implode(' ', $opt) . ' </select>'; // Add an empty hidden field which will send a blank value if all items are unselected. if (!$disabled) { $item .= '<input type="hidden" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="" />'; } // Put it all into a table: $onClick = htmlspecialchars('document.editform[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'] . '[]') . '].selectedIndex=-1;' . implode('', $restoreCmd) . ' return false;'); $width = $this->formMaxWidth($this->defaultInputWidth); $item .= ' <div class="form-control-wrap" ' . ($width ? ' style="max-width: ' . $width . 'px"' : '') . '> <div class="form-wizards-wrap form-wizards-aside"> <div class="form-wizards-element"> ' . $selectBox . ' </div> <div class="form-wizards-items"> <a href="#" class="btn btn-default" onclick="' . $onClick . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.revertSelection')) . '">' . IconUtility::getSpriteIcon('actions-edit-undo') . '</a> </div> </div> </div> <p> <em>' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.holdDownCTRL')) . '</em> </p> '; return $item; }