/**
  * Render tree widget
  *
  * @return array As defined in initializeResultArray() of AbstractNode
  */
 public function render()
 {
     $table = $this->globalOptions['table'];
     $field = $this->globalOptions['fieldName'];
     $row = $this->globalOptions['databaseRow'];
     $parameterArray = $this->globalOptions['parameterArray'];
     // Field configuration from TCA:
     $config = $parameterArray['fieldConf']['config'];
     $disabled = '';
     if ($this->isGlobalReadonly() || $config['readOnly']) {
         $disabled = ' disabled="disabled"';
     }
     $resultArray = $this->initializeResultArray();
     // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist.
     $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
     $selItems = FormEngineUtility::getSelectItems($table, $field, $row, $parameterArray);
     $html = $this->renderField($table, $field, $row, $parameterArray, $config, $selItems);
     // Wizards:
     if (!$disabled) {
         $altItem = '<input type="hidden" name="' . $parameterArray['itemFormElName'] . '" value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />';
         $html = $this->renderWizards(array($html, $altItem), $config['wizards'], $table, $row, $field, $parameterArray, $parameterArray['itemFormElName'], $specConf);
     }
     $resultArray['html'] = $html;
     return $resultArray;
 }
 /**
  * @test
  */
 public function databaseRowCompatibilityImplodesSelectArrayWithValuesAtSecondPosition()
 {
     $input = ['uid' => 42, 'simpleArray' => [0 => [0 => 'aLabel', 1 => 'aValue'], 1 => [0 => 'anotherLabel', 1 => 'anotherValue']]];
     $expected = $input;
     $expected['simpleArray'] = 'aValue,anotherValue';
     $this->assertEquals($expected, FormEngineUtility::databaseRowCompatibility($input));
 }
Beispiel #3
0
 /**
  * This will render a series of radio buttons.
  *
  * @return array As defined in initializeResultArray() of AbstractNode
  */
 public function render()
 {
     $parameterArray = $this->globalOptions['parameterArray'];
     $config = $parameterArray['fieldConf']['config'];
     $html = '';
     $disabled = '';
     if ($this->isGlobalReadonly() || $config['readOnly']) {
         $disabled = ' disabled';
     }
     // Get items for the array
     $selectedItems = FormEngineUtility::initItemArray($parameterArray['fieldConf']);
     if ($config['itemsProcFunc']) {
         $dataPreprocessor = GeneralUtility::makeInstance(DataPreprocessor::class);
         $selectedItems = $dataPreprocessor->procItems($selectedItems, $parameterArray['fieldTSConfig']['itemsProcFunc.'], $config, $this->globalOptions['table'], $this->globalOptions['databaseRow'], $this->globalOptions['fieldName']);
     }
     // Traverse the items, making the form elements
     foreach ($selectedItems as $radioButton => $selectedItem) {
         if (isset($parameterArray['fieldTSConfig']['altLabels.'][$radioButton])) {
             $label = $this->getLanguageService()->sL($parameterArray['fieldTSConfig']['altLabels.'][$radioButton]);
         } else {
             $label = $selectedItem[0];
         }
         $radioId = htmlspecialchars($parameterArray['itemFormElID'] . '_' . $radioButton);
         $radioOnClick = implode('', $parameterArray['fieldChangeFunc']);
         $radioChecked = (string) $selectedItem[1] === (string) $parameterArray['itemFormElValue'] ? ' checked="checked"' : '';
         $html .= '<div class="radio' . $disabled . '">' . '<label for="' . $radioId . '">' . '<input ' . 'type="radio" ' . 'name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" ' . 'id="' . $radioId . '" ' . 'value="' . htmlspecialchars($selectedItem[1]) . '" ' . $radioChecked . ' ' . $parameterArray['onFocus'] . ' ' . $disabled . ' ' . 'onclick="' . htmlspecialchars($radioOnClick) . '" ' . '/>' . htmlspecialchars($label) . '</label>' . '</div>';
     }
     $resultArray = $this->initializeResultArray();
     $resultArray['html'] = $html;
     return $resultArray;
 }
Beispiel #4
0
	/**
	 * 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;
	}
 /**
  * Convert the DOM object-id of an inline container to an array.
  * The object-id could look like 'data-parentPageId-tx_mmftest_company-1-employees'.
  * This initializes $this->inlineStructure - used by AJAX entry points
  * There are two keys:
  * - 'stable': Containing full qualified identifiers (table, uid and field)
  * - 'unstable': Containing partly filled data (e.g. only table and possibly field)
  *
  * @param string $domObjectId The DOM object-id
  * @param bool $loadConfig Load the TCA configuration for that level (default: TRUE)
  * @return void
  */
 public function initializeByParsingDomObjectIdString($domObjectId, $loadConfig = TRUE)
 {
     $unstable = array();
     $vector = array('table', 'uid', 'field');
     // Substitute FlexForm addition and make parsing a bit easier
     $domObjectId = str_replace('---', ':', $domObjectId);
     // The starting pattern of an object identifier (e.g. "data-<firstPidValue>-<anything>)
     $pattern = '/^data' . '-' . '(.+?)' . '-' . '(.+)$/';
     if (preg_match($pattern, $domObjectId, $match)) {
         $inlineFirstPid = $match[1];
         $parts = explode('-', $match[2]);
         $partsCnt = count($parts);
         for ($i = 0; $i < $partsCnt; $i++) {
             if ($i > 0 && $i % 3 == 0) {
                 // Load the TCA configuration of the table field and store it in the stack
                 if ($loadConfig) {
                     $unstable['config'] = $GLOBALS['TCA'][$unstable['table']]['columns'][$unstable['field']]['config'];
                     // Fetch TSconfig:
                     $TSconfig = FormEngineUtility::getTSconfigForTableRow($unstable['table'], array('uid' => $unstable['uid'], 'pid' => $inlineFirstPid), $unstable['field']);
                     // Override TCA field config by TSconfig:
                     if (!$TSconfig['disabled']) {
                         $unstable['config'] = FormEngineUtility::overrideFieldConf($unstable['config'], $TSconfig);
                     }
                     $unstable['localizationMode'] = BackendUtility::getInlineLocalizationMode($unstable['table'], $unstable['config']);
                 }
                 // Extract FlexForm from field part (if any)
                 if (strpos($unstable['field'], ':') !== FALSE) {
                     $fieldParts = GeneralUtility::trimExplode(':', $unstable['field']);
                     $unstable['field'] = array_shift($fieldParts);
                     // FlexForm parts start with data:
                     if (count($fieldParts) > 0 && $fieldParts[0] === 'data') {
                         $unstable['flexform'] = $fieldParts;
                     }
                 }
                 $this->inlineStructure['stable'][] = $unstable;
                 $unstable = array();
             }
             $unstable[$vector[$i % 3]] = $parts[$i];
         }
         if (count($unstable)) {
             $this->inlineStructure['unstable'] = $unstable;
         }
     }
 }
 /**
  * Entry method
  *
  * @return array As defined in initializeResultArray() of AbstractNode
  */
 public function render()
 {
     $table = $this->globalOptions['table'];
     $row = $this->globalOptions['databaseRow'];
     $fieldName = $this->globalOptions['fieldName'];
     $flexFormDataStructureArray = $this->globalOptions['flexFormDataStructureArray'];
     $flexFormRowData = $this->globalOptions['flexFormRowData'];
     $flexFormCurrentLanguage = $this->globalOptions['flexFormCurrentLanguage'];
     $flexFormNoEditDefaultLanguage = $this->globalOptions['flexFormNoEditDefaultLanguage'];
     $flexFormFormPrefix = $this->globalOptions['flexFormFormPrefix'];
     $parameterArray = $this->globalOptions['parameterArray'];
     $languageService = $this->getLanguageService();
     $resultArray = $this->initializeResultArray();
     foreach ($flexFormDataStructureArray as $flexFormFieldName => $flexFormFieldArray) {
         if (!is_array($flexFormFieldArray) || !isset($flexFormFieldArray['type']) && !is_array($flexFormFieldArray['TCEforms']['config'])) {
             continue;
         }
         if ($flexFormFieldArray['type'] === 'array') {
             // Section
             if (empty($flexFormFieldArray['section'])) {
                 $resultArray['html'] = LF . 'Section expected at ' . $flexFormFieldName . ' but not found';
                 continue;
             }
             $sectionTitle = '';
             if (!empty($flexFormFieldArray['title'])) {
                 $sectionTitle = $languageService->sL($flexFormFieldArray['title']);
             }
             $options = $this->globalOptions;
             $options['flexFormDataStructureArray'] = $flexFormFieldArray['el'];
             $options['flexFormRowData'] = is_array($flexFormRowData[$flexFormFieldName]['el']) ? $flexFormRowData[$flexFormFieldName]['el'] : array();
             $options['flexFormSectionType'] = $flexFormFieldName;
             $options['flexFormSectionTitle'] = $sectionTitle;
             $options['renderType'] = 'flexFormSectionContainer';
             /** @var NodeFactory $nodeFactory */
             $nodeFactory = $this->globalOptions['nodeFactory'];
             $sectionContainerResult = $nodeFactory->create($options)->render();
             $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $sectionContainerResult);
         } else {
             // Single element
             $vDEFkey = 'vDEF';
             $displayConditionResult = TRUE;
             if (!empty($flexFormFieldArray['TCEforms']['displayCond'])) {
                 $conditionData = is_array($flexFormRowData) ? $flexFormRowData : array();
                 $conditionData['parentRec'] = $row;
                 /** @var $elementConditionMatcher ElementConditionMatcher */
                 $elementConditionMatcher = GeneralUtility::makeInstance(ElementConditionMatcher::class);
                 $displayConditionResult = $elementConditionMatcher->match($flexFormFieldArray['TCEforms']['displayCond'], $conditionData, $vDEFkey);
             }
             if (!$displayConditionResult) {
                 continue;
             }
             // On-the-fly migration for flex form "TCA"
             // @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. This can be removed *if* no additional TCA migration is added with CMS 8, see class TcaMigration
             $dummyTca = array('dummyTable' => array('columns' => array('dummyField' => $flexFormFieldArray['TCEforms'])));
             $tcaMigration = GeneralUtility::makeInstance(TcaMigration::class);
             $migratedTca = $tcaMigration->migrate($dummyTca);
             $messages = $tcaMigration->getMessages();
             if (!empty($messages)) {
                 $context = 'FormEngine did an on-the-fly migration of a flex form data structure. This is deprecated and will be removed' . ' with TYPO3 CMS 8. Merge the following changes into the flex form definition of table ' . $table . ' in field ' . $fieldName . ':';
                 array_unshift($messages, $context);
                 GeneralUtility::deprecationLog(implode(LF, $messages));
             }
             $flexFormFieldArray['TCEforms'] = $migratedTca['dummyTable']['columns']['dummyField'];
             // Set up options for single element
             $fakeParameterArray = array('fieldConf' => array('label' => $languageService->sL(trim($flexFormFieldArray['TCEforms']['label'])), 'config' => $flexFormFieldArray['TCEforms']['config'], 'defaultExtras' => $flexFormFieldArray['TCEforms']['defaultExtras'], 'onChange' => $flexFormFieldArray['TCEforms']['onChange']));
             // Force a none field if default language can not be edited
             if ($flexFormNoEditDefaultLanguage && $flexFormCurrentLanguage === 'lDEF') {
                 $fakeParameterArray['fieldConf']['config'] = array('type' => 'none', 'rows' => 2);
             }
             $alertMsgOnChange = '';
             if ($fakeParameterArray['fieldConf']['onChange'] === 'reload' || !empty($GLOBALS['TCA'][$table]['ctrl']['type']) && $GLOBALS['TCA'][$table]['ctrl']['type'] === $flexFormFieldName || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate']) && GeneralUtility::inList($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'], $flexFormFieldName)) {
                 if ($this->getBackendUserAuthentication()->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
                     $alertMsgOnChange = 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
                 } else {
                     $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm();}';
                 }
             }
             $fakeParameterArray['fieldChangeFunc'] = $parameterArray['fieldChangeFunc'];
             if ($alertMsgOnChange) {
                 $fakeParameterArray['fieldChangeFunc']['alert'] = $alertMsgOnChange;
             }
             $fakeParameterArray['onFocus'] = $parameterArray['onFocus'];
             $fakeParameterArray['label'] = $parameterArray['label'];
             $fakeParameterArray['itemFormElName'] = $parameterArray['itemFormElName'] . $flexFormFormPrefix . '[' . $flexFormFieldName . '][' . $vDEFkey . ']';
             $fakeParameterArray['itemFormElID'] = $fakeParameterArray['itemFormElName'];
             if (isset($flexFormRowData[$flexFormFieldName][$vDEFkey])) {
                 $fakeParameterArray['itemFormElValue'] = $flexFormRowData[$flexFormFieldName][$vDEFkey];
             } else {
                 $fakeParameterArray['itemFormElValue'] = $fakeParameterArray['fieldConf']['config']['default'];
             }
             $options = $this->globalOptions;
             $options['parameterArray'] = $fakeParameterArray;
             $options['elementBaseName'] = $this->globalOptions['elementBaseName'] . $flexFormFormPrefix . '[' . $flexFormFieldName . '][' . $vDEFkey . ']';
             if (!empty($flexFormFieldArray['TCEforms']['config']['renderType'])) {
                 $options['renderType'] = $flexFormFieldArray['TCEforms']['config']['renderType'];
             } else {
                 // Fallback to type if no renderType is given
                 $options['renderType'] = $flexFormFieldArray['TCEforms']['config']['type'];
             }
             /** @var NodeFactory $nodeFactory */
             $nodeFactory = $this->globalOptions['nodeFactory'];
             $childResult = $nodeFactory->create($options)->render();
             $theTitle = htmlspecialchars($fakeParameterArray['fieldConf']['label']);
             $defInfo = array();
             if (!$flexFormNoEditDefaultLanguage) {
                 $previewLanguages = $this->globalOptions['additionalPreviewLanguages'];
                 foreach ($previewLanguages as $previewLanguage) {
                     $defInfo[] = '<div class="t3-form-original-language">';
                     $defInfo[] = FormEngineUtility::getLanguageIcon($table, $row, 'v' . $previewLanguage['ISOcode']);
                     $defInfo[] = $this->previewFieldValue($flexFormRowData[$flexFormFieldName]['v' . $previewLanguage['ISOcode']], $fakeParameterArray['fieldConf'], $fieldName);
                     $defInfo[] = '</div>';
                 }
             }
             $languageIcon = '';
             if ($vDEFkey !== 'vDEF') {
                 $languageIcon = FormEngineUtility::getLanguageIcon($table, $row, $vDEFkey);
             }
             // Possible line breaks in the label through xml: \n => <br/>, usage of nl2br() not possible, so it's done through str_replace (?!)
             $processedTitle = str_replace('\\n', '<br />', $theTitle);
             // @todo: Similar to the processing within SingleElementContainer ... use it from there?!
             $html = array();
             $html[] = '<div class="form-section">';
             $html[] = '<div class="form-group t3js-formengine-palette-field t3js-formengine-validation-marker">';
             $html[] = '<label class="t3js-formengine-label">';
             $html[] = $languageIcon;
             $html[] = BackendUtility::wrapInHelp($parameterArray['_cshKey'], $flexFormFieldName, $processedTitle);
             $html[] = '</label>';
             $html[] = '<div class="t3js-formengine-field-item">';
             $html[] = $childResult['html'];
             $html[] = implode(LF, $defInfo);
             $html[] = $this->renderVDEFDiff($flexFormRowData[$flexFormFieldName], $vDEFkey);
             $html[] = '</div>';
             $html[] = '</div>';
             $html[] = '</div>';
             $resultArray['html'] .= implode(LF, $html);
             $childResult['html'] = '';
             $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $childResult);
         }
     }
     return $resultArray;
 }
