TYPO3 CMS  TYPO3_8-7
NewContentElementController.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
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.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
29 
34 {
40  public $id;
41 
47  public $sys_language = 0;
48 
54  public $R_URI = '';
55 
61  public $colPos;
62 
66  public $uid_pid;
67 
73  public $modTSconfig = [];
74 
80  public $doc;
81 
87  public $content;
88 
94  public $access;
95 
101  public $config;
102 
106  protected $pageInfo;
107 
111  protected $onClickEvent;
112 
116  protected $MCONF;
117 
121  protected $view;
122 
126  protected $menuItemView;
127 
131  public function __construct()
132  {
133  parent::__construct();
134  $GLOBALS['SOBE'] = $this;
135  $this->view = $this->getFluidTemplateObject();
136  $this->menuItemView = $this->getFluidTemplateObject('MenuItem.html');
137  $this->init();
138  }
139 
143  public function init()
144  {
145  $lang = $this->getLanguageService();
146  $lang->includeLLFile('EXT:lang/Resources/Private/Language/locallang_misc.xlf');
147  $LOCAL_LANG_orig = $GLOBALS['LOCAL_LANG'];
148  $lang->includeLLFile('EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf');
149  ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG_orig, $GLOBALS['LOCAL_LANG']);
150  $GLOBALS['LOCAL_LANG'] = $LOCAL_LANG_orig;
151 
152  // Setting internal vars:
153  $this->id = (int)GeneralUtility::_GP('id');
154  $this->sys_language = (int)GeneralUtility::_GP('sys_language_uid');
155  $this->R_URI = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
156  $this->colPos = GeneralUtility::_GP('colPos') === null ? null : (int)GeneralUtility::_GP('colPos');
157  $this->uid_pid = (int)GeneralUtility::_GP('uid_pid');
158  $this->MCONF['name'] = 'xMOD_db_new_content_el';
159  $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.wizards.newContentElement');
161  $this->config = $config['mod.']['wizards.']['newContentElement.'];
162  // Starting the document template object:
163  // We keep this here in case somebody relies on it in a hook or alike
164  $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
165  // Setting up the context sensitive menu:
166  $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
167  // Getting the current page and receiving access information (used in main())
168  $perms_clause = $this->getBackendUser()->getPagePermsClause(1);
169  $this->pageInfo = BackendUtility::readPageAccess($this->id, $perms_clause);
170  $this->access = is_array($this->pageInfo) ? 1 : 0;
171  }
172 
181  public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
182  {
183  $this->main();
184  $this->moduleTemplate->setContent($this->content);
185  $response->getBody()->write($this->moduleTemplate->renderContent());
186  return $response;
187  }
188 
194  public function main()
195  {
196  $hasAccess = true;
197  if ($this->id && $this->access) {
198 
199  // Init position map object:
200  $posMap = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\View\ContentCreationPagePositionMap::class);
201  $posMap->cur_sys_language = $this->sys_language;
202  // If a column is pre-set:
203  if (isset($this->colPos)) {
204  if ($this->uid_pid < 0) {
205  $row = [];
206  $row['uid'] = abs($this->uid_pid);
207  } else {
208  $row = '';
209  }
210  $this->onClickEvent = $posMap->onClickInsertRecord(
211  $row,
212  $this->colPos,
213  '',
214  $this->uid_pid,
215  $this->sys_language
216  );
217  } else {
218  $this->onClickEvent = '';
219  }
220  // ***************************
221  // Creating content
222  // ***************************
223  // Wizard
224  $wizardItems = $this->wizardArray();
225  // Wrapper for wizards
226  // Hook for manipulating wizardItems, wrapper, onClickEvent etc.
227  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'])) {
228  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'] as $classData) {
229  $hookObject = GeneralUtility::getUserObj($classData);
230  if (!$hookObject instanceof NewContentElementWizardHookInterface) {
231  throw new \UnexpectedValueException(
232  $classData . ' must implement interface ' . NewContentElementWizardHookInterface::class,
233  1227834741
234  );
235  }
236  $hookObject->manipulateWizardItems($wizardItems, $this);
237  }
238  }
239  // Add document inline javascript
240  $this->moduleTemplate->addJavaScriptCode(
241  'NewContentElementWizardInlineJavascript',
242  '
243  function goToalt_doc() {
244  ' . $this->onClickEvent . '
245  }'
246  );
247 
248  // Traverse items for the wizard.
249  // An item is either a header or an item rendered with a radio button and title/description and icon:
250  $cc = ($key = 0);
251  $menuItems = [];
252 
253  $this->view->assign('onClickEvent', $this->onClickEvent);
254 
255  foreach ($wizardItems as $wizardKey => $wInfo) {
256  $wizardOnClick = '';
257  if (isset($wInfo['header'])) {
258  $menuItems[] = [
259  'label' => $wInfo['header'] ?: '-',
260  'content' => ''
261  ];
262  $key = count($menuItems) - 1;
263  } else {
264  if (!$this->onClickEvent) {
265  // Radio button:
266  $wizardOnClick = 'document.editForm.defValues.value=unescape(' . GeneralUtility::quoteJSvalue(rawurlencode($wInfo['params'])) . ');goToalt_doc();' . (!$this->onClickEvent ? 'window.location.hash=\'#sel2\';' : '');
267  // Onclick action for icon/title:
268  $aOnClick = 'document.getElementsByName(\'tempB\')[' . $cc . '].checked=1;' . $wizardOnClick . 'return false;';
269  } else {
270  $aOnClick = "document.editForm.defValues.value=unescape('" . rawurlencode($wInfo['params']) . "');goToalt_doc();" . (!$this->onClickEvent?"window.location.hash='#sel2';":'');
271  }
272 
273  $icon = $this->moduleTemplate->getIconFactory()->getIcon($wInfo['iconIdentifier'])->render();
274 
275  $this->menuItemView->assignMultiple([
276  'onClickEvent' => $this->onClickEvent,
277  'aOnClick' => $aOnClick,
278  'wizardInformation' => $wInfo,
279  'icon' => $icon,
280  'wizardOnClick' => $wizardOnClick,
281  'wizardKey' => $wizardKey
282  ]);
283  $menuItems[$key]['content'] .= $this->menuItemView->render();
284  $cc++;
285  }
286  }
287 
288  $this->view->assign('renderedTabs', $this->moduleTemplate->getDynamicTabMenu(
289  $menuItems,
290  'new-content-element-wizard'
291  ));
292 
293  // If the user must also select a column:
294  if (!$this->onClickEvent) {
295 
296  // Load SHARED page-TSconfig settings and retrieve column list from there, if applicable:
297  $colPosArray = GeneralUtility::callUserFunction(
298  BackendLayoutView::class . '->getColPosListItemsParsed',
299  $this->id,
300  $this
301  );
302  $colPosIds = array_column($colPosArray, 1);
303  // Removing duplicates, if any
304  $colPosList = implode(',', array_unique(array_map('intval', $colPosIds)));
305  // Finally, add the content of the column selector to the content:
306  $this->view->assign('posMap', $posMap->printContentElementColumns($this->id, 0, $colPosList, 1, $this->R_URI));
307  }
308  } else {
309  // In case of no access:
310  $hasAccess = false;
311  }
312  $this->view->assign('hasAccess', $hasAccess);
313 
314  $this->content = $this->view->render();
315  // Setting up the buttons and markers for docheader
316  $this->getButtons();
317  }
318 
322  protected function getButtons()
323  {
324  $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
325  if ($this->R_URI) {
326  $backButton = $buttonBar->makeLinkButton()
327  ->setHref($this->R_URI)
328  ->setTitle($this->getLanguageService()->getLL('goBack'))
329  ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
330  'actions-view-go-back',
332  ));
333  $buttonBar->addButton($backButton);
334  }
335  $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_ce');
336  $buttonBar->addButton($cshButton);
337  }
338 
339  /***************************
340  *
341  * OTHER FUNCTIONS:
342  *
343  ***************************/
344 
351  public function wizardArray()
352  {
353  $wizardItems = [];
354  if (is_array($this->config)) {
355  $wizards = $this->config['wizardItems.'];
356  $appendWizards = $this->wizard_appendWizards($wizards['elements.']);
357  if (is_array($wizards)) {
358  foreach ($wizards as $groupKey => $wizardGroup) {
359  $this->prepareDependencyOrdering($wizards[$groupKey], 'before');
360  $this->prepareDependencyOrdering($wizards[$groupKey], 'after');
361  }
362  $wizards = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($wizards);
363 
364  foreach ($wizards as $groupKey => $wizardGroup) {
365  $groupKey = rtrim($groupKey, '.');
366  $showItems = GeneralUtility::trimExplode(',', $wizardGroup['show'], true);
367  $showAll = in_array('*', $showItems, true);
368  $groupItems = [];
369  if (is_array($appendWizards[$groupKey . '.']['elements.'])) {
370  $wizardElements = array_merge((array)$wizardGroup['elements.'], $appendWizards[$groupKey . '.']['elements.']);
371  } else {
372  $wizardElements = $wizardGroup['elements.'];
373  }
374  if (is_array($wizardElements)) {
375  foreach ($wizardElements as $itemKey => $itemConf) {
376  $itemKey = rtrim($itemKey, '.');
377  if ($showAll || in_array($itemKey, $showItems)) {
378  $tmpItem = $this->wizard_getItem($groupKey, $itemKey, $itemConf);
379  if ($tmpItem) {
380  $groupItems[$groupKey . '_' . $itemKey] = $tmpItem;
381  }
382  }
383  }
384  }
385  if (!empty($groupItems)) {
386  $wizardItems[$groupKey] = $this->wizard_getGroupHeader($groupKey, $wizardGroup);
387  $wizardItems = array_merge($wizardItems, $groupItems);
388  }
389  }
390  }
391  }
392  // Remove elements where preset values are not allowed:
393  $this->removeInvalidElements($wizardItems);
394  return $wizardItems;
395  }
396 
401  public function wizard_appendWizards($wizardElements)
402  {
403  if (!is_array($wizardElements)) {
404  $wizardElements = [];
405  }
406  if (is_array($GLOBALS['TBE_MODULES_EXT']['xMOD_db_new_content_el']['addElClasses'])) {
407  foreach ($GLOBALS['TBE_MODULES_EXT']['xMOD_db_new_content_el']['addElClasses'] as $class => $path) {
408  require_once $path;
409  $modObj = GeneralUtility::makeInstance($class);
410  $wizardElements = $modObj->proc($wizardElements);
411  }
412  }
413  $returnElements = [];
414  foreach ($wizardElements as $key => $wizardItem) {
415  preg_match('/^[a-zA-Z0-9]+_/', $key, $group);
416  $wizardGroup = $group[0] ? substr($group[0], 0, -1) . '.' : $key;
417  $returnElements[$wizardGroup]['elements.'][substr($key, strlen($wizardGroup)) . '.'] = $wizardItem;
418  }
419  return $returnElements;
420  }
421 
428  public function wizard_getItem($groupKey, $itemKey, $itemConf)
429  {
430  $itemConf['title'] = $this->getLanguageService()->sL($itemConf['title']);
431  $itemConf['description'] = $this->getLanguageService()->sL($itemConf['description']);
432  $itemConf['tt_content_defValues'] = $itemConf['tt_content_defValues.'];
433  unset($itemConf['tt_content_defValues.']);
434  return $itemConf;
435  }
436 
442  public function wizard_getGroupHeader($groupKey, $wizardGroup)
443  {
444  return [
445  'header' => $this->getLanguageService()->sL($wizardGroup['header'])
446  ];
447  }
448 
457  public function removeInvalidElements(&$wizardItems)
458  {
459  // Get TCEFORM from TSconfig of current page
460  $row = ['pid' => $this->id];
461  $TCEFORM_TSconfig = BackendUtility::getTCEFORM_TSconfig('tt_content', $row);
462  $headersUsed = [];
463  // Traverse wizard items:
464  foreach ($wizardItems as $key => $cfg) {
465  // Exploding parameter string, if any (old style)
466  if ($wizardItems[$key]['params']) {
467  // Explode GET vars recursively
468  $tempGetVars = GeneralUtility::explodeUrl2Array($wizardItems[$key]['params'], true);
469  // If tt_content values are set, merge them into the tt_content_defValues array,
470  // unset them from $tempGetVars and re-implode $tempGetVars into the param string
471  // (in case remaining parameters are around).
472  if (is_array($tempGetVars['defVals']['tt_content'])) {
473  $wizardItems[$key]['tt_content_defValues'] = array_merge(
474  is_array($wizardItems[$key]['tt_content_defValues']) ? $wizardItems[$key]['tt_content_defValues'] : [],
475  $tempGetVars['defVals']['tt_content']
476  );
477  unset($tempGetVars['defVals']['tt_content']);
478  $wizardItems[$key]['params'] = GeneralUtility::implodeArrayForUrl('', $tempGetVars);
479  }
480  }
481  // If tt_content_defValues are defined...:
482  if (is_array($wizardItems[$key]['tt_content_defValues'])) {
483  $backendUser = $this->getBackendUser();
484  // Traverse field values:
485  foreach ($wizardItems[$key]['tt_content_defValues'] as $fN => $fV) {
486  if (is_array($GLOBALS['TCA']['tt_content']['columns'][$fN])) {
487  // Get information about if the field value is OK:
488  $config = &$GLOBALS['TCA']['tt_content']['columns'][$fN]['config'];
489  $authModeDeny = $config['type'] === 'select' && $config['authMode']
490  && !$backendUser->checkAuthMode('tt_content', $fN, $fV, $config['authMode']);
491  // explode TSconfig keys only as needed
492  if (!isset($removeItems[$fN]) && isset($TCEFORM_TSconfig[$fN]['removeItems']) && $TCEFORM_TSconfig[$fN]['removeItems'] !== '') {
493  $removeItems[$fN] = array_flip(GeneralUtility::trimExplode(
494  ',',
495  $TCEFORM_TSconfig[$fN]['removeItems'],
496  true
497  ));
498  }
499  if (!isset($keepItems[$fN]) && isset($TCEFORM_TSconfig[$fN]['keepItems']) && $TCEFORM_TSconfig[$fN]['keepItems'] !== '') {
500  $keepItems[$fN] = array_flip(GeneralUtility::trimExplode(
501  ',',
502  $TCEFORM_TSconfig[$fN]['keepItems'],
503  true
504  ));
505  }
506  $isNotInKeepItems = !empty($keepItems[$fN]) && !isset($keepItems[$fN][$fV]);
507  if ($authModeDeny || ($fN === 'CType' && (isset($removeItems[$fN][$fV]) || $isNotInKeepItems))) {
508  // Remove element all together:
509  unset($wizardItems[$key]);
510  break;
511  }
512  // Add the parameter:
513  $wizardItems[$key]['params'] .= '&defVals[tt_content][' . $fN . ']=' . rawurlencode($this->getLanguageService()->sL($fV));
514  $tmp = explode('_', $key);
515  $headersUsed[$tmp[0]] = $tmp[0];
516  }
517  }
518  }
519  }
520  // remove headers without elements
521  foreach ($wizardItems as $key => $cfg) {
522  $tmp = explode('_', $key);
523  if ($tmp[0] && !$tmp[1] && !in_array($tmp[0], $headersUsed)) {
524  unset($wizardItems[$key]);
525  }
526  }
527  }
528 
535  protected function prepareDependencyOrdering(&$wizardGroup, $key)
536  {
537  if (isset($wizardGroup[$key])) {
538  $wizardGroup[$key] = GeneralUtility::trimExplode(',', $wizardGroup[$key]);
539  $wizardGroup[$key] = array_map(function ($s) {
540  return $s . '.';
541  }, $wizardGroup[$key]);
542  }
543  }
544 
550  protected function getLanguageService()
551  {
552  return $GLOBALS['LANG'];
553  }
554 
560  protected function getBackendUser()
561  {
562  return $GLOBALS['BE_USER'];
563  }
564 
571  protected function getFluidTemplateObject(string $filename = 'Main.html'): StandaloneView
572  {
574  $view = GeneralUtility::makeInstance(StandaloneView::class);
575  $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/NewContentElement/' . $filename));
576  $view->getRequest()->setControllerExtensionName('Backend');
577  return $view;
578  }
579 }
static getPagesTSconfig($id, $rootLine=null, $returnPartArray=false)
static readPageAccess($id, $perms_clause)
static callUserFunction($funcName, &$params, &$ref, $_='', $errorMode=0)
static getFileAbsFileName($filename, $_=null, $_2=null)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static makeInstance($className,... $constructorArguments)
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
static explodeUrl2Array($string, $multidim=false)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
mainAction(ServerRequestInterface $request, ResponseInterface $response)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']