TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
NewContentElementController.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\Controller\ContentElement;
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 
17 use Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
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 
145  public function init()
146  {
147  $lang = $this->getLanguageService();
148  $lang->includeLLFile('EXT:lang/Resources/Private/Language/locallang_misc.xlf');
149  $LOCAL_LANG_orig = $GLOBALS['LOCAL_LANG'];
150  $lang->includeLLFile('EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf');
151  ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG_orig, $GLOBALS['LOCAL_LANG']);
152  $GLOBALS['LOCAL_LANG'] = $LOCAL_LANG_orig;
153 
154  // Setting internal vars:
155  $this->id = (int)GeneralUtility::_GP('id');
156  $this->sys_language = (int)GeneralUtility::_GP('sys_language_uid');
157  $this->R_URI = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
158  $this->colPos = GeneralUtility::_GP('colPos') === null ? null : (int)GeneralUtility::_GP('colPos');
159  $this->uid_pid = (int)GeneralUtility::_GP('uid_pid');
160  $this->MCONF['name'] = 'xMOD_db_new_content_el';
161  $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.wizards.newContentElement');
163  $this->config = $config['mod.']['wizards.']['newContentElement.'];
164  // Starting the document template object:
165  // We keep this here in case somebody relies on it in a hook or alike
166  $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
167  // Setting up the context sensitive menu:
168  $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ClickMenu');
169  // Getting the current page and receiving access information (used in main())
170  $perms_clause = $this->getBackendUser()->getPagePermsClause(1);
171  $this->pageInfo = BackendUtility::readPageAccess($this->id, $perms_clause);
172  $this->access = is_array($this->pageInfo) ? 1 : 0;
173  }
174 
183  public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
184  {
185  $this->main();
186  $this->moduleTemplate->setContent($this->content);
187  $response->getBody()->write($this->moduleTemplate->renderContent());
188  return $response;
189  }
190 
197  public function main()
198  {
199  $hasAccess = true;
200  if ($this->id && $this->access) {
201 
202  // Init position map object:
203  $posMap = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\View\ContentCreationPagePositionMap::class);
204  $posMap->cur_sys_language = $this->sys_language;
205  // If a column is pre-set:
206  if (isset($this->colPos)) {
207  if ($this->uid_pid < 0) {
208  $row = [];
209  $row['uid'] = abs($this->uid_pid);
210  } else {
211  $row = '';
212  }
213  $this->onClickEvent = $posMap->onClickInsertRecord(
214  $row,
215  $this->colPos,
216  '',
217  $this->uid_pid,
218  $this->sys_language
219  );
220  } else {
221  $this->onClickEvent = '';
222  }
223  // ***************************
224  // Creating content
225  // ***************************
226  // Wizard
227  $wizardItems = $this->wizardArray();
228  // Wrapper for wizards
229  // Hook for manipulating wizardItems, wrapper, onClickEvent etc.
230  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'])) {
231  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'] as $classData) {
232  $hookObject = GeneralUtility::getUserObj($classData);
233  if (!$hookObject instanceof NewContentElementWizardHookInterface) {
234  throw new \UnexpectedValueException(
235  $classData . ' must implement interface ' . NewContentElementWizardHookInterface::class,
236  1227834741
237  );
238  }
239  $hookObject->manipulateWizardItems($wizardItems, $this);
240  }
241  }
242  // Add document inline javascript
243  $this->moduleTemplate->addJavaScriptCode(
244  'NewContentElementWizardInlineJavascript',
245  '
246  function goToalt_doc() {
247  ' . $this->onClickEvent . '
248  }'
249  );
250 
251  // Traverse items for the wizard.
252  // An item is either a header or an item rendered with a radio button and title/description and icon:
253  $cc = ($key = 0);
254  $menuItems = [];
255 
256  $this->view->assign('onClickEvent', $this->onClickEvent);
257 
258  foreach ($wizardItems as $wizardKey => $wInfo) {
259  $wizardOnClick = '';
260  if ($wInfo['header']) {
261  $menuItems[] = [
262  'label' => htmlspecialchars($wInfo['header']),
263  'content' => ''
264  ];
265  $key = count($menuItems) - 1;
266  } else {
267  if (!$this->onClickEvent) {
268  // Radio button:
269  $wizardOnClick = 'document.editForm.defValues.value=unescape(' . GeneralUtility::quoteJSvalue(rawurlencode($wInfo['params'])) . ');goToalt_doc();' . (!$this->onClickEvent ? 'window.location.hash=\'#sel2\';' : '');
270  // Onclick action for icon/title:
271  $aOnClick = 'document.getElementsByName(\'tempB\')[' . $cc . '].checked=1;' . $wizardOnClick . 'return false;';
272  } else {
273  $aOnClick = "document.editForm.defValues.value=unescape('" . rawurlencode($wInfo['params']) . "');goToalt_doc();" . (!$this->onClickEvent?"window.location.hash='#sel2';":'');
274  }
275 
276  $icon = $this->moduleTemplate->getIconFactory()->getIcon($wInfo['iconIdentifier'])->render();
277 
278  $this->menuItemView->assignMultiple([
279  'onClickEvent' => $this->onClickEvent,
280  'aOnClick' => $aOnClick,
281  'wizardInformation' => $wInfo,
282  'icon' => $icon,
283  'wizardOnClick' => $wizardOnClick,
284  'wizardKey' => $wizardKey
285  ]
286  );
287  $menuItems[$key]['content'] .= $this->menuItemView->render();
288  $cc++;
289  }
290  }
291 
292  $this->view->assign('renderedTabs', $this->moduleTemplate->getDynamicTabMenu(
293  $menuItems,
294  'new-content-element-wizard'
295  ));
296 
297  // If the user must also select a column:
298  if (!$this->onClickEvent) {
299 
300  // Load SHARED page-TSconfig settings and retrieve column list from there, if applicable:
301  $colPosArray = GeneralUtility::callUserFunction(
302  BackendLayoutView::class . '->getColPosListItemsParsed',
303  $this->id,
304  $this
305  );
306  $colPosIds = array_column($colPosArray, 1);
307  // Removing duplicates, if any
308  $colPosList = implode(',', array_unique(array_map('intval', $colPosIds)));
309  // Finally, add the content of the column selector to the content:
310  $this->view->assign('posMap', $posMap->printContentElementColumns($this->id, 0, $colPosList, 1, $this->R_URI));
311  }
312  } else {
313  // In case of no access:
314  $hasAccess = false;
315  }
316  $this->view->assign('hasAccess', $hasAccess);
317 
318  $this->content = $this->view->render();
319  // Setting up the buttons and markers for docheader
320  $this->getButtons();
321  }
322 
326  protected function getButtons()
327  {
328  $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
329  if ($this->R_URI) {
330  $backButton = $buttonBar->makeLinkButton()
331  ->setHref($this->R_URI)
332  ->setTitle($this->getLanguageService()->getLL('goBack'))
333  ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
334  'actions-view-go-back',
336  ));
337  $buttonBar->addButton($backButton);
338  }
339  $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_ce');
340  $buttonBar->addButton($cshButton);
341  }
342 
343  /***************************
344  *
345  * OTHER FUNCTIONS:
346  *
347  ***************************/
348 
355  public function wizardArray()
356  {
357  $wizardItems = [];
358  if (is_array($this->config)) {
359  $wizards = $this->config['wizardItems.'];
360  $appendWizards = $this->wizard_appendWizards($wizards['elements.']);
361  if (is_array($wizards)) {
362  foreach ($wizards as $groupKey => $wizardGroup) {
363  $this->prepareDependencyOrdering($wizards[$groupKey], 'before');
364  $this->prepareDependencyOrdering($wizards[$groupKey], 'after');
365  }
366  $wizards = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($wizards);
367 
368  foreach ($wizards as $groupKey => $wizardGroup) {
369  $groupKey = rtrim($groupKey, '.');
370  $showItems = GeneralUtility::trimExplode(',', $wizardGroup['show'], true);
371  $showAll = in_array('*', $showItems, true);
372  $groupItems = [];
373  if (is_array($appendWizards[$groupKey . '.']['elements.'])) {
374  $wizardElements = array_merge((array)$wizardGroup['elements.'], $appendWizards[$groupKey . '.']['elements.']);
375  } else {
376  $wizardElements = $wizardGroup['elements.'];
377  }
378  if (is_array($wizardElements)) {
379  foreach ($wizardElements as $itemKey => $itemConf) {
380  $itemKey = rtrim($itemKey, '.');
381  if ($showAll || in_array($itemKey, $showItems)) {
382  $tmpItem = $this->wizard_getItem($groupKey, $itemKey, $itemConf);
383  if ($tmpItem) {
384  $groupItems[$groupKey . '_' . $itemKey] = $tmpItem;
385  }
386  }
387  }
388  }
389  if (!empty($groupItems)) {
390  $wizardItems[$groupKey] = $this->wizard_getGroupHeader($groupKey, $wizardGroup);
391  $wizardItems = array_merge($wizardItems, $groupItems);
392  }
393  }
394  }
395  }
396  // Remove elements where preset values are not allowed:
397  $this->removeInvalidElements($wizardItems);
398  return $wizardItems;
399  }
400 
405  public function wizard_appendWizards($wizardElements)
406  {
407  if (!is_array($wizardElements)) {
408  $wizardElements = [];
409  }
410  if (is_array($GLOBALS['TBE_MODULES_EXT']['xMOD_db_new_content_el']['addElClasses'])) {
411  foreach ($GLOBALS['TBE_MODULES_EXT']['xMOD_db_new_content_el']['addElClasses'] as $class => $path) {
412  require_once $path;
413  $modObj = GeneralUtility::makeInstance($class);
414  $wizardElements = $modObj->proc($wizardElements);
415  }
416  }
417  $returnElements = [];
418  foreach ($wizardElements as $key => $wizardItem) {
419  preg_match('/^[a-zA-Z0-9]+_/', $key, $group);
420  $wizardGroup = $group[0] ? substr($group[0], 0, -1) . '.' : $key;
421  $returnElements[$wizardGroup]['elements.'][substr($key, strlen($wizardGroup)) . '.'] = $wizardItem;
422  }
423  return $returnElements;
424  }
425 
432  public function wizard_getItem($groupKey, $itemKey, $itemConf)
433  {
434  $itemConf['title'] = $this->getLanguageService()->sL($itemConf['title']);
435  $itemConf['description'] = $this->getLanguageService()->sL($itemConf['description']);
436  $itemConf['tt_content_defValues'] = $itemConf['tt_content_defValues.'];
437  unset($itemConf['tt_content_defValues.']);
438  return $itemConf;
439  }
440 
446  public function wizard_getGroupHeader($groupKey, $wizardGroup)
447  {
448  return [
449  'header' => $this->getLanguageService()->sL($wizardGroup['header'])
450  ];
451  }
452 
462  public function removeInvalidElements(&$wizardItems)
463  {
464  // Get TCEFORM from TSconfig of current page
465  $row = ['pid' => $this->id];
466  $TCEFORM_TSconfig = BackendUtility::getTCEFORM_TSconfig('tt_content', $row);
467  $headersUsed = [];
468  // Traverse wizard items:
469  foreach ($wizardItems as $key => $cfg) {
470  // Exploding parameter string, if any (old style)
471  if ($wizardItems[$key]['params']) {
472  // Explode GET vars recursively
473  $tempGetVars = GeneralUtility::explodeUrl2Array($wizardItems[$key]['params'], true);
474  // If tt_content values are set, merge them into the tt_content_defValues array,
475  // unset them from $tempGetVars and re-implode $tempGetVars into the param string
476  // (in case remaining parameters are around).
477  if (is_array($tempGetVars['defVals']['tt_content'])) {
478  $wizardItems[$key]['tt_content_defValues'] = array_merge(
479  is_array($wizardItems[$key]['tt_content_defValues']) ? $wizardItems[$key]['tt_content_defValues'] : [],
480  $tempGetVars['defVals']['tt_content']
481  );
482  unset($tempGetVars['defVals']['tt_content']);
483  $wizardItems[$key]['params'] = GeneralUtility::implodeArrayForUrl('', $tempGetVars);
484  }
485  }
486  // If tt_content_defValues are defined...:
487  if (is_array($wizardItems[$key]['tt_content_defValues'])) {
488  $backendUser = $this->getBackendUser();
489  // Traverse field values:
490  foreach ($wizardItems[$key]['tt_content_defValues'] as $fN => $fV) {
491  if (is_array($GLOBALS['TCA']['tt_content']['columns'][$fN])) {
492  // Get information about if the field value is OK:
493  $config = &$GLOBALS['TCA']['tt_content']['columns'][$fN]['config'];
494  $authModeDeny = $config['type'] == 'select' && $config['authMode']
495  && !$backendUser->checkAuthMode('tt_content', $fN, $fV, $config['authMode']);
496  // explode TSconfig keys only as needed
497  if (!isset($removeItems[$fN])) {
498  $removeItems[$fN] = GeneralUtility::trimExplode(
499  ',',
500  $TCEFORM_TSconfig[$fN]['removeItems'],
501  true
502  );
503  }
504  if (!isset($keepItems[$fN])) {
505  $keepItems[$fN] = GeneralUtility::trimExplode(
506  ',',
507  $TCEFORM_TSconfig[$fN]['keepItems'],
508  true
509  );
510  }
511  $isNotInKeepItems = !empty($keepItems[$fN]) && !in_array($fV, $keepItems[$fN]);
512  if ($authModeDeny || $fN === 'CType' && (in_array($fV, $removeItems[$fN]) || $isNotInKeepItems)) {
513  // Remove element all together:
514  unset($wizardItems[$key]);
515  break;
516  } else {
517  // Add the parameter:
518  $wizardItems[$key]['params'] .= '&defVals[tt_content][' . $fN . ']=' . rawurlencode($fV);
519  $tmp = explode('_', $key);
520  $headersUsed[$tmp[0]] = $tmp[0];
521  }
522  }
523  }
524  }
525  }
526  // remove headers without elements
527  foreach ($wizardItems as $key => $cfg) {
528  $tmp = explode('_', $key);
529  if ($tmp[0] && !$tmp[1] && !in_array($tmp[0], $headersUsed)) {
530  unset($wizardItems[$key]);
531  }
532  }
533  }
534 
543  protected function prepareDependencyOrdering(&$wizardGroup, $key)
544  {
545  if (isset($wizardGroup[$key])) {
546  $wizardGroup[$key] = GeneralUtility::trimExplode(',', $wizardGroup[$key]);
547  $wizardGroup[$key] = array_map(function ($s) {
548  return $s . '.';
549  }, $wizardGroup[$key]);
550  }
551  }
552 
558  protected function getLanguageService()
559  {
560  return $GLOBALS['LANG'];
561  }
562 
568  protected function getBackendUser()
569  {
570  return $GLOBALS['BE_USER'];
571  }
572 
579  protected function getFluidTemplateObject(string $filename = 'Main.html'):StandaloneView
580  {
582  $view = GeneralUtility::makeInstance(StandaloneView::class);
583  $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/NewContentElement/' . $filename));
584  $view->getRequest()->setControllerExtensionName('Backend');
585  return $view;
586  }
587 }
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static explodeUrl2Array($string, $multidim=false)
static implodeArrayForUrl($name, array $theArray, $str= '', $skipBlank=false, $rawurlencodeParamName=false)
mainAction(ServerRequestInterface $request, ResponseInterface $response)
static getPagesTSconfig($id, $rootLine=null, $returnPartArray=false)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static getFileAbsFileName($filename, $_=null, $_2=null)
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)