Beispiel #7
0
 /**
  * Handle AJAX calls to localize all records of a parent, localize a single record or to synchronize with the original language parent.
  *
  * @param string $type Defines the type 'localize' or 'synchronize' (string) or a single uid to be localized (int)
  * @param int $inlineFirstPid Inline first pid
  * @return array An array to be used for JSON
  */
 protected function renderInlineSynchronizeLocalizeRecords($type, $inlineFirstPid)
 {
     $jsonArray = FALSE;
     if (GeneralUtility::inList('localize,synchronize', $type) || MathUtility::canBeInterpretedAsInteger($type)) {
         $inlineRelatedRecordResolver = GeneralUtility::makeInstance(InlineRelatedRecordResolver::class);
         // The parent level:
         $parent = $this->inlineStackProcessor->getStructureLevel(-1);
         $current = $this->inlineStackProcessor->getUnstableStructure();
         $parentRecord = $inlineRelatedRecordResolver->getRecord($parent['table'], $parent['uid']);
         $cmd = array();
         $cmd[$parent['table']][$parent['uid']]['inlineLocalizeSynchronize'] = $parent['field'] . ',' . $type;
         /** @var $tce \TYPO3\CMS\Core\DataHandling\DataHandler */
         $tce = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
         $tce->stripslashes_values = FALSE;
         $tce->start(array(), $cmd);
         $tce->process_cmdmap();
         $oldItemList = $parentRecord[$parent['field']];
         $newItemList = $tce->registerDBList[$parent['table']][$parent['uid']][$parent['field']];
         $jsonArray = array('scriptCall' => array());
         $nameObject = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid);
         $nameObjectForeignTable = $nameObject . '-' . $current['table'];
         // Get the name of the field pointing to the original record:
         $transOrigPointerField = $GLOBALS['TCA'][$current['table']]['ctrl']['transOrigPointerField'];
         // Get the name of the field used as foreign selector (if any):
         $foreignSelector = isset($parent['config']['foreign_selector']) && $parent['config']['foreign_selector'] ? $parent['config']['foreign_selector'] : FALSE;
         // Convert lists to array with uids of child records:
         $oldItems = FormEngineUtility::getInlineRelatedRecordsUidArray($oldItemList);
         $newItems = FormEngineUtility::getInlineRelatedRecordsUidArray($newItemList);
         // Determine the items that were localized or localized:
         $removedItems = array_diff($oldItems, $newItems);
         $localizedItems = array_diff($newItems, $oldItems);
         // Set the items that should be removed in the forms view:
         foreach ($removedItems as $item) {
             $jsonArray['scriptCall'][] = 'inline.deleteRecord(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable . '-' . $item) . ', {forceDirectRemoval: true});';
         }
         // Set the items that should be added in the forms view:
         $html = '';
         $resultArray = NULL;
         // @todo: This should be another container ...
         foreach ($localizedItems as $item) {
             $row = $inlineRelatedRecordResolver->getRecord($current['table'], $item);
             $selectedValue = $foreignSelector ? GeneralUtility::quoteJSvalue($row[$foreignSelector]) : 'null';
             $options = $this->getConfigurationOptionsForChildElements();
             $options['databaseRow'] = array('uid' => $parent['uid']);
             $options['inlineFirstPid'] = $inlineFirstPid;
             $options['inlineRelatedRecordToRender'] = $row;
             $options['inlineRelatedRecordConfig'] = $parent['config'];
             $options['inlineStructure'] = $this->inlineStackProcessor->getStructure();
             $options['isAjaxContext'] = TRUE;
             $options['renderType'] = 'inlineRecordContainer';
             $childArray = $this->nodeFactory->create($options)->render();
             $html .= $childArray['html'];
             $childArray['html'] = '';
             // @todo: Obsolete if a container and copied from AbstractContainer for now
             if ($resultArray === NULL) {
                 $resultArray = $childArray;
             } else {
                 if (!empty($childArray['extJSCODE'])) {
                     $resultArray['extJSCODE'] .= LF . $childArray['extJSCODE'];
                 }
                 foreach ($childArray['additionalJavaScriptPost'] as $value) {
                     $resultArray['additionalJavaScriptPost'][] = $value;
                 }
                 foreach ($childArray['additionalJavaScriptSubmit'] as $value) {
                     $resultArray['additionalJavaScriptSubmit'][] = $value;
                 }
                 if (!empty($childArray['inlineData'])) {
                     $resultArrayInlineData = $resultArray['inlineData'];
                     $childInlineData = $childArray['inlineData'];
                     ArrayUtility::mergeRecursiveWithOverrule($resultArrayInlineData, $childInlineData);
                     $resultArray['inlineData'] = $resultArrayInlineData;
                 }
             }
             $jsonArray['scriptCall'][] = 'inline.memorizeAddRecord(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable) . ', ' . GeneralUtility::quoteJSvalue($item) . ', null, ' . $selectedValue . ');';
             // Remove possible virtual records in the form which showed that a child records could be localized:
             if (isset($row[$transOrigPointerField]) && $row[$transOrigPointerField]) {
                 $jsonArray['scriptCall'][] = 'inline.fadeAndRemove(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable . '-' . $row[$transOrigPointerField] . '_div') . ');';
             }
         }
         if (!empty($html)) {
             $jsonArray['data'] = $html;
             array_unshift($jsonArray['scriptCall'], 'inline.domAddNewRecord(\'bottom\', ' . GeneralUtility::quoteJSvalue($nameObject . '_records') . ', ' . GeneralUtility::quoteJSvalue($nameObjectForeignTable) . ', json.data);');
         }
         $this->mergeResult($resultArray);
         $jsonArray = $this->getInlineAjaxCommonScriptCalls($jsonArray, $parent['config'], $inlineFirstPid);
     }
     return $jsonArray;
 }
 /**
  * Determine label of a single field (not a palette label)
  *
  * @param string $fieldName The field name to calculate the label for
  * @param string $labelFromShowItem Given label, typically from show item configuration
  * @return string Field label
  */
 protected function getSingleFieldLabel($fieldName, $labelFromShowItem)
 {
     $languageService = $this->getLanguageService();
     $table = $this->globalOptions['table'];
     $label = $labelFromShowItem;
     if (!empty($GLOBALS['TCA'][$table]['columns'][$fieldName]['label'])) {
         $label = $GLOBALS['TCA'][$table]['columns'][$fieldName]['label'];
     }
     if (!empty($labelFromShowItem)) {
         $label = $labelFromShowItem;
     }
     $fieldTSConfig = FormEngineUtility::getTSconfigForTableRow($table, $this->globalOptions['databaseRow'], $fieldName);
     if (!empty($fieldTSConfig['label'])) {
         $label = $fieldTSConfig['label'];
     }
     if (!empty($fieldTSConfig['label.'][$languageService->lang])) {
         $label = $fieldTSConfig['label.'][$languageService->lang];
     }
     return $languageService->sL($label);
 }
    /**
     * 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;
    }
    /**
     * Renders the HTML header for a foreign record, such as the title, toggle-function, drag'n'drop, etc.
     * Later on the command-icons are inserted here.
     *
     * @param string $parentUid The uid of the parent (embedding) record (uid or NEW...)
     * @param string $foreign_table The foreign_table we create a header for
     * @param array $data Current data
     * @param array $config content of $PA['fieldConf']['config']
     * @param bool $isVirtualRecord
     * @return string The HTML code of the header
     */
    protected function renderForeignRecordHeader($parentUid, $foreign_table, $data, $config, $isVirtualRecord = false)
    {
        $rec = $data['databaseRow'];
        // Init:
        $domObjectId = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
        $objectId = $domObjectId . '-' . $foreign_table . '-' . $rec['uid'];
        // We need the returnUrl of the main script when loading the fields via AJAX-call (to correct wizard code, so include it as 3rd parameter)
        // Pre-Processing:
        $isOnSymmetricSide = RelationHandler::isOnSymmetricSide($parentUid, $config, $rec);
        $hasForeignLabel = (bool) (!$isOnSymmetricSide && $config['foreign_label']);
        $hasSymmetricLabel = (bool) $isOnSymmetricSide && $config['symmetric_label'];
        // Get the record title/label for a record:
        // Try using a self-defined user function only for formatted labels
        if (isset($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc'])) {
            $params = array('table' => $foreign_table, 'row' => $rec, 'title' => '', 'isOnSymmetricSide' => $isOnSymmetricSide, 'options' => isset($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc_options']) ? $GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc_options'] : array(), 'parent' => array('uid' => $parentUid, 'config' => $config));
            // callUserFunction requires a third parameter, but we don't want to give $this as reference!
            $null = null;
            GeneralUtility::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['formattedLabel_userFunc'], $params, $null);
            $recTitle = $params['title'];
            // Try using a normal self-defined user function
        } elseif (isset($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'])) {
            $recTitle = $data['recordTitle'];
        } elseif ($hasForeignLabel || $hasSymmetricLabel) {
            $titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label'];
            // Render title for everything else than group/db:
            if (isset($this->data['processedTca']['columns'][$titleCol]['config']['type']) && $this->data['processedTca']['columns'][$titleCol]['config']['type'] === 'group' && isset($this->data['processedTca']['columns'][$titleCol]['config']['internal_type']) && $this->data['processedTca']['columns'][$titleCol]['config']['internal_type'] === 'db') {
                $recTitle = BackendUtility::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, false);
            } else {
                // $recTitle could be something like: "tx_table_123|...",
                $valueParts = GeneralUtility::trimExplode('|', $rec[$titleCol]);
                $itemParts = GeneralUtility::revExplode('_', $valueParts[0], 2);
                $recTemp = BackendUtility::getRecordWSOL($itemParts[0], $itemParts[1]);
                $recTitle = BackendUtility::getRecordTitle($itemParts[0], $recTemp, false);
            }
            $recTitle = BackendUtility::getRecordTitlePrep($recTitle);
            if (trim($recTitle) === '') {
                $recTitle = BackendUtility::getNoRecordTitle(true);
            }
        } else {
            $recTitle = BackendUtility::getRecordTitle($foreign_table, FormEngineUtility::databaseRowCompatibility($rec), true);
        }
        $altText = BackendUtility::getRecordIconAltText($rec, $foreign_table);
        $iconImg = '<span title="' . $altText . '" id="' . htmlspecialchars($objectId) . '_icon' . '">' . $this->iconFactory->getIconForRecord($foreign_table, $rec, Icon::SIZE_SMALL)->render() . '</span>';
        $label = '<span id="' . $objectId . '_label">' . $recTitle . '</span>';
        $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $data, $config, $isVirtualRecord);
        $thumbnail = false;
        // Renders a thumbnail for the header
        if (!empty($config['appearance']['headerThumbnail']['field'])) {
            $fieldValue = $rec[$config['appearance']['headerThumbnail']['field']];
            $firstElement = array_shift(GeneralUtility::trimExplode('|', array_shift(GeneralUtility::trimExplode(',', $fieldValue))));
            $fileUid = array_pop(BackendUtility::splitTable_Uid($firstElement));
            if (!empty($fileUid)) {
                $fileObject = ResourceFactory::getInstance()->getFileObject($fileUid);
                if ($fileObject && $fileObject->isMissing()) {
                    $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
                    $thumbnail = $flashMessage->render();
                } elseif ($fileObject) {
                    $imageSetup = $config['appearance']['headerThumbnail'];
                    unset($imageSetup['field']);
                    if (!empty($rec['crop'])) {
                        $imageSetup['crop'] = $rec['crop'];
                    }
                    $imageSetup = array_merge(array('width' => '45', 'height' => '45c'), $imageSetup);
                    $processedImage = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $imageSetup);
                    // Only use a thumbnail if the processing process was successful by checking if image width is set
                    if ($processedImage->getProperty('width')) {
                        $imageUrl = $processedImage->getPublicUrl(true);
                        $thumbnail = '<img src="' . $imageUrl . '" ' . 'width="' . $processedImage->getProperty('width') . '" ' . 'height="' . $processedImage->getProperty('height') . '" ' . 'alt="' . htmlspecialchars($altText) . '" ' . 'title="' . htmlspecialchars($altText) . '">';
                    }
                }
            }
        }
        if (!empty($config['appearance']['headerThumbnail']['field']) && $thumbnail) {
            $mediaContainer = '<div class="form-irre-header-cell form-irre-header-thumbnail" id="' . $objectId . '_thumbnailcontainer">' . $thumbnail . '</div>';
        } else {
            $mediaContainer = '<div class="form-irre-header-cell form-irre-header-icon" id="' . $objectId . '_iconcontainer">' . $iconImg . '</div>';
        }
        $header = $mediaContainer . '
				<div class="form-irre-header-cell form-irre-header-body">' . $label . '</div>
				<div class="form-irre-header-cell form-irre-header-control t3js-formengine-irre-control">' . $ctrl . '</div>';
        return $header;
    }
Beispiel #11
0
 /**
  * Modify a single FlexForm sheet according to given configuration
  *
  * @param array $sheet Flexform sheet to manipulate
  * @param string $table The table name
  * @param string $tableField The field name
  * @param array $tableRow The record data
  * @param array $sheetConf Sheet configuration
  * @param array $nonExcludeFields Non-exclude-fields for this sheet
  * @return array Modified sheet
  * @see \TYPO3\CMS\Backend\Form\FlexFormsHelper::modifyFlexFormDS()
  */
 public function modifySingleFlexFormSheet(array $sheet, $table, $tableField, array $tableRow, array $sheetConf, array $nonExcludeFields)
 {
     if (empty($sheet) || empty($table) || empty($tableField) || empty($tableRow)) {
         return $sheet;
     }
     // Modify fields
     foreach ($sheet as $fieldName => $field) {
         // Remove excluded fields
         if (!$GLOBALS['BE_USER']->isAdmin() && !empty($field['TCEforms']['exclude']) && empty($nonExcludeFields[$fieldName])) {
             unset($sheet[$fieldName]);
             continue;
         }
         // Stop here if no TSConfig was found for this field
         if (empty($sheetConf[$fieldName]) || !is_array($sheetConf[$fieldName])) {
             continue;
         }
         // Remove disabled fields
         if (!empty($sheetConf[$fieldName]['disabled'])) {
             unset($sheet[$fieldName]);
             continue;
         }
         $fieldConf = $sheetConf[$fieldName];
         $removeItems = !empty($fieldConf['removeItems']) ? GeneralUtility::trimExplode(',', $fieldConf['removeItems'], TRUE) : array();
         $keepItems = !empty($fieldConf['keepItems']) ? GeneralUtility::trimExplode(',', $fieldConf['keepItems'], TRUE) : array();
         $renameItems = !empty($fieldConf['altLabels']) && is_array($fieldConf['altLabels']) ? $fieldConf['altLabels'] : array();
         $changeIcons = !empty($fieldConf['altIcons']) && is_array($fieldConf['altIcons']) ? $fieldConf['altIcons'] : array();
         $addItems = !empty($fieldConf['addItems']) && is_array($fieldConf['addItems']) ? $fieldConf['addItems'] : array();
         unset($fieldConf['removeItems']);
         unset($fieldConf['keepItems']);
         unset($fieldConf['altLabels']);
         unset($fieldConf['altIcons']);
         unset($fieldConf['addItems']);
         // Manipulate field
         if (!empty($field['TCEforms']) && is_array($field['TCEforms'])) {
             $sheet[$fieldName]['TCEforms'] = $field['TCEforms'];
             ArrayUtility::mergeRecursiveWithOverrule($sheet[$fieldName]['TCEforms'], $fieldConf);
         }
         // Manipulate only select fields, other field types will stop here
         if (empty($field['TCEforms']['config']['type']) || $field['TCEforms']['config']['type'] != 'select' || $field['TCEforms']['config']['renderMode'] === 'tree') {
             continue;
         }
         // Getting the selector box items from system
         $selItems = FormEngineUtility::addSelectOptionsToItemArray(FormEngineUtility::initItemArray($field['TCEforms']), $field['TCEforms'], FormEngineUtility::getTSconfigForTableRow($table, $tableRow), $tableField);
         // Possibly filter some items
         $selItems = ArrayUtility::keepItemsInArray($selItems, $keepItems, function ($value) {
             return $value[1];
         });
         // Possibly add some items
         $selItems = FormEngineUtility::addItems($selItems, $addItems);
         // Process items by a user function
         if (!empty($field['TCEforms']['config']['itemsProcFunc'])) {
             $dataPreprocessor = GeneralUtility::makeInstance(DataPreprocessor::class);
             $selItems = $dataPreprocessor->procItems($selItems, $fieldConf['config'], $field['TCEforms']['config'], $table, $tableRow, $tableField);
         }
         // Remove special configuration options after creating items to prevent double parsing
         foreach ($this->removeSelectConfig as $option) {
             unset($sheet[$fieldName]['TCEforms']['config'][$option]);
         }
         // Rename and remove items or change item icon in select
         if ((!empty($removeItems) || !empty($renameItems) || !empty($changeIcons)) && !empty($selItems) && is_array($selItems)) {
             foreach ($selItems as $itemKey => $itemConf) {
                 // Option has no key, no manipulation possible
                 if (!isset($itemConf[1])) {
                     continue;
                 }
                 // Remove
                 foreach ($removeItems as $removeKey => $removeValue) {
                     if (strcasecmp($removeValue, $itemConf[1]) == 0) {
                         unset($selItems[$itemKey]);
                         unset($removeItems[$removeKey]);
                     }
                 }
                 // Rename
                 foreach ($renameItems as $renameKey => $renameValue) {
                     if (strcasecmp($renameKey, $itemConf[1]) == 0) {
                         $selItems[$itemKey][0] = htmlspecialchars($renameValue);
                         unset($renameItems[$renameKey]);
                     }
                 }
                 // Change icon
                 foreach ($changeIcons as $iconKey => $iconValue) {
                     if (strcasecmp($iconKey, $itemConf[1]) == 0) {
                         $selItems[$itemKey][2] = $iconValue;
                         unset($changeIcons[$iconKey]);
                     }
                 }
             }
         }
         $sheet[$fieldName]['TCEforms']['config']['items'] = $selItems;
     }
     return $sheet;
 }
 /**
  * Gets the related records of the embedding item, this could be 1:n, m:n.
  *
  * @param string $table The table name of the record
  * @param string $itemList The list of related child records
  * @return array The records related to the parent item
  */
 protected function getRelatedRecordsArray($table, $itemList)
 {
     $records = array();
     $itemArray = FormEngineUtility::getInlineRelatedRecordsUidArray($itemList);
     // Perform modification of the selected items array:
     foreach ($itemArray as $uid) {
         // Get the records for this uid using \TYPO3\CMS\Backend\Form\DataPreprocessor
         if ($record = $this->getRecord($table, $uid)) {
             $records[$uid] = $record;
         }
     }
     return $records;
 }
