/** * Generation of TCEform elements of the type "inline" * This will render inline-relational-record sets. Relations. * * @param string $table The table name of the record * @param string $field The field name which this element is supposed to edit * @param array $row The record data array where the value(s) for the field can be found * @param array $PA An array with additional configuration options. * @return string The HTML code for the TCEform field * @todo Define visibility */ public function getSingleField_typeInline($table, $field, $row, &$PA) { // Check the TCA configuration - if FALSE is returned, something was wrong if ($this->checkConfiguration($PA['fieldConf']['config']) === FALSE) { return FALSE; } $item = ''; $levelLinks = ''; $localizationLinks = ''; // Count the number of processed inline elements $this->inlineCount++; // Init: $config = $PA['fieldConf']['config']; $foreign_table = $config['foreign_table']; $language = 0; if (BackendUtility::isTableLocalizable($table)) { $language = (int) $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]; } $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0); $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0); if (!$maxitems) { $maxitems = 100000; } // Register the required number of elements: $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems, $maxitems, 'imgName' => $table . '_' . $row['uid'] . '_' . $field); // Remember the page id (pid of record) where inline editing started first // We need that pid for ajax calls, so that they would know where the action takes place on the page structure if (!isset($this->inlineFirstPid)) { // If this record is not new, try to fetch the inlineView states // @TODO: Add checking/cleaning for unused tables, records, etc. to save space in uc-field if (MathUtility::canBeInterpretedAsInteger($row['uid'])) { $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']); $this->inlineView = $inlineView[$table][$row['uid']]; } // If the parent is a page, use the uid(!) of the (new?) page as pid for the child records: if ($table == 'pages') { $liveVersionId = BackendUtility::getLiveVersionIdOfRecord('pages', $row['uid']); $this->inlineFirstPid = is_null($liveVersionId) ? $row['uid'] : $liveVersionId; } elseif ($row['pid'] < 0) { $prevRec = BackendUtility::getRecord($table, abs($row['pid'])); $this->inlineFirstPid = $prevRec['pid']; } else { $this->inlineFirstPid = $row['pid']; } } // Add the current inline job to the structure stack $this->pushStructure($table, $row['uid'], $field, $config, $PA); // e.g. data[<table>][<uid>][<field>] $nameForm = $this->inlineNames['form']; // e.g. data-<pid>-<table1>-<uid1>-<field1>-<table2>-<uid2>-<field2> $nameObject = $this->inlineNames['object']; // Get the records related to this inline record $relatedRecords = $this->getRelatedRecords($table, $field, $row, $PA, $config); // Set the first and last record to the config array $relatedRecordsUids = array_keys($relatedRecords['records']); $config['inline']['first'] = reset($relatedRecordsUids); $config['inline']['last'] = end($relatedRecordsUids); // Tell the browser what we have (using JSON later): $top = $this->getStructureLevel(0); $this->inlineData['config'][$nameObject] = array('table' => $foreign_table, 'md5' => md5($nameObject)); $this->inlineData['config'][$nameObject . self::Structure_Separator . $foreign_table] = array('min' => $minitems, 'max' => $maxitems, 'sortable' => $config['appearance']['useSortable'], 'top' => array('table' => $top['table'], 'uid' => $top['uid']), 'context' => array('config' => $config, 'hmac' => GeneralUtility::hmac(serialize($config)))); // Set a hint for nested IRRE and tab elements: $this->inlineData['nested'][$nameObject] = $this->fObj->getDynNestedStack(FALSE, $this->isAjaxCall); // If relations are required to be unique, get the uids that have already been used on the foreign side of the relation if ($config['foreign_unique']) { // If uniqueness *and* selector are set, they should point to the same field - so, get the configuration of one: $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_unique']); // Get the used unique ids: $uniqueIds = $this->getUniqueIds($relatedRecords['records'], $config, $selConfig['type'] == 'groupdb'); $possibleRecords = $this->getPossibleRecords($table, $field, $row, $config, 'foreign_unique'); $uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === FALSE ? -1 : count($possibleRecords); $this->inlineData['unique'][$nameObject . self::Structure_Separator . $foreign_table] = array('max' => $uniqueMax, 'used' => $uniqueIds, 'type' => $selConfig['type'], 'table' => $config['foreign_table'], 'elTable' => $selConfig['table'], 'field' => $config['foreign_unique'], 'selector' => $selConfig['selector'], 'possible' => $this->getPossibleRecordsFlat($possibleRecords)); } // Render the localization links if ($language > 0 && $row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] > 0 && MathUtility::canBeInterpretedAsInteger($row['uid'])) { // Add the "Localize all records" link before all child records: if (isset($config['appearance']['showAllLocalizationLink']) && $config['appearance']['showAllLocalizationLink']) { $localizationLinks .= ' ' . $this->getLevelInteractionLink('localize', $nameObject . self::Structure_Separator . $foreign_table, $config); } // Add the "Synchronize with default language" link before all child records: if (isset($config['appearance']['showSynchronizationLink']) && $config['appearance']['showSynchronizationLink']) { $localizationLinks .= ' ' . $this->getLevelInteractionLink('synchronize', $nameObject . self::Structure_Separator . $foreign_table, $config); } } // If it's required to select from possible child records (reusable children), add a selector box if ($config['foreign_selector'] && $config['appearance']['showPossibleRecordsSelector'] !== FALSE) { // If not already set by the foreign_unique, set the possibleRecords here and the uniqueIds to an empty array if (!$config['foreign_unique']) { $possibleRecords = $this->getPossibleRecords($table, $field, $row, $config); $uniqueIds = array(); } $selectorBox = $this->renderPossibleRecordsSelector($possibleRecords, $config, $uniqueIds); $item .= $selectorBox . $localizationLinks; // Render the level links (create new record): } else { $levelLinks = $this->getLevelInteractionLink('newRecord', $nameObject . self::Structure_Separator . $foreign_table, $config); } // Wrap all inline fields of a record with a <div> (like a container) $item .= '<div id="' . $nameObject . '">'; // Define how to show the "Create new record" link - if there are more than maxitems, hide it if ($relatedRecords['count'] >= $maxitems || $uniqueMax > 0 && $relatedRecords['count'] >= $uniqueMax) { $config['inline']['inlineNewButtonStyle'] = 'display: none;'; } // Add the level links before all child records: if (in_array($config['appearance']['levelLinksPosition'], array('both', 'top'))) { $item .= $levelLinks . $localizationLinks; } $item .= '<div id="' . $nameObject . '_records">'; $relationList = array(); if (count($relatedRecords['records'])) { foreach ($relatedRecords['records'] as $rec) { $item .= $this->renderForeignRecord($row['uid'], $rec, $config); if (!isset($rec['__virtual']) || !$rec['__virtual']) { $relationList[] = $rec['uid']; } } } $item .= '</div>'; // Add the level links after all child records: if (in_array($config['appearance']['levelLinksPosition'], array('both', 'bottom'))) { $item .= $levelLinks . $localizationLinks; } if (is_array($config['customControls'])) { $item .= '<div id="' . $nameObject . '_customControls">'; foreach ($config['customControls'] as $customControlConfig) { $parameters = array('table' => $table, 'field' => $field, 'row' => $row, 'nameObject' => $nameObject, 'nameForm' => $nameForm, 'config' => $config); $item .= GeneralUtility::callUserFunction($customControlConfig, $parameters, $this); } $item .= '</div>'; } // Add Drag&Drop functions for sorting to TCEforms::$additionalJS_post if (count($relationList) > 1 && $config['appearance']['useSortable']) { $this->addJavaScriptSortable($nameObject . '_records'); } // Publish the uids of the child records in the given order to the browser $item .= '<input type="hidden" name="' . $nameForm . '" value="' . implode(',', $relationList) . '" class="inlineRecord" />'; // Close the wrap for all inline fields (container) $item .= '</div>'; // On finishing this section, remove the last item from the structure stack $this->popStructure(); // If this was the first call to the inline type, restore the values if (!$this->getStructureDepth()) { unset($this->inlineFirstPid); } return $item; }
/** * Return the Javascript code for configuring the RTE * * @param integer $RTEcounter: The index number of the current RTE editing area within the form. * @param string $table: The table that includes this RTE (optional, necessary for IRRE). * @param string $uid: The uid of that table that includes this RTE (optional, necessary for IRRE). * @param string $field: The field of that record that includes this RTE (optional). * @param string $textAreaId ID of the textarea, to have a unigue number for the editor * @return string the Javascript code for configuring the RTE * @todo Define visibility */ public function registerRTEinJS($RTEcounter, $table = '', $uid = '', $field = '', $textAreaId = '') { $configureRTEInJavascriptString = ' if (typeof(configureEditorInstance) == "undefined") { configureEditorInstance = new Object(); } configureEditorInstance["' . $textAreaId . '"] = function() { if (typeof(RTEarea) == "undefined" || typeof(HTMLArea) == "undefined") { window.setTimeout("configureEditorInstance[\'' . $textAreaId . '\']();", 40); } else { editornumber = "' . $textAreaId . '"; RTEarea[editornumber] = new Object(); RTEarea[editornumber].RTEtsConfigParams = "&RTEtsConfigParams=' . rawurlencode($this->RTEtsConfigParams()) . '"; RTEarea[editornumber].number = editornumber; RTEarea[editornumber].deleted = false; RTEarea[editornumber].textAreaId = "' . $textAreaId . '"; RTEarea[editornumber].id = "RTEarea" + editornumber; RTEarea[editornumber].RTEWidthOverride = "' . (is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteWidth']) && trim($GLOBALS['BE_USER']->uc['rteWidth']) ? trim($GLOBALS['BE_USER']->uc['rteWidth']) : trim($this->thisConfig['RTEWidthOverride'])) . '"; RTEarea[editornumber].RTEHeightOverride = "' . (is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteHeight']) && (int) $GLOBALS['BE_USER']->uc['rteHeight'] ? (int) $GLOBALS['BE_USER']->uc['rteHeight'] : (int) $this->thisConfig['RTEHeightOverride']) . '"; RTEarea[editornumber].resizable = ' . (is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteResize']) && $GLOBALS['BE_USER']->uc['rteResize'] ? 'true' : (trim($this->thisConfig['rteResize']) ? 'true' : 'false')) . '; RTEarea[editornumber].maxHeight = "' . (is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteMaxHeight']) && (int) $GLOBALS['BE_USER']->uc['rteMaxHeight'] ? trim($GLOBALS['BE_USER']->uc['rteMaxHeight']) : ((int) $this->thisConfig['rteMaxHeight'] ?: '2000')) . '"; RTEarea[editornumber].fullScreen = ' . ($this->fullScreen ? 'true' : 'false') . '; RTEarea[editornumber].showStatusBar = ' . (trim($this->thisConfig['showStatusBar']) ? 'true' : 'false') . '; RTEarea[editornumber].enableWordClean = ' . (trim($this->thisConfig['enableWordClean']) ? 'true' : 'false') . '; RTEarea[editornumber].htmlRemoveComments = ' . (trim($this->thisConfig['removeComments']) ? 'true' : 'false') . '; RTEarea[editornumber].disableEnterParagraphs = ' . (trim($this->thisConfig['disableEnterParagraphs']) ? 'true' : 'false') . '; RTEarea[editornumber].disableObjectResizing = ' . (trim($this->thisConfig['disableObjectResizing']) ? 'true' : 'false') . '; RTEarea[editornumber].removeTrailingBR = ' . (trim($this->thisConfig['removeTrailingBR']) ? 'true' : 'false') . '; RTEarea[editornumber].useCSS = ' . (trim($this->thisConfig['useCSS']) ? 'true' : 'false') . '; RTEarea[editornumber].keepButtonGroupTogether = ' . (trim($this->thisConfig['keepButtonGroupTogether']) ? 'true' : 'false') . '; RTEarea[editornumber].disablePCexamples = ' . (trim($this->thisConfig['disablePCexamples']) ? 'true' : 'false') . '; RTEarea[editornumber].showTagFreeClasses = ' . (trim($this->thisConfig['showTagFreeClasses']) ? 'true' : 'false') . '; RTEarea[editornumber].useHTTPS = ' . (trim(stristr($this->siteURL, 'https')) || $this->thisConfig['forceHTTPS'] ? 'true' : 'false') . '; RTEarea[editornumber].tceformsNested = ' . (is_object($this->TCEform) && method_exists($this->TCEform, 'getDynNestedStack') ? $this->TCEform->getDynNestedStack(TRUE) : '[]') . '; RTEarea[editornumber].dialogueWindows = new Object();'; if (isset($this->thisConfig['dialogueWindows.']['defaultPositionFromTop'])) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].dialogueWindows.positionFromTop = ' . (int) $this->thisConfig['dialogueWindows.']['defaultPositionFromTop'] . ';'; } if (isset($this->thisConfig['dialogueWindows.']['defaultPositionFromLeft'])) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].dialogueWindows.positionFromLeft = ' . (int) $this->thisConfig['dialogueWindows.']['defaultPositionFromLeft'] . ';'; } // The following properties apply only to the backend if (!$this->is_FE()) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].sys_language_content = "' . $this->contentLanguageUid . '"; RTEarea[editornumber].typo3ContentLanguage = "' . $this->contentTypo3Language . '"; RTEarea[editornumber].typo3ContentCharset = "' . $this->contentCharset . '"; RTEarea[editornumber].userUid = "' . $this->userUid . '";'; } // Setting the plugin flags $configureRTEInJavascriptString .= ' RTEarea[editornumber].plugin = new Object(); RTEarea[editornumber].pathToPluginDirectory = new Object();'; foreach ($this->pluginEnabledArray as $pluginId) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].plugin.' . $pluginId . ' = true;'; if (is_object($this->registeredPlugins[$pluginId])) { $pathToPluginDirectory = $this->registeredPlugins[$pluginId]->getPathToPluginDirectory(); if ($pathToPluginDirectory) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].pathToPluginDirectory.' . $pluginId . ' = "' . $pathToPluginDirectory . '";'; } } } // Setting the buttons configuration $configureRTEInJavascriptString .= ' RTEarea[editornumber].buttons = new Object();'; if (is_array($this->thisConfig['buttons.'])) { foreach ($this->thisConfig['buttons.'] as $buttonIndex => $conf) { $button = substr($buttonIndex, 0, -1); if (is_array($conf)) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].buttons.' . $button . ' = ' . $this->buildNestedJSArray($conf) . ';'; } } } // Setting the list of tags to be removed if specified in the RTE config if (trim($this->thisConfig['removeTags'])) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].htmlRemoveTags = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->thisConfig['removeTags'], TRUE)) . ')$/i;'; } // Setting the list of tags to be removed with their contents if specified in the RTE config if (trim($this->thisConfig['removeTagsAndContents'])) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].htmlRemoveTagsAndContents = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->thisConfig['removeTagsAndContents'], TRUE)) . ')$/i;'; } // Setting array of custom tags if specified in the RTE config if (!empty($this->thisConfig['customTags'])) { $customTags = GeneralUtility::trimExplode(',', $this->thisConfig['customTags'], TRUE); if (!empty($customTags)) { $configureRTEInJavascriptString .= ' RTEarea[editornumber].customTags= ' . json_encode($customTags) . ';'; } } // Setting the pageStyle $configureRTEInJavascriptString .= ' RTEarea[editornumber].pageStyle = "' . GeneralUtility::createVersionNumberedFilename($this->getContentCssFileName()) . '";'; // Process classes configuration $classesConfigurationRequired = FALSE; foreach ($this->registeredPlugins as $pluginId => $plugin) { if ($this->isPluginEnabled($pluginId)) { $classesConfigurationRequired = $classesConfigurationRequired || $plugin->requiresClassesConfiguration(); } } if ($classesConfigurationRequired) { $configureRTEInJavascriptString .= $this->buildJSClassesConfig($RTEcounter); } // Add Javascript configuration for registered plugins foreach ($this->registeredPlugins as $pluginId => $plugin) { if ($this->isPluginEnabled($pluginId)) { $configureRTEInJavascriptString .= $plugin->buildJavascriptConfiguration('editornumber'); } } // Avoid premature reference to HTMLArea when being initially loaded by IRRE Ajax call $configureRTEInJavascriptString .= ' RTEarea[editornumber].toolbar = ' . $this->getJSToolbarArray() . '; RTEarea[editornumber].convertButtonId = ' . json_encode(array_flip($this->convertToolbarForHtmlAreaArray)) . '; RTEarea.initEditor(editornumber); } }; configureEditorInstance["' . $textAreaId . '"]();'; return $configureRTEInJavascriptString; }