[SECURITY] Untrusted GP data is unserialized in old CSH handling
[Packages/TYPO3.CMS.git] / typo3 / alt_doc.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Main form rendering script
30 * By sending certain parameters to this script you can bring up a form
31 * which allows the user to edit the content of one or more database records.
32 *
33 * Revised for TYPO3 3.6 November/2003 by Kasper Skårhøj
34 * XHTML compliant
35 *
36 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
37 */
38
39 require_once('init.php');
40 $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_alt_doc.xml');
41
42 t3lib_BEfunc::lockRecords();
43
44 /**
45 * Script Class: Drawing the editing form for editing records in TYPO3.
46 * Notice: It does NOT use tce_db.php to submit data to, rather it handles submissions itself
47 *
48 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
49 * @package TYPO3
50 * @subpackage core
51 */
52 class SC_alt_doc {
53
54 // Internal, static: GPvars:
55 // GPvar "edit": Is an array looking approx like [tablename][list-of-ids]=command, eg.
56 // "&edit[pages][123]=edit". See t3lib_BEfunc::editOnClick(). Value can be seen modified
57 // internally (converting NEW keyword to id, workspace/versioning etc).
58 var $editconf;
59 // Commalist of fieldnames to edit. The point is IF you specify this list, only those
60 // fields will be rendered in the form. Otherwise all (available) fields in the record
61 // is shown according to the types configuration in $GLOBALS['TCA']
62 var $columnsOnly;
63 // Default values for fields (array with tablenames, fields etc. as keys).
64 // Can be seen modified internally.
65 var $defVals;
66 // Array of values to force being set (as hidden fields). Will be set as $this->defVals
67 // IF defVals does not exist.
68 var $overrideVals;
69 // If set, this value will be set in $this->retUrl (which is used quite many places
70 // as the return URL). If not set, "dummy.php" will be set in $this->retUrl
71 var $returnUrl;
72 // Close-document command. Not really sure of all options...
73 var $closeDoc;
74 // Quite simply, if this variable is set, then the processing of incoming data will be performed
75 // - as if a save-button is pressed. Used in the forms as a hidden field which can be set through
76 // JavaScript if the form is somehow submitted by JavaScript).
77 var $doSave;
78 // GPvar (for processing only) : The data array from which the data comes...
79 var $data;
80 // GPvar (for processing only) : ?
81 var $mirror;
82 // GPvar (for processing only) : Clear-cache cmd.
83 var $cacheCmd;
84 // GPvar (for processing only) : Redirect (not used???)
85 var $redirect;
86 // GPvar (for processing only) : Boolean: If set, then the GET var "&id=" will be added to the
87 // retUrl string so that the NEW id of something is returned to the script calling the form.
88 var $returnNewPageId;
89 // GPvar (for processing only) : Verification code, internal stuff.
90 var $vC;
91 // GPvar : update BE_USER->uc
92 var $uc;
93 // GPvar (module) : ID for displaying the page in the frontend (used for SAVE/VIEW operations)
94 var $popViewId;
95 // GPvar (module) : Additional GET vars for the link, eg. "&L=xxx"
96 var $popViewId_addParams;
97 // GPvar (module) : Alternative URL for viewing the frontend pages.
98 var $viewUrl;
99 // If this is pointing to a page id it will automatically load all content elements
100 // (NORMAL column/default language) from that page into the form!
101 var $editRegularContentFromId;
102 // Alternative title for the document handler.
103 var $recTitle;
104 // Disable help... ?
105 var $disHelp;
106 // If set, then no SAVE/VIEW button is printed
107 var $noView;
108 // If set, the $this->editconf array is returned to the calling script
109 // (used by wizard_add.php for instance)
110 var $returnEditConf;
111 // GP var, localization mode for TCEforms (eg. "text")
112 var $localizationMode;
113
114 // Internal, static:
115 /**
116 * document template object
117 *
118 * @var mediumDoc
119 */
120 var $doc;
121 // a static HTML template, usually in templates/alt_doc.html
122 var $template;
123 // Content accumulation
124 var $content;
125 // Return URL script, processed. This contains the script (if any) that we should
126 // RETURN TO from the alt_doc.php script IF we press the close button. Thus this
127 // variable is normally passed along from the calling script so we can properly return if needed.
128 var $retUrl;
129 // Contains the parts of the REQUEST_URI (current url). By parts we mean the result of resolving
130 // REQUEST_URI (current url) by the parse_url() function. The result is an array where eg. "path"
131 // is the script path and "query" is the parameters...
132 var $R_URL_parts;
133 // Contains the current GET vars array; More specifically this array is the foundation for creating
134 // the R_URI internal var (which becomes the "url of this script" to which we submit the forms etc.)
135 var $R_URL_getvars;
136 // Set to the URL of this script including variables which is needed to re-display the form. See main()
137 var $R_URI;
138 // Is loaded with the "title" of the currently "open document" - this is used in the
139 // Document Selector box. (see makeDocSel())
140 var $storeTitle;
141 // Contains an array with key/value pairs of GET parameters needed to reach the
142 // current document displayed - used in the Document Selector box. (see compileStoreDat())
143 var $storeArray;
144 // Contains storeArray, but imploded into a GET parameter string (see compileStoreDat())
145 var $storeUrl;
146 // Hashed value of storeURL (see compileStoreDat())
147 var $storeUrlMd5;
148 // Module session data
149 var $docDat;
150 // An array of the "open documents" - keys are md5 hashes (see $storeUrlMd5) identifying
151 // the various documents on the GET parameter list needed to open it. The values are
152 // arrays with 0,1,2 keys with information about the document (see compileStoreDat()).
153 // The docHandler variable is stored in the $docDat session data, key "0".
154 var $docHandler;
155
156 // Internal: Related to the form rendering:
157 // Array of the elements to create edit forms for.
158 var $elementsData;
159 // Pointer to the first element in $elementsData
160 var $firstEl;
161 // Counter, used to count the number of errors (when users do not have edit permissions)
162 var $errorC;
163 // Counter, used to count the number of new record forms displayed
164 var $newC;
165 // Is set to the pid value of the last shown record - thus indicating which page to
166 // show when clicking the SAVE/VIEW button
167 var $viewId;
168 // Is set to additional parameters (like "&L=xxx") if the record supports it.
169 var $viewId_addParams;
170 // Module TSconfig, loaded from main() based on the page id value of viewId
171 var $modTSconfig;
172
173 /**
174 * instance of TCEforms class
175 *
176 * @var t3lib_TCEforms
177 */
178 var $tceforms;
179 // Contains the root-line path of the currently edited record(s) - for display.
180 var $generalPathOfForm;
181
182 // Internal, dynamic:
183 // Used internally to disable the storage of the document reference (eg. new records)
184 var $dontStoreDocumentRef;
185
186 /**
187 * First initialization.
188 *
189 * @return void
190 */
191 function preInit() {
192 if (t3lib_div::_GP('justLocalized')) {
193 $this->localizationRedirect(t3lib_div::_GP('justLocalized'));
194 }
195
196 // Setting GPvars:
197 $this->editconf = t3lib_div::_GP('edit');
198 $this->defVals = t3lib_div::_GP('defVals');
199 $this->overrideVals = t3lib_div::_GP('overrideVals');
200 $this->columnsOnly = t3lib_div::_GP('columnsOnly');
201 $this->returnUrl = t3lib_div::sanitizeLocalUrl(t3lib_div::_GP('returnUrl'));
202 $this->closeDoc = t3lib_div::_GP('closeDoc');
203 $this->doSave = t3lib_div::_GP('doSave');
204 $this->returnEditConf = t3lib_div::_GP('returnEditConf');
205 $this->localizationMode = t3lib_div::_GP('localizationMode');
206 $this->uc = t3lib_div::_GP('uc');
207
208 // Setting override values as default if defVals does not exist.
209 if (!is_array($this->defVals) && is_array($this->overrideVals)) {
210 $this->defVals = $this->overrideVals;
211 }
212
213 // Setting return URL
214 $this->retUrl = $this->returnUrl ? $this->returnUrl : 'dummy.php';
215
216 // Fix $this->editconf if versioning applies to any of the records
217 $this->fixWSversioningInEditConf();
218
219 // Make R_URL (request url) based on input GETvars:
220 $this->R_URL_parts = parse_url(t3lib_div::getIndpEnv('REQUEST_URI'));
221 $this->R_URL_getvars = t3lib_div::_GET();
222 $this->R_URL_getvars['edit'] = $this->editconf;
223
224 // MAKE url for storing
225 $this->compileStoreDat();
226
227 // Initialize more variables.
228 $this->dontStoreDocumentRef = 0;
229 $this->storeTitle = '';
230
231 // Get session data for the module:
232 $this->docDat = $GLOBALS['BE_USER']->getModuleData('alt_doc.php', 'ses');
233 $this->docHandler = $this->docDat[0];
234
235 // If a request for closing the document has been sent, act accordingly:
236 if ($this->closeDoc>0) {
237 $this->closeDocument($this->closeDoc);
238 }
239
240 // If NO vars are sent to the script, try to read first document:
241 // Added !is_array($this->editconf) because editConf must not be set either.
242 // Anyways I can't figure out when this situation here will apply...
243 if (is_array($this->R_URL_getvars) && count($this->R_URL_getvars) < 2 && !is_array($this->editconf)) {
244 $this->setDocument($this->docDat[1]);
245 }
246 }
247
248 /**
249 * Detects, if a save command has been triggered.
250 *
251 * @return boolean TRUE, then save the document (data submitted)
252 */
253 function doProcessData() {
254 $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']);
255 return $out;
256 }
257
258 /**
259 * Do processing of data, submitting it to TCEmain.
260 *
261 * @return void
262 */
263 function processData() {
264 // GPvars specifically for processing:
265 $this->data = t3lib_div::_GP('data');
266 $this->cmd = t3lib_div::_GP('cmd');
267 $this->mirror = t3lib_div::_GP('mirror');
268 $this->cacheCmd = t3lib_div::_GP('cacheCmd');
269 $this->redirect = t3lib_div::_GP('redirect');
270 $this->returnNewPageId = t3lib_div::_GP('returnNewPageId');
271 $this->vC = t3lib_div::_GP('vC');
272
273 // See tce_db.php for relevate options here:
274 // Only options related to $this->data submission are included here.
275 /** @var $tce t3lib_TCEmain */
276 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
277 $tce->stripslashes_values = 0;
278
279 if (isset($_POST['_translation_savedok_x'])) {
280 $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
281 }
282 if (isset($_POST['_translation_savedokclear_x'])) {
283 $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
284 $tce->updateModeL10NdiffDataClear = TRUE;
285 }
286
287 // Setting default values specific for the user:
288 $TCAdefaultOverride = $GLOBALS['BE_USER']->getTSConfigProp('TCAdefaults');
289 if (is_array($TCAdefaultOverride)) {
290 $tce->setDefaultsFromUserTS($TCAdefaultOverride);
291 }
292
293 // Setting internal vars:
294 if ($GLOBALS['BE_USER']->uc['neverHideAtCopy']) {
295 $tce->neverHideAtCopy = 1;
296 }
297 $tce->debug = 0;
298 $tce->disableRTE = !$GLOBALS['BE_USER']->isRTE();
299
300 // Loading TCEmain with data:
301 $tce->start($this->data, $this->cmd);
302 if (is_array($this->mirror)) {
303 $tce->setMirror($this->mirror);
304 }
305
306 // If pages are being edited, we set an instruction about updating the page tree after this operation.
307 if (isset($this->data['pages']) || $GLOBALS['BE_USER']->workspace != 0 && count($this->data)) {
308 t3lib_BEfunc::setUpdateSignal('updatePageTree');
309 }
310
311 // Checking referer / executing
312 $refInfo = parse_url(t3lib_div::getIndpEnv('HTTP_REFERER'));
313 $httpHost = t3lib_div::getIndpEnv('TYPO3_HOST_ONLY');
314 if ($httpHost != $refInfo['host'] && $this->vC != $GLOBALS['BE_USER']->veriCode() && !$GLOBALS['TYPO3_CONF_VARS']['SYS']['doNotCheckReferer']) {
315 $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));
316 debug('Error: Referer host did not match with server host.');
317 } else {
318
319 // Perform the saving operation with TCEmain:
320 $tce->process_uploads($_FILES);
321 $tce->process_datamap();
322 $tce->process_cmdmap();
323
324 // If there was saved any new items, load them:
325 if (count($tce->substNEWwithIDs_table)) {
326 // save the expanded/collapsed states for new inline records, if any
327 t3lib_TCEforms_inline::updateInlineView($this->uc, $tce);
328
329 $newEditConf = array();
330
331 foreach ($this->editconf as $tableName => $tableCmds) {
332 $keys = array_keys($tce->substNEWwithIDs_table, $tableName);
333 if (count($keys) > 0) {
334 foreach ($keys as $key) {
335 $editId = $tce->substNEWwithIDs[$key];
336 // Check if the $editId isn't a child record of an IRRE action
337 if (!(is_array($tce->newRelatedIDs[$tableName]) && in_array($editId, $tce->newRelatedIDs[$tableName]))) {
338 // Translate new id to the workspace version:
339 if ($versionRec = t3lib_BEfunc::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $tableName, $editId, 'uid')) {
340 $editId = $versionRec['uid'];
341 }
342 $newEditConf[$tableName][$editId] = 'edit';
343 }
344
345 // Traverse all new records and forge the content of ->editconf so we can continue to EDIT these records!
346 if ($tableName == 'pages' && $this->retUrl != 'dummy.php' && $this->returnNewPageId) {
347 $this->retUrl .= '&id='.$tce->substNEWwithIDs[$key];
348 }
349 }
350 } else {
351 $newEditConf[$tableName] = $tableCmds;
352 }
353 }
354
355 // Resetting editconf if newEditConf has values:
356 if (count($newEditConf)) {
357 $this->editconf = $newEditConf;
358 }
359
360 // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
361 $this->R_URL_getvars['edit'] = $this->editconf;
362
363 // Unsetting default values since we don't need them anymore.
364 unset($this->R_URL_getvars['defVals']);
365
366 // Re-compile the store* values since editconf changed...
367 $this->compileStoreDat();
368 }
369
370 // See if any records was auto-created as new versions?
371 if (count($tce->autoVersionIdMap)) {
372 $this->fixWSversioningInEditConf($tce->autoVersionIdMap);
373 }
374
375 // If a document is saved and a new one is created right after.
376 if (isset($_POST['_savedoknew_x']) && is_array($this->editconf)) {
377
378 // Finding the current table:
379 reset($this->editconf);
380 $nTable = key($this->editconf);
381
382 // Finding the first id, getting the records pid+uid
383 reset($this->editconf[$nTable]);
384 $nUid = key($this->editconf[$nTable]);
385 $nRec = t3lib_BEfunc::getRecord($nTable, $nUid, 'pid,uid');
386
387 // Setting a blank editconf array for a new record:
388 $this->editconf=array();
389 if ($this->getNewIconMode($nTable) == 'top') {
390 $this->editconf[$nTable][$nRec['pid']] = 'new';
391 } else {
392 $this->editconf[$nTable][-$nRec['uid']] = 'new';
393 }
394
395 // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
396 $this->R_URL_getvars['edit'] = $this->editconf;
397
398 // Re-compile the store* values since editconf changed...
399 $this->compileStoreDat();
400 }
401
402 $tce->printLogErrorMessages(
403 (isset($_POST['_saveandclosedok_x']) || isset($_POST['_translation_savedok_x'])) ?
404 $this->retUrl :
405 // popView will not be invoked here, because the information from the submit
406 // button for save/view will be lost .... But does it matter if there is an error anyways?
407 $this->R_URL_parts['path'].'?'.t3lib_div::implodeArrayForUrl('', $this->R_URL_getvars)
408 );
409 }
410 // || count($tce->substNEWwithIDs)... If any new items has been save, the document is CLOSED
411 // because if not, we just get that element re-listed as new. And we don't want that!
412 if ((isset($_POST['_saveandclosedok_x']) || isset($_POST['_translation_savedok_x'])) || $this->closeDoc < 0) {
413 $this->closeDocument(abs($this->closeDoc));
414 }
415 }
416
417 /**
418 * Initialize the normal module operation
419 *
420 * @return void
421 */
422 function init() {
423
424 // Setting more GPvars:
425 $this->popViewId = t3lib_div::_GP('popViewId');
426 $this->popViewId_addParams = t3lib_div::_GP('popViewId_addParams');
427 $this->viewUrl = t3lib_div::_GP('viewUrl');
428 $this->editRegularContentFromId = t3lib_div::_GP('editRegularContentFromId');
429 $this->recTitle = t3lib_div::_GP('recTitle');
430 $this->disHelp = t3lib_div::_GP('disHelp');
431 $this->noView = t3lib_div::_GP('noView');
432
433 $this->perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
434
435 // Set other internal variables:
436 $this->R_URL_getvars['returnUrl'] = $this->retUrl;
437 $this->R_URI = $this->R_URL_parts['path'].'?'.t3lib_div::implodeArrayForUrl('', $this->R_URL_getvars);
438
439 // MENU-ITEMS:
440 // If array, then it's a selector box menu
441 // If empty string it's just a variable, that'll be saved.
442 // Values NOT in this array will not be saved in the settings-array for the module.
443 $this->MOD_MENU = array(
444 'showPalettes' => ''
445 );
446
447 // Setting virtual document name
448 $this->MCONF['name']='xMOD_alt_doc.php';
449
450 // CLEANSE SETTINGS
451 $this->MOD_SETTINGS = t3lib_BEfunc::getModuleData($this->MOD_MENU, t3lib_div::_GP('SET'), $this->MCONF['name']);
452
453 // Create an instance of the document template object
454 $this->doc = $GLOBALS['TBE_TEMPLATE'];
455 $this->doc->backPath = $GLOBALS['BACK_PATH'];
456 $this->doc->setModuleTemplate('templates/alt_doc.html');
457 $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);">';
458
459 $this->doc->getPageRenderer()->loadPrototype();
460 $this->doc->JScode = $this->doc->wrapScriptTags('
461 function jumpToUrl(URL,formEl) { //
462 if (!TBE_EDITOR.isFormChanged()) {
463 window.location.href = URL;
464 } else if (formEl && formEl.type=="checkbox") {
465 formEl.checked = formEl.checked ? 0 : 1;
466 }
467 }
468 // Object: TS:
469 // passwordDummy and decimalSign are used by tbe_editor.js and have to be declared here as
470 // TS object overwrites the object declared in tbe_editor.js
471 function typoSetup() { //
472 this.uniqueID = "";
473 this.passwordDummy = "********";
474 this.PATH_typo3 = " ";
475 this.decimalSign = ".";
476 }
477 var TS = new typoSetup();
478
479 // Info view:
480 function launchView(table,uid,bP) { //
481 var backPath= bP ? bP : "";
482 var thePreviewWindow="";
483 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");
484 if (thePreviewWindow && thePreviewWindow.focus) {
485 thePreviewWindow.focus();
486 }
487 }
488 function deleteRecord(table,id,url) { //
489 if (
490 ' . ($GLOBALS['BE_USER']->jsConfirmation(4) ? 'confirm(' . $GLOBALS['LANG']->JScharCode($GLOBALS['LANG']->getLL('deleteWarning')) . ')' : '1==1') . '
491 ) {
492 window.location.href = "tce_db.php?cmd["+table+"]["+id+"][delete]=1' . t3lib_BEfunc::getUrlToken('tceAction') . '&redirect="+escape(url)+"&vC=' . $GLOBALS['BE_USER']->veriCode() . '&prErr=1&uPT=1";
493 }
494 return false;
495 }
496 '.(isset($_POST['_savedokview_x']) && $this->popViewId ?
497 'if (window.opener) { '.
498 t3lib_BEfunc::viewOnClick($this->popViewId, '', t3lib_BEfunc::BEgetRootLine($this->popViewId), '', $this->viewUrl, $this->popViewId_addParams, FALSE).
499 ' } else { '.
500 t3lib_BEfunc::viewOnClick($this->popViewId, '', t3lib_BEfunc::BEgetRootLine($this->popViewId), '', $this->viewUrl, $this->popViewId_addParams).
501 ' } '
502 : '')
503 );
504
505 // Setting up the context sensitive menu:
506 $this->doc->getContextMenuCode();
507 $this->doc->bodyTagAdditions = 'onload="window.scrollTo(0,'.t3lib_utility_Math::forceIntegerInRange(t3lib_div::_GP('_scrollPosition'), 0, 10000).');"';
508 }
509
510 /**
511 * Main module operation
512 *
513 * @return void
514 */
515 function main() {
516
517 // Begin edit:
518 if (is_array($this->editconf)) {
519
520 // Initialize TCEforms (rendering the forms)
521 $this->tceforms = t3lib_div::makeInstance('t3lib_TCEforms');
522 $this->tceforms->initDefaultBEMode();
523 $this->tceforms->doSaveFieldName = 'doSave';
524 $this->tceforms->localizationMode = t3lib_div::inList('text,media', $this->localizationMode) ? $this->localizationMode : ''; // text,media is keywords defined in TYPO3 Core API..., see "l10n_cat"
525 $this->tceforms->returnUrl = $this->R_URI;
526 $this->tceforms->palettesCollapsed = !$this->MOD_SETTINGS['showPalettes'];
527 $this->tceforms->disableRTE = !$GLOBALS['BE_USER']->isRTE();
528 $this->tceforms->enableClickMenu = TRUE;
529 $this->tceforms->enableTabMenu = TRUE;
530
531 // Clipboard is initialized:
532 // Start clipboard
533 $this->tceforms->clipObj = t3lib_div::makeInstance('t3lib_clipboard');
534 // Initialize - reads the clipboard content from the user session
535 $this->tceforms->clipObj->initializeClipboard();
536
537 // Setting external variables:
538 $this->tceforms->edit_showFieldHelp = $GLOBALS['BE_USER']->uc['edit_showFieldHelp'];
539
540 if ($this->editRegularContentFromId) {
541 $this->editRegularContentFromId();
542 }
543
544 // Creating the editing form, wrap it with buttons, document selector etc.
545 $editForm = $this->makeEditForm();
546
547 if ($editForm) {
548 $this->firstEl = reset($this->elementsData);
549
550 // Checking if the currently open document is stored in the list of "open documents" - if not, then add it:
551 if ((strcmp($this->docDat[1], $this->storeUrlMd5) || !isset($this->docHandler[$this->storeUrlMd5])) && !$this->dontStoreDocumentRef) {
552 $this->docHandler[$this->storeUrlMd5] = array($this->storeTitle, $this->storeArray, $this->storeUrl, $this->firstEl);
553 $GLOBALS['BE_USER']->pushModuleData('alt_doc.php', array($this->docHandler, $this->storeUrlMd5));
554 t3lib_BEfunc::setUpdateSignal('tx_opendocs::updateNumber', count($this->docHandler));
555 }
556
557 // Module configuration
558 $this->modTSconfig = ($this->viewId ? t3lib_BEfunc::getModTSconfig($this->viewId, 'mod.xMOD_alt_doc') : array());
559
560 $body .= $this->tceforms->printNeededJSFunctions_top();
561 $body .= $this->compileForm($editForm);
562 $body .= $this->tceforms->printNeededJSFunctions();
563 $body .= $this->functionMenus();
564 $body .= $this->tceformMessages();
565 }
566 }
567
568 // Access check...
569 // The page will show only if there is a valid page and if this page may be viewed by the user
570 $this->pageinfo = t3lib_BEfunc::readPageAccess($this->viewId, $this->perms_clause);
571
572 // Setting up the buttons and markers for docheader
573 $docHeaderButtons = $this->getButtons();
574 $markers = array(
575 'LANGSELECTOR' => $this->langSelector(),
576 'EXTRAHEADER' => $this->extraFormHeaders(),
577 'CSH' => $docHeaderButtons['csh'],
578 'CONTENT' => $body
579 );
580
581 // Build the <body> for the module
582 $this->content = $this->doc->startPage('TYPO3 Edit Document');
583 $this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
584 $this->content .= $this->doc->endPage();
585 $this->content = $this->doc->insertStylesAndJS($this->content);
586 }
587
588
589 /**
590 * Outputting the accumulated content to screen
591 *
592 * @return void
593 */
594 function printContent() {
595 echo $this->content;
596 }
597
598 /***************************
599 *
600 * Sub-content functions, rendering specific parts of the module content.
601 *
602 ***************************/
603
604 /**
605 * Creates the editing form with TCEforms, based on the input from GPvars.
606 *
607 * @return string HTML form elements wrapped in tables
608 */
609 function makeEditForm() {
610
611 // Initialize variables:
612 $this->elementsData = array();
613 $this->errorC = 0;
614 $this->newC = 0;
615 $thePrevUid = '';
616 $editForm = '';
617 $trData = NULL;
618
619 // Traverse the GPvar edit array
620 // Tables:
621 foreach ($this->editconf as $table => $conf) {
622 if (is_array($conf) && $GLOBALS['TCA'][$table] && $GLOBALS['BE_USER']->check('tables_modify', $table)) {
623
624 // Traverse the keys/comments of each table (keys can be a commalist of uids)
625 foreach ($conf as $cKey => $cmd) {
626 if ($cmd == 'edit' || $cmd == 'new') {
627
628 // Get the ids:
629 $ids = t3lib_div::trimExplode(',', $cKey, 1);
630
631 // Traverse the ids:
632 foreach ($ids as $theUid) {
633
634 // Checking if the user has permissions? (Only working as a precaution,
635 // because the final permission check is always down in TCE. But it's
636 // good to notify the user on beforehand...)
637 // First, resetting flags.
638 $hasAccess = 1;
639 $deniedAccessReason = '';
640 $deleteAccess = 0;
641 $this->viewId = 0;
642
643 // If the command is to create a NEW record...:
644 if ($cmd == 'new') {
645 // NOTICE: the id values in this case points to the page uid onto which the
646 // record should be create OR (if the id is negativ) to a record from the
647 // same table AFTER which to create the record.
648 if (intval($theUid)) {
649
650 // Find parent page on which the new record reside
651 // Less than zero - find parent page
652 if ($theUid < 0) {
653 $calcPRec = t3lib_BEfunc::getRecord($table, abs($theUid));
654 $calcPRec = t3lib_BEfunc::getRecord('pages', $calcPRec['pid']);
655 } else { // always a page
656 $calcPRec = t3lib_BEfunc::getRecord('pages', abs($theUid));
657 }
658
659 // Now, calculate whether the user has access to creating new records on this position:
660 if (is_array($calcPRec)) {
661 // Permissions for the parent page
662 $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec);
663 if ($table == 'pages') { // If pages:
664 $hasAccess = $CALC_PERMS&8 ? 1 : 0;
665 $this->viewId = 0;
666 } else {
667 $hasAccess = $CALC_PERMS&16 ? 1 : 0;
668 $this->viewId = $calcPRec['uid'];
669 }
670 }
671 }
672 // Don't save this document title in the document selector if the document is new.
673 $this->dontStoreDocumentRef = 1;
674 } else { // Edit:
675 $calcPRec = t3lib_BEfunc::getRecord($table, $theUid);
676 t3lib_BEfunc::fixVersioningPid($table, $calcPRec);
677 if (is_array($calcPRec)) {
678 // If pages:
679 if ($table == 'pages') {
680 $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec);
681 $hasAccess = $CALC_PERMS&2 ? 1 : 0;
682 $deleteAccess = $CALC_PERMS&4 ? 1 : 0;
683 $this->viewId = $calcPRec['uid'];
684 } else {
685 // Fetching pid-record first
686 $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms(t3lib_BEfunc::getRecord('pages', $calcPRec['pid']));
687 $hasAccess = $CALC_PERMS&16 ? 1 : 0;
688 $deleteAccess = $CALC_PERMS&16 ? 1 : 0;
689 $this->viewId = $calcPRec['pid'];
690
691 // Adding "&L=xx" if the record being edited has a languageField with a value larger than zero!
692 if ($GLOBALS['TCA'][$table]['ctrl']['languageField'] && $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
693 $this->viewId_addParams = '&L=' . $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
694 }
695 }
696
697 // Check internals regarding access:
698 if ($hasAccess) {
699 $hasAccess = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $calcPRec);
700 $deniedAccessReason = $GLOBALS['BE_USER']->errorMsg;
701 }
702 } else {
703 $hasAccess = 0;
704 }
705 }
706
707 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'])) {
708 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] as $_funcRef) {
709 $_params = array(
710 'table' => $table,
711 'uid' => $theUid,
712 'cmd' => $cmd,
713 'hasAccess' => $hasAccess
714 );
715 $hasAccess = t3lib_div::callUserFunction($_funcRef, $_params, $this);
716 }
717 }
718
719 // AT THIS POINT we have checked the access status of the editing/creation of
720 // records and we can now proceed with creating the form elements:
721
722 if ($hasAccess) {
723 $prevPageID = is_object($trData)?$trData->prevPageID:'';
724 $trData = t3lib_div::makeInstance('t3lib_transferData');
725 $trData->addRawData = TRUE;
726 $trData->defVals = $this->defVals;
727 $trData->lockRecords = 1;
728 $trData->disableRTE = !$GLOBALS['BE_USER']->isRTE();
729 $trData->prevPageID = $prevPageID;
730 // 'new'
731 $trData->fetchRecord($table, $theUid, $cmd == 'new' ? 'new' : '');
732 $rec = reset($trData->regTableItems_data);
733 $rec['uid'] = $cmd == 'new' ? uniqid('NEW') : $theUid;
734 if ($cmd == 'new') {
735 $rec['pid'] = $theUid == 'prev' ? $thePrevUid : $theUid;
736 }
737 $this->elementsData[] = array(
738 'table' => $table,
739 'uid' => $rec['uid'],
740 'pid' => $rec['pid'],
741 'cmd' => $cmd,
742 'deleteAccess' => $deleteAccess
743 );
744
745 // Now, render the form:
746 if (is_array($rec)) {
747
748 // Setting visual path / title of form:
749 $this->generalPathOfForm = $this->tceforms->getRecordPath($table, $rec);
750 if (!$this->storeTitle) {
751 $this->storeTitle = $this->recTitle ? htmlspecialchars($this->recTitle) : t3lib_BEfunc::getRecordTitle($table, $rec, TRUE);
752 }
753
754 // Setting variables in TCEforms object:
755 $this->tceforms->hiddenFieldList = '';
756 $this->tceforms->globalShowHelp = $this->disHelp ? 0 : 1;
757 if (is_array($this->overrideVals[$table])) {
758 $this->tceforms->hiddenFieldListArr = array_keys($this->overrideVals[$table]);
759 }
760
761 // Register default language labels, if any:
762 $this->tceforms->registerDefaultLanguageData($table, $rec);
763
764 // Create form for the record (either specific list of fields or the whole record):
765 $panel = '';
766 if ($this->columnsOnly) {
767 if(is_array($this->columnsOnly)){
768 $panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly[$table]);
769 } else {
770 $panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly);
771 }
772 } else {
773 $panel .= $this->tceforms->getMainFields($table, $rec);
774 }
775 $panel = $this->tceforms->wrapTotal($panel, $rec, $table);
776
777 // Setting the pid value for new records:
778 if ($cmd == 'new') {
779 $panel .= '<input type="hidden" name="data['.$table.']['.$rec['uid'].'][pid]" value="'.$rec['pid'].'" />';
780 $this->newC++;
781 }
782
783 // Display "is-locked" message:
784 if ($lockInfo = t3lib_BEfunc::isRecordLocked($table, $rec['uid'])) {
785 $lockedMessage = t3lib_div::makeInstance(
786 't3lib_FlashMessage',
787 htmlspecialchars($lockInfo['msg']),
788 '',
789 t3lib_FlashMessage::WARNING
790 );
791 t3lib_FlashMessageQueue::addMessage($lockedMessage);
792 }
793
794 // Combine it all:
795 $editForm .= $panel;
796 }
797
798 $thePrevUid = $rec['uid'];
799 } else {
800 $this->errorC++;
801 $editForm .= $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.noEditPermission', 1) . '<br /><br />' .
802 ($deniedAccessReason ? 'Reason: ' . htmlspecialchars($deniedAccessReason) . '<br /><br />' : '');
803 }
804 }
805 }
806 }
807 }
808 }
809
810 return $editForm;
811 }
812
813 /**
814 * Create the panel of buttons for submitting the form or otherwise perform operations.
815 *
816 * @return array All available buttons as an assoc. array
817 */
818 protected function getButtons() {
819 $buttons = array(
820 'save' => '',
821 'save_view' => '',
822 'save_new' => '',
823 'save_close' => '',
824 'close' => '',
825 'delete' => '',
826 'undo' => '',
827 'history' => '',
828 'columns_only' => '',
829 'csh' => '',
830 'translation_save' => '',
831 'translation_saveclear' => ''
832 );
833
834 // Render SAVE type buttons:
835 // The action of each button is decided by its name attribute. (See doProcessData())
836 if (!$this->errorC && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']) {
837
838 // SAVE button:
839 $buttons['save'] = t3lib_iconWorks::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.php:rm.saveDoc', 1) . '" />' ));
840
841 // SAVE / VIEW button:
842 if ($this->viewId && !$this->noView && t3lib_extMgm::isLoaded('cms') && $this->getNewIconMode($this->firstEl['table'], 'saveDocView')) {
843 $buttons['save_view'] = t3lib_iconWorks::getSpriteIcon('actions-document-save-view', array('html'=>'<input type="image" class="c-inputButton" name="_savedokview" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.saveDocShow', 1) . '" />'));
844 }
845
846 // SAVE / NEW button:
847 if (count($this->elementsData)==1 && $this->getNewIconMode($this->firstEl['table'])) {
848 $buttons['save_new'] = t3lib_iconWorks::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.php:rm.saveNewDoc', 1) . '" />'));
849 }
850
851 // SAVE / CLOSE
852 $buttons['save_close'] = t3lib_iconWorks::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.php:rm.saveCloseDoc', 1) . '" />'));
853
854 // FINISH TRANSLATION / SAVE / CLOSE
855 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['explicitConfirmationOfTranslation']) {
856 $buttons['translation_save'] = '<input type="image" class="c-inputButton" name="_translation_savedok" src="' . t3lib_iconWorks::skinImg($this->doc->backPath, 'gfx/translationsavedok.gif', '', 1) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.translationSaveDoc', 1) . '" />';
857 $buttons['translation_saveclear'] = '<input type="image" class="c-inputButton" name="_translation_savedokclear" src="' . t3lib_iconWorks::skinImg($this->doc->backPath, 'gfx/translationsavedok_clear.gif', '', 1) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.translationSaveDocClear', 1) . '" />';
858 }
859 }
860
861 // CLOSE button:
862 $buttons['close'] = '<a href="#" onclick="document.editform.closeDoc.value=1; document.editform.submit(); return false;" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.closeDoc', TRUE) . '">' .
863 t3lib_iconWorks::getSpriteIcon('actions-document-close') .
864 '</a>';
865
866 // DELETE + UNDO buttons:
867 if (!$this->errorC && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly'] && count($this->elementsData) == 1) {
868 if ($this->firstEl['cmd'] != 'new' && t3lib_utility_Math::canBeInterpretedAsInteger($this->firstEl['uid'])) {
869
870 // Delete:
871 if ($this->firstEl['deleteAccess'] && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly'] && !$this->getNewIconMode($this->firstEl['table'], 'disableDelete')) {
872 $aOnClick = 'return deleteRecord(\''.$this->firstEl['table'].'\',\''.$this->firstEl['uid'].'\', unescape(\''.rawurlencode($this->retUrl).'\'));';
873 $buttons['delete'] = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '" title="' . $GLOBALS['LANG']->getLL('deleteItem', TRUE) . '">' .
874 t3lib_iconWorks::getSpriteIcon('actions-edit-delete') .
875 '</a>';
876 }
877
878 // Undo:
879 $undoRes = $GLOBALS['TYPO3_DB']->exec_SELECTquery('tstamp', 'sys_history', 'tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->firstEl['table'], 'sys_history').' AND recuid='.intval($this->firstEl['uid']), '', 'tstamp DESC', '1');
880 if ($undoButtonR = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($undoRes)) {
881 $aOnClick = 'window.location.href=\'show_rechis.php?element='.rawurlencode($this->firstEl['table'].':'.$this->firstEl['uid']).'&revert=ALL_FIELDS&sumUp=-1&returnUrl='.rawurlencode($this->R_URI).'\'; return false;';
882 $buttons['undo'] = '<a href="#" onclick="'.htmlspecialchars($aOnClick).'"'.
883 ' title="' . htmlspecialchars(sprintf($GLOBALS['LANG']->getLL('undoLastChange'), t3lib_BEfunc::calcAge($GLOBALS['EXEC_TIME'] - $undoButtonR['tstamp'], $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears')))) . '">' .
884 t3lib_iconWorks::getSpriteIcon('actions-edit-undo') .
885 '</a>';
886 }
887 if ($this->getNewIconMode($this->firstEl['table'], 'showHistory')) {
888 $aOnClick = 'window.location.href=\'show_rechis.php?element='.rawurlencode($this->firstEl['table'].':'.$this->firstEl['uid']).'&returnUrl='.rawurlencode($this->R_URI).'\'; return false;';
889 $buttons['history'] = '<a href="#" onclick="'.htmlspecialchars($aOnClick).'">'.
890 t3lib_iconWorks::getSpriteIcon('actions-document-history-open') .
891 '</a>';
892 }
893
894 // If only SOME fields are shown in the form, this will link the user to the FULL form:
895 if ($this->columnsOnly) {
896 $buttons['columns_only'] = '<a href="'.htmlspecialchars($this->R_URI.'&columnsOnly=').'" title="' . $GLOBALS['LANG']->getLL('editWholeRecord', TRUE) . '">' .
897 t3lib_iconWorks::getSpriteIcon('actions-document-open') .
898 '</a>';
899 }
900 }
901 }
902
903 // add the CSH icon
904 $buttons['csh'] = t3lib_BEfunc::cshItem('xMOD_csh_corebe', 'TCEforms', $GLOBALS['BACK_PATH'], '', TRUE);
905 $buttons['shortcut'] = $this->shortCutLink();
906 $buttons['open_in_new_window'] = $this->openInNewWindowLink();
907 return $buttons;
908 }
909
910 /**
911 * Returns the language switch/selector for editing,
912 * show only when a single record is edited
913 * - multiple records are too confusing
914 * @return string The HTML
915 */
916 function langSelector() {
917 $langSelector = '';
918 if (count($this->elementsData) == 1) {
919 $langSelector = $this->languageSwitch($this->firstEl['table'], $this->firstEl['uid'], $this->firstEl['pid']);
920 }
921 return $langSelector;
922 }
923
924 /**
925 * Compiles the extra form headers if the tceforms
926 *
927 * @return string The HTML
928 */
929 function extraFormHeaders() {
930 $extraTemplate = '';
931
932 if (is_array($this->tceforms->extraFormHeaders)) {
933 $extraTemplate = t3lib_parsehtml::getSubpart($this->doc->moduleTemplate, '###DOCHEADER_EXTRAHEADER###');
934 $extraTemplate = t3lib_parsehtml::substituteMarker($extraTemplate, '###EXTRAHEADER###', implode(LF, $this->tceforms->extraFormHeaders));
935 }
936 return $extraTemplate;
937 }
938
939 /**
940 * Put together the various elements (buttons, selectors, form) into a table
941 *
942 * @param string $editForm HTML form.
943 * @return string Composite HTML
944 */
945 function compileForm($editForm) {
946 $formContent = '
947 <!-- EDITING FORM -->
948 '.$editForm.'
949
950 <input type="hidden" name="returnUrl" value="'.htmlspecialchars($this->retUrl).'" />
951 <input type="hidden" name="viewUrl" value="'.htmlspecialchars($this->viewUrl).'" />';
952
953 if ($this->returnNewPageId) {
954 $formContent .= '<input type="hidden" name="returnNewPageId" value="1" />';
955 }
956 $formContent .= '<input type="hidden" name="popViewId" value="'.htmlspecialchars($this->viewId).'" />';
957 if ($this->viewId_addParams) {
958 $formContent .= '<input type="hidden" name="popViewId_addParams" value="'.htmlspecialchars($this->viewId_addParams).'" />';
959 }
960 $formContent .= '
961 <input type="hidden" name="closeDoc" value="0" />
962 <input type="hidden" name="doSave" value="0" />
963 <input type="hidden" name="_serialNumber" value="'.md5(microtime()).'" />
964 <input type="hidden" name="_scrollPosition" value="" />' . t3lib_TCEforms::getHiddenTokenField('editRecord');
965
966 return $formContent;
967 }
968
969 /**
970 * Create the checkbox buttons in the bottom of the pages.
971 *
972 * @return string HTML for function menus.
973 */
974 function functionMenus() {
975 if ($GLOBALS['BE_USER']->getTSConfigVal('options.enableShowPalettes')) {
976 // Show palettes:
977 return '
978 <!-- Function menu (checkbox for showing all palettes): -->
979 <br />'.t3lib_BEfunc::getFuncCheck(
980 '',
981 'SET[showPalettes]',
982 $this->MOD_SETTINGS['showPalettes'],
983 'alt_doc.php',
984 t3lib_div::implodeArrayForUrl(
985 '',
986 array_merge($this->R_URL_getvars, array('SET'=>''))
987 ) . t3lib_BEfunc::getUrlToken('editRecord'), 'id="checkShowPalettes"') . '<label for="checkShowPalettes">' .
988 $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.showPalettes', 1) . '</label>';
989 } else {
990 return '';
991 }
992 }
993
994 /**
995 * Create shortcut icon
996 *
997 * @return string
998 */
999 function shortCutLink() {
1000 if ($this->returnUrl == 'close.html' || !$GLOBALS['BE_USER']->mayMakeShortcut()) {
1001 return '';
1002 }
1003 return $this->doc->makeShortcutIcon('returnUrl,edit,defVals,overrideVals,columnsOnly,returnNewPageId,editRegularContentFromId,disHelp,noView', implode(',', array_keys($this->MOD_MENU)), $this->MCONF['name'], 1);
1004 }
1005
1006 /**
1007 * Creates open-in-window link
1008 *
1009 * @return string
1010 */
1011 function openInNewWindowLink() {
1012 if ($this->returnUrl == 'close.html') {
1013 return '';
1014 }
1015 $aOnClick = 'vHWin=window.open(\''.t3lib_div::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;';
1016 return '<a href="#" onclick="'.htmlspecialchars($aOnClick).'" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.openInNewWindow', TRUE) . '">' .
1017 t3lib_iconWorks::getSpriteIcon('actions-window-open') .
1018 '</a>';
1019 }
1020
1021 /**
1022 * Reads comment messages from TCEforms and prints them in a HTML comment in the bottom of the page.
1023 *
1024 * @return void
1025 */
1026 function tceformMessages() {
1027 if (count($this->tceforms->commentMessages)) {
1028 $tceformMessages = '
1029 <!-- TCEFORM messages
1030 '.htmlspecialchars(implode(LF, $this->tceforms->commentMessages)).'
1031 -->
1032 ';
1033 }
1034 return $tceformMessages;
1035 }
1036
1037 /***************************
1038 *
1039 * Localization stuff
1040 *
1041 ***************************/
1042
1043 /**
1044 * Make selector box for creating new translation for a record or switching to edit the record in an existing language.
1045 * Displays only languages which are available for the current page.
1046 *
1047 * @param string $table Table name
1048 * @param integer $uid Uid for which to create a new language
1049 * @param integer $pid Pid of the record
1050 * @return string <select> HTML element (if there were items for the box anyways...)
1051 */
1052 function languageSwitch($table, $uid, $pid=NULL) {
1053 $content = '';
1054
1055 $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
1056 $transOrigPointerField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
1057
1058 // Table editable and activated for languages?
1059 if ($GLOBALS['BE_USER']->check('tables_modify', $table) && $languageField && $transOrigPointerField && !$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']) {
1060
1061 if (is_null($pid)) {
1062 $row = t3lib_befunc::getRecord($table, $uid, 'pid');
1063 $pid = $row['pid'];
1064 }
1065
1066 // Get all avalibale languages for the page
1067 $langRows = $this->getLanguages($pid);
1068
1069 // Page available in other languages than default language?
1070 if (is_array($langRows) && count($langRows) > 1) {
1071
1072 $rowsByLang = array();
1073 $fetchFields = 'uid,'.$languageField.','.$transOrigPointerField;
1074
1075 // Get record in current language
1076 $rowCurrent = t3lib_befunc::getLiveVersionOfRecord($table, $uid, $fetchFields);
1077 if (!is_array($rowCurrent)) {
1078 $rowCurrent = t3lib_befunc::getRecord($table, $uid, $fetchFields);
1079 }
1080
1081 $currentLanguage = $rowCurrent[$languageField];
1082
1083 // Disabled for records with [all] language!
1084 if ($currentLanguage > -1) {
1085 // Get record in default language if needed
1086 if ($currentLanguage && $rowCurrent[$transOrigPointerField]) {
1087 $rowsByLang[0] = t3lib_befunc::getLiveVersionOfRecord($table, $rowCurrent[$transOrigPointerField], $fetchFields);
1088 if (!is_array($rowsByLang[0])) {
1089 $rowsByLang[0] = t3lib_befunc::getRecord($table, $rowCurrent[$transOrigPointerField], $fetchFields);
1090 }
1091 } else {
1092 $rowsByLang[$rowCurrent[$languageField]] = $rowCurrent;
1093 }
1094
1095 if ($rowCurrent[$transOrigPointerField] || $currentLanguage === '0') {
1096 // Get record in other languages to see what's already available
1097 $translations = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1098 $fetchFields,
1099 $table,
1100 'pid='.intval($pid).
1101 ' AND '.$languageField.'>0'.
1102 ' AND '.$transOrigPointerField.'='.intval($rowsByLang[0]['uid']).
1103 t3lib_BEfunc::deleteClause($table).
1104 t3lib_BEfunc::versioningPlaceholderClause($table)
1105 );
1106 foreach ($translations as $row) {
1107 $rowsByLang[$row[$languageField]] = $row;
1108 }
1109 }
1110
1111 $langSelItems=array();
1112 foreach ($langRows as $lang) {
1113 if ($GLOBALS['BE_USER']->checkLanguageAccess($lang['uid'])) {
1114
1115 $newTranslation = isset($rowsByLang[$lang['uid']]) ? '' : ' ['.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:labels.new', 1).']';
1116
1117 // Create url for creating a localized record
1118 if($newTranslation) {
1119 $href = $this->doc->issueCommand(
1120 '&cmd['.$table.']['.$rowsByLang[0]['uid'].'][localize]='.$lang['uid'],
1121 $this->backPath.'alt_doc.php?justLocalized='.rawurlencode($table.':'.$rowsByLang[0]['uid'].':'.$lang['uid']).'&returnUrl='.rawurlencode($this->retUrl) . t3lib_BEfunc::getUrlToken('editRecord')
1122 );
1123
1124 // Create edit url
1125 } else {
1126 $href = $this->backPath.'alt_doc.php?';
1127 $href .= '&edit['.$table.']['.$rowsByLang[$lang['uid']]['uid'].']=edit';
1128 $href .= '&returnUrl='.rawurlencode($this->retUrl) . t3lib_BEfunc::getUrlToken('editRecord');
1129 }
1130
1131 $langSelItems[$lang['uid']]='
1132 <option value="'.htmlspecialchars($href).'"'.($currentLanguage==$lang['uid']?' selected="selected"':'').'>'.htmlspecialchars($lang['title'].$newTranslation).'</option>';
1133 }
1134 }
1135
1136 // If any languages are left, make selector:
1137 if (count($langSelItems)>1) {
1138 $onChange = 'if(this.options[this.selectedIndex].value){window.location.href=(this.options[this.selectedIndex].value);}';
1139 $content = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_general.xml:LGL.language', 1).' <select name="_langSelector" onchange="'.htmlspecialchars($onChange).'">
1140 '.implode('', $langSelItems).'
1141 </select>';
1142 }
1143 }
1144 }
1145 }
1146 return $content;
1147 }
1148
1149 /**
1150 * Redirects to alt_doc with new parameters to edit a just created localized record
1151 *
1152 * @param string $justLocalized String passed by GET &justLocalized=
1153 * @return void
1154 */
1155 function localizationRedirect($justLocalized) {
1156 list($table, $orig_uid, $language) = explode(':', $justLocalized);
1157
1158 if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
1159 $localizedRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
1160 'uid',
1161 $table,
1162 $GLOBALS['TCA'][$table]['ctrl']['languageField'].'='.intval($language).' AND '.
1163 $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'].'='.intval($orig_uid).
1164 t3lib_BEfunc::deleteClause($table).
1165 t3lib_BEfunc::versioningPlaceholderClause($table)
1166 );
1167
1168 if (is_array($localizedRecord)) {
1169 // Create parameters and finally run the classic page module for creating a new page translation
1170 $params = '&edit['.$table.']['.$localizedRecord['uid'].']=edit';
1171 $returnUrl = '&returnUrl='.rawurlencode(t3lib_div::sanitizeLocalUrl(t3lib_div::_GP('returnUrl')));
1172 $location = $GLOBALS['BACK_PATH'].'alt_doc.php?'.$params.$returnUrl . t3lib_BEfunc::getUrlToken('editRecord');
1173
1174 t3lib_utility_Http::redirect($location);
1175 }
1176 }
1177 }
1178
1179 /**
1180 * Returns sys_language records available for record translations on given page.
1181 *
1182 * @param integer $id Page id: If zero, the query will select all sys_language records from root level which are NOT hidden. If set to another value, the query will select all sys_language records that has a pages_language_overlay record on that page (and is not hidden, unless you are admin user)
1183 * @return array Language records including faked record for default language
1184 */
1185 function getLanguages($id) {
1186 $modSharedTSconfig = t3lib_BEfunc::getModTSconfig($id, 'mod.SHARED');
1187
1188 // Fallback non sprite-configuration
1189 if (preg_match('/\.gif$/', $modSharedTSconfig['properties']['defaultLanguageFlag'])) {
1190 $modSharedTSconfig['properties']['defaultLanguageFlag'] = str_replace('.gif', '', $modSharedTSconfig['properties']['defaultLanguageFlag']);
1191 }
1192
1193 $languages = array(
1194 0 => array(
1195 'uid' => 0,
1196 'pid' => 0,
1197 'hidden' => 0,
1198 'title' => strlen($modSharedTSconfig['properties']['defaultLanguageLabel']) ? $modSharedTSconfig['properties']['defaultLanguageLabel'].' ('.$GLOBALS['LANG']->sl('LLL:EXT:lang/locallang_mod_web_list.xml:defaultLanguage').')' : $GLOBALS['LANG']->sl('LLL:EXT:lang/locallang_mod_web_list.xml:defaultLanguage'),
1199 'flag' => $modSharedTSconfig['properties']['defaultLanguageFlag'],
1200 )
1201 );
1202
1203 $exQ = $GLOBALS['BE_USER']->isAdmin() ? '' : ' AND sys_language.hidden=0';
1204 if ($id) {
1205 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1206 'sys_language.*',
1207 'pages_language_overlay,sys_language',
1208 'pages_language_overlay.sys_language_uid=sys_language.uid AND pages_language_overlay.pid=' . intval($id) . t3lib_BEfunc::deleteClause('pages_language_overlay') . $exQ,
1209 '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',
1210 'sys_language.title'
1211 );
1212 } else {
1213 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1214 'sys_language.*',
1215 'sys_language',
1216 'sys_language.hidden=0',
1217 '',
1218 'sys_language.title'
1219 );
1220 }
1221 if ($rows) {
1222 foreach ($rows as $row) {
1223 $languages[$row['uid']] = $row;
1224 }
1225 }
1226 return $languages;
1227 }
1228
1229 /***************************
1230 *
1231 * Other functions
1232 *
1233 ***************************/
1234
1235 /**
1236 * Fix $this->editconf if versioning applies to any of the records
1237 *
1238 * @param array $mapArray Mapping between old and new ids if auto-versioning has been performed.
1239 * @return void
1240 */
1241 function fixWSversioningInEditConf($mapArray = FALSE) {
1242
1243 // Traverse the editConf array
1244 if (is_array($this->editconf)) {
1245 // Tables:
1246 foreach ($this->editconf as $table => $conf) {
1247 if (is_array($conf) && $GLOBALS['TCA'][$table]) {
1248
1249 // Traverse the keys/comments of each table (keys can be a commalist of uids)
1250 $newConf = array();
1251 foreach ($conf as $cKey => $cmd) {
1252 if ($cmd=='edit') {
1253 // Traverse the ids:
1254 $ids = t3lib_div::trimExplode(',', $cKey, 1);
1255 foreach ($ids as $idKey => $theUid) {
1256 if (is_array($mapArray)) {
1257 if ($mapArray[$table][$theUid]) {
1258 $ids[$idKey] = $mapArray[$table][$theUid];
1259 }
1260 } else { // Default, look for versions in workspace for record:
1261 $calcPRec = $this->getRecordForEdit($table, $theUid);
1262 if (is_array($calcPRec)) {
1263 // Setting UID again if it had changed, eg. due to workspace versioning.
1264 $ids[$idKey] = $calcPRec['uid'];
1265 }
1266 }
1267 }
1268
1269 // Add the possibly manipulated IDs to the new-build newConf array:
1270 $newConf[implode(',', $ids)] = $cmd;
1271 } else {
1272 $newConf[$cKey] = $cmd;
1273 }
1274 }
1275 // Store the new conf array:
1276 $this->editconf[$table] = $newConf;
1277 }
1278 }
1279 }
1280 }
1281
1282 /**
1283 * Get record for editing.
1284 *
1285 * @param string $table Table name
1286 * @param integer $theUid Record UID
1287 * @return array Returns record to edit, FALSE if none
1288 */
1289 function getRecordForEdit($table, $theUid) {
1290
1291 // Fetch requested record:
1292 $reqRecord = t3lib_BEfunc::getRecord($table, $theUid, 'uid,pid');
1293
1294 if (is_array($reqRecord)) {
1295 // If workspace is OFFLINE:
1296 if ($GLOBALS['BE_USER']->workspace != 0) {
1297
1298 // Check for versioning support of the table:
1299 if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1300
1301 // If the record is already a version of "something" pass it by.
1302 if ($reqRecord['pid'] == -1) {
1303
1304 // (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)
1305 return $reqRecord;
1306 } else { // The input record was online and an offline version must be found or made:
1307
1308 // Look for version of this workspace:
1309 $versionRec = t3lib_BEfunc::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $reqRecord['uid'], 'uid,pid,t3ver_oid');
1310 return is_array($versionRec) ? $versionRec : $reqRecord;
1311 }
1312 } else {
1313 // This means that editing cannot occur on this record because it was not supporting versioning which is required inside an offline workspace.
1314 return FALSE;
1315 }
1316 } else {
1317 // In ONLINE workspace, just return the originally requested record:
1318 return $reqRecord;
1319 }
1320 } else {
1321 // Return FALSE because the table/uid was not found anyway.
1322 return FALSE;
1323 }
1324 }
1325
1326 /**
1327 * Function, which populates the internal editconf array with editing commands for all tt_content elements from the normal column in normal language from the page pointed to by $this->editRegularContentFromId
1328 *
1329 * @return void
1330 */
1331 function editRegularContentFromId() {
1332 if (t3lib_extMgm::isLoaded('cms')) {
1333 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
1334 'uid',
1335 'tt_content',
1336 'pid='.intval($this->editRegularContentFromId).
1337 t3lib_BEfunc::deleteClause('tt_content').
1338 t3lib_BEfunc::versioningPlaceholderClause('tt_content').
1339 ' AND colPos=0 AND sys_language_uid=0',
1340 '',
1341 'sorting'
1342 );
1343 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
1344 $ecUids = array();
1345 while ($ecRec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1346 $ecUids[] = $ecRec['uid'];
1347 }
1348 $this->editconf['tt_content'][implode(',', $ecUids)] = 'edit';
1349 }
1350
1351 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1352 }
1353 }
1354
1355 /**
1356 * Populates the variables $this->storeArray, $this->storeUrl, $this->storeUrlMd5
1357 *
1358 * @return void
1359 * @see makeDocSel()
1360 */
1361 function compileStoreDat() {
1362 $this->storeArray = t3lib_div::compileSelectedGetVarsFromArray('edit,defVals,overrideVals,columnsOnly,disHelp,noView,editRegularContentFromId', $this->R_URL_getvars);
1363 $this->storeUrl = t3lib_div::implodeArrayForUrl('', $this->storeArray);
1364 $this->storeUrlMd5 = md5($this->storeUrl);
1365 }
1366
1367 /**
1368 * Function used to look for configuration of buttons in the form: Fx. disabling buttons or showing them at various positions.
1369 *
1370 * @param string $table The table for which the configuration may be specific
1371 * @param string $key The option for look for. Default is checking if the saveDocNew button should be displayed.
1372 * @return string Return value fetched from USER TSconfig
1373 */
1374 function getNewIconMode($table, $key = 'saveDocNew') {
1375 $TSconfig = $GLOBALS['BE_USER']->getTSConfig('options.'.$key);
1376 $output = trim(isset($TSconfig['properties'][$table]) ? $TSconfig['properties'][$table] : $TSconfig['value']);
1377 return $output;
1378 }
1379
1380 /**
1381 * Handling the closing of a document
1382 *
1383 * @param integer $code Close code: 0/1 will redirect to $this->retUrl, 3 will clear the docHandler (thus closing all documents) and otehr values will call setDocument with ->retUrl
1384 * @return void
1385 */
1386 function closeDocument($code = 0) {
1387
1388 // If current document is found in docHandler,
1389 // then unset it, possibly unset it ALL and finally, write it to the session data
1390 if (isset($this->docHandler[$this->storeUrlMd5])) {
1391
1392 // add the closing document to the recent documents
1393 $recentDocs = $GLOBALS['BE_USER']->getModuleData('opendocs::recent');
1394 if (!is_array($recentDocs)) {
1395 $recentDocs = array();
1396 }
1397 $closedDoc = $this->docHandler[$this->storeUrlMd5];
1398 $recentDocs = array_merge(array($this->storeUrlMd5 => $closedDoc), $recentDocs);
1399 if (count($recentDocs) > 8) {
1400 $recentDocs = array_slice($recentDocs, 0, 8);
1401 }
1402
1403 // remove it from the list of the open documents
1404 unset($this->docHandler[$this->storeUrlMd5]);
1405 if ($code == '3') {
1406 $recentDocs = array_merge($this->docHandler, $recentDocs);
1407 $this->docHandler = array();
1408 }
1409 $GLOBALS['BE_USER']->pushModuleData('opendocs::recent', $recentDocs);
1410 $GLOBALS['BE_USER']->pushModuleData('alt_doc.php', array($this->docHandler, $this->docDat[1]));
1411 t3lib_BEfunc::setUpdateSignal('tx_opendocs::updateNumber', count($this->docHandler));
1412 }
1413
1414 // 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...)
1415 if ($this->returnEditConf && $this->retUrl != 'dummy.php') {
1416 $this->retUrl.='&returnEditConf='.rawurlencode(serialize($this->editconf));
1417 }
1418
1419 // If code is NOT set OR set to 1, then make a header location redirect to $this->retUrl
1420 if (!$code || $code == 1) {
1421 t3lib_utility_Http::redirect($this->retUrl);
1422 } else {
1423 $this->setDocument('', $this->retUrl);
1424 }
1425 }
1426
1427 /**
1428 * Redirects to the document pointed to by $currentDocFromHandlerMD5 OR $retUrl (depending on some internal calculations).
1429 * Most likely you will get a header-location redirect from this function.
1430 *
1431 * @param string $currentDocFromHandlerMD5 Pointer to the document in the docHandler array
1432 * @param string $retUrl Alternative/Default retUrl
1433 * @return void
1434 */
1435 function setDocument($currentDocFromHandlerMD5 = '', $retUrl = 'alt_doc_nodoc.php') {
1436 if (!t3lib_extMgm::isLoaded('cms') && !strcmp($retUrl, 'alt_doc_nodoc.php')) {
1437 return;
1438 }
1439
1440 if (!$this->modTSconfig['properties']['disableDocSelector'] && is_array($this->docHandler) && count($this->docHandler)) {
1441 if (isset($this->docHandler[$currentDocFromHandlerMD5])) {
1442 $setupArr=$this->docHandler[$currentDocFromHandlerMD5];
1443 } else {
1444 $setupArr = reset($this->docHandler);
1445 }
1446 if ($setupArr[2]) {
1447 $sParts = parse_url(t3lib_div::getIndpEnv('REQUEST_URI'));
1448 $retUrl = $sParts['path'].'?'.$setupArr[2].'&returnUrl='.rawurlencode($retUrl);
1449 }
1450 }
1451 t3lib_utility_Http::redirect($retUrl);
1452 }
1453 }
1454
1455 // Make instance:
1456 $SOBE = t3lib_div::makeInstance('SC_alt_doc');
1457
1458 // Preprocessing, storing data if submitted to
1459 $SOBE->preInit();
1460
1461 $formprotection = t3lib_formprotection_Factory::get();
1462
1463 // Checks, if a save button has been clicked (or the doSave variable is sent)
1464 if ($SOBE->doProcessData()) {
1465 if ($formprotection->validateToken(t3lib_div::_GP('formToken'), 'editRecord')) {
1466 $SOBE->processData();
1467 }
1468 }
1469
1470 // Main:
1471 $SOBE->init();
1472 $SOBE->main();
1473 $SOBE->printContent();
1474
1475 ?>