Beispiel #13
0
    /**
     * Main function, rendering the document with the iFrame with the RTE in.
     *
     * @return void
     */
    public function main()
    {
        // Translate id to the workspace version:
        if ($versionedRecord = BackendUtility::getWorkspaceVersionOfRecord($this->getBackendUserAuthentication()->workspace, $this->P['table'], $this->P['uid'], 'uid')) {
            $this->P['uid'] = $versionedRecord['uid'];
        }
        // If all parameters are available:
        if ($this->P['table'] && $this->P['field'] && $this->P['uid'] && $this->checkEditAccess($this->P['table'], $this->P['uid'])) {
            // Getting the raw record (we need only the pid-value from here...)
            $rawRecord = BackendUtility::getRecord($this->P['table'], $this->P['uid']);
            BackendUtility::fixVersioningPid($this->P['table'], $rawRecord);
            // override the default jumpToUrl
            $this->doc->JScodeArray['jumpToUrl'] = '
		function jumpToUrl(URL,formEl) {
			if (document.editform) {
				if (!TBE_EDITOR.isFormChanged()) {
					window.location.href = URL;
				} else if (formEl) {
					if (formEl.type=="checkbox") formEl.checked = formEl.checked ? 0 : 1;
				}
			} else {
				window.location.href = URL;
			}
		}
';
            // Setting JavaScript of the pid value for viewing:
            if ($this->popView) {
                $this->doc->JScode = $this->doc->wrapScriptTags(BackendUtility::viewOnClick($rawRecord['pid'], '', BackendUtility::BEgetRootLine($rawRecord['pid'])));
            }
            // Initialize FormEngine - for rendering the field:
            /** @var FormEngine $formEngine */
            $formEngine = GeneralUtility::makeInstance(FormEngine::class);
            // SPECIAL: Disables all wizards - we are NOT going to need them.
            $formEngine->disableWizards = 1;
            // Fetching content of record:
            /** @var DataPreprocessor $dataPreprocessor */
            $dataPreprocessor = GeneralUtility::makeInstance(DataPreprocessor::class);
            $dataPreprocessor->lockRecords = 1;
            $dataPreprocessor->fetchRecord($this->P['table'], $this->P['uid'], '');
            // Getting the processed record content out:
            $processedRecord = reset($dataPreprocessor->regTableItems_data);
            $processedRecord['uid'] = $this->P['uid'];
            $processedRecord['pid'] = $rawRecord['pid'];
            // TSconfig, setting width:
            $fieldTSConfig = FormEngineUtility::getTSconfigForTableRow($this->P['table'], $processedRecord, $this->P['field']);
            if ((string) $fieldTSConfig['RTEfullScreenWidth'] !== '') {
                $width = $fieldTSConfig['RTEfullScreenWidth'];
            } else {
                $width = '100%';
            }
            // Get the form field and wrap it in the table with the buttons:
            $formContent = $formEngine->getSoloField($this->P['table'], $processedRecord, $this->P['field']);
            $formContent = '

			<!-- RTE wizard: -->
				<table border="0" cellpadding="0" cellspacing="0" width="' . $width . '" id="typo3-rtewizard">
					<tr>
						<td width="' . $width . '" colspan="2" id="c-formContent">' . $formContent . '</td>
						<td></td>
					</tr>
				</table>';
            // Adding hidden fields:
            $formContent .= '<input type="hidden" name="redirect" value="' . htmlspecialchars($this->R_URI) . '" />
						<input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />' . FormEngine::getHiddenTokenField('tceAction');
            // Finally, add the whole setup:
            $this->content .= $formEngine->printNeededJSFunctions_top() . $formContent . $formEngine->printNeededJSFunctions();
        } else {
            // ERROR:
            $this->content .= $this->doc->section($this->getLanguageService()->getLL('forms_title'), '<span class="text-danger">' . $this->getLanguageService()->getLL('table_noData', TRUE) . '</span>', 0, 1);
        }
        // Setting up the buttons and markers for docHeader
        $docHeaderButtons = $this->getButtons();
        $markers['CONTENT'] = $this->content;
        // Build the <body> for the module
        $this->content = $this->doc->startPage('');
        $this->content .= $this->doc->moduleBody(array(), $docHeaderButtons, $markers);
        $this->content .= $this->doc->endPage();
        $this->content = $this->doc->insertStylesAndJS($this->content);
    }
 /**
  * Renders the display of default language record content around current field.
  * Will render content if any is found in the internal array, $this->defaultLanguageData,
  * depending on registerDefaultLanguageData() being called prior to this.
  *
  * @param string $table Table name of the record being edited
  * @param string $field Field name represented by $item
  * @param array $row Record array of the record being edited
  * @param string $item HTML of the form field. This is what we add the content to.
  * @return string Item string returned again, possibly with the original value added to.
  */
 protected function renderDefaultLanguageContent($table, $field, $row, $item)
 {
     if (is_array($this->globalOptions['defaultLanguageData'][$table . ':' . $row['uid']])) {
         $defaultLanguageValue = BackendUtility::getProcessedValue($table, $field, $this->globalOptions['defaultLanguageData'][$table . ':' . $row['uid']][$field], 0, 1, FALSE, $this->globalOptions['defaultLanguageData'][$table . ':' . $row['uid']]['uid']);
         $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$field];
         // Don't show content if it's for IRRE child records:
         if ($fieldConfig['config']['type'] !== 'inline') {
             if ($defaultLanguageValue !== '') {
                 $item .= '<div class="t3-form-original-language">' . FormEngineUtility::getLanguageIcon($table, $row, 0) . $this->getMergeBehaviourIcon($fieldConfig['l10n_mode']) . $this->previewFieldValue($defaultLanguageValue, $fieldConfig, $field) . '</div>';
             }
             $additionalPreviewLanguages = $this->globalOptions['additionalPreviewLanguages'];
             foreach ($additionalPreviewLanguages as $previewLanguage) {
                 $defaultLanguageValue = BackendUtility::getProcessedValue($table, $field, $this->globalOptions['additionalPreviewLanguageData'][$table . ':' . $row['uid']][$previewLanguage['uid']][$field], 0, 1);
                 if ($defaultLanguageValue !== '') {
                     $item .= '<div class="t3-form-original-language">' . FormEngineUtility::getLanguageIcon($table, $row, 'v' . $previewLanguage['ISOcode']) . $this->getMergeBehaviourIcon($fieldConfig['l10n_mode']) . $this->previewFieldValue($defaultLanguageValue, $fieldConfig, $field) . '</div>';
                 }
             }
         }
     }
     return $item;
 }
 /**
  * If the select field is build by a foreign_table the related UIDs
  * will be returned.
  *
  * Otherwise the label of the currently selected value will be written
  * to the alternativeFieldValue class property.
  *
  * @param array $fieldConfig The "config" section of the TCA for the current select field.
  * @param string $fieldName The name of the select field.
  * @param string $value The current value in the local record, usually a comma separated list of selected values.
  * @return array Array of related UIDs.
  */
 protected function getRelatedSelectFieldUids(array $fieldConfig, $fieldName, $value)
 {
     $relatedUids = array();
     $isTraversable = FALSE;
     if (isset($fieldConfig['foreign_table'])) {
         $isTraversable = TRUE;
         // if a foreign_table is used we pre-filter the records for performance
         $fieldConfig['foreign_table_where'] .= ' AND ' . $fieldConfig['foreign_table'] . '.uid IN (' . $value . ')';
     }
     $PA = array();
     $PA['fieldConf']['config'] = $fieldConfig;
     $PA['fieldTSConfig'] = FormEngineUtility::getTSconfigForTableRow($this->currentTable, $this->currentRow, $fieldName);
     $PA['fieldConf']['config'] = FormEngineUtility::overrideFieldConf($PA['fieldConf']['config'], $PA['fieldTSConfig']);
     $selectItemArray = FormEngineUtility::getSelectItems($this->currentTable, $fieldName, $this->currentRow, $PA);
     if ($isTraversable && !empty($selectItemArray)) {
         $this->currentTable = $fieldConfig['foreign_table'];
         $relatedUids = $this->getSelectedValuesFromSelectItemArray($selectItemArray, $value);
     } else {
         $selectedLabels = $this->getSelectedValuesFromSelectItemArray($selectItemArray, $value, 1, TRUE);
         if (count($selectedLabels) === 1) {
             $this->alternativeFieldValue = $selectedLabels[0];
             $this->forceAlternativeFieldValueUse = TRUE;
         }
     }
     return $relatedUids;
 }
 /**
  * Adds localizations or synchronizes the locations of all child records.
  * Handle AJAX calls to localize all records of a parent, localize a single record or to synchronize with the original language parent.
  *
  * @param ServerRequestInterface $request the incoming request
  * @param ResponseInterface $response the empty response
  * @return ResponseInterface the filled response
  */
 public function synchronizeLocalizeAction(ServerRequestInterface $request, ResponseInterface $response)
 {
     $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
     $domObjectId = $ajaxArguments[0];
     $type = $ajaxArguments[1];
     /** @var InlineStackProcessor $inlineStackProcessor */
     $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
     // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config:
     $inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
     $inlineStackProcessor->injectAjaxConfiguration($ajaxArguments['context']);
     $inlineFirstPid = $this->getInlineFirstPidFromDomObjectId($domObjectId);
     $jsonArray = false;
     if ($type === 'localize' || $type === 'synchronize' || MathUtility::canBeInterpretedAsInteger($type)) {
         // Parent, this table embeds the child table
         $parent = $inlineStackProcessor->getStructureLevel(-1);
         $parentFieldName = $parent['field'];
         // Child, a record from this table should be rendered
         $child = $inlineStackProcessor->getUnstableStructure();
         $formDataCompilerInputForParent = ['vanillaUid' => (int) $parent['uid'], 'command' => 'edit', 'tableName' => $parent['table'], 'databaseRow' => ['uid' => (int) $parent['uid']], 'inlineFirstPid' => $inlineFirstPid, 'columnsToProcess' => [$parentFieldName], 'inlineStructure' => $inlineStackProcessor->getStructure(), 'inlineCompileExistingChildren' => false];
         /** @var TcaDatabaseRecord $formDataGroup */
         $formDataGroup = GeneralUtility::makeInstance(InlineParentRecord::class);
         /** @var FormDataCompiler $formDataCompiler */
         $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
         $parentData = $formDataCompiler->compile($formDataCompilerInputForParent);
         $parentConfig = $parentData['processedTca']['columns'][$parentFieldName]['config'];
         $oldItemList = $parentData['databaseRow'][$parentFieldName];
         $cmd = array();
         $cmd[$parent['table']][$parent['uid']]['inlineLocalizeSynchronize'] = $parent['field'] . ',' . $type;
         /** @var $tce DataHandler */
         $tce = GeneralUtility::makeInstance(DataHandler::class);
         $tce->stripslashes_values = false;
         $tce->start(array(), $cmd);
         $tce->process_cmdmap();
         $newItemList = $tce->registerDBList[$parent['table']][$parent['uid']][$parentFieldName];
         $jsonArray = array('data' => '', 'stylesheetFiles' => [], 'scriptCall' => []);
         $nameObject = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid);
         $nameObjectForeignTable = $nameObject . '-' . $child['table'];
         $oldItems = FormEngineUtility::getInlineRelatedRecordsUidArray($oldItemList);
         $newItems = FormEngineUtility::getInlineRelatedRecordsUidArray($newItemList);
         // Set the items that should be removed in the forms view:
         $removedItems = array_diff($oldItems, $newItems);
         foreach ($removedItems as $childUid) {
             $jsonArray['scriptCall'][] = 'inline.deleteRecord(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable . '-' . $childUid) . ', {forceDirectRemoval: true});';
         }
         $localizedItems = array_diff($newItems, $oldItems);
         foreach ($localizedItems as $childUid) {
             $childData = $this->compileChild($parentData, $parentFieldName, (int) $childUid);
             $childData['inlineParentUid'] = (int) $parent['uid'];
             // @todo: needed?
             $childData['inlineStructure'] = $inlineStackProcessor->getStructure();
             // @todo: needed?
             $childData['inlineExpandCollapseStateArray'] = $parentData['inlineExpandCollapseStateArray'];
             $childData['renderType'] = 'inlineRecordContainer';
             $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
             $childResult = $nodeFactory->create($childData)->render();
             $jsonArray = $this->mergeChildResultIntoJsonResult($jsonArray, $childResult);
             // Get the name of the field used as foreign selector (if any):
             $foreignSelector = isset($parentConfig['foreign_selector']) && $parentConfig['foreign_selector'] ? $parentConfig['foreign_selector'] : false;
             $selectedValue = $foreignSelector ? GeneralUtility::quoteJSvalue($childData['databaseRow'][$foreignSelector]) : 'null';
             if (is_array($selectedValue)) {
                 $selectedValue = $selectedValue[0];
             }
             $jsonArray['scriptCall'][] = 'inline.memorizeAddRecord(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable) . ', ' . GeneralUtility::quoteJSvalue($childUid) . ', null, ' . $selectedValue . ');';
             // Remove possible virtual records in the form which showed that a child records could be localized:
             $transOrigPointerFieldName = $GLOBALS['TCA'][$childData['table']]['ctrl']['transOrigPointerField'];
             if (isset($childData['databaseRow'][$transOrigPointerFieldName]) && $childData['databaseRow'][$transOrigPointerFieldName]) {
                 $transOrigPointerField = $childData['databaseRow'][$transOrigPointerFieldName];
                 if (is_array($transOrigPointerField)) {
                     $transOrigPointerField = $transOrigPointerField[0];
                 }
                 $jsonArray['scriptCall'][] = 'inline.fadeAndRemove(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable . '-' . $transOrigPointerField . '_div') . ');';
             }
             if (!empty($childResult['html'])) {
                 array_unshift($jsonArray['scriptCall'], 'inline.domAddNewRecord(\'bottom\', ' . GeneralUtility::quoteJSvalue($nameObject . '_records') . ', ' . GeneralUtility::quoteJSvalue($nameObjectForeignTable) . ', json.data);');
             }
         }
     }
     $response->getBody()->write(json_encode($jsonArray));
     return $response;
 }
