TYPO3 CMS  TYPO3_6-2
EditDocumentController.php
Go to the documentation of this file.
1 <?php
3 
25 
33 
34  // Internal, static: GPvars:
35  // GPvar "edit": Is an array looking approx like [tablename][list-of-ids]=command, eg.
36  // "&edit[pages][123]=edit". See \TYPO3\CMS\Backend\Utility\BackendUtility::editOnClick(). Value can be seen modified
37  // internally (converting NEW keyword to id, workspace/versioning etc).
41  public $editconf;
42 
43  // Commalist of fieldnames to edit. The point is IF you specify this list, only those
44  // fields will be rendered in the form. Otherwise all (available) fields in the record
45  // is shown according to the types configuration in $GLOBALS['TCA']
49  public $columnsOnly;
50 
51  // Default values for fields (array with tablenames, fields etc. as keys).
52  // Can be seen modified internally.
56  public $defVals;
57 
58  // Array of values to force being set (as hidden fields). Will be set as $this->defVals
59  // IF defVals does not exist.
63  public $overrideVals;
64 
65  // If set, this value will be set in $this->retUrl (which is used quite many places
66  // as the return URL). If not set, "dummy.php" will be set in $this->retUrl
70  public $returnUrl;
71 
72  // Close-document command. Not really sure of all options...
76  public $closeDoc;
77 
78  // Quite simply, if this variable is set, then the processing of incoming data will be performed
79  // - as if a save-button is pressed. Used in the forms as a hidden field which can be set through
80  // JavaScript if the form is somehow submitted by JavaScript).
84  public $doSave;
85 
86  // GPvar (for processing only) : The data array from which the data comes...
90  public $data;
91 
92  // GPvar (for processing only) : ?
96  public $mirror;
97 
98  // GPvar (for processing only) : Clear-cache cmd.
102  public $cacheCmd;
103 
104  // GPvar (for processing only) : Redirect (not used???)
108  public $redirect;
109 
110  // GPvar (for processing only) : Boolean: If set, then the GET var "&id=" will be added to the
111  // retUrl string so that the NEW id of something is returned to the script calling the form.
116 
117  // GPvar (for processing only) : Verification code, internal stuff.
121  public $vC;
122 
123  // GPvar : update BE_USER->uc
127  public $uc;
128 
129  // GPvar (module) : ID for displaying the page in the frontend (used for SAVE/VIEW operations)
133  public $popViewId;
134 
135  // GPvar (module) : Additional GET vars for the link, eg. "&L=xxx"
140 
141  // GPvar (module) : Alternative URL for viewing the frontend pages.
145  public $viewUrl;
146 
147  // If this is pointing to a page id it will automatically load all content elements
148  // (NORMAL column/default language) from that page into the form!
153 
154  // Alternative title for the document handler.
158  public $recTitle;
159 
160  // Disable help... ?
164  public $disHelp;
165 
166  // If set, then no SAVE/VIEW button is printed
170  public $noView;
171 
172  // If set, the $this->editconf array is returned to the calling script
173  // (used by wizard_add.php for instance)
178 
179  // GP var, localization mode for TCEforms (eg. "text")
184 
190  protected $workspace;
191 
192  // Internal, static:
199  public $doc;
200 
201  // a static HTML template, usually in templates/alt_doc.html
205  public $template;
206 
207  // Content accumulation
211  public $content;
212 
213  // Return URL script, processed. This contains the script (if any) that we should
214  // RETURN TO from the alt_doc.php script IF we press the close button. Thus this
215  // variable is normally passed along from the calling script so we can properly return if needed.
219  public $retUrl;
220 
221  // Contains the parts of the REQUEST_URI (current url). By parts we mean the result of resolving
222  // REQUEST_URI (current url) by the parse_url() function. The result is an array where eg. "path"
223  // is the script path and "query" is the parameters...
227  public $R_URL_parts;
228 
229  // Contains the current GET vars array; More specifically this array is the foundation for creating
230  // the R_URI internal var (which becomes the "url of this script" to which we submit the forms etc.)
235 
236  // Set to the URL of this script including variables which is needed to re-display the form. See main()
240  public $R_URI;
241 
242  // Is loaded with the "title" of the currently "open document" - this is used in the
243  // Document Selector box. (see makeDocSel())
247  public $storeTitle;
248 
249  // Contains an array with key/value pairs of GET parameters needed to reach the
250  // current document displayed - used in the Document Selector box. (see compileStoreDat())
254  public $storeArray;
255 
256  // Contains storeArray, but imploded into a GET parameter string (see compileStoreDat())
260  public $storeUrl;
261 
262  // Hashed value of storeURL (see compileStoreDat())
266  public $storeUrlMd5;
267 
268  // Module session data
272  public $docDat;
273 
274  // An array of the "open documents" - keys are md5 hashes (see $storeUrlMd5) identifying
275  // the various documents on the GET parameter list needed to open it. The values are
276  // arrays with 0,1,2 keys with information about the document (see compileStoreDat()).
277  // The docHandler variable is stored in the $docDat session data, key "0".
281  public $docHandler;
282 
283  // Internal: Related to the form rendering:
284  // Array of the elements to create edit forms for.
289 
290  // Pointer to the first element in $elementsData
294  public $firstEl;
295 
296  // Counter, used to count the number of errors (when users do not have edit permissions)
300  public $errorC;
301 
302  // Counter, used to count the number of new record forms displayed
306  public $newC;
307 
308  // Is set to the pid value of the last shown record - thus indicating which page to
309  // show when clicking the SAVE/VIEW button
313  public $viewId;
314 
315  // Is set to additional parameters (like "&L=xxx") if the record supports it.
320 
321  // Module TSconfig, loaded from main() based on the page id value of viewId
325  public $modTSconfig;
326 
333  public $tceforms;
334 
335  // Contains the root-line path of the currently edited record(s) - for display.
340 
341  // Internal, dynamic:
342  // Used internally to disable the storage of the document reference (eg. new records)
347 
352 
356  public function __construct() {
357  $GLOBALS['SOBE'] = $this;
358  $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_alt_doc.xml');
359  }
360 
366  protected function getSignalSlotDispatcher() {
367  if (!isset($this->signalSlotDispatcher)) {
368  $this->signalSlotDispatcher = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
369  }
371  }
372 
378  protected function emitFunctionAfterSignal($signalName) {
379  $this->getSignalSlotDispatcher()->dispatch(__CLASS__, $signalName . 'After', array($this));
380  }
381 
387  public function preInit() {
388  if (GeneralUtility::_GP('justLocalized')) {
389  $this->localizationRedirect(GeneralUtility::_GP('justLocalized'));
390  }
391  // Setting GPvars:
392  $this->editconf = GeneralUtility::_GP('edit');
393  $this->defVals = GeneralUtility::_GP('defVals');
394  $this->overrideVals = GeneralUtility::_GP('overrideVals');
395  $this->columnsOnly = GeneralUtility::_GP('columnsOnly');
396  $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
397  $this->closeDoc = GeneralUtility::_GP('closeDoc');
398  $this->doSave = GeneralUtility::_GP('doSave');
399  $this->returnEditConf = GeneralUtility::_GP('returnEditConf');
400  $this->localizationMode = GeneralUtility::_GP('localizationMode');
401  $this->workspace = GeneralUtility::_GP('workspace');
402  $this->uc = GeneralUtility::_GP('uc');
403  // Setting override values as default if defVals does not exist.
404  if (!is_array($this->defVals) && is_array($this->overrideVals)) {
405  $this->defVals = $this->overrideVals;
406  }
407  // Setting return URL
408  $this->retUrl = $this->returnUrl ?: 'dummy.php';
409  // Fix $this->editconf if versioning applies to any of the records
410  $this->fixWSversioningInEditConf();
411  // Make R_URL (request url) based on input GETvars:
412  $this->R_URL_parts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
413  $this->R_URL_getvars = GeneralUtility::_GET();
414  $this->R_URL_getvars['edit'] = $this->editconf;
415  // MAKE url for storing
416  $this->compileStoreDat();
417  // Initialize more variables.
418  $this->dontStoreDocumentRef = 0;
419  $this->storeTitle = '';
420  // Get session data for the module:
421  $this->docDat = $GLOBALS['BE_USER']->getModuleData('alt_doc.php', 'ses');
422  $this->docHandler = $this->docDat[0];
423  // If a request for closing the document has been sent, act accordingly:
424  if ($this->closeDoc > 0) {
425  $this->closeDocument($this->closeDoc);
426  }
427  // If NO vars are sent to the script, try to read first document:
428  // Added !is_array($this->editconf) because editConf must not be set either.
429  // Anyways I can't figure out when this situation here will apply...
430  if (is_array($this->R_URL_getvars) && count($this->R_URL_getvars) < 2 && !is_array($this->editconf)) {
431  $this->setDocument($this->docDat[1]);
432  }
433 
434  // Sets a temporary workspace, this request is based on
435  if ($this->workspace !== NULL) {
436  $this->getBackendUser()->setTemporaryWorkspace($this->workspace);
437  }
438 
439  $this->emitFunctionAfterSignal(__FUNCTION__);
440  }
441 
447  public function doProcessData() {
448  $out = $this->doSave || isset($_POST['_savedok_x']) || isset($_POST['_saveandclosedok_x']) || isset($_POST['_savedokview_x']) || isset($_POST['_savedoknew_x']) || isset($_POST['_translation_savedok_x']) || isset($_POST['_translation_savedokclear_x']);
449  return $out;
450  }
451 
457  public function processData() {
458  // GPvars specifically for processing:
459  $control = GeneralUtility::_GP('control');
460  $this->data = GeneralUtility::_GP('data');
461  $this->cmd = GeneralUtility::_GP('cmd');
462  $this->mirror = GeneralUtility::_GP('mirror');
463  $this->cacheCmd = GeneralUtility::_GP('cacheCmd');
464  $this->redirect = GeneralUtility::_GP('redirect');
465  $this->returnNewPageId = GeneralUtility::_GP('returnNewPageId');
466  $this->vC = GeneralUtility::_GP('vC');
467  // See tce_db.php for relevate options here:
468  // Only options related to $this->data submission are included here.
470  $tce = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
471  $tce->stripslashes_values = 0;
472 
473  if (!empty($control)) {
474  $tce->setControl($control);
475  }
476  if (isset($_POST['_translation_savedok_x'])) {
477  $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
478  }
479  if (isset($_POST['_translation_savedokclear_x'])) {
480  $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
481  $tce->updateModeL10NdiffDataClear = TRUE;
482  }
483  // Setting default values specific for the user:
484  $TCAdefaultOverride = $GLOBALS['BE_USER']->getTSConfigProp('TCAdefaults');
485  if (is_array($TCAdefaultOverride)) {
486  $tce->setDefaultsFromUserTS($TCAdefaultOverride);
487  }
488  // Setting internal vars:
489  if ($GLOBALS['BE_USER']->uc['neverHideAtCopy']) {
490  $tce->neverHideAtCopy = 1;
491  }
492  $tce->debug = 0;
493  $tce->disableRTE = !$GLOBALS['BE_USER']->isRTE();
494  // Loading TCEmain with data:
495  $tce->start($this->data, $this->cmd);
496  if (is_array($this->mirror)) {
497  $tce->setMirror($this->mirror);
498  }
499  // If pages are being edited, we set an instruction about updating the page tree after this operation.
500  if (isset($this->data['pages']) || $GLOBALS['BE_USER']->workspace != 0 && count($this->data)) {
501  BackendUtility::setUpdateSignal('updatePageTree');
502  }
503  // Checking referer / executing
504  $refInfo = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
505  $httpHost = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY');
506  if ($httpHost != $refInfo['host'] && $this->vC != $GLOBALS['BE_USER']->veriCode() && !$GLOBALS['TYPO3_CONF_VARS']['SYS']['doNotCheckReferer']) {
507  $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));
508  debug('Error: Referer host did not match with server host.');
509  } else {
510  // Perform the saving operation with TCEmain:
511  $tce->process_uploads($_FILES);
512  $tce->process_datamap();
513  $tce->process_cmdmap();
514  // If there was saved any new items, load them:
515  if (count($tce->substNEWwithIDs_table)) {
516  // save the expanded/collapsed states for new inline records, if any
518  $newEditConf = array();
519  foreach ($this->editconf as $tableName => $tableCmds) {
520  $keys = array_keys($tce->substNEWwithIDs_table, $tableName);
521  if (count($keys) > 0) {
522  foreach ($keys as $key) {
523  $editId = $tce->substNEWwithIDs[$key];
524  // Check if the $editId isn't a child record of an IRRE action
525  if (!(is_array($tce->newRelatedIDs[$tableName]) && in_array($editId, $tce->newRelatedIDs[$tableName]))) {
526  // Translate new id to the workspace version:
527  if ($versionRec = BackendUtility::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $tableName, $editId, 'uid')) {
528  $editId = $versionRec['uid'];
529  }
530  $newEditConf[$tableName][$editId] = 'edit';
531  }
532  // Traverse all new records and forge the content of ->editconf so we can continue to EDIT these records!
533  if ($tableName == 'pages' && $this->retUrl != 'dummy.php' && $this->returnNewPageId) {
534  $this->retUrl .= '&id=' . $tce->substNEWwithIDs[$key];
535  }
536  }
537  } else {
538  $newEditConf[$tableName] = $tableCmds;
539  }
540  }
541  // Resetting editconf if newEditConf has values:
542  if (count($newEditConf)) {
543  $this->editconf = $newEditConf;
544  }
545  // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
546  $this->R_URL_getvars['edit'] = $this->editconf;
547  // Unsetting default values since we don't need them anymore.
548  unset($this->R_URL_getvars['defVals']);
549  // Re-compile the store* values since editconf changed...
550  $this->compileStoreDat();
551  }
552  // See if any records was auto-created as new versions?
553  if (count($tce->autoVersionIdMap)) {
554  $this->fixWSversioningInEditConf($tce->autoVersionIdMap);
555  }
556  // If a document is saved and a new one is created right after.
557  if (isset($_POST['_savedoknew_x']) && is_array($this->editconf)) {
558  // Finding the current table:
559  reset($this->editconf);
560  $nTable = key($this->editconf);
561  // Finding the first id, getting the records pid+uid
562  reset($this->editconf[$nTable]);
563  $nUid = key($this->editconf[$nTable]);
564  $nRec = BackendUtility::getRecord($nTable, $nUid, 'pid,uid');
565  // Setting a blank editconf array for a new record:
566  $this->editconf = array();
567  if ($this->getNewIconMode($nTable) == 'top') {
568  $this->editconf[$nTable][$nRec['pid']] = 'new';
569  } else {
570  $this->editconf[$nTable][-$nRec['uid']] = 'new';
571  }
572  // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
573  $this->R_URL_getvars['edit'] = $this->editconf;
574  // Re-compile the store* values since editconf changed...
575  $this->compileStoreDat();
576  }
577  $tce->printLogErrorMessages(isset($_POST['_saveandclosedok_x']) || isset($_POST['_translation_savedok_x']) ? $this->retUrl : $this->R_URL_parts['path'] . '?' . GeneralUtility::implodeArrayForUrl('', $this->R_URL_getvars));
578  }
579  // || count($tce->substNEWwithIDs)... If any new items has been save, the document is CLOSED
580  // because if not, we just get that element re-listed as new. And we don't want that!
581  if (isset($_POST['_saveandclosedok_x']) || isset($_POST['_translation_savedok_x']) || $this->closeDoc < 0) {
582  $this->closeDocument(abs($this->closeDoc));
583  }
584  }
585 
591  public function init() {
592  // Setting more GPvars:
593  $this->popViewId = GeneralUtility::_GP('popViewId');
594  $this->popViewId_addParams = GeneralUtility::_GP('popViewId_addParams');
595  $this->viewUrl = GeneralUtility::_GP('viewUrl');
596  $this->editRegularContentFromId = GeneralUtility::_GP('editRegularContentFromId');
597  $this->recTitle = GeneralUtility::_GP('recTitle');
598  $this->disHelp = GeneralUtility::_GP('disHelp');
599  $this->noView = GeneralUtility::_GP('noView');
600  $this->perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
601  // Set other internal variables:
602  $this->R_URL_getvars['returnUrl'] = $this->retUrl;
603  $this->R_URI = $this->R_URL_parts['path'] . '?' . GeneralUtility::implodeArrayForUrl('', $this->R_URL_getvars);
604  // MENU-ITEMS:
605  // If array, then it's a selector box menu
606  // If empty string it's just a variable, that'll be saved.
607  // Values NOT in this array will not be saved in the settings-array for the module.
608  $this->MOD_MENU = array(
609  'showPalettes' => ''
610  );
611  // Setting virtual document name
612  $this->MCONF['name'] = 'xMOD_alt_doc.php';
613  // CLEANSE SETTINGS
614  $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->MCONF['name']);
615  // Create an instance of the document template object
616  $this->doc = $GLOBALS['TBE_TEMPLATE'];
617  $this->doc->backPath = $GLOBALS['BACK_PATH'];
618  $this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/alt_doc.html');
619  $this->doc->form = '<form action="' . htmlspecialchars($this->R_URI) . '" method="post" enctype="' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype'] . '" name="editform" onsubmit="document.editform._scrollPosition.value=(document.documentElement.scrollTop || document.body.scrollTop); return TBE_EDITOR.checkSubmit(1);">';
620  $this->doc->getPageRenderer()->loadPrototype();
621  // override the default jumpToUrl
622  $this->doc->JScodeArray['jumpToUrl'] = '
623  function jumpToUrl(URL,formEl) {
624  if (!TBE_EDITOR.isFormChanged()) {
625  window.location.href = URL;
626  } else if (formEl && formEl.type=="checkbox") {
627  formEl.checked = formEl.checked ? 0 : 1;
628  }
629  }
630 ';
631  $this->doc->JScode = $this->doc->wrapScriptTags('
632  // Object: TS:
633  // passwordDummy and decimalSign are used by tbe_editor.js and have to be declared here as
634  // TS object overwrites the object declared in tbe_editor.js
635  function typoSetup() { //
636  this.uniqueID = "";
637  this.passwordDummy = "********";
638  this.PATH_typo3 = " ";
639  this.decimalSign = ".";
640  }
641  var TS = new typoSetup();
642 
643  // Info view:
644  function launchView(table,uid,bP) { //
645  var backPath= bP ? bP : "";
646  var thePreviewWindow="";
647  thePreviewWindow = window.open(backPath+"show_item.php?table="+encodeURIComponent(table)+"&uid="+encodeURIComponent(uid),"ShowItem"+TS.uniqueID,"height=300,width=410,status=0,menubar=0,resizable=0,location=0,directories=0,scrollbars=1,toolbar=0");
648  if (thePreviewWindow && thePreviewWindow.focus) {
649  thePreviewWindow.focus();
650  }
651  }
652  function deleteRecord(table,id,url) { //
653  if (
654  ' . ($GLOBALS['BE_USER']->jsConfirmation(4) ? 'confirm(' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->getLL('deleteWarning')) . ')' : '1==1') . '
655  ) {
656  window.location.href = "tce_db.php?cmd["+table+"]["+id+"][delete]=1' . BackendUtility::getUrlToken('tceAction') . '&redirect="+escape(url)+"&vC=' . $GLOBALS['BE_USER']->veriCode() . '&prErr=1&uPT=1";
657  }
658  return false;
659  }
660  ' . (isset($_POST['_savedokview_x']) && $this->popViewId ? 'if (window.opener) { ' . BackendUtility::viewOnClick($this->popViewId, '', BackendUtility::BEgetRootLine($this->popViewId), '', $this->viewUrl, $this->popViewId_addParams, FALSE) . ' } else { ' . BackendUtility::viewOnClick($this->popViewId, '', BackendUtility::BEgetRootLine($this->popViewId), '', $this->viewUrl, $this->popViewId_addParams) . ' } ' : ''));
661  // Setting up the context sensitive menu:
662  $this->doc->getContextMenuCode();
663  $this->doc->bodyTagAdditions = 'onload="window.scrollTo(0,' . MathUtility::forceIntegerInRange(GeneralUtility::_GP('_scrollPosition'), 0, 10000) . ');"';
664 
665  $this->emitFunctionAfterSignal(__FUNCTION__);
666  }
667 
673  public function main() {
674  // Begin edit:
675  if (is_array($this->editconf)) {
676  // Initialize TCEforms (rendering the forms)
677  $this->tceforms = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\FormEngine');
678  $this->tceforms->initDefaultBEMode();
679  $this->tceforms->doSaveFieldName = 'doSave';
680  $this->tceforms->localizationMode = GeneralUtility::inList('text,media', $this->localizationMode) ? $this->localizationMode : '';
681  // text,media is keywords defined in TYPO3 Core API..., see "l10n_cat"
682  $this->tceforms->returnUrl = $this->R_URI;
683  $this->tceforms->palettesCollapsed = !$this->MOD_SETTINGS['showPalettes'];
684  $this->tceforms->disableRTE = !$GLOBALS['BE_USER']->isRTE();
685  $this->tceforms->enableClickMenu = TRUE;
686  $this->tceforms->enableTabMenu = TRUE;
687  // Clipboard is initialized:
688  // Start clipboard
689  $this->tceforms->clipObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Clipboard\\Clipboard');
690  // Initialize - reads the clipboard content from the user session
691  $this->tceforms->clipObj->initializeClipboard();
692  // Setting external variables:
693  $this->tceforms->edit_showFieldHelp = $GLOBALS['BE_USER']->uc['edit_showFieldHelp'];
694  if ($this->editRegularContentFromId) {
695  $this->editRegularContentFromId();
696  }
697  // Creating the editing form, wrap it with buttons, document selector etc.
698  $editForm = $this->makeEditForm();
699  if ($editForm) {
700  $this->firstEl = reset($this->elementsData);
701  // Checking if the currently open document is stored in the list of "open documents" - if not, then add it:
702  if (($this->docDat[1] !== $this->storeUrlMd5 || !isset($this->docHandler[$this->storeUrlMd5])) && !$this->dontStoreDocumentRef) {
703  $this->docHandler[$this->storeUrlMd5] = array($this->storeTitle, $this->storeArray, $this->storeUrl, $this->firstEl);
704  $GLOBALS['BE_USER']->pushModuleData('alt_doc.php', array($this->docHandler, $this->storeUrlMd5));
705  BackendUtility::setUpdateSignal('OpendocsController::updateNumber', count($this->docHandler));
706  }
707  // Module configuration
708  $this->modTSconfig = $this->viewId ? BackendUtility::getModTSconfig($this->viewId, 'mod.xMOD_alt_doc') : array();
709  $body = $this->tceforms->printNeededJSFunctions_top();
710  $body .= $this->compileForm($editForm);
711  $body .= $this->tceforms->printNeededJSFunctions();
712  $body .= $this->functionMenus();
713  $body .= $this->tceformMessages();
714  }
715  }
716  // Access check...
717  // The page will show only if there is a valid page and if this page may be viewed by the user
718  $this->pageinfo = BackendUtility::readPageAccess($this->viewId, $this->perms_clause);
719  // Setting up the buttons and markers for docheader
720  $docHeaderButtons = $this->getButtons();
721  $markers = array(
722  'LANGSELECTOR' => $this->langSelector(),
723  'EXTRAHEADER' => $this->extraFormHeaders(),
724  'CSH' => $docHeaderButtons['csh'],
725  'CONTENT' => $body
726  );
727  // Build the <body> for the module
728  $this->content = $this->doc->startPage('TYPO3 Edit Document');
729  $this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
730  $this->content .= $this->doc->endPage();
731  $this->content = $this->doc->insertStylesAndJS($this->content);
732  }
733 
739  public function printContent() {
740  echo $this->content;
741  }
742 
743  /***************************
744  *
745  * Sub-content functions, rendering specific parts of the module content.
746  *
747  ***************************/
754  public function makeEditForm() {
755  // Initialize variables:
756  $this->elementsData = array();
757  $this->errorC = 0;
758  $this->newC = 0;
759  $thePrevUid = '';
760  $editForm = '';
761  $trData = NULL;
762  // Traverse the GPvar edit array
763  // Tables:
764  foreach ($this->editconf as $table => $conf) {
765  if (is_array($conf) && $GLOBALS['TCA'][$table] && $GLOBALS['BE_USER']->check('tables_modify', $table)) {
766  // Traverse the keys/comments of each table (keys can be a commalist of uids)
767  foreach ($conf as $cKey => $cmd) {
768  if ($cmd == 'edit' || $cmd == 'new') {
769  // Get the ids:
770  $ids = GeneralUtility::trimExplode(',', $cKey, TRUE);
771  // Traverse the ids:
772  foreach ($ids as $theUid) {
773  // Checking if the user has permissions? (Only working as a precaution,
774  // because the final permission check is always down in TCE. But it's
775  // good to notify the user on beforehand...)
776  // First, resetting flags.
777  $hasAccess = 1;
778  $deniedAccessReason = '';
779  $deleteAccess = 0;
780  $this->viewId = 0;
781  // If the command is to create a NEW record...:
782  if ($cmd == 'new') {
783  // NOTICE: the id values in this case points to the page uid onto which the
784  // record should be create OR (if the id is negativ) to a record from the
785  // same table AFTER which to create the record.
786  if ((int)$theUid) {
787  // Find parent page on which the new record reside
788  // Less than zero - find parent page
789  if ($theUid < 0) {
790  $calcPRec = BackendUtility::getRecord($table, abs($theUid));
791  $calcPRec = BackendUtility::getRecord('pages', $calcPRec['pid']);
792  } else {
793  // always a page
794  $calcPRec = BackendUtility::getRecord('pages', abs($theUid));
795  }
796  // Now, calculate whether the user has access to creating new records on this position:
797  if (is_array($calcPRec)) {
798  // Permissions for the parent page
799  $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec);
800  if ($table == 'pages') {
801  // If pages:
802  $hasAccess = $CALC_PERMS & 8 ? 1 : 0;
803  $this->viewId = 0;
804  } else {
805  $hasAccess = $CALC_PERMS & 16 ? 1 : 0;
806  $this->viewId = $calcPRec['uid'];
807  }
808  }
809  }
810  // Don't save this document title in the document selector if the document is new.
811  $this->dontStoreDocumentRef = 1;
812  } else {
813  // Edit:
814  $calcPRec = BackendUtility::getRecord($table, $theUid);
815  BackendUtility::fixVersioningPid($table, $calcPRec);
816  if (is_array($calcPRec)) {
817  if ($table == 'pages') { // If pages:
818  $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec);
819  $hasAccess = $CALC_PERMS & 2 ? 1 : 0;
820  $deleteAccess = $CALC_PERMS & 4 ? 1 : 0;
821  $this->viewId = $calcPRec['uid'];
822  } else {
823  // Fetching pid-record first
824  $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $calcPRec['pid']));
825  $hasAccess = $CALC_PERMS & 16 ? 1 : 0;
826  $deleteAccess = $CALC_PERMS & 16 ? 1 : 0;
827  $this->viewId = $calcPRec['pid'];
828  // Adding "&L=xx" if the record being edited has a languageField with a value larger than zero!
829  if ($GLOBALS['TCA'][$table]['ctrl']['languageField'] && $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
830  $this->viewId_addParams = '&L=' . $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
831  }
832  }
833  // Check internals regarding access:
834  $isRootLevelRestrictionIgnored = BackendUtility::isRootLevelRestrictionIgnored($table);
835  if ($hasAccess || (int)$calcPRec['pid'] === 0 && $isRootLevelRestrictionIgnored) {
836  $hasAccess = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $calcPRec);
837  $deniedAccessReason = $GLOBALS['BE_USER']->errorMsg;
838  }
839  } else {
840  $hasAccess = 0;
841  }
842  }
843  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'])) {
844  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] as $_funcRef) {
845  $_params = array(
846  'table' => $table,
847  'uid' => $theUid,
848  'cmd' => $cmd,
849  'hasAccess' => $hasAccess
850  );
851  $hasAccess = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
852  }
853  }
854  // AT THIS POINT we have checked the access status of the editing/creation of
855  // records and we can now proceed with creating the form elements:
856  if ($hasAccess) {
857  $prevPageID = is_object($trData) ? $trData->prevPageID : '';
858  $trData = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\DataPreprocessor');
859  $trData->addRawData = TRUE;
860  $trData->defVals = $this->defVals;
861  $trData->lockRecords = 1;
862  $trData->disableRTE = !$GLOBALS['BE_USER']->isRTE();
863  $trData->prevPageID = $prevPageID;
864  // 'new'
865  $trData->fetchRecord($table, $theUid, $cmd == 'new' ? 'new' : '');
866  $rec = reset($trData->regTableItems_data);
867  $rec['uid'] = $cmd == 'new' ? uniqid('NEW', TRUE) : $theUid;
868  if ($cmd == 'new') {
869  $rec['pid'] = $theUid == 'prev' ? (int)$thePrevUid : (int)$theUid;
870  }
871  $this->elementsData[] = array(
872  'table' => $table,
873  'uid' => $rec['uid'],
874  'pid' => $rec['pid'],
875  'cmd' => $cmd,
876  'deleteAccess' => $deleteAccess
877  );
878  // Now, render the form:
879  if (is_array($rec)) {
880  // Setting visual path / title of form:
881  $this->generalPathOfForm = $this->tceforms->getRecordPath($table, $rec);
882  if (!$this->storeTitle) {
883  $this->storeTitle = $this->recTitle ? htmlspecialchars($this->recTitle) : BackendUtility::getRecordTitle($table, $rec, TRUE);
884  }
885  // Setting variables in TCEforms object:
886  $this->tceforms->hiddenFieldList = '';
887  $this->tceforms->globalShowHelp = !$this->disHelp;
888  if (is_array($this->overrideVals) && is_array($this->overrideVals[$table])) {
889  $this->tceforms->hiddenFieldListArr = array_keys($this->overrideVals[$table]);
890  }
891  // Register default language labels, if any:
892  $this->tceforms->registerDefaultLanguageData($table, $rec);
893  // Create form for the record (either specific list of fields or the whole record):
894  $panel = '';
895  if ($this->columnsOnly) {
896  if (is_array($this->columnsOnly)) {
897  $panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly[$table]);
898  } else {
899  $panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly);
900  }
901  } else {
902  $panel .= $this->tceforms->getMainFields($table, $rec);
903  }
904  $panel = $this->tceforms->wrapTotal($panel, $rec, $table);
905  // Setting the pid value for new records:
906  if ($cmd == 'new') {
907  $panel .= '<input type="hidden" name="data[' . htmlspecialchars($table) . '][' . htmlspecialchars($rec['uid']) . '][pid]" value="' . (int)$rec['pid'] . '" />';
908  $this->newC++;
909  }
910  // Display "is-locked" message:
911  if ($lockInfo = BackendUtility::isRecordLocked($table, $rec['uid'])) {
912  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', htmlspecialchars($lockInfo['msg']), '', \TYPO3\CMS\Core\Messaging\FlashMessage::WARNING);
914  $flashMessageService = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessageService');
916  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
917  $defaultFlashMessageQueue->enqueue($flashMessage);
918  }
919  // Combine it all:
920  $editForm .= $panel;
921  }
922  $thePrevUid = $rec['uid'];
923  } else {
924  $this->errorC++;
925  $editForm .= $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.noEditPermission', TRUE) . '<br /><br />' . ($deniedAccessReason ? 'Reason: ' . htmlspecialchars($deniedAccessReason) . '<br /><br />' : '');
926  }
927  }
928  }
929  }
930  }
931  }
932  return $editForm;
933  }
934 
940  protected function getButtons() {
941  $buttons = array(
942  'save' => '',
943  'save_view' => '',
944  'save_new' => '',
945  'save_close' => '',
946  'close' => '',
947  'delete' => '',
948  'undo' => '',
949  'history' => '',
950  'columns_only' => '',
951  'csh' => '',
952  'translation_save' => '',
953  'translation_saveclear' => ''
954  );
955  // Render SAVE type buttons:
956  // The action of each button is decided by its name attribute. (See doProcessData())
957  if (!$this->errorC && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']) {
958  // SAVE button:
959  $buttons['save'] = IconUtility::getSpriteIcon('actions-document-save', array('html' => '<input type="image" name="_savedok" class="c-inputButton" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc', TRUE) . '" />'));
960  // SAVE / VIEW button:
961  if ($this->viewId && !$this->noView && $this->getNewIconMode($this->firstEl['table'], 'saveDocView')) {
962  $buttons['save_view'] = IconUtility::getSpriteIcon('actions-document-save-view', array('html' => '<input onclick="window.open(\'\', \'newTYPO3frontendWindow\');" type="image" class="c-inputButton" name="_savedokview" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDocShow', TRUE) . '" />'));
963  }
964  // SAVE / NEW button:
965  if (count($this->elementsData) == 1 && $this->getNewIconMode($this->firstEl['table'])) {
966  $buttons['save_new'] = IconUtility::getSpriteIcon('actions-document-save-new', array('html' => '<input type="image" class="c-inputButton" name="_savedoknew" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveNewDoc', TRUE) . '" />'));
967  }
968  // SAVE / CLOSE
969  $buttons['save_close'] = IconUtility::getSpriteIcon('actions-document-save-close', array('html' => '<input type="image" class="c-inputButton" name="_saveandclosedok" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveCloseDoc', TRUE) . '" />'));
970  // FINISH TRANSLATION / SAVE / CLOSE
971  if ($GLOBALS['TYPO3_CONF_VARS']['BE']['explicitConfirmationOfTranslation']) {
972  $buttons['translation_save'] = '<input type="image" class="c-inputButton" name="_translation_savedok" src="' . IconUtility::skinImg($this->doc->backPath, 'sysext/t3skin/images/icons/actions/document-save-translation.png', '', 1) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.translationSaveDoc', TRUE) . '" /> ';
973  $buttons['translation_saveclear'] = '<input type="image" class="c-inputButton" name="_translation_savedokclear" src="' . IconUtility::skinImg($this->doc->backPath, 'sysext/t3skin/images/icons/actions/document-save-cleartranslationcache.png', '', 1) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.translationSaveDocClear', TRUE) . '" />';
974  }
975  }
976  // CLOSE button:
977  $buttons['close'] = '<a href="#" onclick="document.editform.closeDoc.value=1; document.editform.submit(); return false;" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-close') . '</a>';
978  // DELETE + UNDO buttons:
979  if (!$this->errorC && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly'] && count($this->elementsData) == 1) {
980  if ($this->firstEl['cmd'] != 'new' && MathUtility::canBeInterpretedAsInteger($this->firstEl['uid'])) {
981  // Delete:
982  if ($this->firstEl['deleteAccess'] && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly'] && !$this->getNewIconMode($this->firstEl['table'], 'disableDelete')) {
983  $aOnClick = 'return deleteRecord(\'' . $this->firstEl['table'] . '\',\'' . $this->firstEl['uid'] . '\', unescape(\'' . rawurlencode($this->retUrl) . '\'));';
984  $buttons['delete'] = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '" title="' . $GLOBALS['LANG']->getLL('deleteItem', TRUE) . '">' . IconUtility::getSpriteIcon('actions-edit-delete') . '</a>';
985  }
986  // Undo:
987  $undoRes = $GLOBALS['TYPO3_DB']->exec_SELECTquery('tstamp', 'sys_history', 'tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->firstEl['table'], 'sys_history') . ' AND recuid=' . (int)$this->firstEl['uid'], '', 'tstamp DESC', '1');
988  if ($undoButtonR = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($undoRes)) {
989  $aOnClick = 'window.location.href=' .
990  GeneralUtility::quoteJSvalue(
991  BackendUtility::getModuleUrl(
992  'record_history',
993  array(
994  'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
995  'revert' => 'ALL_FIELDS',
996  'sumUp' => -1,
997  'returnUrl' => $this->R_URI,
998  )
999  )
1000  ) . '; return false;';
1001  $buttons['undo'] = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '"' . ' title="' . htmlspecialchars(sprintf($GLOBALS['LANG']->getLL('undoLastChange'), BackendUtility::calcAge(($GLOBALS['EXEC_TIME'] - $undoButtonR['tstamp']), $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')))) . '">' . IconUtility::getSpriteIcon('actions-edit-undo') . '</a>';
1002  }
1003  if ($this->getNewIconMode($this->firstEl['table'], 'showHistory')) {
1004  $aOnClick = 'window.location.href=' .
1005  GeneralUtility::quoteJSvalue(
1006  BackendUtility::getModuleUrl(
1007  'record_history',
1008  array(
1009  'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
1010  'returnUrl' => $this->R_URI,
1011  )
1012  )
1013  ) . '; return false;';
1014  $buttons['history'] = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . IconUtility::getSpriteIcon('actions-document-history-open') . '</a>';
1015  }
1016  // If only SOME fields are shown in the form, this will link the user to the FULL form:
1017  if ($this->columnsOnly) {
1018  $buttons['columns_only'] = '<a href="' . htmlspecialchars(($this->R_URI . '&columnsOnly=')) . '" title="' . $GLOBALS['LANG']->getLL('editWholeRecord', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-open') . '</a>';
1019  }
1020  }
1021  }
1022  // add the CSH icon
1023  $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'TCEforms', $GLOBALS['BACK_PATH'], '', TRUE);
1024  $buttons['shortcut'] = $this->shortCutLink();
1025  $buttons['open_in_new_window'] = $this->openInNewWindowLink();
1026  return $buttons;
1027  }
1028 
1037  public function langSelector() {
1038  $langSelector = '';
1039  if (count($this->elementsData) == 1) {
1040  $langSelector = $this->languageSwitch($this->firstEl['table'], $this->firstEl['uid'], $this->firstEl['pid']);
1041  }
1042  return $langSelector;
1043  }
1044 
1051  public function extraFormHeaders() {
1052  $extraTemplate = '';
1053  if (is_array($this->tceforms->extraFormHeaders)) {
1054  $extraTemplate = HtmlParser::getSubpart($this->doc->moduleTemplate, '###DOCHEADER_EXTRAHEADER###');
1055  $extraTemplate = HtmlParser::substituteMarker($extraTemplate, '###EXTRAHEADER###', implode(LF, $this->tceforms->extraFormHeaders));
1056  }
1057  return $extraTemplate;
1058  }
1059 
1067  public function compileForm($editForm) {
1068  $formContent = '
1069  <!-- EDITING FORM -->
1070  ' . $editForm . '
1071 
1072  <input type="hidden" name="returnUrl" value="' . htmlspecialchars($this->retUrl) . '" />
1073  <input type="hidden" name="viewUrl" value="' . htmlspecialchars($this->viewUrl) . '" />';
1074  if ($this->returnNewPageId) {
1075  $formContent .= '<input type="hidden" name="returnNewPageId" value="1" />';
1076  }
1077  $formContent .= '<input type="hidden" name="popViewId" value="' . htmlspecialchars($this->viewId) . '" />';
1078  if ($this->viewId_addParams) {
1079  $formContent .= '<input type="hidden" name="popViewId_addParams" value="' . htmlspecialchars($this->viewId_addParams) . '" />';
1080  }
1081  $formContent .= '
1082  <input type="hidden" name="closeDoc" value="0" />
1083  <input type="hidden" name="doSave" value="0" />
1084  <input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />
1085  <input type="hidden" name="_scrollPosition" value="" />' . FormEngine::getHiddenTokenField('editRecord');
1086  return $formContent;
1087  }
1088 
1095  public function functionMenus() {
1096  if ($GLOBALS['BE_USER']->getTSConfigVal('options.enableShowPalettes')) {
1097  // Show palettes:
1098  return '
1099  <!-- Function menu (checkbox for showing all palettes): -->
1100  <br />' . BackendUtility::getFuncCheck('', 'SET[showPalettes]', $this->MOD_SETTINGS['showPalettes'], 'alt_doc.php', (GeneralUtility::implodeArrayForUrl('', array_merge($this->R_URL_getvars, array('SET' => ''))) . BackendUtility::getUrlToken('editRecord')), 'id="checkShowPalettes"') . '<label for="checkShowPalettes">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPalettes', TRUE) . '</label>';
1101  } else {
1102  return '';
1103  }
1104  }
1105 
1112  public function shortCutLink() {
1113  if ($this->returnUrl == 'close.html' || !$GLOBALS['BE_USER']->mayMakeShortcut()) {
1114  return '';
1115  }
1116  return $this->doc->makeShortcutIcon('returnUrl,edit,defVals,overrideVals,columnsOnly,returnNewPageId,editRegularContentFromId,disHelp,noView', implode(',', array_keys($this->MOD_MENU)), $this->MCONF['name'], 1);
1117  }
1118 
1125  public function openInNewWindowLink() {
1126  if ($this->returnUrl == 'close.html') {
1127  return '';
1128  }
1129  $aOnClick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue(GeneralUtility::linkThisScript(array('returnUrl' => 'close.html'))) . ',\'' . md5($this->R_URI) . '\',\'width=670,height=500,status=0,menubar=0,scrollbars=1,resizable=1\');vHWin.focus();return false;';
1130  return '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.openInNewWindow', TRUE) . '">' . IconUtility::getSpriteIcon('actions-window-open') . '</a>';
1131  }
1132 
1139  public function tceformMessages() {
1140  if (count($this->tceforms->commentMessages)) {
1141  $tceformMessages = '
1142  <!-- TCEFORM messages
1143  ' . htmlspecialchars(implode(LF, $this->tceforms->commentMessages)) . '
1144  -->
1145  ';
1146  }
1147  return $tceformMessages;
1148  }
1149 
1150  /***************************
1151  *
1152  * Localization stuff
1153  *
1154  ***************************/
1165  public function languageSwitch($table, $uid, $pid = NULL) {
1166  $content = '';
1167  $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
1168  $transOrigPointerField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
1169  // Table editable and activated for languages?
1170  if ($GLOBALS['BE_USER']->check('tables_modify', $table) && $languageField && $transOrigPointerField && !$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']) {
1171  if (is_null($pid)) {
1172  $row = BackendUtility::getRecord($table, $uid, 'pid');
1173  $pid = $row['pid'];
1174  }
1175  // Get all avalibale languages for the page
1176  $langRows = $this->getLanguages($pid);
1177  // Page available in other languages than default language?
1178  if (is_array($langRows) && count($langRows) > 1) {
1179  $rowsByLang = array();
1180  $fetchFields = 'uid,' . $languageField . ',' . $transOrigPointerField;
1181  // Get record in current language
1182  $rowCurrent = BackendUtility::getLiveVersionOfRecord($table, $uid, $fetchFields);
1183  if (!is_array($rowCurrent)) {
1184  $rowCurrent = BackendUtility::getRecord($table, $uid, $fetchFields);
1185  }
1186  $currentLanguage = $rowCurrent[$languageField];
1187  // Disabled for records with [all] language!
1188  if ($currentLanguage > -1) {
1189  // Get record in default language if needed
1190  if ($currentLanguage && $rowCurrent[$transOrigPointerField]) {
1191  $rowsByLang[0] = BackendUtility::getLiveVersionOfRecord($table, $rowCurrent[$transOrigPointerField], $fetchFields);
1192  if (!is_array($rowsByLang[0])) {
1193  $rowsByLang[0] = BackendUtility::getRecord($table, $rowCurrent[$transOrigPointerField], $fetchFields);
1194  }
1195  } else {
1196  $rowsByLang[$rowCurrent[$languageField]] = $rowCurrent;
1197  }
1198  if ($rowCurrent[$transOrigPointerField] || $currentLanguage === '0') {
1199  // Get record in other languages to see what's already available
1200  $translations = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows($fetchFields, $table, 'pid=' . (int)$pid . ' AND ' . $languageField . '>0' . ' AND ' . $transOrigPointerField . '=' . (int)$rowsByLang[0]['uid'] . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table));
1201  foreach ($translations as $row) {
1202  $rowsByLang[$row[$languageField]] = $row;
1203  }
1204  }
1205  $langSelItems = array();
1206  foreach ($langRows as $lang) {
1207  if ($GLOBALS['BE_USER']->checkLanguageAccess($lang['uid'])) {
1208  $newTranslation = isset($rowsByLang[$lang['uid']]) ? '' : ' [' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.new', TRUE) . ']';
1209  // Create url for creating a localized record
1210  if ($newTranslation) {
1211  $href = $this->doc->issueCommand('&cmd[' . $table . '][' . $rowsByLang[0]['uid'] . '][localize]=' . $lang['uid'], $this->backPath . 'alt_doc.php?justLocalized=' . rawurlencode(($table . ':' . $rowsByLang[0]['uid'] . ':' . $lang['uid'])) . '&returnUrl=' . rawurlencode($this->retUrl) . BackendUtility::getUrlToken('editRecord'));
1212  } else {
1213  $href = $this->backPath . 'alt_doc.php?';
1214  $href .= '&edit[' . $table . '][' . $rowsByLang[$lang['uid']]['uid'] . ']=edit';
1215  $href .= '&returnUrl=' . rawurlencode($this->retUrl) . BackendUtility::getUrlToken('editRecord');
1216  }
1217  $langSelItems[$lang['uid']] = '
1218  <option value="' . htmlspecialchars($href) . '"' . ($currentLanguage == $lang['uid'] ? ' selected="selected"' : '') . '>' . htmlspecialchars(($lang['title'] . $newTranslation)) . '</option>';
1219  }
1220  }
1221  // If any languages are left, make selector:
1222  if (count($langSelItems) > 1) {
1223  $onChange = 'if(this.options[this.selectedIndex].value){window.location.href=(this.options[this.selectedIndex].value);}';
1224  $content = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_general.xlf:LGL.language', TRUE) . ' <select name="_langSelector" onchange="' . htmlspecialchars($onChange) . '">
1225  ' . implode('', $langSelItems) . '
1226  </select>';
1227  }
1228  }
1229  }
1230  }
1231  return $content;
1232  }
1233 
1241  public function localizationRedirect($justLocalized) {
1242  list($table, $orig_uid, $language) = explode(':', $justLocalized);
1243  if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
1244  $localizedRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid', $table, $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . (int)$language . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=' . (int)$orig_uid . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table));
1245  if (is_array($localizedRecord)) {
1246  // Create parameters and finally run the classic page module for creating a new page translation
1247  $params = '&edit[' . $table . '][' . $localizedRecord['uid'] . ']=edit';
1248  $returnUrl = '&returnUrl=' . rawurlencode(GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl')));
1249  $location = $GLOBALS['BACK_PATH'] . 'alt_doc.php?' . $params . $returnUrl . BackendUtility::getUrlToken('editRecord');
1250  HttpUtility::redirect($location);
1251  }
1252  }
1253  }
1254 
1262  public function getLanguages($id) {
1263  $modSharedTSconfig = BackendUtility::getModTSconfig($id, 'mod.SHARED');
1264  // Fallback non sprite-configuration
1265  if (preg_match('/\\.gif$/', $modSharedTSconfig['properties']['defaultLanguageFlag'])) {
1266  $modSharedTSconfig['properties']['defaultLanguageFlag'] = str_replace('.gif', '', $modSharedTSconfig['properties']['defaultLanguageFlag']);
1267  }
1268  $languages = array(
1269  0 => array(
1270  'uid' => 0,
1271  'pid' => 0,
1272  'hidden' => 0,
1273  'title' => strlen($modSharedTSconfig['properties']['defaultLanguageLabel']) ? $modSharedTSconfig['properties']['defaultLanguageLabel'] . ' (' . $GLOBALS['LANG']->sl('LLL:EXT:lang/locallang_mod_web_list.xlf:defaultLanguage') . ')' : $GLOBALS['LANG']->sl('LLL:EXT:lang/locallang_mod_web_list.xlf:defaultLanguage'),
1274  'flag' => $modSharedTSconfig['properties']['defaultLanguageFlag']
1275  )
1276  );
1277  $exQ = $GLOBALS['BE_USER']->isAdmin() ? '' : ' AND sys_language.hidden=0';
1278  if ($id) {
1279  $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('sys_language.*', 'pages_language_overlay,sys_language', 'pages_language_overlay.sys_language_uid=sys_language.uid AND pages_language_overlay.pid=' . (int)$id . BackendUtility::deleteClause('pages_language_overlay') . $exQ, 'pages_language_overlay.sys_language_uid,sys_language.uid,sys_language.pid,sys_language.tstamp,sys_language.hidden,sys_language.title,sys_language.static_lang_isocode,sys_language.flag', 'sys_language.title');
1280  } else {
1281  $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('sys_language.*', 'sys_language', 'sys_language.hidden=0', '', 'sys_language.title');
1282  }
1283  if ($rows) {
1284  foreach ($rows as $row) {
1285  $languages[$row['uid']] = $row;
1286  }
1287  }
1288  return $languages;
1289  }
1290 
1291  /***************************
1292  *
1293  * Other functions
1294  *
1295  ***************************/
1303  public function fixWSversioningInEditConf($mapArray = FALSE) {
1304  // Traverse the editConf array
1305  if (is_array($this->editconf)) {
1306  // Tables:
1307  foreach ($this->editconf as $table => $conf) {
1308  if (is_array($conf) && $GLOBALS['TCA'][$table]) {
1309  // Traverse the keys/comments of each table (keys can be a commalist of uids)
1310  $newConf = array();
1311  foreach ($conf as $cKey => $cmd) {
1312  if ($cmd == 'edit') {
1313  // Traverse the ids:
1314  $ids = GeneralUtility::trimExplode(',', $cKey, TRUE);
1315  foreach ($ids as $idKey => $theUid) {
1316  if (is_array($mapArray)) {
1317  if ($mapArray[$table][$theUid]) {
1318  $ids[$idKey] = $mapArray[$table][$theUid];
1319  }
1320  } else {
1321  // Default, look for versions in workspace for record:
1322  $calcPRec = $this->getRecordForEdit($table, $theUid);
1323  if (is_array($calcPRec)) {
1324  // Setting UID again if it had changed, eg. due to workspace versioning.
1325  $ids[$idKey] = $calcPRec['uid'];
1326  }
1327  }
1328  }
1329  // Add the possibly manipulated IDs to the new-build newConf array:
1330  $newConf[implode(',', $ids)] = $cmd;
1331  } else {
1332  $newConf[$cKey] = $cmd;
1333  }
1334  }
1335  // Store the new conf array:
1336  $this->editconf[$table] = $newConf;
1337  }
1338  }
1339  }
1340  }
1341 
1350  public function getRecordForEdit($table, $theUid) {
1351  // Fetch requested record:
1352  $reqRecord = BackendUtility::getRecord($table, $theUid, 'uid,pid');
1353  if (is_array($reqRecord)) {
1354  // If workspace is OFFLINE:
1355  if ($GLOBALS['BE_USER']->workspace != 0) {
1356  // Check for versioning support of the table:
1357  if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1358  // If the record is already a version of "something" pass it by.
1359  if ($reqRecord['pid'] == -1) {
1360  // (If it turns out not to be a version of the current workspace there will be trouble, but that is handled inside TCEmain then and in the interface it would clearly be an error of links if the user accesses such a scenario)
1361  return $reqRecord;
1362  } else {
1363  // The input record was online and an offline version must be found or made:
1364  // Look for version of this workspace:
1365  $versionRec = BackendUtility::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $reqRecord['uid'], 'uid,pid,t3ver_oid');
1366  return is_array($versionRec) ? $versionRec : $reqRecord;
1367  }
1368  } else {
1369  // This means that editing cannot occur on this record because it was not supporting versioning which is required inside an offline workspace.
1370  return FALSE;
1371  }
1372  } else {
1373  // In ONLINE workspace, just return the originally requested record:
1374  return $reqRecord;
1375  }
1376  } else {
1377  // Return FALSE because the table/uid was not found anyway.
1378  return FALSE;
1379  }
1380  }
1381 
1388  public function editRegularContentFromId() {
1389  $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'tt_content', 'pid=' . (int)$this->editRegularContentFromId . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content') . ' AND colPos=0 AND sys_language_uid=0', '', 'sorting');
1390  if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
1391  $ecUids = array();
1392  while ($ecRec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1393  $ecUids[] = $ecRec['uid'];
1394  }
1395  $this->editconf['tt_content'][implode(',', $ecUids)] = 'edit';
1396  }
1397  $GLOBALS['TYPO3_DB']->sql_free_result($res);
1398  }
1399 
1407  public function compileStoreDat() {
1408  $this->storeArray = GeneralUtility::compileSelectedGetVarsFromArray('edit,defVals,overrideVals,columnsOnly,disHelp,noView,editRegularContentFromId,workspace', $this->R_URL_getvars);
1409  $this->storeUrl = GeneralUtility::implodeArrayForUrl('', $this->storeArray);
1410  $this->storeUrlMd5 = md5($this->storeUrl);
1411  }
1412 
1421  public function getNewIconMode($table, $key = 'saveDocNew') {
1422  $TSconfig = $GLOBALS['BE_USER']->getTSConfig('options.' . $key);
1423  $output = trim(isset($TSconfig['properties'][$table]) ? $TSconfig['properties'][$table] : $TSconfig['value']);
1424  return $output;
1425  }
1426 
1434  public function closeDocument($code = 0) {
1435  // If current document is found in docHandler,
1436  // then unset it, possibly unset it ALL and finally, write it to the session data
1437  if (isset($this->docHandler[$this->storeUrlMd5])) {
1438  // add the closing document to the recent documents
1439  $recentDocs = $GLOBALS['BE_USER']->getModuleData('opendocs::recent');
1440  if (!is_array($recentDocs)) {
1441  $recentDocs = array();
1442  }
1443  $closedDoc = $this->docHandler[$this->storeUrlMd5];
1444  $recentDocs = array_merge(array($this->storeUrlMd5 => $closedDoc), $recentDocs);
1445  if (count($recentDocs) > 8) {
1446  $recentDocs = array_slice($recentDocs, 0, 8);
1447  }
1448  // remove it from the list of the open documents
1449  unset($this->docHandler[$this->storeUrlMd5]);
1450  if ($code == '3') {
1451  $recentDocs = array_merge($this->docHandler, $recentDocs);
1452  $this->docHandler = array();
1453  }
1454  $GLOBALS['BE_USER']->pushModuleData('opendocs::recent', $recentDocs);
1455  $GLOBALS['BE_USER']->pushModuleData('alt_doc.php', array($this->docHandler, $this->docDat[1]));
1456  BackendUtility::setUpdateSignal('OpendocsController::updateNumber', count($this->docHandler));
1457  }
1458  // If ->returnEditConf is set, then add the current content of editconf to the ->retUrl variable: (used by other scripts, like wizard_add, to know which records was created or so...)
1459  if ($this->returnEditConf && $this->retUrl != 'dummy.php') {
1460  $this->retUrl .= '&returnEditConf=' . rawurlencode(json_encode($this->editconf));
1461  }
1462  // If code is NOT set OR set to 1, then make a header location redirect to $this->retUrl
1463  if (!$code || $code == 1) {
1464  HttpUtility::redirect($this->retUrl);
1465  } else {
1466  $this->setDocument('', $this->retUrl);
1467  }
1468  }
1469 
1479  public function setDocument($currentDocFromHandlerMD5 = '', $retUrl = 'alt_doc_nodoc.php') {
1480  if ($retUrl === 'alt_doc_nodoc.php') {
1481  return;
1482  }
1483  if (!$this->modTSconfig['properties']['disableDocSelector'] && is_array($this->docHandler) && count($this->docHandler)) {
1484  if (isset($this->docHandler[$currentDocFromHandlerMD5])) {
1485  $setupArr = $this->docHandler[$currentDocFromHandlerMD5];
1486  } else {
1487  $setupArr = reset($this->docHandler);
1488  }
1489  if ($setupArr[2]) {
1490  $sParts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
1491  $retUrl = $sParts['path'] . '?' . $setupArr[2] . '&returnUrl=' . rawurlencode($retUrl);
1492  }
1493  }
1495  }
1496 
1500  protected function getBackendUser() {
1501  return $GLOBALS['BE_USER'];
1502  }
1503 
1504 }
static skinImg($backPath, $src, $wHattribs='', $outputMode=0)
static readPageAccess($id, $perms_clause)
static getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields=' *')
setDocument($currentDocFromHandlerMD5='', $retUrl='alt_doc_nodoc.php')
static BEgetRootLine($uid, $clause='', $workspaceOL=FALSE)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
$uid
Definition: server.php:36
static getUrlToken($formName='securityToken', $tokenName='formToken')
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
static viewOnClick($pageUid, $backPath='', $rootLine='', $anchorSection='', $alternativeUrl='', $additionalGetVars='', $switchFocus=TRUE)
static getRecordTitle($table, $row, $prep=FALSE, $forceResult=TRUE)
static compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt=TRUE)
static getModuleData($MOD_MENU, $CHANGED_SETTINGS, $modName, $type='', $dontValidateList='', $setDefaultList='')
static getSpriteIcon($iconName, array $options=array(), array $overlays=array())
static setUpdateSignal($set='', $params='')
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=FALSE, $rawurlencodeParamName=FALSE)
debug($variable='', $name=' *variable *', $line=' *line *', $file=' *file *', $recursiveDepth=3, $debugLevel=E_DEBUG)
static redirect($url, $httpStatus=self::HTTP_STATUS_303)
Definition: HttpUtility.php:76
static fixVersioningPid($table, &$rr, $ignoreWorkspaceMatch=FALSE)
static getLiveVersionOfRecord($table, $uid, $fields=' *')
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
static deleteClause($table, $tableAlias='')