2 namespace TYPO3\CMS\Backend\Controller\ContentElement
;
5 * This file is part of the TYPO3 CMS project.
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Backend\Utility\BackendUtility
;
18 use TYPO3\CMS\Backend\Utility\IconUtility
;
19 use TYPO3\CMS\Core\Utility\GeneralUtility
;
20 use TYPO3\CMS\Core\Utility\PathUtility
;
23 * Script Class for the New Content element wizard
25 class NewContentElementController
{
39 public $sys_language = 0;
49 * If set, the content is destined for a specific column.
65 public $modTSconfig = array();
68 * Internal backend template object
70 * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
75 * Used to accumulate the content of the module.
89 * config of the wizard
103 protected $elementWrapper;
108 protected $elementWrapperForTabs;
113 protected $onClickEvent;
121 * Constructor, initializing internal variables.
125 public function init() {
126 $lang = $this->getLanguageService();
127 $lang->includeLLFile('EXT:lang/locallang_misc.xlf');
128 $LOCAL_LANG_orig = $GLOBALS['LOCAL_LANG'];
129 $lang->includeLLFile('EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf');
130 \TYPO3\CMS\Core\Utility\ArrayUtility
::mergeRecursiveWithOverrule($LOCAL_LANG_orig, $GLOBALS['LOCAL_LANG']);
131 $GLOBALS['LOCAL_LANG'] = $LOCAL_LANG_orig;
133 // Setting internal vars:
134 $this->id
= (int)GeneralUtility
::_GP('id');
135 $this->sys_language
= (int)GeneralUtility
::_GP('sys_language_uid');
136 $this->R_URI
= GeneralUtility
::sanitizeLocalUrl(GeneralUtility
::_GP('returnUrl'));
137 $this->colPos
= GeneralUtility
::_GP('colPos') === NULL ? NULL
: (int)GeneralUtility
::_GP('colPos');
138 $this->uid_pid
= (int)GeneralUtility
::_GP('uid_pid');
139 $this->MCONF
['name'] = 'xMOD_db_new_content_el';
140 $this->modTSconfig
= BackendUtility
::getModTSconfig($this->id
, 'mod.wizards.newContentElement');
141 $config = BackendUtility
::getPagesTSconfig($this->id
);
142 $this->config
= $config['mod.']['wizards.']['newContentElement.'];
143 // Starting the document template object:
144 $this->doc
= GeneralUtility
::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate
::class);
145 $this->doc
->backPath
= $GLOBALS['BACK_PATH'];
146 $this->doc
->setModuleTemplate('EXT:backend/Resources/Private/Templates/db_new_content_el.html');
147 $this->doc
->JScode
= '';
148 $this->doc
->form
= '<form action="" name="editForm"><input type="hidden" name="defValues" value="" />';
149 // Setting up the context sensitive menu:
150 $this->doc
->getContextMenuCode();
151 // Getting the current page and receiving access information (used in main())
152 $perms_clause = $this->getBackendUser()->getPagePermsClause(1);
153 $this->pageInfo
= BackendUtility
::readPageAccess($this->id
, $perms_clause);
154 $this->access
= is_array($this->pageInfo
) ?
1 : 0;
158 * Creating the module output.
162 public function main() {
163 $lang = $this->getLanguageService();
164 if ($this->id
&& $this->access
) {
165 // Init position map object:
166 $posMap = GeneralUtility
::makeInstance(\TYPO3\CMS\Backend\Tree\View\ContentCreationPagePositionMap
::class);
167 $posMap->cur_sys_language
= $this->sys_language
;
168 $posMap->backPath
= $GLOBALS['BACK_PATH'];
169 // If a column is pre-set:
170 if (isset($this->colPos
)) {
171 if ($this->uid_pid
< 0) {
173 $row['uid'] = abs($this->uid_pid
);
177 $this->onClickEvent
= $posMap->onClickInsertRecord($row, $this->colPos
, '', $this->uid_pid
, $this->sys_language
);
179 $this->onClickEvent
= '';
181 // ***************************
183 // ***************************
184 $this->content
.= $this->doc
->header($lang->getLL('newContentElement'));
186 $wizardItems = $this->getWizardItems();
187 // Wrapper for wizards
188 $this->elementWrapper
['section'] = array('', '');
189 // Copy wrapper for tabs
190 $this->elementWrapperForTabs
= $this->elementWrapper
;
191 // Hook for manipulating wizardItems, wrapper, onClickEvent etc.
192 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'])) {
193 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'] as $classData) {
194 $hookObject = GeneralUtility
::getUserObj($classData);
195 if (!$hookObject instanceof \TYPO3\CMS\Backend\Wizard\NewContentElementWizardHookInterface
) {
196 throw new \
UnexpectedValueException('$hookObject must implement interface ' . \TYPO3\CMS\Backend\Wizard\NewContentElementWizardHookInterface
::class, 1227834741);
198 $hookObject->manipulateWizardItems($wizardItems, $this);
201 if ($this->config
['renderMode'] == 'tabs' && $this->elementWrapperForTabs
!= $this->elementWrapper
) {
202 // Restore wrapper for tabs if they are overwritten in hook
203 $this->elementWrapper
= $this->elementWrapperForTabs
;
205 // Add document inline javascript
206 $this->doc
->JScode
= $this->doc
->wrapScriptTags('
207 function goToalt_doc() { //
208 ' . $this->onClickEvent
. '
211 if(top.refreshMenu) {
214 top.TYPO3ModuleMenu.refreshMenu();
217 // Traverse items for the wizard.
218 // An item is either a header or an item rendered with a radio button and title/description and icon:
220 $menuItems = array();
221 foreach ($wizardItems as $k => $wInfo) {
222 if ($wInfo['header']) {
223 $menuItems[] = array(
224 'label' => htmlspecialchars($wInfo['header']),
225 'content' => $this->elementWrapper
['section'][0]
227 $key = count($menuItems) - 1;
231 if (!$this->onClickEvent
) {
233 $oC = 'document.editForm.defValues.value=unescape(' . GeneralUtility
::quoteJSvalue(rawurlencode($wInfo['params'])) . ');goToalt_doc();' . (!$this->onClickEvent ?
'window.location.hash=\'#sel2\';' : '');
234 $content .= '<div class="media-left"><input type="radio" name="tempB" value="' . htmlspecialchars($k) . '" onclick="' . htmlspecialchars($oC) . '" /></div>';
235 // Onclick action for icon/title:
236 $aOnClick = 'document.getElementsByName(\'tempB\')[' . $cc . '].checked=1;' . $oC . 'return false;';
238 $aOnClick = "document.editForm.defValues.value=unescape('" . rawurlencode($wInfo['params']) . "');goToalt_doc();" . (!$this->onClickEvent?
"window.location.hash='#sel2';":'');
241 $icon = $wInfo['icon'];
242 if (strpos($wInfo['icon'], '..') === FALSE
&& !GeneralUtility
::isAbsPath($icon)) {
243 $icon = GeneralUtility
::getFileAbsFileName($icon, TRUE
, TRUE
);
244 $pathInfo = PathUtility
::pathinfo($icon);
245 $path = PathUtility
::getRelativePathTo($pathInfo['dirname']);
246 $icon = $path . $pathInfo['basename'];
248 $menuItems[$key]['content'] .= '
250 <a href="#" onclick="' . htmlspecialchars($aOnClick) . '">
252 <div class="media-left">
253 <img' . IconUtility
::skinImg($this->doc
->backPath
, $icon) . ' alt="" />
255 <div class="media-body">
256 <strong>' . htmlspecialchars($wInfo['title']) . '</strong>' .
258 nl2br(htmlspecialchars(trim($wInfo['description']))) .
265 // Add closing section-tag
266 foreach ($menuItems as $key => $val) {
267 $menuItems[$key]['content'] .= $this->elementWrapper
['section'][1];
269 // Add the wizard table to the content, wrapped in tabs:
270 if ($this->config
['renderMode'] == 'tabs') {
271 $code = '<p>' . $lang->getLL('sel1', 1) . '</p>' . $this->doc
->getDynamicTabMenu($menuItems, 'new-content-element-wizard');
273 $code = '<p>' . $lang->getLL('sel1', 1) . '</p>';
274 foreach ($menuItems as $section) {
275 $code .= '<h3 class="divider">' . $section['label'] . '</h3>' . $section['content'];
278 $this->content
.= $this->doc
->section(!$this->onClickEvent ?
$lang->getLL('1_selectType') : '', $code, 0, 1);
279 // If the user must also select a column:
280 if (!$this->onClickEvent
) {
282 $this->content
.= $this->doc
->section('', '<a name="sel2"></a>');
284 $code = '<p>' . $lang->getLL('sel2', 1) . '</p>';
286 // Load SHARED page-TSconfig settings and retrieve column list from there, if applicable:
287 $colPosArray = GeneralUtility
::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView
::class . '->getColPosListItemsParsed', $this->id
, $this);
288 $colPosIds = array_column($colPosArray, 1);
289 // Removing duplicates, if any
290 $colPosList = implode(',', array_unique(array_map('intval', $colPosIds)));
291 // Finally, add the content of the column selector to the content:
292 $code .= $posMap->printContentElementColumns($this->id
, 0, $colPosList, 1, $this->R_URI
);
293 $this->content
.= $this->doc
->section($lang->getLL('2_selectPosition'), $code, 0, 1);
296 // In case of no access:
298 $this->content
.= $this->doc
->header($lang->getLL('newContentElement'));
299 $this->content
.= $this->doc
->spacer(5);
301 // Setting up the buttons and markers for docheader
302 $docHeaderButtons = $this->getButtons();
303 $markers['CSH'] = $docHeaderButtons['csh'];
304 $markers['CONTENT'] = $this->content
;
305 // Build the <body> for the module
306 $this->content
= $this->doc
->startPage($lang->getLL('newContentElement'));
307 $this->content
.= $this->doc
->moduleBody($this->pageInfo
, $docHeaderButtons, $markers);
308 $this->content
.= $this->doc
->sectionEnd();
309 $this->content
.= $this->doc
->endPage();
310 $this->content
= $this->doc
->insertStylesAndJS($this->content
);
314 * Print out the accumulated content:
318 public function printContent() {
323 * Create the panel of buttons for submitting the form or otherwise perform operations.
325 * @return array All available buttons as an assoc. array
327 protected function getButtons() {
332 if ($this->id
&& $this->access
) {
333 $buttons['csh'] = BackendUtility
::cshItem('xMOD_csh_corebe', 'new_ce');
335 $buttons['back'] = '<a href="' . htmlspecialchars($this->R_URI
) . '" class="typo3-goBack" title="' . $this->getLanguageService()->getLL('goBack', TRUE
) . '">' . IconUtility
::getSpriteIcon('actions-view-go-back') . '</a>';
341 /***************************
345 ***************************/
347 * Returns the content of wizardArray() function...
349 * @return array Returns the content of wizardArray() function...
351 public function getWizardItems() {
352 return $this->wizardArray();
356 * Returns the array of elements in the wizard display.
357 * For the plugin section there is support for adding elements there from a global variable.
361 public function wizardArray() {
362 $wizardItems = array();
363 if (is_array($this->config
)) {
364 $wizards = $this->config
['wizardItems.'];
365 $appendWizards = $this->wizard_appendWizards($wizards['elements.']);
366 if (is_array($wizards)) {
367 foreach ($wizards as $groupKey => $wizardGroup) {
368 $groupKey = rtrim($groupKey, '.');
369 $showItems = GeneralUtility
::trimExplode(',', $wizardGroup['show'], TRUE
);
370 $showAll = $wizardGroup['show'] === '*';
371 $groupItems = array();
372 if (is_array($appendWizards[$groupKey . '.']['elements.'])) {
373 $wizardElements = array_merge((array)$wizardGroup['elements.'], $appendWizards[$groupKey . '.']['elements.']);
375 $wizardElements = $wizardGroup['elements.'];
377 if (is_array($wizardElements)) {
378 foreach ($wizardElements as $itemKey => $itemConf) {
379 $itemKey = rtrim($itemKey, '.');
380 if ($showAll ||
in_array($itemKey, $showItems)) {
381 $tmpItem = $this->wizard_getItem($groupKey, $itemKey, $itemConf);
383 $groupItems[$groupKey . '_' . $itemKey] = $tmpItem;
388 if (!empty($groupItems)) {
389 $wizardItems[$groupKey] = $this->wizard_getGroupHeader($groupKey, $wizardGroup);
390 $wizardItems = array_merge($wizardItems, $groupItems);
395 // Remove elements where preset values are not allowed:
396 $this->removeInvalidElements($wizardItems);
401 * @param mixed $wizardElements
404 public function wizard_appendWizards($wizardElements) {
405 if (!is_array($wizardElements)) {
406 $wizardElements = array();
408 if (is_array($GLOBALS['TBE_MODULES_EXT']['xMOD_db_new_content_el']['addElClasses'])) {
409 foreach ($GLOBALS['TBE_MODULES_EXT']['xMOD_db_new_content_el']['addElClasses'] as $class => $path) {
411 $modObj = GeneralUtility
::makeInstance($class);
412 $wizardElements = $modObj->proc($wizardElements);
415 $returnElements = array();
416 foreach ($wizardElements as $key => $wizardItem) {
417 preg_match('/^[a-zA-Z0-9]+_/', $key, $group);
418 $wizardGroup = $group[0] ?
substr($group[0], 0, -1) . '.' : $key;
419 $returnElements[$wizardGroup]['elements.'][substr($key, strlen($wizardGroup)) . '.'] = $wizardItem;
421 return $returnElements;
425 * @param string $groupKey Not used
426 * @param string $itemKey Not used
427 * @param array $itemConf
430 public function wizard_getItem($groupKey, $itemKey, $itemConf) {
431 $itemConf['title'] = $this->getLanguageService()->sL($itemConf['title']);
432 $itemConf['description'] = $this->getLanguageService()->sL($itemConf['description']);
433 $itemConf['tt_content_defValues'] = $itemConf['tt_content_defValues.'];
434 unset($itemConf['tt_content_defValues.']);
439 * @param string $groupKey Not used
440 * @param array $wizardGroup
443 public function wizard_getGroupHeader($groupKey, $wizardGroup) {
445 'header' => $this->getLanguageService()->sL($wizardGroup['header'])
450 * Checks the array for elements which might contain unallowed default values and will unset them!
451 * Looks for the "tt_content_defValues" key in each element and if found it will traverse that array as fieldname / value pairs and check.
452 * The values will be added to the "params" key of the array (which should probably be unset or empty by default).
454 * @param array $wizardItems Wizard items, passed by reference
457 public function removeInvalidElements(&$wizardItems) {
458 // Get TCEFORM from TSconfig of current page
459 $row = array('pid' => $this->id
);
460 $TCEFORM_TSconfig = BackendUtility
::getTCEFORM_TSconfig('tt_content', $row);
461 $headersUsed = array();
462 // Traverse wizard items:
463 foreach ($wizardItems as $key => $cfg) {
464 // Exploding parameter string, if any (old style)
465 if ($wizardItems[$key]['params']) {
466 // Explode GET vars recursively
467 $tempGetVars = GeneralUtility
::explodeUrl2Array($wizardItems[$key]['params'], TRUE
);
468 // If tt_content values are set, merge them into the tt_content_defValues array,
469 // unset them from $tempGetVars and re-implode $tempGetVars into the param string
470 // (in case remaining parameters are around).
471 if (is_array($tempGetVars['defVals']['tt_content'])) {
472 $wizardItems[$key]['tt_content_defValues'] = array_merge(
473 is_array($wizardItems[$key]['tt_content_defValues'])
474 ?
$wizardItems[$key]['tt_content_defValues']
476 $tempGetVars['defVals']['tt_content']
478 unset($tempGetVars['defVals']['tt_content']);
479 $wizardItems[$key]['params'] = GeneralUtility
::implodeArrayForUrl('', $tempGetVars);
482 // If tt_content_defValues are defined...:
483 if (is_array($wizardItems[$key]['tt_content_defValues'])) {
484 $backendUser = $this->getBackendUser();
485 // Traverse field values:
486 foreach ($wizardItems[$key]['tt_content_defValues'] as $fN => $fV) {
487 if (is_array($GLOBALS['TCA']['tt_content']['columns'][$fN])) {
488 // Get information about if the field value is OK:
489 $config = &$GLOBALS['TCA']['tt_content']['columns'][$fN]['config'];
490 $authModeDeny = $config['type'] == 'select' && $config['authMode']
491 && !$backendUser->checkAuthMode('tt_content', $fN, $fV, $config['authMode']);
492 // explode TSconfig keys only as needed
493 if (!isset($removeItems[$fN])) {
494 $removeItems[$fN] = GeneralUtility
::trimExplode(',', $TCEFORM_TSconfig[$fN]['removeItems'], TRUE
);
496 if (!isset($keepItems[$fN])) {
497 $keepItems[$fN] = GeneralUtility
::trimExplode(',', $TCEFORM_TSconfig[$fN]['keepItems'], TRUE
);
499 $isNotInKeepItems = !empty($keepItems[$fN]) && !in_array($fV, $keepItems[$fN]);
500 if ($authModeDeny ||
$fN === 'CType' && in_array($fV, $removeItems[$fN]) ||
$isNotInKeepItems) {
501 // Remove element all together:
502 unset($wizardItems[$key]);
505 // Add the parameter:
506 $wizardItems[$key]['params'] .= '&defVals[tt_content][' . $fN . ']=' . rawurlencode($fV);
507 $tmp = explode('_', $key);
508 $headersUsed[$tmp[0]] = $tmp[0];
514 // remove headers without elements
515 foreach ($wizardItems as $key => $cfg) {
516 $tmp = explode('_', $key);
517 if ($tmp[0] && !$tmp[1] && !in_array($tmp[0], $headersUsed)) {
518 unset($wizardItems[$key]);
524 * Returns LanguageService
526 * @return \TYPO3\CMS\Lang\LanguageService
528 protected function getLanguageService() {
529 return $GLOBALS['LANG'];
533 * Returns the current BE user.
535 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
537 protected function getBackendUser() {
538 return $GLOBALS['BE_USER'];