Beispiel #17
0
 /**
  * This will render a checkbox or an array of checkboxes
  *
  * @return array As defined in initializeResultArray() of AbstractNode
  */
 public function render()
 {
     $config = $this->globalOptions['parameterArray']['fieldConf']['config'];
     $html = '';
     $disabled = FALSE;
     if ($this->isGlobalReadonly() || $config['readOnly']) {
         $disabled = TRUE;
     }
     // Traversing the array of items
     $items = FormEngineUtility::initItemArray($this->globalOptions['parameterArray']['fieldConf']);
     if ($config['itemsProcFunc']) {
         $dataPreprocessor = GeneralUtility::makeInstance(DataPreprocessor::class);
         $items = $dataPreprocessor->procItems($items, $this->globalOptions['parameterArray']['fieldTSConfig']['itemsProcFunc.'], $config, $this->globalOptions['table'], $this->globalOptions['databaseRow'], $this->globalOptions['fieldName']);
     }
     $numberOfItems = count($items);
     if ($numberOfItems === 0) {
         $items[] = array('', '');
         $numberOfItems = 1;
     }
     $formElementValue = (int) $this->globalOptions['parameterArray']['itemFormElValue'];
     $cols = (int) $config['cols'];
     if ($cols > 1) {
         $colWidth = (int) floor(12 / $cols);
         $colClass = "col-md-12";
         $colClear = array();
         if ($colWidth == 6) {
             $colClass = "col-sm-6";
             $colClear = array(2 => 'visible-sm-block visible-md-block visible-lg-block');
         } elseif ($colWidth === 4) {
             $colClass = "col-sm-4";
             $colClear = array(3 => 'visible-sm-block visible-md-block visible-lg-block');
         } elseif ($colWidth === 3) {
             $colClass = "col-sm-6 col-md-3";
             $colClear = array(2 => 'visible-sm-block', 4 => 'visible-md-block visible-lg-block');
         } elseif ($colWidth <= 2) {
             $colClass = "checkbox-column col-sm-6 col-md-3 col-lg-2";
             $colClear = array(2 => 'visible-sm-block', 4 => 'visible-md-block', 6 => 'visible-lg-block');
         }
         $html .= '<div class="checkbox-row row">';
         for ($counter = 0; $counter < $numberOfItems; $counter++) {
             // use "default" for typical single checkboxes
             $tsConfigKey = $numberOfItems === 1 ? 'default' : $items[$counter][1];
             // useful for e.g. pages.l18n_cfg, where there is no value set
             if ($tsConfigKey === '') {
                 $tsConfigKey = $counter;
             }
             if (isset($this->globalOptions['parameterArray']['fieldTSConfig']['altLabels.'][$tsConfigKey])) {
                 $label = $this->getLanguageService()->sL($this->globalOptions['parameterArray']['fieldTSConfig']['altLabels.'][$tsConfigKey]);
             } else {
                 $label = $items[$counter][0];
             }
             $html .= '<div class="checkbox-column ' . $colClass . '">' . $this->renderSingleCheckboxElement($label, $counter, $formElementValue, $numberOfItems, $this->globalOptions['parameterArray'], $disabled) . '</div>';
             if ($counter + 1 < $numberOfItems && !empty($colClear)) {
                 foreach ($colClear as $rowBreakAfter => $clearClass) {
                     if (($counter + 1) % $rowBreakAfter === 0) {
                         $html .= '<div class="clearfix ' . $clearClass . '"></div>';
                     }
                 }
             }
         }
         $html .= '</div>';
     } else {
         for ($counter = 0; $counter < $numberOfItems; $counter++) {
             // use "default" for typical single checkboxes
             $tsConfigKey = $numberOfItems === 1 ? 'default' : $items[$counter][1];
             // useful for e.g. pages.l18n_cfg, where there is no value set
             if ($tsConfigKey === '') {
                 $tsConfigKey = $counter;
             }
             if (isset($this->globalOptions['parameterArray']['fieldTSConfig']['altLabels.'][$tsConfigKey])) {
                 $label = $this->getLanguageService()->sL($this->globalOptions['parameterArray']['fieldTSConfig']['altLabels.'][$tsConfigKey]);
             } else {
                 $label = $items[$counter][0];
             }
             $html .= $this->renderSingleCheckboxElement($label, $counter, $formElementValue, $numberOfItems, $this->globalOptions['parameterArray'], $disabled);
         }
     }
     if (!$disabled) {
         $html .= '<input type="hidden" name="' . $this->globalOptions['parameterArray']['itemFormElName'] . '" value="' . htmlspecialchars($formElementValue) . '" />';
     }
     $resultArray = $this->initializeResultArray();
     $resultArray['html'] = $html;
     return $resultArray;
 }
 /**
  * Render check boxes
  *
  * @return array As defined in initializeResultArray() of AbstractNode
  */
 public function render()
 {
     $html = [];
     // Field configuration from TCA:
     $parameterArray = $this->data['parameterArray'];
     $config = $parameterArray['fieldConf']['config'];
     $disabled = !empty($config['readOnly']);
     $selItems = $config['items'];
     if (!empty($selItems)) {
         // Get values in an array (and make unique, which is fine because there can be no duplicates anyway):
         $itemArray = array_flip($parameterArray['itemFormElValue']);
         // 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' => StringUtility::getUniqueId('select_checkbox_row_'), 'name' => $parameterArray['itemFormElName'] . '[' . $c . ']', 'value' => $p[1], 'checked' => $checked, 'disabled' => false, 'class' => '', 'icon' => !empty($p[2]) ? FormEngineUtility::getIconHtml($p[2]) : $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render(), 'title' => htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', false), 'help' => $help);
                     $c++;
                 }
             }
         }
         // Add an empty hidden field which will send a blank value if all items are unselected.
         $html[] = '<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;
             $html[] = '<div class="panel panel-default">';
             if (is_array($group['header'])) {
                 $html[] = '<div class="panel-heading">';
                 $html[] = '<a data-toggle="collapse" href="#' . $groupId . '" aria-expanded="false" aria-controls="' . $groupId . '">';
                 $html[] = $group['header']['icon'];
                 $html[] = $group['header']['title'];
                 $html[] = '</a>';
                 $html[] = '</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'] . '">';
                     $tableRows[] = '<td class="col-checkbox">';
                     $tableRows[] = '<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'] . '>';
                     $tableRows[] = '</td>';
                     $tableRows[] = '<td class="col-icon">';
                     $tableRows[] = '<label class="label-block" for="' . $item['id'] . '">' . $item['icon'] . '</label>';
                     $tableRows[] = '</td>';
                     $tableRows[] = '<td class="col-title">';
                     $tableRows[] = '<label class="label-block" for="' . $item['id'] . '">' . $item['title'] . '</label>';
                     $tableRows[] = '</td>';
                     $tableRows[] = '<td>' . $item['help'] . '</td>';
                     $tableRows[] = '</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)) {
                     $title = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.revertSelection', true);
                     $resetGroupBtn = '<a href="#" ' . 'class="btn btn-default" ' . 'onclick="' . implode('', $resetGroup) . ' return false;" ' . 'title="' . $title . '">' . $this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL)->render() . ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.revertSelection') . '</a>';
                 }
                 if (is_array($group['header'])) {
                     $html[] = '<div id="' . $groupId . '" class="panel-collapse collapse" role="tabpanel">';
                 }
                 $html[] = '<div class="table-fit">';
                 $html[] = '<table class="table table-transparent table-hover">';
                 $html[] = '<thead>';
                 $html[] = '<tr>';
                 $html[] = '<th class="col-checkbox">' . $toggleGroupCheckbox . '</th>';
                 $html[] = '<th class="col-icon"></th>';
                 $html[] = '<th class="text-right" colspan="2">' . $resetGroupBtn . '</th>';
                 $html[] = '</tr>';
                 $html[] = '</thead>';
                 $html[] = '<tbody>' . implode(LF, $tableRows) . '</tbody>';
                 $html[] = '</table>';
                 $html[] = '</div>';
                 if (is_array($group['header'])) {
                     $html[] = '</div>';
                 }
             }
             $html[] = '</div>';
         }
     }
     if (!$disabled) {
         $html = $this->renderWizards(array(implode(LF, $html)), $config['wizards'], $this->data['tableName'], $this->data['databaseRow'], $this->data['fieldName'], $parameterArray, $parameterArray['itemFormElName'], BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']));
     }
     $resultArray = $this->initializeResultArray();
     $resultArray['html'] = $html;
     return $resultArray;
 }
 /**
  * Render single element
  *
  * @return array As defined in initializeResultArray() of AbstractNode
  */
 public function render()
 {
     $table = $this->data['tableName'];
     $field = $this->data['fieldName'];
     $row = $this->data['databaseRow'];
     $parameterArray = $this->data['parameterArray'];
     $config = $parameterArray['fieldConf']['config'];
     $selectItems = $parameterArray['fieldConf']['config']['items'];
     // Check against inline uniqueness
     /** @var InlineStackProcessor $inlineStackProcessor */
     $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
     $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
     $uniqueIds = null;
     if ($this->data['isInlineChild'] && $this->data['inlineParentUid']) {
         $inlineObjectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
         $inlineFormName = $inlineStackProcessor->getCurrentStructureFormPrefix();
         if ($this->data['inlineParentConfig']['foreign_table'] === $table && $this->data['inlineParentConfig']['foreign_unique'] === $field) {
             $uniqueIds = $this->data['inlineData']['unique'][$inlineObjectName . '-' . $table]['used'];
             $parameterArray['fieldChangeFunc']['inlineUnique'] = 'inline.updateUnique(this,' . GeneralUtility::quoteJSvalue($inlineObjectName . '-' . $table) . ',' . GeneralUtility::quoteJSvalue($inlineFormName) . ',' . GeneralUtility::quoteJSvalue($row['uid']) . ');';
         }
         // hide uid of parent record for symmetric relations
         if ($this->data['inlineParentConfig']['foreign_table'] === $table && ($this->data['inlineParentConfig']['foreign_field'] === $field || $this->data['inlineParentConfig']['symmetric_field'] === $field)) {
             $uniqueIds[] = $this->data['inlineParentUid'];
         }
     }
     // Initialization:
     $selectId = StringUtility::getUniqueId('tceforms-select-');
     $selectedIcon = '';
     $size = (int) $config['size'];
     // Style set on <select/>
     $options = '';
     $disabled = false;
     if (!empty($config['readOnly'])) {
         $disabled = true;
     }
     // Prepare groups
     $selectItemCounter = 0;
     $selectItemGroupCount = 0;
     $selectItemGroups = array();
     $selectIcons = array();
     $selectedValue = '';
     $hasIcons = false;
     if (!empty($parameterArray['itemFormElValue'])) {
         $selectedValue = (string) $parameterArray['itemFormElValue'][0];
     }
     foreach ($selectItems as $item) {
         if ($item[1] === '--div--') {
             // IS OPTGROUP
             if ($selectItemCounter !== 0) {
                 $selectItemGroupCount++;
             }
             $selectItemGroups[$selectItemGroupCount]['header'] = array('title' => $item[0]);
         } else {
             // IS ITEM
             $title = htmlspecialchars($item['0'], ENT_COMPAT, 'UTF-8', false);
             $icon = !empty($item[2]) ? FormEngineUtility::getIconHtml($item[2], $title, $title) : '';
             $selected = $selectedValue === (string) $item[1];
             if ($selected) {
                 $selectedIcon = $icon;
             }
             $selectItemGroups[$selectItemGroupCount]['items'][] = array('title' => $title, 'value' => $item[1], 'icon' => $icon, 'selected' => $selected, 'index' => $selectItemCounter);
             // ICON
             if ($icon) {
                 $selectIcons[] = array('title' => $title, 'icon' => $icon, 'index' => $selectItemCounter);
             }
             $selectItemCounter++;
         }
     }
     // Fallback icon
     // @todo: assign a special icon for non matching values?
     if (!$selectedIcon && $selectItemGroups[0]['items'][0]['icon']) {
         $selectedIcon = $selectItemGroups[0]['items'][0]['icon'];
     }
     // Process groups
     foreach ($selectItemGroups as $selectItemGroup) {
         // suppress groups without items
         if (empty($selectItemGroup['items'])) {
             continue;
         }
         $optionGroup = is_array($selectItemGroup['header']);
         $options .= $optionGroup ? '<optgroup label="' . htmlspecialchars($selectItemGroup['header']['title'], ENT_COMPAT, 'UTF-8', false) . '">' : '';
         if (is_array($selectItemGroup['items'])) {
             foreach ($selectItemGroup['items'] as $item) {
                 $options .= '<option value="' . htmlspecialchars($item['value']) . '" data-icon="' . htmlspecialchars($item['icon']) . '"' . ($item['selected'] ? ' selected="selected"' : '') . '>' . $item['title'] . '</option>';
             }
             $hasIcons = !empty($item['icon']);
         }
         $options .= $optionGroup ? '</optgroup>' : '';
     }
     // Build the element
     $html = ['<div class="form-control-wrap">'];
     if ($hasIcons) {
         $html[] = '<div class="input-group">';
         $html[] = '<span class="input-group-addon input-group-icon">';
         $html[] = $selectedIcon;
         $html[] = '</span>';
     }
     $html[] = '<select' . ' id="' . $selectId . '"' . ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"' . $this->getValidationDataAsDataAttribute($config) . ' class="form-control form-control-adapt"' . ($size ? ' size="' . $size . '"' : '') . ($disabled ? ' disabled="disabled"' : '') . '>';
     $html[] = $options;
     $html[] = '</select>';
     if ($hasIcons) {
         $html[] = '</div>';
     }
     $html[] = '</div>';
     // Create icon table:
     if (!empty($selectIcons) && !empty($config['showIconTable'])) {
         $selectIconColumns = (int) $config['selicon_cols'];
         if (!$selectIconColumns) {
             $selectIconColumns = count($selectIcons);
         }
         $selectIconColumns = $selectIconColumns > 12 ? 12 : $selectIconColumns;
         $selectIconRows = ceil(count($selectIcons) / $selectIconColumns);
         $selectIcons = array_pad($selectIcons, $selectIconRows * $selectIconColumns, '');
         $html[] = '<div class="t3js-forms-select-single-icons table-icons table-fit table-fit-inline-block">';
         $html[] = '<table class="table table-condensed table-white table-center">';
         $html[] = '<tbody>';
         $html[] = '<tr>';
         foreach ($selectIcons as $i => $selectIcon) {
             if ($i % $selectIconColumns === 0 && $i !== 0) {
                 $html[] = '</tr>';
                 $html[] = '<tr>';
             }
             $html[] = '<td>';
             if (is_array($selectIcon)) {
                 $html[] = '<a href="#" title="' . $selectIcon['title'] . '" data-select-index="' . $selectIcon['index'] . '">';
                 $html[] = $selectIcon['icon'];
                 $html[] = '</a>';
             }
             $html[] = '</td>';
         }
         $html[] = '</tr>';
         $html[] = '</tbody>';
         $html[] = '</table>';
         $html[] = '</div>';
     }
     $html = implode(LF, $html);
     // Wizards:
     if (!$disabled) {
         $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $field, $parameterArray, $parameterArray['itemFormElName'], BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']));
     }
     $resultArray = $this->initializeResultArray();
     $resultArray['html'] = $html;
     $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectSingleElement' => implode(LF, ['function(SelectSingleElement) {', 'SelectSingleElement.initialize(', GeneralUtility::quoteJSvalue('#' . $selectId) . ',', '{', 'onChange: function() {', implode('', $parameterArray['fieldChangeFunc']), '}', '}', ');', '}'])];
     return $resultArray;
 }
 /**
  * Gets the list of available columns for a given page id
  *
  * @param int $id
  * @return array $tcaItems
  */
 public function getColPosListItemsParsed($id)
 {
     $tsConfig = BackendUtility::getModTSconfig($id, 'TCEFORM.tt_content.colPos');
     $tcaConfig = $GLOBALS['TCA']['tt_content']['columns']['colPos']['config'];
     $tcaItems = $tcaConfig['items'];
     $tcaItems = FormEngineUtility::addItems($tcaItems, $tsConfig['properties']['addItems.']);
     if (isset($tcaConfig['itemsProcFunc']) && $tcaConfig['itemsProcFunc']) {
         $tcaItems = $this->addColPosListLayoutItems($id, $tcaItems);
     }
     foreach (GeneralUtility::trimExplode(',', $tsConfig['properties']['removeItems'], TRUE) as $removeId) {
         foreach ($tcaItems as $key => $item) {
             if ($item[1] == $removeId) {
                 unset($tcaItems[$key]);
             }
         }
     }
     return $tcaItems;
 }
 /**
  * Creates the editing form with FormEnigne, based on the input from GPvars.
  *
  * @return string HTML form elements wrapped in tables
  */
 public function makeEditForm()
 {
     // Initialize variables:
     $this->elementsData = array();
     $this->errorC = 0;
     $this->newC = 0;
     $editForm = '';
     $trData = null;
     $beUser = $this->getBackendUser();
     // Traverse the GPvar edit array
     // Tables:
     foreach ($this->editconf as $table => $conf) {
         if (is_array($conf) && $GLOBALS['TCA'][$table] && $beUser->check('tables_modify', $table)) {
             // Traverse the keys/comments of each table (keys can be a commalist of uids)
             foreach ($conf as $cKey => $command) {
                 if ($command == 'edit' || $command == 'new') {
                     // Get the ids:
                     $ids = GeneralUtility::trimExplode(',', $cKey, true);
                     // Traverse the ids:
                     foreach ($ids as $theUid) {
                         // Don't save this document title in the document selector if the document is new.
                         if ($command === 'new') {
                             $this->dontStoreDocumentRef = 1;
                         }
                         /** @var TcaDatabaseRecord $formDataGroup */
                         $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
                         /** @var FormDataCompiler $formDataCompiler */
                         $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
                         /** @var NodeFactory $nodeFactory */
                         $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
                         try {
                             // Reset viewId - it should hold data of last entry only
                             $this->viewId = 0;
                             $this->viewId_addParams = '';
                             $formDataCompilerInput = ['tableName' => $table, 'vanillaUid' => (int) $theUid, 'command' => $command, 'returnUrl' => $this->R_URI];
                             if (is_array($this->overrideVals) && is_array($this->overrideVals[$table])) {
                                 $formDataCompilerInput['overrideValues'] = $this->overrideVals[$table];
                             }
                             $formData = $formDataCompiler->compile($formDataCompilerInput);
                             // Set this->viewId if possible
                             if ($command === 'new' && $table !== 'pages' && !empty($formData['parentPageRow']['uid'])) {
                                 $this->viewId = $formData['parentPageRow']['uid'];
                             } else {
                                 if ($table == 'pages') {
                                     $this->viewId = $formData['databaseRow']['uid'];
                                 } elseif (!empty($formData['parentPageRow']['uid'])) {
                                     $this->viewId = $formData['parentPageRow']['uid'];
                                     // Adding "&L=xx" if the record being edited has a languageField with a value larger than zero!
                                     if (!empty($formData['processedTca']['ctrl']['languageField']) && is_array($formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']]) && $formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']][0] > 0) {
                                         $this->viewId_addParams = '&L=' . $formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']][0];
                                     }
                                 }
                             }
                             // Determine if delete button can be shown
                             $deleteAccess = false;
                             if ($command === 'edit') {
                                 $permission = $formData['userPermissionOnPage'];
                                 if ($formData['tableName'] === 'pages') {
                                     $deleteAccess = $permission & Permission::PAGE_DELETE ? true : false;
                                 } else {
                                     $deleteAccess = $permission & Permission::CONTENT_EDIT ? true : false;
                                 }
                             }
                             // Display "is-locked" message:
                             if ($command === 'edit') {
                                 $lockInfo = BackendUtility::isRecordLocked($table, $formData['databaseRow']['uid']);
                                 if ($lockInfo) {
                                     /** @var $flashMessage \TYPO3\CMS\Core\Messaging\FlashMessage */
                                     $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, htmlspecialchars($lockInfo['msg']), '', FlashMessage::WARNING);
                                     /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
                                     $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
                                     /** @var $defaultFlashMessageQueue FlashMessageQueue */
                                     $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
                                     $defaultFlashMessageQueue->enqueue($flashMessage);
                                 }
                             }
                             // Record title
                             if (!$this->storeTitle) {
                                 $this->storeTitle = $this->recTitle ? htmlspecialchars($this->recTitle) : BackendUtility::getRecordTitle($table, FormEngineUtility::databaseRowCompatibility($formData['databaseRow']), true);
                             }
                             $this->elementsData[] = array('table' => $table, 'uid' => $formData['databaseRow']['uid'], 'pid' => $formData['databaseRow']['pid'], 'cmd' => $command, 'deleteAccess' => $deleteAccess);
                             if ($command !== 'new') {
                                 BackendUtility::lockRecords($table, $formData['databaseRow']['uid'], $table === 'tt_content' ? $formData['databaseRow']['pid'] : 0);
                             }
                             // Set list if only specific fields should be rendered. This will trigger
                             // ListOfFieldsContainer instead of FullRecordContainer in OuterWrapContainer
                             if ($this->columnsOnly) {
                                 if (is_array($this->columnsOnly)) {
                                     $formData['fieldListToRender'] = $this->columnsOnly[$table];
                                 } else {
                                     $formData['fieldListToRender'] = $this->columnsOnly;
                                 }
                             }
                             $formData['renderType'] = 'outerWrapContainer';
                             $formResult = $nodeFactory->create($formData)->render();
                             $html = $formResult['html'];
                             $formResult['html'] = '';
                             $formResult['doSaveFieldName'] = 'doSave';
                             // @todo: Put all the stuff into FormEngine as final "compiler" class
                             // @todo: This is done here for now to not rewrite JStop()
                             // @todo: and printNeededJSFunctions() now
                             $this->formResultCompiler->mergeResult($formResult);
                             // Seems the pid is set as hidden field (again) at end?!
                             if ($command == 'new') {
                                 // @todo: looks ugly
                                 $html .= LF . '<input type="hidden"' . ' name="data[' . $table . '][' . $formData['databaseRow']['uid'] . '][pid]"' . ' value="' . $formData['databaseRow']['pid'] . '" />';
                                 $this->newC++;
                             }
                             $editForm .= $html;
                         } catch (AccessDeniedException $e) {
                             $this->errorC++;
                             // Try to fetch error message from "recordInternals" be user object
                             // @todo: This construct should be logged and localized and de-uglified
                             $message = $beUser->errorMsg;
                             if (empty($message)) {
                                 // Create message from exception.
                                 $message = $e->getMessage() . ' ' . $e->getCode();
                             }
                             $editForm .= $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noEditPermission', true) . '<br /><br />' . htmlspecialchars($message) . '<br /><br />';
                         }
                     }
                     // End of for each uid
                 }
             }
         }
     }
     return $editForm;
 }
Beispiel #22
0
 /**
  * Determine the configuration and the type of a record selector.
  * This is a helper method for inline / IRRE handling
  *
  * @param array $conf TCA configuration of the parent(!) field
  * @param string $field Field name
  * @return array Associative array with the keys 'PA' and 'type', both are FALSE if the selector was not valid.
  * @internal
  */
 public static function getInlinePossibleRecordsSelectorConfig($conf, $field = '')
 {
     $foreign_table = $conf['foreign_table'];
     $foreign_selector = $conf['foreign_selector'];
     $PA = false;
     $type = false;
     $table = false;
     $selector = false;
     if ($field) {
         $PA = array();
         $PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$field];
         if ($PA['fieldConf'] && $conf['foreign_selector_fieldTcaOverride']) {
             ArrayUtility::mergeRecursiveWithOverrule($PA['fieldConf'], $conf['foreign_selector_fieldTcaOverride']);
         }
         $PA['fieldTSConfig'] = FormEngineUtility::getTSconfigForTableRow($foreign_table, array(), $field);
         $config = $PA['fieldConf']['config'];
         // Determine type of Selector:
         $type = static::getInlinePossibleRecordsSelectorType($config);
         // Return table on this level:
         $table = $type === 'select' ? $config['foreign_table'] : $config['allowed'];
         // Return type of the selector if foreign_selector is defined and points to the same field as in $field:
         if ($foreign_selector && $foreign_selector == $field && $type) {
             $selector = $type;
         }
     }
     return array('PA' => $PA, 'type' => $type, 'table' => $table, 'selector' => $selector);
 }
 /**
  * Entry method
  *
  * @return array As defined in initializeResultArray() of AbstractNode
  */
 public function render()
 {
     $backendUser = $this->getBackendUserAuthentication();
     $languageService = $this->getLanguageService();
     $resultArray = $this->initializeResultArray();
     $table = $this->data['tableName'];
     $row = $this->data['databaseRow'];
     $fieldName = $this->data['fieldName'];
     // @todo: it should be safe at this point, this array exists ...
     if (!is_array($this->data['processedTca']['columns'][$fieldName])) {
         return $resultArray;
     }
     $parameterArray = array();
     $parameterArray['fieldConf'] = $this->data['processedTca']['columns'][$fieldName];
     $isOverlay = false;
     // This field decides whether the current record is an overlay (as opposed to being a standalone record)
     // Based on this decision we need to trigger field exclusion or special rendering (like readOnly)
     if (isset($this->data['processedTca']['ctrl']['transOrigPointerField']) && is_array($this->data['processedTca']['columns'][$this->data['processedTca']['ctrl']['transOrigPointerField']]) && is_array($row[$this->data['processedTca']['ctrl']['transOrigPointerField']]) && $row[$this->data['processedTca']['ctrl']['transOrigPointerField']][0] > 0) {
         $isOverlay = true;
     }
     // A couple of early returns in case the field should not be rendered
     // Check if this field is configured and editable according to exclude fields and other configuration
     if ($parameterArray['fieldConf']['exclude'] && !$backendUser->check('non_exclude_fields', $table . ':' . $fieldName) || $parameterArray['fieldConf']['config']['type'] === 'passthrough' || !$backendUser->isRTE() && $parameterArray['fieldConf']['config']['showIfRTE'] || $isOverlay && !$parameterArray['fieldConf']['l10n_display'] && $parameterArray['fieldConf']['l10n_mode'] === 'exclude' || $isOverlay && $this->data['localizationMode'] && $this->data['localizationMode'] !== $parameterArray['fieldConf']['l10n_cat'] || $this->inlineFieldShouldBeSkipped()) {
         return $resultArray;
     }
     $parameterArray['fieldTSConfig'] = [];
     if (isset($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']) && is_array($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'])) {
         $parameterArray['fieldTSConfig'] = $this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'];
     }
     if ($parameterArray['fieldTSConfig']['disabled']) {
         return $resultArray;
     }
     // Override fieldConf by fieldTSconfig:
     $parameterArray['fieldConf']['config'] = FormEngineUtility::overrideFieldConf($parameterArray['fieldConf']['config'], $parameterArray['fieldTSConfig']);
     $parameterArray['itemFormElName'] = 'data[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
     $parameterArray['itemFormElID'] = 'data_' . $table . '_' . $row['uid'] . '_' . $fieldName;
     $newElementBaseName = $this->data['elementBaseName'] . '[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
     // The value to show in the form field.
     $parameterArray['itemFormElValue'] = $row[$fieldName];
     // Set field to read-only if configured for translated records to show default language content as readonly
     if ($parameterArray['fieldConf']['l10n_display'] && GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'defaultAsReadonly') && $isOverlay) {
         $parameterArray['fieldConf']['config']['readOnly'] = true;
         $parameterArray['itemFormElValue'] = $this->data['defaultLanguageRow'][$fieldName];
     }
     if (strpos($this->data['processedTca']['ctrl']['type'], ':') === false) {
         $typeField = $this->data['processedTca']['ctrl']['type'];
     } else {
         $typeField = substr($this->data['processedTca']['ctrl']['type'], 0, strpos($this->data['processedTca']['ctrl']['type'], ':'));
     }
     // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
     // This is used for eg. "type" fields and others configured with "requestUpdate"
     if (!empty($this->data['processedTca']['ctrl']['type']) && $fieldName === $typeField || !empty($this->data['processedTca']['ctrl']['requestUpdate']) && GeneralUtility::inList(str_replace(' ', '', $this->data['processedTca']['ctrl']['requestUpdate']), $fieldName)) {
         if ($backendUser->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
             $alertMsgOnChange = 'top.TYPO3.Modal.confirm(TBE_EDITOR.labels.refreshRequired.title, TBE_EDITOR.labels.refreshRequired.content).on("button.clicked", function(e) { if (e.target.name == "ok" && TBE_EDITOR.checkSubmit(-1)) { TBE_EDITOR.submitForm() } top.TYPO3.Modal.dismiss(); });';
         } else {
             $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
         }
     } else {
         $alertMsgOnChange = '';
     }
     // JavaScript code for event handlers:
     $parameterArray['fieldChangeFunc'] = array();
     $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'TBE_EDITOR.fieldChanged(' . GeneralUtility::quoteJSvalue($table) . ',' . GeneralUtility::quoteJSvalue($row['uid']) . ',' . GeneralUtility::quoteJSvalue($fieldName) . ',' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ');';
     $parameterArray['fieldChangeFunc']['alert'] = $alertMsgOnChange;
     // If this is the child of an inline type and it is the field creating the label
     if ($this->isInlineChildAndLabelField($table, $fieldName)) {
         /** @var InlineStackProcessor $inlineStackProcessor */
         $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
         $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
         $inlineDomObjectId = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
         $inlineObjectId = implode('-', array($inlineDomObjectId, $table, $row['uid']));
         $parameterArray['fieldChangeFunc']['inline'] = 'inline.handleChangedField(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ',' . GeneralUtility::quoteJSvalue($inlineObjectId) . ');';
     }
     // Based on the type of the item, call a render function on a child element
     $options = $this->data;
     $options['parameterArray'] = $parameterArray;
     $options['elementBaseName'] = $newElementBaseName;
     if (!empty($parameterArray['fieldConf']['config']['renderType'])) {
         $options['renderType'] = $parameterArray['fieldConf']['config']['renderType'];
     } else {
         // Fallback to type if no renderType is given
         $options['renderType'] = $parameterArray['fieldConf']['config']['type'];
     }
     $resultArray = $this->nodeFactory->create($options)->render();
     // If output is empty stop further processing.
     // This means there was internal processing only and we don't need to add additional information
     if (empty($resultArray['html'])) {
         return $resultArray;
     }
     $html = $resultArray['html'];
     // @todo: the language handling, the null and the placeholder stuff should be embedded in the single
     // @todo: element classes. Basically, this method should return here and have the element classes
     // @todo: decide on language stuff and other wraps already.
     // Add language + diff
     $renderLanguageDiff = true;
     if ($parameterArray['fieldConf']['l10n_display'] && (GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'hideDiff') || GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'defaultAsReadonly'))) {
         $renderLanguageDiff = false;
     }
     if ($renderLanguageDiff) {
         $html = $this->renderDefaultLanguageContent($table, $fieldName, $row, $html);
         $html = $this->renderDefaultLanguageDiff($table, $fieldName, $row, $html);
     }
     $fieldItemClasses = array('t3js-formengine-field-item');
     // NULL value and placeholder handling
     $nullControlNameAttribute = ' name="' . htmlspecialchars('control[active][' . $table . '][' . $row['uid'] . '][' . $fieldName . ']') . '"';
     if (!empty($parameterArray['fieldConf']['config']['eval']) && GeneralUtility::inList($parameterArray['fieldConf']['config']['eval'], 'null') && (empty($parameterArray['fieldConf']['config']['mode']) || $parameterArray['fieldConf']['config']['mode'] !== 'useOrOverridePlaceholder')) {
         // This field has eval=null set, but has no useOverridePlaceholder defined.
         // Goal is to have a field that can distinct between NULL and empty string in the database.
         // A checkbox and an additional hidden field will be created, both with the same name
         // and prefixed with "control[active]". If the checkbox is set (value 1), the value from the casual
         // input field will be written to the database. If the checkbox is not set, the hidden field
         // transfers value=0 to DataHandler, the value of the input field will then be reset to NULL by the
         // DataHandler at an early point in processing, so NULL will be written to DB as field value.
         // If the value of the field *is* NULL at the moment, an additional class is set
         // @todo: This does not work well at the moment, but is kept for now. see input_14 of ext:styleguide as example
         $checked = ' checked="checked"';
         if ($this->data['databaseRow'][$fieldName] === null) {
             $fieldItemClasses[] = 'disabled';
             $checked = '';
         }
         $formElementName = 'data[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
         $onChange = htmlspecialchars('typo3form.fieldSetNull(' . GeneralUtility::quoteJSvalue($formElementName) . ', !this.checked)');
         $nullValueWrap = array();
         $nullValueWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">';
         $nullValueWrap[] = '<div class="t3-form-field-disable"></div>';
         $nullValueWrap[] = '<div class="checkbox">';
         $nullValueWrap[] = '<label>';
         $nullValueWrap[] = '<input type="hidden"' . $nullControlNameAttribute . ' value="0" />';
         $nullValueWrap[] = '<input type="checkbox"' . $nullControlNameAttribute . ' value="1" onchange="' . $onChange . '"' . $checked . ' /> &nbsp;';
         $nullValueWrap[] = '</label>';
         $nullValueWrap[] = $html;
         $nullValueWrap[] = '</div>';
         $nullValueWrap[] = '</div>';
         $html = implode(LF, $nullValueWrap);
     } elseif (isset($parameterArray['fieldConf']['config']['mode']) && $parameterArray['fieldConf']['config']['mode'] === 'useOrOverridePlaceholder') {
         // This field has useOverridePlaceholder set.
         // Here, a value from a deeper DB structure can be "fetched up" as value, and can also be overridden by a
         // local value. This is used in FAL, where eg. the "title" field can have the default value from sys_file_metadata,
         // the title field of sys_file_reference is then set to NULL. Or the "override" checkbox is set, and a string
         // or an empty string is then written to the field of sys_file_reference.
         // The situation is similar to the NULL handling above, but additionally a "default" value should be shown.
         // To achieve this, again a hidden control[hidden] field is added together with a checkbox with the same name
         // to transfer the information whether the default value should be used or not: Checkbox checked transfers 1 as
         // value in control[active], meaning the overridden value should be used.
         // Additionally to the casual input field, a second field is added containing the "placeholder" value. This
         // field has no name attribute and is not transferred at all. Those two are then hidden / shown depending
         // on the state of the above checkbox in via JS.
         $placeholder = empty($parameterArray['fieldConf']['config']['placeholder']) ? '' : $parameterArray['fieldConf']['config']['placeholder'];
         $onChange = 'typo3form.fieldTogglePlaceholder(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ', !this.checked)';
         $checked = $parameterArray['itemFormElValue'] === null ? '' : ' checked="checked"';
         $resultArray['additionalJavaScriptPost'][] = 'typo3form.fieldTogglePlaceholder(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ', ' . ($checked ? 'false' : 'true') . ');';
         // Renders an input or textarea field depending on type of "parent"
         $options = array();
         $options['databaseRow'] = array();
         $options['table'] = '';
         $options['parameterArray'] = $parameterArray;
         $options['parameterArray']['itemFormElValue'] = GeneralUtility::fixed_lgd_cs($placeholder, 30);
         $options['renderType'] = 'none';
         $noneElementResult = $this->nodeFactory->create($options)->render();
         $noneElementHtml = $noneElementResult['html'];
         $placeholderWrap = array();
         $placeholderWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">';
         $placeholderWrap[] = '<div class="t3-form-field-disable"></div>';
         $placeholderWrap[] = '<div class="checkbox">';
         $placeholderWrap[] = '<label>';
         $placeholderWrap[] = '<input type="hidden"' . $nullControlNameAttribute . ' value="0" />';
         $placeholderWrap[] = '<input type="checkbox"' . $nullControlNameAttribute . ' value="1" id="tce-forms-textfield-use-override-' . $fieldName . '-' . $row['uid'] . '" onchange="' . htmlspecialchars($onChange) . '"' . $checked . ' />';
         $placeholderWrap[] = sprintf($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.placeholder.override'), BackendUtility::getRecordTitlePrep($placeholder, 20));
         $placeholderWrap[] = '</label>';
         $placeholderWrap[] = '</div>';
         $placeholderWrap[] = '<div class="t3js-formengine-placeholder-placeholder">';
         $placeholderWrap[] = $noneElementHtml;
         $placeholderWrap[] = '</div>';
         $placeholderWrap[] = '<div class="t3js-formengine-placeholder-formfield">';
         $placeholderWrap[] = $html;
         $placeholderWrap[] = '</div>';
         $placeholderWrap[] = '</div>';
         $html = implode(LF, $placeholderWrap);
     } elseif ($parameterArray['fieldConf']['config']['type'] !== 'user' || empty($parameterArray['fieldConf']['config']['noTableWrapping'])) {
         // Add a casual wrap if the field is not of type user with no wrap requested.
         $standardWrap = array();
         $standardWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">';
         $standardWrap[] = '<div class="t3-form-field-disable"></div>';
         $standardWrap[] = $html;
         $standardWrap[] = '</div>';
         $html = implode(LF, $standardWrap);
     }
     $resultArray['html'] = $html;
     return $resultArray;
 }
    /**
     * Get a selector as used for the select type, to select from all available
     * records and to create a relation to the embedding record (e.g. like MM).
     *
     * @param array $selItems Array of all possible records
     * @param array $conf TCA configuration of the parent(!) field
     * @param array $PA An array with additional configuration options
     * @param array $uniqueIds The uids that have already been used and should be unique
     * @return string A HTML <select> box with all possible records
     */
    protected function renderPossibleRecordsSelectorTypeSelect($selItems, $conf, &$PA, $uniqueIds = array())
    {
        $foreign_table = $conf['foreign_table'];
        $foreign_selector = $conf['foreign_selector'];
        $PA = array();
        $PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector];
        $PA['fieldTSConfig'] = FormEngineUtility::getTSconfigForTableRow($foreign_table, array(), $foreign_selector);
        $config = $PA['fieldConf']['config'];
        $item = '';
        // @todo $disabled is not present - should be read from config?
        $disabled = false;
        if (!$disabled) {
            $nameObject = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
            // Create option tags:
            $opt = array();
            foreach ($selItems as $p) {
                if (!in_array($p[1], $uniqueIds)) {
                    $opt[] = '<option value="' . htmlspecialchars($p[1]) . '">' . htmlspecialchars($p[0]) . '</option>';
                }
            }
            // Put together the selector box:
            $itemListStyle = isset($config['itemListStyle']) ? ' style="' . htmlspecialchars($config['itemListStyle']) . '"' : '';
            $size = (int) $conf['size'];
            $size = $conf['autoSizeMax'] ? MathUtility::forceIntegerInRange(count($selItems) + 1, MathUtility::forceIntegerInRange($size, 1), $conf['autoSizeMax']) : $size;
            $onChange = 'return inline.importNewRecord(' . GeneralUtility::quoteJSvalue($nameObject . '-' . $conf['foreign_table']) . ')';
            $item = '
				<select id="' . $nameObject . '-' . $conf['foreign_table'] . '_selector" class="form-control"' . ($size ? ' size="' . $size . '"' : '') . ' onchange="' . htmlspecialchars($onChange) . '"' . $PA['onFocus'] . $itemListStyle . ($conf['foreign_unique'] ? ' isunique="isunique"' : '') . '>
					' . implode('', $opt) . '
				</select>';
            if ($size <= 1) {
                // Add a "Create new relation" link for adding new relations
                // This is necessary, if the size of the selector is "1" or if
                // there is only one record item in the select-box, that is selected by default
                // The selector-box creates a new relation on using an onChange event (see some line above)
                if (!empty($conf['appearance']['createNewRelationLinkTitle'])) {
                    $createNewRelationText = $this->getLanguageService()->sL($conf['appearance']['createNewRelationLinkTitle'], true);
                } else {
                    $createNewRelationText = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.createNewRelation', true);
                }
                $item .= '
				<span class="input-group-btn">
					<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($onChange) . '" title="' . $createNewRelationText . '">
						' . $this->iconFactory->getIcon('actions-document-new', Icon::SIZE_SMALL)->render() . $createNewRelationText . '
					</a>
				</span>';
            } else {
                $item .= '
				<span class="input-group-btn btn"></span>';
            }
            // Wrap the selector and add a spacer to the bottom
            $item = '<div class="input-group form-group t3js-formengine-validation-marker ' . $this->inlineData['config'][$nameObject]['md5'] . '">' . $item . '</div>';
        }
        return $item;
    }
    /**
     * 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;
    }
    /**
     * Rendering wizards for form fields.
     *
     * @param array $itemKinds Array with the real item in the first value, and an alternative item in the second value.
     * @param array $wizConf The "wizard" key from the config array for the field (from TCA)
     * @param string $table Table name
     * @param array $row The record array
     * @param string $field The field name
     * @param array $PA Additional configuration array.
     * @param string $itemName The field name
     * @param array $specConf Special configuration if available.
     * @param bool $RTE Whether the RTE could have been loaded.
     * @return string The new item value.
     */
    protected function renderWizards($itemKinds, $wizConf, $table, $row, $field, $PA, $itemName, $specConf, $RTE = FALSE)
    {
        // Return not changed main item directly if wizards are disabled
        if (!is_array($wizConf) || $this->isWizardsDisabled()) {
            return $itemKinds[0];
        }
        $languageService = $this->getLanguageService();
        $fieldChangeFunc = $PA['fieldChangeFunc'];
        $item = $itemKinds[0];
        $fName = '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
        $md5ID = 'ID' . GeneralUtility::shortmd5($itemName);
        $fieldConfig = $PA['fieldConf']['config'];
        $prefixOfFormElName = 'data[' . $table . '][' . $row['uid'] . '][' . $field . ']';
        $flexFormPath = '';
        if (GeneralUtility::isFirstPartOfStr($PA['itemFormElName'], $prefixOfFormElName)) {
            $flexFormPath = str_replace('][', '/', substr($PA['itemFormElName'], strlen($prefixOfFormElName) + 1, -1));
        }
        // Manipulate the field name (to be the TRUE form field name) and remove
        // a suffix-value if the item is a selector box with renderMode "singlebox":
        $listFlag = '_list';
        if ($PA['fieldConf']['config']['type'] == 'select') {
            // Single select situation:
            if ($PA['fieldConf']['config']['maxitems'] <= 1) {
                $listFlag = '';
            } elseif ($PA['fieldConf']['config']['renderMode'] == 'singlebox') {
                $itemName .= '[]';
                $listFlag = '';
            }
        }
        // Contains wizard identifiers enabled for this record type, see "special configuration" docs
        $wizardsEnabledByType = $specConf['wizards']['parameters'];
        $buttonWizards = array();
        $otherWizards = array();
        foreach ($wizConf as $wizardIdentifier => $wizardConfiguration) {
            // If an identifier starts with "_", this is a configuration option like _POSITION and not a wizard
            if ($wizardIdentifier[0] === '_') {
                continue;
            }
            // Sanitize wizard type
            $wizardConfiguration['type'] = (string) $wizardConfiguration['type'];
            // Wizards can be shown based on selected "type" of record. If this is the case, the wizard configuration
            // is set to enableByTypeConfig = 1, and the wizardIdentifier is found in $wizardsEnabledByType
            $wizardIsEnabled = TRUE;
            if (isset($wizardConfiguration['enableByTypeConfig']) && (bool) $wizardConfiguration['enableByTypeConfig'] && (!is_array($wizardsEnabledByType) || !in_array($wizardIdentifier, $wizardsEnabledByType))) {
                $wizardIsEnabled = FALSE;
            }
            // Disable if wizard is for RTE fields only and the handled field is no RTE field or RTE can not be loaded
            if (isset($wizardConfiguration['RTEonly']) && (bool) $wizardConfiguration['RTEonly'] && !$RTE) {
                $wizardIsEnabled = FALSE;
            }
            // Disable if wizard is for not-new records only and we're handling a new record
            if (isset($wizardConfiguration['notNewRecords']) && $wizardConfiguration['notNewRecords'] && !MathUtility::canBeInterpretedAsInteger($row['uid'])) {
                $wizardIsEnabled = FALSE;
            }
            // Wizard types script, colorbox and popup must contain a module name configuration
            if (!isset($wizardConfiguration['module']['name']) && in_array($wizardConfiguration['type'], array('script', 'colorbox', 'popup'), TRUE)) {
                $wizardIsEnabled = FALSE;
            }
            if (!$wizardIsEnabled) {
                continue;
            }
            // Title / icon:
            $iTitle = htmlspecialchars($languageService->sL($wizardConfiguration['title']));
            if (isset($wizardConfiguration['icon'])) {
                $icon = FormEngineUtility::getIconHtml($wizardConfiguration['icon'], $iTitle, $iTitle);
            } else {
                $icon = $iTitle;
            }
            switch ($wizardConfiguration['type']) {
                case 'userFunc':
                    $params = array();
                    $params['fieldConfig'] = $fieldConfig;
                    $params['params'] = $wizardConfiguration['params'];
                    $params['exampleImg'] = $wizardConfiguration['exampleImg'];
                    $params['table'] = $table;
                    $params['uid'] = $row['uid'];
                    $params['pid'] = $row['pid'];
                    $params['field'] = $field;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['returnUrl'] = $this->getReturnUrl();
                    $params['formName'] = 'editform';
                    $params['itemName'] = $itemName;
                    $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
                    $params['fieldChangeFunc'] = $fieldChangeFunc;
                    $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
                    $params['item'] =& $item;
                    $params['icon'] = $icon;
                    $params['iTitle'] = $iTitle;
                    $params['wConf'] = $wizardConfiguration;
                    $params['row'] = $row;
                    $formEngineDummy = new FormEngine();
                    $otherWizards[] = GeneralUtility::callUserFunction($wizardConfiguration['userFunc'], $params, $formEngineDummy);
                    break;
                case 'script':
                    $params = array();
                    // Including the full fieldConfig from TCA may produce too long an URL
                    if ($wizardIdentifier != 'RTE') {
                        $params['fieldConfig'] = $fieldConfig;
                    }
                    $params['params'] = $wizardConfiguration['params'];
                    $params['exampleImg'] = $wizardConfiguration['exampleImg'];
                    $params['table'] = $table;
                    $params['uid'] = $row['uid'];
                    $params['pid'] = $row['pid'];
                    $params['field'] = $field;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['returnUrl'] = $this->getReturnUrl();
                    // Resolving script filename and setting URL.
                    $urlParameters = array();
                    if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
                        $urlParameters = $wizardConfiguration['module']['urlParameters'];
                    }
                    $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
                    $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', array('P' => $params));
                    $buttonWizards[] = '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" onclick="this.blur(); return !TBE_EDITOR.isFormChanged();">' . $icon . '</a>';
                    break;
                case 'popup':
                    $params = array();
                    $params['fieldConfig'] = $fieldConfig;
                    $params['params'] = $wizardConfiguration['params'];
                    $params['exampleImg'] = $wizardConfiguration['exampleImg'];
                    $params['table'] = $table;
                    $params['uid'] = $row['uid'];
                    $params['pid'] = $row['pid'];
                    $params['field'] = $field;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['returnUrl'] = $this->getReturnUrl();
                    $params['formName'] = 'editform';
                    $params['itemName'] = $itemName;
                    $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
                    $params['fieldChangeFunc'] = $fieldChangeFunc;
                    $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
                    // Resolving script filename and setting URL.
                    $urlParameters = array();
                    if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
                        $urlParameters = $wizardConfiguration['module']['urlParameters'];
                    }
                    $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
                    $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', array('P' => $params));
                    $onlyIfSelectedJS = '';
                    if (isset($wizardConfiguration['popup_onlyOpenIfSelected']) && $wizardConfiguration['popup_onlyOpenIfSelected']) {
                        $notSelectedText = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:mess.noSelItemForEdit');
                        $onlyIfSelectedJS = 'if (!TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName . $listFlag) . ')){' . 'alert(' . GeneralUtility::quoteJSvalue($notSelectedText) . ');' . 'return false;' . '}';
                    }
                    $aOnClick = 'this.blur();' . $onlyIfSelectedJS . 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($url) . '+\'&P[currentValue]=\'+TBE_EDITOR.rawurlencode(' . 'document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value,200' . ')' . '+\'&P[currentSelectedValues]=\'+TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName . $listFlag) . '),' . GeneralUtility::quoteJSvalue('popUp' . $md5ID) . ',' . GeneralUtility::quoteJSvalue($wizardConfiguration['JSopenParams']) . ');' . 'vHWin.focus();' . 'return false;';
                    $buttonWizards[] = '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $icon . '</a>';
                    break;
                case 'colorbox':
                    $params = array();
                    $params['fieldConfig'] = $fieldConfig;
                    $params['params'] = $wizardConfiguration['params'];
                    $params['exampleImg'] = $wizardConfiguration['exampleImg'];
                    $params['table'] = $table;
                    $params['uid'] = $row['uid'];
                    $params['pid'] = $row['pid'];
                    $params['field'] = $field;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['returnUrl'] = $this->getReturnUrl();
                    $params['formName'] = 'editform';
                    $params['itemName'] = $itemName;
                    $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
                    $params['fieldChangeFunc'] = $fieldChangeFunc;
                    $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
                    // Resolving script filename and setting URL.
                    $urlParameters = array();
                    if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
                        $urlParameters = $wizardConfiguration['module']['urlParameters'];
                    }
                    $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
                    $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', array('P' => $params));
                    $aOnClick = 'this.blur();' . 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($url) . '+\'&P[currentValue]=\'+TBE_EDITOR.rawurlencode(' . 'document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value,200' . ')' . '+\'&P[currentSelectedValues]=\'+TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName . $listFlag) . '),' . GeneralUtility::quoteJSvalue('popUp' . $md5ID) . ',' . GeneralUtility::quoteJSvalue($wizardConfiguration['JSopenParams']) . ');' . 'vHWin.focus();' . 'return false;';
                    $otherWizards[] = '<a id="' . $md5ID . '" class="btn btn-default" href="#" onclick="' . htmlspecialchars($aOnClick) . '"><span class="t3-icon fa fa-eyedropper"></span></a>';
                    break;
                case 'slider':
                    $params = array();
                    $params['fieldConfig'] = $fieldConfig;
                    $params['field'] = $field;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['itemName'] = $itemName;
                    $params['fieldChangeFunc'] = $fieldChangeFunc;
                    $params['wConf'] = $wizardConfiguration;
                    $params['row'] = $row;
                    /** @var ValueSliderWizard $wizard */
                    $wizard = GeneralUtility::makeInstance(ValueSliderWizard::class);
                    $otherWizards[] = $wizard->renderWizard($params);
                    break;
                case 'select':
                    $fieldValue = array('config' => $wizardConfiguration);
                    $TSconfig = FormEngineUtility::getTSconfigForTableRow($table, $row);
                    $TSconfig[$field] = $TSconfig[$field]['wizards.'][$wizardIdentifier . '.'];
                    $selItems = FormEngineUtility::addSelectOptionsToItemArray(FormEngineUtility::initItemArray($fieldValue), $fieldValue, $TSconfig, $field);
                    // Process items by a user function:
                    if (!empty($wizardConfiguration['itemsProcFunc'])) {
                        $funcConfig = !empty($wizardConfiguration['itemsProcFunc.']) ? $wizardConfiguration['itemsProcFunc.'] : array();
                        $dataPreprocessor = GeneralUtility::makeInstance(DataPreprocessor::class);
                        $selItems = $dataPreprocessor->procItems($selItems, $funcConfig, $wizardConfiguration, $table, $row, $field);
                    }
                    $options = array();
                    $options[] = '<option>' . $iTitle . '</option>';
                    foreach ($selItems as $p) {
                        $options[] = '<option value="' . htmlspecialchars($p[1]) . '">' . htmlspecialchars($p[0]) . '</option>';
                    }
                    if ($wizardConfiguration['mode'] == 'append') {
                        $assignValue = 'document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value=\'\'+this.options[this.selectedIndex].value+document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value';
                    } elseif ($wizardConfiguration['mode'] == 'prepend') {
                        $assignValue = 'document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value+=\'\'+this.options[this.selectedIndex].value';
                    } else {
                        $assignValue = 'document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value=this.options[this.selectedIndex].value';
                    }
                    $otherWizards[] = '<select' . ' id="' . str_replace('.', '', uniqid('tceforms-select-', TRUE)) . '"' . ' class="form-control tceforms-select tceforms-wizardselect"' . ' name="_WIZARD' . $fName . '"' . ' onchange="' . htmlspecialchars($assignValue . ';this.blur();this.selectedIndex=0;' . implode('', $fieldChangeFunc)) . '"' . '>' . implode('', $options) . '</select>';
                    break;
                case 'suggest':
                    if (!empty($PA['fieldTSConfig']['suggest.']['default.']['hide'])) {
                        break;
                    }
                    /** @var SuggestWizard $suggestWizard */
                    $suggestWizard = GeneralUtility::makeInstance(SuggestWizard::class);
                    $otherWizards[] = $suggestWizard->renderSuggestSelector($PA['itemFormElName'], $table, $field, $row, $PA);
                    break;
            }
            // Hide the real form element?
            if (is_array($wizardConfiguration['hideParent']) || $wizardConfiguration['hideParent']) {
                // Setting the item to a hidden-field.
                $item = $itemKinds[1];
                if (is_array($wizardConfiguration['hideParent'])) {
                    $options = $this->globalOptions;
                    $options['parameterArray'] = array('fieldConf' => array('config' => $wizardConfiguration['hideParent']), 'itemFormElValue' => $PA['itemFormElValue']);
                    $options['renderType'] = 'none';
                    /** @var NodeFactory $nodeFactory */
                    $nodeFactory = $this->globalOptions['nodeFactory'];
                    $noneElementResult = $nodeFactory->create($options)->render();
                    $item .= $noneElementResult['html'];
                }
            }
        }
        // For each rendered wizard, put them together around the item.
        if (!empty($buttonWizards) || !empty($otherWizards)) {
            if ($wizConf['_HIDDENFIELD']) {
                $item = $itemKinds[1];
            }
            $innerContent = '';
            if (!empty($buttonWizards)) {
                $innerContent .= '<div class="btn-group' . ($wizConf['_VERTICAL'] ? ' btn-group-vertical' : '') . '">' . implode('', $buttonWizards) . '</div>';
            }
            $innerContent .= implode(' ', $otherWizards);
            // Position
            $classes = array('form-wizards-wrap');
            if ($wizConf['_POSITION'] === 'left') {
                $classes[] = 'form-wizards-aside';
                $innerContent = '<div class="form-wizards-items">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
            } elseif ($wizConf['_POSITION'] === 'top') {
                $classes[] = 'form-wizards-top';
                $innerContent = '<div class="form-wizards-items">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
            } elseif ($wizConf['_POSITION'] === 'bottom') {
                $classes[] = 'form-wizards-bottom';
                $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items">' . $innerContent . '</div>';
            } else {
                $classes[] = 'form-wizards-aside';
                $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items">' . $innerContent . '</div>';
            }
            $item = '
				<div class="' . implode(' ', $classes) . '">
					' . $innerContent . '
				</div>';
        }
        return $item;
    }
Beispiel #27
0
    /**
     * Rendering wizards for form fields.
     *
     * @param array $itemKinds Array with the real item in the first value
     * @param array $wizConf The "wizards" key from the config array for the field (from TCA)
     * @param string $table Table name
     * @param array $row The record array
     * @param string $field The field name
     * @param array $PA Additional configuration array.
     * @param string $itemName The field name
     * @param array $specConf Special configuration if available.
     * @param bool $RTE Whether the RTE could have been loaded.
     *
     * @return string The new item value.
     * @throws \InvalidArgumentException
     */
    protected function renderWizards($itemKinds, $wizConf, $table, $row, $field, $PA, $itemName, $specConf, $RTE = false)
    {
        // Return not changed main item directly if wizards are disabled
        if (!is_array($wizConf) || $this->isWizardsDisabled()) {
            return $itemKinds[0];
        }
        $languageService = $this->getLanguageService();
        $fieldChangeFunc = $PA['fieldChangeFunc'];
        $item = $itemKinds[0];
        $md5ID = 'ID' . GeneralUtility::shortMD5($itemName);
        $prefixOfFormElName = 'data[' . $table . '][' . $row['uid'] . '][' . $field . ']';
        $flexFormPath = '';
        if (GeneralUtility::isFirstPartOfStr($PA['itemFormElName'], $prefixOfFormElName)) {
            $flexFormPath = str_replace('][', '/', substr($PA['itemFormElName'], strlen($prefixOfFormElName) + 1, -1));
        }
        // Add a suffix-value if the item is a selector box with renderType "selectSingleBox":
        if ($PA['fieldConf']['config']['type'] === 'select' && (int) $PA['fieldConf']['config']['maxitems'] > 1 && $PA['fieldConf']['config']['renderType'] === 'selectSingleBox') {
            $itemName .= '[]';
        }
        // Contains wizard identifiers enabled for this record type, see "special configuration" docs
        $wizardsEnabledByType = $specConf['wizards']['parameters'];
        $buttonWizards = [];
        $otherWizards = [];
        foreach ($wizConf as $wizardIdentifier => $wizardConfiguration) {
            if (!isset($wizardConfiguration['module']['name']) && isset($wizardConfiguration['script'])) {
                throw new \InvalidArgumentException('The way registering a wizard in TCA has changed in 6.2 and was removed in CMS 7. ' . 'Please set module[name]=module_name instead of using script=path/to/script.php in your TCA. ', 1437750231);
            }
            // If an identifier starts with "_", this is a configuration option like _POSITION and not a wizard
            if ($wizardIdentifier[0] === '_') {
                continue;
            }
            // Sanitize wizard type
            $wizardConfiguration['type'] = (string) $wizardConfiguration['type'];
            // Wizards can be shown based on selected "type" of record. If this is the case, the wizard configuration
            // is set to enableByTypeConfig = 1, and the wizardIdentifier is found in $wizardsEnabledByType
            $wizardIsEnabled = true;
            if (isset($wizardConfiguration['enableByTypeConfig']) && (bool) $wizardConfiguration['enableByTypeConfig'] && (!is_array($wizardsEnabledByType) || !in_array($wizardIdentifier, $wizardsEnabledByType))) {
                $wizardIsEnabled = false;
            }
            // Disable if wizard is for RTE fields only and the handled field is no RTE field or RTE can not be loaded
            if (isset($wizardConfiguration['RTEonly']) && (bool) $wizardConfiguration['RTEonly'] && !$RTE) {
                $wizardIsEnabled = false;
            }
            // Disable if wizard is for not-new records only and we're handling a new record
            if (isset($wizardConfiguration['notNewRecords']) && $wizardConfiguration['notNewRecords'] && !MathUtility::canBeInterpretedAsInteger($row['uid'])) {
                $wizardIsEnabled = false;
            }
            // Wizard types script, colorbox and popup must contain a module name configuration
            if (!isset($wizardConfiguration['module']['name']) && in_array($wizardConfiguration['type'], ['script', 'colorbox', 'popup'], true)) {
                $wizardIsEnabled = false;
            }
            if (!$wizardIsEnabled) {
                continue;
            }
            // Title / icon:
            $iTitle = htmlspecialchars($languageService->sL($wizardConfiguration['title']));
            if (isset($wizardConfiguration['icon'])) {
                $icon = FormEngineUtility::getIconHtml($wizardConfiguration['icon'], $iTitle, $iTitle);
            } else {
                $icon = $iTitle;
            }
            switch ($wizardConfiguration['type']) {
                case 'userFunc':
                    $params = [];
                    $params['params'] = $wizardConfiguration['params'];
                    $params['exampleImg'] = $wizardConfiguration['exampleImg'];
                    $params['table'] = $table;
                    $params['uid'] = $row['uid'];
                    $params['pid'] = $row['pid'];
                    $params['field'] = $field;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['returnUrl'] = $this->data['returnUrl'];
                    $params['formName'] = 'editform';
                    $params['itemName'] = $itemName;
                    $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
                    $params['fieldChangeFunc'] = $fieldChangeFunc;
                    $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
                    $params['item'] =& $item;
                    $params['icon'] = $icon;
                    $params['iTitle'] = $iTitle;
                    $params['wConf'] = $wizardConfiguration;
                    $params['row'] = $row;
                    $otherWizards[] = GeneralUtility::callUserFunction($wizardConfiguration['userFunc'], $params, $this);
                    break;
                case 'script':
                    $params = [];
                    $params['params'] = $wizardConfiguration['params'];
                    $params['exampleImg'] = $wizardConfiguration['exampleImg'];
                    $params['table'] = $table;
                    $params['uid'] = $row['uid'];
                    $params['pid'] = $row['pid'];
                    $params['field'] = $field;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['returnUrl'] = $this->data['returnUrl'];
                    // Resolving script filename and setting URL.
                    $urlParameters = [];
                    if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
                        $urlParameters = $wizardConfiguration['module']['urlParameters'];
                    }
                    $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
                    $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', ['P' => $params]);
                    $buttonWizards[] = '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" onclick="this.blur(); return !TBE_EDITOR.isFormChanged();">' . $icon . '</a>';
                    break;
                case 'popup':
                    $params = [];
                    $params['params'] = $wizardConfiguration['params'];
                    $params['exampleImg'] = $wizardConfiguration['exampleImg'];
                    $params['table'] = $table;
                    $params['uid'] = $row['uid'];
                    $params['pid'] = $row['pid'];
                    $params['field'] = $field;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['returnUrl'] = $this->data['returnUrl'];
                    $params['formName'] = 'editform';
                    $params['itemName'] = $itemName;
                    $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
                    $params['fieldChangeFunc'] = $fieldChangeFunc;
                    $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
                    // Resolving script filename and setting URL.
                    $urlParameters = [];
                    if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
                        $urlParameters = $wizardConfiguration['module']['urlParameters'];
                    }
                    $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
                    $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', ['P' => $params]);
                    $onlyIfSelectedJS = '';
                    if (isset($wizardConfiguration['popup_onlyOpenIfSelected']) && $wizardConfiguration['popup_onlyOpenIfSelected']) {
                        $notSelectedText = $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.noSelItemForEdit');
                        $onlyIfSelectedJS = 'if (!TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName) . ')){' . 'alert(' . GeneralUtility::quoteJSvalue($notSelectedText) . ');' . 'return false;' . '}';
                    }
                    $aOnClick = 'this.blur();' . $onlyIfSelectedJS . 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($url) . '+\'&P[currentValue]=\'+TBE_EDITOR.rawurlencode(' . 'document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value,300' . ')' . '+\'&P[currentSelectedValues]=\'+TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName) . '),' . GeneralUtility::quoteJSvalue('popUp' . $md5ID) . ',' . GeneralUtility::quoteJSvalue($wizardConfiguration['JSopenParams']) . ');' . 'vHWin.focus();' . 'return false;';
                    $buttonWizards[] = '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $icon . '</a>';
                    break;
                case 'slider':
                    $params = [];
                    $params['fieldConfig'] = $PA['fieldConf']['config'];
                    $params['field'] = $field;
                    $params['table'] = $table;
                    $params['flexFormPath'] = $flexFormPath;
                    $params['md5ID'] = $md5ID;
                    $params['itemName'] = $itemName;
                    $params['wConf'] = $wizardConfiguration;
                    $params['row'] = $row;
                    /** @var ValueSliderWizard $wizard */
                    $wizard = GeneralUtility::makeInstance(ValueSliderWizard::class);
                    $otherWizards[] = $wizard->renderWizard($params);
                    break;
                case 'select':
                    // The select wizard is a select drop down added to the main element. It provides all the functionality
                    // that select items can do for us, so we process this element via data processing.
                    // @todo: This should be embedded in an own provider called in the main data group to not handle this on the fly here
                    // Select wizard page TS can be set in TCEFORM."table"."field".wizards."wizardName"
                    $pageTsConfig = [];
                    if (isset($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$field . '.']['wizards.'][$wizardIdentifier . '.']) && is_array($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$field . '.']['wizards.'][$wizardIdentifier . '.'])) {
                        $pageTsConfig['TCEFORM.']['dummySelectWizard.'][$wizardIdentifier . '.'] = $this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$field . '.']['wizards.'][$wizardIdentifier . '.'];
                    }
                    $selectWizardDataInput = ['tableName' => 'dummySelectWizard', 'command' => 'edit', 'pageTsConfig' => $pageTsConfig, 'processedTca' => ['ctrl' => [], 'columns' => [$wizardIdentifier => ['type' => 'select', 'renderType' => 'selectSingle', 'config' => $wizardConfiguration]]]];
                    /** @var OnTheFly $formDataGroup */
                    $formDataGroup = GeneralUtility::makeInstance(OnTheFly::class);
                    $formDataGroup->setProviderList([TcaSelectItems::class]);
                    /** @var FormDataCompiler $formDataCompiler */
                    $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
                    $compilerResult = $formDataCompiler->compile($selectWizardDataInput);
                    $selectWizardItems = $compilerResult['processedTca']['columns'][$wizardIdentifier]['config']['items'];
                    $options = [];
                    $options[] = '<option>' . $iTitle . '</option>';
                    foreach ($selectWizardItems as $selectWizardItem) {
                        $options[] = '<option value="' . htmlspecialchars($selectWizardItem[1]) . '">' . htmlspecialchars($selectWizardItem[0]) . '</option>';
                    }
                    if ($wizardConfiguration['mode'] == 'append') {
                        $assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0].value=\'\'+this.options[this.selectedIndex].value+document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value';
                    } elseif ($wizardConfiguration['mode'] == 'prepend') {
                        $assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0].value+=\'\'+this.options[this.selectedIndex].value';
                    } else {
                        $assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0].value=this.options[this.selectedIndex].value';
                    }
                    $otherWizards[] = '<select' . ' id="' . StringUtility::getUniqueId('tceforms-select-') . '"' . ' class="form-control tceforms-select tceforms-wizardselect"' . ' onchange="' . htmlspecialchars($assignValue . ';this.blur();this.selectedIndex=0;' . implode('', $fieldChangeFunc)) . '"' . '>' . implode('', $options) . '</select>';
                    break;
                case 'suggest':
                    if (!empty($PA['fieldTSConfig']['suggest.']['default.']['hide'])) {
                        break;
                    }
                    // The suggest wizard needs to know if we're in flex form scope to use the dataStructureIdentifier.
                    // If so, add the processedTca of the flex config as wizard argument.
                    $flexFormConfig = [];
                    if ($this->data['processedTca']['columns'][$field]['config']['type'] === 'flex') {
                        $flexFormConfig = $this->data['processedTca']['columns'][$field];
                    }
                    /** @var SuggestWizard $suggestWizard */
                    $suggestWizard = GeneralUtility::makeInstance(SuggestWizard::class);
                    $otherWizards[] = $suggestWizard->renderSuggestSelector($PA['itemFormElName'], $table, $field, $row, $PA, $flexFormConfig);
                    break;
            }
        }
        // For each rendered wizard, put them together around the item.
        if (!empty($buttonWizards) || !empty($otherWizards)) {
            $innerContent = '';
            if (!empty($buttonWizards)) {
                $innerContent .= '<div class="btn-group' . ($wizConf['_VERTICAL'] ? ' btn-group-vertical' : '') . '">' . implode('', $buttonWizards) . '</div>';
            }
            $innerContent .= implode(' ', $otherWizards);
            // Position
            $classes = ['form-wizards-wrap'];
            if ($wizConf['_POSITION'] === 'left') {
                $classes[] = 'form-wizards-aside';
                $innerContent = '<div class="form-wizards-items">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
            } elseif ($wizConf['_POSITION'] === 'top') {
                $classes[] = 'form-wizards-top';
                $innerContent = '<div class="form-wizards-items">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
            } elseif ($wizConf['_POSITION'] === 'bottom') {
                $classes[] = 'form-wizards-bottom';
                $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items">' . $innerContent . '</div>';
            } else {
                $classes[] = 'form-wizards-aside';
                $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items">' . $innerContent . '</div>';
            }
            $item = '
				<div class="' . implode(' ', $classes) . '">
					' . $innerContent . '
				</div>';
        }
        return $item;
    }
 /**
  * Do processing of data, submitting it to TCEmain.
  *
  * @return void
  */
 public function processData()
 {
     $beUser = $this->getBackendUser();
     // GPvars specifically for processing:
     $control = GeneralUtility::_GP('control');
     $this->data = GeneralUtility::_GP('data');
     $this->cmd = GeneralUtility::_GP('cmd');
     $this->mirror = GeneralUtility::_GP('mirror');
     $this->cacheCmd = GeneralUtility::_GP('cacheCmd');
     $this->redirect = GeneralUtility::_GP('redirect');
     $this->returnNewPageId = GeneralUtility::_GP('returnNewPageId');
     $this->vC = GeneralUtility::_GP('vC');
     // See tce_db.php for relevate options here:
     // Only options related to $this->data submission are included here.
     /** @var $tce \TYPO3\CMS\Core\DataHandling\DataHandler */
     $tce = GeneralUtility::makeInstance(DataHandler::class);
     $tce->stripslashes_values = FALSE;
     if (!empty($control)) {
         $tce->setControl($control);
     }
     if (isset($_POST['_translation_savedok_x'])) {
         $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
     }
     if (isset($_POST['_translation_savedokclear_x'])) {
         $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
         $tce->updateModeL10NdiffDataClear = TRUE;
     }
     // Setting default values specific for the user:
     $TCAdefaultOverride = $beUser->getTSConfigProp('TCAdefaults');
     if (is_array($TCAdefaultOverride)) {
         $tce->setDefaultsFromUserTS($TCAdefaultOverride);
     }
     // Setting internal vars:
     if ($beUser->uc['neverHideAtCopy']) {
         $tce->neverHideAtCopy = 1;
     }
     // Loading TCEmain with data:
     $tce->start($this->data, $this->cmd);
     if (is_array($this->mirror)) {
         $tce->setMirror($this->mirror);
     }
     // Checking referer / executing
     $refInfo = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
     $httpHost = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY');
     if ($httpHost != $refInfo['host'] && $this->vC != $beUser->veriCode() && !$GLOBALS['TYPO3_CONF_VARS']['SYS']['doNotCheckReferer']) {
         $tce->log('', 0, 0, 0, 1, 'Referer host \'%s\' and server host \'%s\' did not match and veriCode was not valid either!', 1, array($refInfo['host'], $httpHost));
         debug('Error: Referer host did not match with server host.');
     } else {
         // Perform the saving operation with TCEmain:
         $tce->process_uploads($_FILES);
         $tce->process_datamap();
         $tce->process_cmdmap();
         // If pages are being edited, we set an instruction about updating the page tree after this operation.
         if ($tce->pagetreeNeedsRefresh && (isset($this->data['pages']) || $beUser->workspace != 0 && !empty($this->data))) {
             BackendUtility::setUpdateSignal('updatePageTree');
         }
         // If there was saved any new items, load them:
         if (!empty($tce->substNEWwithIDs_table)) {
             // save the expanded/collapsed states for new inline records, if any
             FormEngineUtility::updateInlineView($this->uc, $tce);
             $newEditConf = array();
             foreach ($this->editconf as $tableName => $tableCmds) {
                 $keys = array_keys($tce->substNEWwithIDs_table, $tableName);
                 if (!empty($keys)) {
                     foreach ($keys as $key) {
                         $editId = $tce->substNEWwithIDs[$key];
                         // Check if the $editId isn't a child record of an IRRE action
                         if (!(is_array($tce->newRelatedIDs[$tableName]) && in_array($editId, $tce->newRelatedIDs[$tableName]))) {
                             // Translate new id to the workspace version:
                             if ($versionRec = BackendUtility::getWorkspaceVersionOfRecord($beUser->workspace, $tableName, $editId, 'uid')) {
                                 $editId = $versionRec['uid'];
                             }
                             $newEditConf[$tableName][$editId] = 'edit';
                         }
                         // Traverse all new records and forge the content of ->editconf so we can continue to EDIT these records!
                         if ($tableName == 'pages' && $this->retUrl != BackendUtility::getModuleUrl('dummy') && $this->returnNewPageId) {
                             $this->retUrl .= '&id=' . $tce->substNEWwithIDs[$key];
                         }
                     }
                 } else {
                     $newEditConf[$tableName] = $tableCmds;
                 }
             }
             // Resetting editconf if newEditConf has values:
             if (!empty($newEditConf)) {
                 $this->editconf = $newEditConf;
             }
             // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
             $this->R_URL_getvars['edit'] = $this->editconf;
             // Unsetting default values since we don't need them anymore.
             unset($this->R_URL_getvars['defVals']);
             // Re-compile the store* values since editconf changed...
             $this->compileStoreDat();
         }
         // See if any records was auto-created as new versions?
         if (!empty($tce->autoVersionIdMap)) {
             $this->fixWSversioningInEditConf($tce->autoVersionIdMap);
         }
         // If a document is saved and a new one is created right after.
         if (isset($_POST['_savedoknew_x']) && is_array($this->editconf)) {
             // Finding the current table:
             reset($this->editconf);
             $nTable = key($this->editconf);
             // Finding the first id, getting the records pid+uid
             reset($this->editconf[$nTable]);
             $nUid = key($this->editconf[$nTable]);
             $nRec = BackendUtility::getRecord($nTable, $nUid, 'pid,uid');
             // Setting a blank editconf array for a new record:
             $this->editconf = array();
             if ($this->getNewIconMode($nTable) == 'top') {
                 $this->editconf[$nTable][$nRec['pid']] = 'new';
             } else {
                 $this->editconf[$nTable][-$nRec['uid']] = 'new';
             }
             // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
             $this->R_URL_getvars['edit'] = $this->editconf;
             // Re-compile the store* values since editconf changed...
             $this->compileStoreDat();
         }
         // If a preview is requested
         if (isset($_POST['_savedokview_x'])) {
             // Get the first table and id of the data array from DataHandler
             $table = reset(array_keys($this->data));
             $id = reset(array_keys($this->data[$table]));
             if (!MathUtility::canBeInterpretedAsInteger($id)) {
                 $id = $tce->substNEWwithIDs[$id];
             }
             // Store this information for later use
             $this->previewData['table'] = $table;
             $this->previewData['id'] = $id;
         }
         $tce->printLogErrorMessages(isset($_POST['_saveandclosedok_x']) || isset($_POST['_translation_savedok_x']) ? $this->retUrl : $this->R_URL_parts['path'] . '?' . GeneralUtility::implodeArrayForUrl('', $this->R_URL_getvars));
     }
     //  || count($tce->substNEWwithIDs)... If any new items has been save, the document is CLOSED
     // because if not, we just get that element re-listed as new. And we don't want that!
     if (isset($_POST['_saveandclosedok_x']) || isset($_POST['_translation_savedok_x']) || $this->closeDoc < 0) {
         $this->closeDocument(abs($this->closeDoc));
     }
 }
 /**
  * Get type selector
  *
  * @return string
  */
 protected function getTypeSelectHtml()
 {
     $content = '';
     // find all available doktypes for the current user
     $types = $GLOBALS['PAGES_TYPES'];
     unset($types['default']);
     $types = array_keys($types);
     $types[] = PageRepository::DOKTYPE_DEFAULT;
     if (!$this->getBackendUser()->isAdmin() && isset($this->getBackendUser()->groupData['pagetypes_select'])) {
         $types = GeneralUtility::trimExplode(',', $this->getBackendUser()->groupData['pagetypes_select'], true);
     }
     $removeItems = isset($this->pagesTsConfig['doktype.']['removeItems']) ? GeneralUtility::trimExplode(',', $this->pagesTsConfig['doktype.']['removeItems'], true) : array();
     $allowedDoktypes = array_diff($types, $removeItems);
     // fetch all doktypes in the TCA
     $availableDoktypes = $GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'];
     // sort by group and allowedDoktypes
     $groupedData = array();
     foreach ($availableDoktypes as $doktypeData) {
         // if it is a group, save the group label for the children underneath
         if ($doktypeData[1] == '--div--') {
             $groupLabel = $doktypeData[0];
         } else {
             if (in_array($doktypeData[1], $allowedDoktypes)) {
                 $groupedData[$groupLabel][] = $doktypeData;
             }
         }
     }
     // render the HTML
     foreach ($groupedData as $groupLabel => $items) {
         $groupContent = '';
         foreach ($items as $item) {
             $label = $this->getLanguageService()->sL($item[0], true);
             $value = $item[1];
             $icon = !empty($item[2]) ? FormEngineUtility::getIconHtml($item[2], $label, $label) : '';
             $groupContent .= '<option value="' . htmlspecialchars($value) . '" data-icon="' . htmlspecialchars($icon) . '">' . $label . '</option>';
         }
         $groupLabel = $this->getLanguageService()->sL($groupLabel, true);
         $content .= '<optgroup label="' . $groupLabel . '">' . $groupContent . '</optgroup>';
     }
     return $content;
 }
    /**
     * Creates a multiple-selector box (two boxes, side-by-side)
     *
     * @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_multiple($table, $field, $row, $parameterArray, $config, $selItems, $noMatchingLabel)
    {
        $languageService = $this->getLanguageService();
        $item = '';
        $disabled = '';
        if ($this->isGlobalReadonly() || $config['readOnly']) {
            $disabled = ' disabled="disabled"';
        }
        // Setting this hidden field (as a flag that JavaScript can read out)
        if (!$disabled) {
            $item .= '<input type="hidden" name="' . $parameterArray['itemFormElName'] . '_mul" value="' . ($config['multiple'] ? 1 : 0) . '" />';
        }
        // Set max and min items:
        $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0);
        if (!$maxitems) {
            $maxitems = 100000;
        }
        // Get "removeItems":
        $removeItems = GeneralUtility::trimExplode(',', $parameterArray['fieldTSConfig']['removeItems'], TRUE);
        // Get the array with selected items:
        $itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], TRUE);
        // Possibly filter some items:
        $itemArray = ArrayUtility::keepItemsInArray($itemArray, $parameterArray['fieldTSConfig']['keepItems'], function ($value) {
            $parts = explode('|', $value, 2);
            return rawurldecode($parts[0]);
        });
        // Perform modification of the selected items array:
        foreach ($itemArray as $tk => $tv) {
            $tvP = explode('|', $tv, 2);
            $evalValue = $tvP[0];
            $isRemoved = in_array($evalValue, $removeItems) || $config['type'] == 'select' && $config['authMode'] && !$this->getBackendUserAuthentication()->checkAuthMode($table, $field, $evalValue, $config['authMode']);
            if ($isRemoved && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
                $tvP[1] = rawurlencode(@sprintf($noMatchingLabel, $evalValue));
            } else {
                if (isset($parameterArray['fieldTSConfig']['altLabels.'][$evalValue])) {
                    $tvP[1] = rawurlencode($languageService->sL($parameterArray['fieldTSConfig']['altLabels.'][$evalValue]));
                }
                if (isset($parameterArray['fieldTSConfig']['altIcons.'][$evalValue])) {
                    $tvP[2] = $parameterArray['fieldTSConfig']['altIcons.'][$evalValue];
                }
            }
            if ($tvP[1] == '') {
                // Case: flexform, default values supplied, no label provided (bug #9795)
                foreach ($selItems as $selItem) {
                    if ($selItem[1] == $tvP[0]) {
                        $tvP[1] = html_entity_decode($selItem[0]);
                        break;
                    }
                }
            }
            $itemArray[$tk] = implode('|', $tvP);
        }
        // size must be at least two, as there are always maxitems > 1 (see parent function)
        if (isset($config['size'])) {
            $size = (int) $config['size'];
        } else {
            $size = 2;
        }
        $size = $config['autoSizeMax'] ? MathUtility::forceIntegerInRange(count($itemArray) + 1, MathUtility::forceIntegerInRange($size, 1), $config['autoSizeMax']) : $size;
        $itemsToSelect = '';
        $filterTextfield = '';
        $filterSelectbox = '';
        if (!$disabled) {
            // Create option tags:
            $opt = array();
            $styleAttrValue = '';
            foreach ($selItems as $p) {
                if ($config['iconsInOptionTags']) {
                    $styleAttrValue = FormEngineUtility::optionTagStyle($p[2]);
                }
                $opt[] = '<option value="' . htmlspecialchars($p[1]) . '"' . ($styleAttrValue ? ' style="' . htmlspecialchars($styleAttrValue) . '"' : '') . ' title="' . $p[0] . '">' . $p[0] . '</option>';
            }
            // Put together the selector box:
            $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="' . htmlspecialchars($config['itemListStyle']) . '"' : '';
            $sOnChange = implode('', $parameterArray['fieldChangeFunc']);
            $multiSelectId = str_replace('.', '', uniqid('tceforms-multiselect-', TRUE));
            $itemsToSelect = '
				<select data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '" data-exclusivevalues="' . htmlspecialchars($config['exclusiveKeys']) . '" id="' . $multiSelectId . '" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '_sel" ' . ' class="form-control t3js-formengine-select-itemstoselect" ' . ($size ? ' size="' . $size . '"' : '') . ' onchange="' . htmlspecialchars($sOnChange) . '"' . $parameterArray['onFocus'] . $this->getValidationDataAsDataAttribute($config) . $selector_itemListStyle . '>
					' . implode('
					', $opt) . '
				</select>';
            // enable filter functionality via a text field
            if ($config['enableMultiSelectFilterTextfield']) {
                $filterTextfield = '
					<span class="input-group input-group-sm">
						<span class="input-group-addon">
							<span class="fa fa-filter"></span>
						</span>
						<input class="t3js-formengine-multiselect-filter-textfield form-control" value="" />
					</span>';
            }
            // enable filter functionality via a select
            if (isset($config['multiSelectFilterItems']) && is_array($config['multiSelectFilterItems']) && count($config['multiSelectFilterItems']) > 1) {
                $filterDropDownOptions = array();
                foreach ($config['multiSelectFilterItems'] as $optionElement) {
                    $optionValue = $languageService->sL(isset($optionElement[1]) && $optionElement[1] != '' ? $optionElement[1] : $optionElement[0]);
                    $filterDropDownOptions[] = '<option value="' . htmlspecialchars($languageService->sL($optionElement[0])) . '">' . htmlspecialchars($optionValue) . '</option>';
                }
                $filterSelectbox = '<select class="form-control input-sm t3js-formengine-multiselect-filter-dropdown">
						' . implode('
						', $filterDropDownOptions) . '
					</select>';
            }
        }
        if (!empty(trim($filterSelectbox)) && !empty(trim($filterTextfield))) {
            $filterSelectbox = '<div class="form-multigroup-item form-multigroup-element">' . $filterSelectbox . '</div>';
            $filterTextfield = '<div class="form-multigroup-item form-multigroup-element">' . $filterTextfield . '</div>';
            $selectBoxFilterContents = '<div class="t3js-formengine-multiselect-filter-container form-multigroup-wrap">' . $filterSelectbox . $filterTextfield . '</div>';
        } else {
            $selectBoxFilterContents = trim($filterSelectbox . ' ' . $filterTextfield);
        }
        // Pass to "dbFileIcons" function:
        $params = array('size' => $size, 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0), 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : '', 'dontShowMoveIcons' => $maxitems <= 1, 'maxitems' => $maxitems, 'info' => '', 'headers' => array('selector' => $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.selected'), 'items' => $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.items'), 'selectorbox' => $selectBoxFilterContents), 'noBrowser' => 1, 'rightbox' => $itemsToSelect, 'readOnly' => $disabled);
        $item .= $this->dbFileIcons($parameterArray['itemFormElName'], '', '', $itemArray, '', $params, $parameterArray['onFocus']);
        return $item;
    }