Test Failed
Branch master (7b1793)
by Tymoteusz
15:35
created

EditDocumentController::init()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 57
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 36
nc 1
nop 0
dl 0
loc 57
rs 9.6818
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace TYPO3\CMS\Backend\Controller;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use TYPO3\CMS\Backend\Form\Exception\AccessDeniedException;
20
use TYPO3\CMS\Backend\Form\FormDataCompiler;
21
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
22
use TYPO3\CMS\Backend\Form\FormResultCompiler;
23
use TYPO3\CMS\Backend\Form\NodeFactory;
24
use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
25
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
26
use TYPO3\CMS\Backend\Template\ModuleTemplate;
27
use TYPO3\CMS\Backend\Utility\BackendUtility;
28
use TYPO3\CMS\Core\Database\ConnectionPool;
29
use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
30
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
31
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
32
use TYPO3\CMS\Core\DataHandling\DataHandler;
33
use TYPO3\CMS\Core\Imaging\Icon;
34
use TYPO3\CMS\Core\Messaging\FlashMessage;
35
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
36
use TYPO3\CMS\Core\Messaging\FlashMessageService;
37
use TYPO3\CMS\Core\Page\PageRenderer;
38
use TYPO3\CMS\Core\Type\Bitmask\Permission;
39
use TYPO3\CMS\Core\Utility\GeneralUtility;
40
use TYPO3\CMS\Core\Utility\HttpUtility;
41
use TYPO3\CMS\Core\Utility\MathUtility;
42
use TYPO3\CMS\Core\Utility\PathUtility;
43
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
44
use TYPO3\CMS\Frontend\Page\PageRepository;
45
46
/**
47
 * Script Class: Drawing the editing form for editing records in TYPO3.
48
 * Notice: It does NOT use tce_db.php to submit data to, rather it handles submissions itself
49
 */
50
class EditDocumentController
51
{
52
    const DOCUMENT_CLOSE_MODE_DEFAULT = 0;
53
    const DOCUMENT_CLOSE_MODE_REDIRECT = 1; // works like DOCUMENT_CLOSE_MODE_DEFAULT
54
    const DOCUMENT_CLOSE_MODE_CLEAR_ALL = 3;
55
    const DOCUMENT_CLOSE_MODE_NO_REDIRECT = 4;
56
57
    /**
58
     * GPvar "edit": Is an array looking approx like [tablename][list-of-ids]=command, eg.
59
     * "&edit[pages][123]=edit". See \TYPO3\CMS\Backend\Utility\BackendUtility::editOnClick(). Value can be seen
60
     * modified internally (converting NEW keyword to id, workspace/versioning etc).
61
     *
62
     * @var array
63
     */
64
    public $editconf;
65
66
    /**
67
     * Commalist of fieldnames to edit. The point is IF you specify this list, only those
68
     * fields will be rendered in the form. Otherwise all (available) fields in the record
69
     * is shown according to the types configuration in $GLOBALS['TCA']
70
     *
71
     * @var bool
72
     */
73
    public $columnsOnly;
74
75
    /**
76
     * Default values for fields (array with tablenames, fields etc. as keys).
77
     * Can be seen modified internally.
78
     *
79
     * @var array
80
     */
81
    public $defVals;
82
83
    /**
84
     * Array of values to force being set (as hidden fields). Will be set as $this->defVals
85
     * IF defVals does not exist.
86
     *
87
     * @var array
88
     */
89
    public $overrideVals;
90
91
    /**
92
     * If set, this value will be set in $this->retUrl (which is used quite many places
93
     * as the return URL). If not set, "dummy.php" will be set in $this->retUrl
94
     *
95
     * @var string
96
     */
97
    public $returnUrl;
98
99
    /**
100
     * Close-document command. Not really sure of all options...
101
     *
102
     * @var int
103
     */
104
    public $closeDoc;
105
106
    /**
107
     * Quite simply, if this variable is set, then the processing of incoming data will be performed
108
     * as if a save-button is pressed. Used in the forms as a hidden field which can be set through
109
     * JavaScript if the form is somehow submitted by JavaScript).
110
     *
111
     * @var bool
112
     */
113
    public $doSave;
114
115
    /**
116
     * The data array from which the data comes...
117
     *
118
     * @var array
119
     */
120
    public $data;
121
122
    /**
123
     * @var string
124
     */
125
    public $cmd;
126
127
    /**
128
     * @var array
129
     */
130
    public $mirror;
131
132
    /**
133
     * Clear-cache cmd.
134
     *
135
     * @var string
136
     */
137
    public $cacheCmd;
138
139
    /**
140
     * Redirect (not used???)
141
     *
142
     * @var string
143
     */
144
    public $redirect;
145
146
    /**
147
     * Boolean: If set, then the GET var "&id=" will be added to the
148
     * retUrl string so that the NEW id of something is returned to the script calling the form.
149
     *
150
     * @var bool
151
     */
152
    public $returnNewPageId;
153
154
    /**
155
     * update BE_USER->uc
156
     *
157
     * @var array
158
     */
159
    public $uc;
160
161
    /**
162
     * ID for displaying the page in the frontend (used for SAVE/VIEW operations)
163
     *
164
     * @var int
165
     */
166
    public $popViewId;
167
168
    /**
169
     * Additional GET vars for the link, eg. "&L=xxx"
170
     *
171
     * @var string
172
     */
173
    public $popViewId_addParams;
174
175
    /**
176
     * Alternative URL for viewing the frontend pages.
177
     *
178
     * @var string
179
     */
180
    public $viewUrl;
181
182
    /**
183
     * Alternative title for the document handler.
184
     *
185
     * @var string
186
     */
187
    public $recTitle;
188
189
    /**
190
     * If set, then no SAVE/VIEW button is printed
191
     *
192
     * @var bool
193
     */
194
    public $noView;
195
196
    /**
197
     * @var string
198
     */
199
    public $perms_clause;
200
201
    /**
202
     * If set, the $this->editconf array is returned to the calling script
203
     * (used by wizard_add.php for instance)
204
     *
205
     * @var bool
206
     */
207
    public $returnEditConf;
208
209
    /**
210
     * Workspace used for the editing action.
211
     *
212
     * @var int|null
213
     */
214
    protected $workspace;
215
216
    /**
217
     * document template object
218
     *
219
     * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
220
     */
221
    public $doc;
222
223
    /**
224
     * a static HTML template, usually in templates/alt_doc.html
225
     *
226
     * @var string
227
     */
228
    public $template;
229
230
    /**
231
     * Content accumulation
232
     *
233
     * @var string
234
     */
235
    public $content;
236
237
    /**
238
     * Return URL script, processed. This contains the script (if any) that we should
239
     * RETURN TO from the FormEngine script IF we press the close button. Thus this
240
     * variable is normally passed along from the calling script so we can properly return if needed.
241
     *
242
     * @var string
243
     */
244
    public $retUrl;
245
246
    /**
247
     * Contains the parts of the REQUEST_URI (current url). By parts we mean the result of resolving
248
     * REQUEST_URI (current url) by the parse_url() function. The result is an array where eg. "path"
249
     * is the script path and "query" is the parameters...
250
     *
251
     * @var array
252
     */
253
    public $R_URL_parts;
254
255
    /**
256
     * Contains the current GET vars array; More specifically this array is the foundation for creating
257
     * the R_URI internal var (which becomes the "url of this script" to which we submit the forms etc.)
258
     *
259
     * @var array
260
     */
261
    public $R_URL_getvars;
262
263
    /**
264
     * Set to the URL of this script including variables which is needed to re-display the form. See main()
265
     *
266
     * @var string
267
     */
268
    public $R_URI;
269
270
    /**
271
     * @var array
272
     */
273
    public $MCONF;
274
275
    /**
276
     * @var array
277
     */
278
    public $pageinfo;
279
280
    /**
281
     * Is loaded with the "title" of the currently "open document" - this is used in the
282
     * Document Selector box. (see makeDocSel())
283
     *
284
     * @var string
285
     */
286
    public $storeTitle = '';
287
288
    /**
289
     * Contains an array with key/value pairs of GET parameters needed to reach the
290
     * current document displayed - used in the Document Selector box. (see compileStoreDat())
291
     *
292
     * @var array
293
     */
294
    public $storeArray;
295
296
    /**
297
     * Contains storeArray, but imploded into a GET parameter string (see compileStoreDat())
298
     *
299
     * @var string
300
     */
301
    public $storeUrl;
302
303
    /**
304
     * Hashed value of storeURL (see compileStoreDat())
305
     *
306
     * @var string
307
     */
308
    public $storeUrlMd5;
309
310
    /**
311
     * Module session data
312
     *
313
     * @var array
314
     */
315
    public $docDat;
316
317
    /**
318
     * An array of the "open documents" - keys are md5 hashes (see $storeUrlMd5) identifying
319
     * the various documents on the GET parameter list needed to open it. The values are
320
     * arrays with 0,1,2 keys with information about the document (see compileStoreDat()).
321
     * The docHandler variable is stored in the $docDat session data, key "0".
322
     *
323
     * @var array
324
     */
325
    public $docHandler;
326
327
    /**
328
     * Array of the elements to create edit forms for.
329
     *
330
     * @var array
331
     */
332
    public $elementsData;
333
334
    /**
335
     * Pointer to the first element in $elementsData
336
     *
337
     * @var array
338
     */
339
    public $firstEl;
340
341
    /**
342
     * Counter, used to count the number of errors (when users do not have edit permissions)
343
     *
344
     * @var int
345
     */
346
    public $errorC;
347
348
    /**
349
     * Counter, used to count the number of new record forms displayed
350
     *
351
     * @var int
352
     */
353
    public $newC;
354
355
    /**
356
     * Is set to the pid value of the last shown record - thus indicating which page to
357
     * show when clicking the SAVE/VIEW button
358
     *
359
     * @var int
360
     */
361
    public $viewId;
362
363
    /**
364
     * Is set to additional parameters (like "&L=xxx") if the record supports it.
365
     *
366
     * @var string
367
     */
368
    public $viewId_addParams;
369
370
    /**
371
     * Module TSconfig, loaded from main() based on the page id value of viewId
372
     *
373
     * @var array
374
     */
375
    public $modTSconfig;
376
377
    /**
378
     * @var FormResultCompiler
379
     */
380
    protected $formResultCompiler;
381
382
    /**
383
     * Used internally to disable the storage of the document reference (eg. new records)
384
     *
385
     * @var bool
386
     */
387
    public $dontStoreDocumentRef = 0;
388
389
    /**
390
     * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
391
     */
392
    protected $signalSlotDispatcher;
393
394
    /**
395
     * Stores information needed to preview the currently saved record
396
     *
397
     * @var array
398
     */
399
    protected $previewData = [];
400
401
    /**
402
     * ModuleTemplate object
403
     *
404
     * @var ModuleTemplate
405
     */
406
    protected $moduleTemplate;
407
408
    /**
409
     * Constructor
410
     */
411
    public function __construct()
412
    {
413
        $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
414
        $this->moduleTemplate->setUiBlock(true);
415
        $GLOBALS['SOBE'] = $this;
416
        $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_alt_doc.xlf');
417
    }
418
419
    /**
420
     * Get the SignalSlot dispatcher
421
     *
422
     * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
423
     */
424
    protected function getSignalSlotDispatcher()
425
    {
426
        if (!isset($this->signalSlotDispatcher)) {
427
            $this->signalSlotDispatcher = GeneralUtility::makeInstance(Dispatcher::class);
428
        }
429
        return $this->signalSlotDispatcher;
430
    }
431
432
    /**
433
     * Emits a signal after a function was executed
434
     *
435
     * @param string $signalName
436
     */
437
    protected function emitFunctionAfterSignal($signalName)
438
    {
439
        $this->getSignalSlotDispatcher()->dispatch(__CLASS__, $signalName . 'After', [$this]);
440
    }
441
442
    /**
443
     * First initialization.
444
     */
445
    public function preInit()
446
    {
447
        if (GeneralUtility::_GP('justLocalized')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression TYPO3\CMS\Core\Utility\G...y::_GP('justLocalized') of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
448
            $this->localizationRedirect(GeneralUtility::_GP('justLocalized'));
449
        }
450
        // Setting GPvars:
451
        $this->editconf = GeneralUtility::_GP('edit');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ralUtility::_GP('edit') can also be of type string. However, the property $editconf is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
452
        $this->defVals = GeneralUtility::_GP('defVals');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...Utility::_GP('defVals') can also be of type string. However, the property $defVals is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
453
        $this->overrideVals = GeneralUtility::_GP('overrideVals');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ty::_GP('overrideVals') can also be of type string. However, the property $overrideVals is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
454
        $this->columnsOnly = GeneralUtility::_GP('columnsOnly');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ity::_GP('columnsOnly') can also be of type string. However, the property $columnsOnly is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
455
        $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
456
        $this->closeDoc = (int)GeneralUtility::_GP('closeDoc');
457
        $this->doSave = GeneralUtility::_GP('doSave');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...lUtility::_GP('doSave') can also be of type string. However, the property $doSave is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
458
        $this->returnEditConf = GeneralUtility::_GP('returnEditConf');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...::_GP('returnEditConf') can also be of type string. However, the property $returnEditConf is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
459
        $this->workspace = GeneralUtility::_GP('workspace');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ility::_GP('workspace') can also be of type string. However, the property $workspace is declared as type null|integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
460
        $this->uc = GeneralUtility::_GP('uc');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\GeneralUtility::_GP('uc') can also be of type string. However, the property $uc is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
461
        // Setting override values as default if defVals does not exist.
462
        if (!is_array($this->defVals) && is_array($this->overrideVals)) {
463
            $this->defVals = $this->overrideVals;
464
        }
465
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
466
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
467
        // Setting return URL
468
        $this->retUrl = $this->returnUrl ?: (string)$uriBuilder->buildUriFromRoute('dummy');
469
        // Fix $this->editconf if versioning applies to any of the records
470
        $this->fixWSversioningInEditConf();
471
        // Make R_URL (request url) based on input GETvars:
472
        $this->R_URL_parts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
473
        $this->R_URL_getvars = GeneralUtility::_GET();
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\GeneralUtility::_GET() can also be of type string. However, the property $R_URL_getvars is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
474
        $this->R_URL_getvars['edit'] = $this->editconf;
475
        // MAKE url for storing
476
        $this->compileStoreDat();
477
        // Get session data for the module:
478
        $this->docDat = $this->getBackendUser()->getModuleData('FormEngine', 'ses');
479
        $this->docHandler = $this->docDat[0];
480
        // If a request for closing the document has been sent, act accordingly:
481
        if ((int)$this->closeDoc > self::DOCUMENT_CLOSE_MODE_DEFAULT) {
482
            $this->closeDocument($this->closeDoc);
483
        }
484
        // If NO vars are sent to the script, try to read first document:
485
        // Added !is_array($this->editconf) because editConf must not be set either.
486
        // Anyways I can't figure out when this situation here will apply...
487
        if (is_array($this->R_URL_getvars) && count($this->R_URL_getvars) < 2 && !is_array($this->editconf)) {
488
            $this->setDocument($this->docDat[1]);
489
        }
490
491
        // Sets a temporary workspace, this request is based on
492
        if ($this->workspace !== null) {
493
            $this->getBackendUser()->setTemporaryWorkspace($this->workspace);
0 ignored issues
show
Bug introduced by
$this->workspace of type string is incompatible with the type integer expected by parameter $workspaceId of TYPO3\CMS\Core\Authentic...setTemporaryWorkspace(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

493
            $this->getBackendUser()->setTemporaryWorkspace(/** @scrutinizer ignore-type */ $this->workspace);
Loading history...
494
        }
495
496
        $this->emitFunctionAfterSignal(__FUNCTION__);
497
    }
498
499
    /**
500
     * Detects, if a save command has been triggered.
501
     *
502
     * @return bool TRUE, then save the document (data submitted)
503
     */
504
    public function doProcessData()
505
    {
506
        $out = $this->doSave
507
            || isset($_POST['_savedok'])
508
            || isset($_POST['_saveandclosedok'])
509
            || isset($_POST['_savedokview'])
510
            || isset($_POST['_savedoknew'])
511
            || isset($_POST['_translation_savedok'])
512
            || isset($_POST['_translation_savedokclear']);
513
        return $out;
514
    }
515
516
    /**
517
     * Do processing of data, submitting it to DataHandler.
518
     */
519
    public function processData()
520
    {
521
        $beUser = $this->getBackendUser();
522
        // GPvars specifically for processing:
523
        $control = GeneralUtility::_GP('control');
524
        $this->data = GeneralUtility::_GP('data');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ralUtility::_GP('data') can also be of type string. However, the property $data is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
525
        $this->cmd = GeneralUtility::_GP('cmd');
526
        $this->mirror = GeneralUtility::_GP('mirror');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...lUtility::_GP('mirror') can also be of type string. However, the property $mirror is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
527
        $this->cacheCmd = GeneralUtility::_GP('cacheCmd');
528
        $this->redirect = GeneralUtility::_GP('redirect');
529
        $this->returnNewPageId = GeneralUtility::_GP('returnNewPageId');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...:_GP('returnNewPageId') can also be of type string. However, the property $returnNewPageId is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
530
        // See tce_db.php for relevate options here:
531
        // Only options related to $this->data submission are included here.
532
        /** @var $tce \TYPO3\CMS\Core\DataHandling\DataHandler */
533
        $tce = GeneralUtility::makeInstance(DataHandler::class);
534
535
        if (!empty($control)) {
536
            $tce->setControl($control);
0 ignored issues
show
Bug introduced by
$control of type string is incompatible with the type array expected by parameter $control of TYPO3\CMS\Core\DataHandl...taHandler::setControl(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

536
            $tce->setControl(/** @scrutinizer ignore-type */ $control);
Loading history...
537
        }
538
        if (isset($_POST['_translation_savedok'])) {
539
            $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
540
        }
541
        if (isset($_POST['_translation_savedokclear'])) {
542
            $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
543
            $tce->updateModeL10NdiffDataClear = true;
544
        }
545
        // Setting default values specific for the user:
546
        $TCAdefaultOverride = $beUser->getTSConfigProp('TCAdefaults');
547
        if (is_array($TCAdefaultOverride)) {
548
            $tce->setDefaultsFromUserTS($TCAdefaultOverride);
549
        }
550
        // Setting internal vars:
551
        if ($beUser->uc['neverHideAtCopy']) {
552
            $tce->neverHideAtCopy = 1;
0 ignored issues
show
Documentation Bug introduced by
The property $neverHideAtCopy was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
553
        }
554
        // Loading DataHandler with data:
555
        $tce->start($this->data, $this->cmd);
0 ignored issues
show
Bug introduced by
It seems like $this->cmd can also be of type string; however, parameter $cmd of TYPO3\CMS\Core\DataHandling\DataHandler::start() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

555
        $tce->start($this->data, /** @scrutinizer ignore-type */ $this->cmd);
Loading history...
Bug introduced by
It seems like $this->data can also be of type string; however, parameter $data of TYPO3\CMS\Core\DataHandling\DataHandler::start() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

555
        $tce->start(/** @scrutinizer ignore-type */ $this->data, $this->cmd);
Loading history...
556
        if (is_array($this->mirror)) {
557
            $tce->setMirror($this->mirror);
558
        }
559
        // Checking referer / executing
560
        $refInfo = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
561
        $httpHost = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY');
562
        if ($httpHost != $refInfo['host']
563
            && !$GLOBALS['TYPO3_CONF_VARS']['SYS']['doNotCheckReferer']
564
        ) {
565
            $tce->log(
566
                '',
567
                0,
568
                0,
569
                0,
570
                1,
571
                'Referer host \'%s\' and server host \'%s\' did not match!',
572
                1,
573
                [$refInfo['host'], $httpHost]
574
            );
575
            debug('Error: Referer host did not match with server host.');
576
        } else {
577
            // Perform the saving operation with DataHandler:
578
            $tce->process_uploads($_FILES);
579
            $tce->process_datamap();
580
            $tce->process_cmdmap();
581
            // If pages are being edited, we set an instruction about updating the page tree after this operation.
582
            if ($tce->pagetreeNeedsRefresh
583
                && (isset($this->data['pages']) || $beUser->workspace != 0 && !empty($this->data))
584
            ) {
585
                BackendUtility::setUpdateSignal('updatePageTree');
586
            }
587
            // If there was saved any new items, load them:
588
            if (!empty($tce->substNEWwithIDs_table)) {
589
                // save the expanded/collapsed states for new inline records, if any
590
                FormEngineUtility::updateInlineView($this->uc, $tce);
591
                $newEditConf = [];
592
                foreach ($this->editconf as $tableName => $tableCmds) {
593
                    $keys = array_keys($tce->substNEWwithIDs_table, $tableName);
594
                    if (!empty($keys)) {
595
                        foreach ($keys as $key) {
596
                            $editId = $tce->substNEWwithIDs[$key];
597
                            // Check if the $editId isn't a child record of an IRRE action
598
                            if (!(is_array($tce->newRelatedIDs[$tableName])
599
                                && in_array($editId, $tce->newRelatedIDs[$tableName]))
600
                            ) {
601
                                // Translate new id to the workspace version:
602
                                if ($versionRec = BackendUtility::getWorkspaceVersionOfRecord(
603
                                    $beUser->workspace,
604
                                    $tableName,
605
                                    $editId,
606
                                    'uid'
607
                                )) {
608
                                    $editId = $versionRec['uid'];
609
                                }
610
                                $newEditConf[$tableName][$editId] = 'edit';
611
                            }
612
                            /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
613
                            $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
614
                            // Traverse all new records and forge the content of ->editconf so we can continue to EDIT
615
                            // these records!
616
                            if ($tableName === 'pages'
617
                                && $this->retUrl != (string)$uriBuilder->buildUriFromRoute('dummy')
618
                                && $this->returnNewPageId
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->returnNewPageId of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
619
                            ) {
620
                                $this->retUrl .= '&id=' . $tce->substNEWwithIDs[$key];
621
                            }
622
                        }
623
                    } else {
624
                        $newEditConf[$tableName] = $tableCmds;
625
                    }
626
                }
627
                // Resetting editconf if newEditConf has values:
628
                if (!empty($newEditConf)) {
629
                    $this->editconf = $newEditConf;
630
                }
631
                // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
632
                $this->R_URL_getvars['edit'] = $this->editconf;
633
                // Unsetting default values since we don't need them anymore.
634
                unset($this->R_URL_getvars['defVals']);
635
                // Re-compile the store* values since editconf changed...
636
                $this->compileStoreDat();
637
            }
638
            // See if any records was auto-created as new versions?
639
            if (!empty($tce->autoVersionIdMap)) {
640
                $this->fixWSversioningInEditConf($tce->autoVersionIdMap);
641
            }
642
            // If a document is saved and a new one is created right after.
643
            if (isset($_POST['_savedoknew']) && is_array($this->editconf)) {
644
                $this->closeDocument(self::DOCUMENT_CLOSE_MODE_NO_REDIRECT);
645
                // Finding the current table:
646
                reset($this->editconf);
647
                $nTable = key($this->editconf);
648
                // Finding the first id, getting the records pid+uid
649
                reset($this->editconf[$nTable]);
650
                $nUid = key($this->editconf[$nTable]);
651
                $recordFields = 'pid,uid';
652
                if (!empty($GLOBALS['TCA'][$nTable]['ctrl']['versioningWS'])) {
653
                    $recordFields .= ',t3ver_oid';
654
                }
655
                $nRec = BackendUtility::getRecord($nTable, $nUid, $recordFields);
0 ignored issues
show
Bug introduced by
It seems like $nUid can also be of type string; however, parameter $uid of TYPO3\CMS\Backend\Utilit...endUtility::getRecord() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

655
                $nRec = BackendUtility::getRecord($nTable, /** @scrutinizer ignore-type */ $nUid, $recordFields);
Loading history...
656
                // Determine insertion mode ('top' is self-explaining,
657
                // otherwise new elements are inserted after one using a negative uid)
658
                $insertRecordOnTop = ($this->getNewIconMode($nTable) === 'top');
659
                // Setting a blank editconf array for a new record:
660
                $this->editconf = [];
661
                // Determine related page ID for regular live context
662
                if ($nRec['pid'] != -1) {
663
                    if ($insertRecordOnTop) {
664
                        $relatedPageId = $nRec['pid'];
665
                    } else {
666
                        $relatedPageId = -$nRec['uid'];
667
                    }
668
                } else {
669
                    // Determine related page ID for workspace context
670
                    if ($insertRecordOnTop) {
671
                        // Fetch live version of workspace version since the pid value is always -1 in workspaces
672
                        $liveRecord = BackendUtility::getRecord($nTable, $nRec['t3ver_oid'], $recordFields);
673
                        $relatedPageId = $liveRecord['pid'];
674
                    } else {
675
                        // Use uid of live version of workspace version
676
                        $relatedPageId = -$nRec['t3ver_oid'];
677
                    }
678
                }
679
                $this->editconf[$nTable][$relatedPageId] = 'new';
680
                // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
681
                $this->R_URL_getvars['edit'] = $this->editconf;
682
                // Re-compile the store* values since editconf changed...
683
                $this->compileStoreDat();
684
            }
685
            // If a preview is requested
686
            if (isset($_POST['_savedokview'])) {
687
                // Get the first table and id of the data array from DataHandler
688
                $table = reset(array_keys($this->data));
0 ignored issues
show
Bug introduced by
$this->data of type null|string is incompatible with the type array expected by parameter $input of array_keys(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

688
                $table = reset(array_keys(/** @scrutinizer ignore-type */ $this->data));
Loading history...
689
                $id = reset(array_keys($this->data[$table]));
690
                if (!MathUtility::canBeInterpretedAsInteger($id)) {
691
                    $id = $tce->substNEWwithIDs[$id];
692
                }
693
                // Store this information for later use
694
                $this->previewData['table'] = $table;
695
                $this->previewData['id'] = $id;
696
            }
697
            $tce->printLogErrorMessages();
698
        }
699
        //  || count($tce->substNEWwithIDs)... If any new items has been save, the document is CLOSED
700
        // because if not, we just get that element re-listed as new. And we don't want that!
701
        if ((int)$this->closeDoc < self::DOCUMENT_CLOSE_MODE_DEFAULT
702
            || isset($_POST['_saveandclosedok'])
703
            || isset($_POST['_translation_savedok'])
704
        ) {
705
            $this->closeDocument(abs($this->closeDoc));
0 ignored issues
show
Bug introduced by
It seems like abs($this->closeDoc) can also be of type double; however, parameter $mode of TYPO3\CMS\Backend\Contro...roller::closeDocument() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

705
            $this->closeDocument(/** @scrutinizer ignore-type */ abs($this->closeDoc));
Loading history...
706
        }
707
    }
708
709
    /**
710
     * Initialize the normal module operation
711
     */
712
    public function init()
713
    {
714
        $beUser = $this->getBackendUser();
715
        // Setting more GPvars:
716
        $this->popViewId = GeneralUtility::_GP('popViewId');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ility::_GP('popViewId') can also be of type string. However, the property $popViewId is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
717
        $this->popViewId_addParams = GeneralUtility::_GP('popViewId_addParams');
718
        $this->viewUrl = GeneralUtility::_GP('viewUrl');
719
        $this->recTitle = GeneralUtility::_GP('recTitle');
720
        $this->noView = GeneralUtility::_GP('noView');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...lUtility::_GP('noView') can also be of type string. However, the property $noView is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
721
        $this->perms_clause = $beUser->getPagePermsClause(Permission::PAGE_SHOW);
722
        // Set other internal variables:
723
        $this->R_URL_getvars['returnUrl'] = $this->retUrl;
724
        $this->R_URI = $this->R_URL_parts['path'] . '?' . ltrim(GeneralUtility::implodeArrayForUrl(
725
            '',
726
            $this->R_URL_getvars
727
        ), '&');
728
        // Setting virtual document name
729
        $this->MCONF['name'] = 'xMOD_alt_doc.php';
730
731
        // Create an instance of the document template object
732
        $this->doc = $GLOBALS['TBE_TEMPLATE'];
733
        $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
734
        $pageRenderer->addInlineLanguageLabelFile('EXT:lang/Resources/Private/Language/locallang_alt_doc.xlf');
735
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
736
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
737
        // override the default jumpToUrl
738
        $this->moduleTemplate->addJavaScriptCode(
739
            'jumpToUrl',
740
            '
741
			function jumpToUrl(URL,formEl) {
742
				if (!TBE_EDITOR.isFormChanged()) {
743
					window.location.href = URL;
744
				} else if (formEl && formEl.type=="checkbox") {
745
					formEl.checked = formEl.checked ? 0 : 1;
746
				}
747
			}
748
749
				// Info view:
750
			function launchView(table,uid) {
751
				var thePreviewWindow = window.open(
752
					' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('show_item') . '&table=') . ' + encodeURIComponent(table) + "&uid=" + encodeURIComponent(uid),
753
					"ShowItem" + Math.random().toString(16).slice(2),
754
					"height=300,width=410,status=0,menubar=0,resizable=0,location=0,directories=0,scrollbars=1,toolbar=0"
755
				);
756
				if (thePreviewWindow && thePreviewWindow.focus) {
757
					thePreviewWindow.focus();
758
				}
759
			}
760
			function deleteRecord(table,id,url) {
761
				window.location.href = ' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('tce_db') . '&cmd[') . '+table+"]["+id+"][delete]=1&redirect="+escape(url);
762
			}
763
		' . (isset($_POST['_savedokview']) && $this->popViewId ? $this->generatePreviewCode() : '')
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->popViewId of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
764
        );
765
        // Setting up the context sensitive menu:
766
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
767
768
        $this->emitFunctionAfterSignal(__FUNCTION__);
769
    }
770
771
    /**
772
     * @return string
773
     */
774
    protected function generatePreviewCode()
775
    {
776
        $table = $this->previewData['table'];
777
        $recordId = $this->previewData['id'];
778
779
        if ($table === 'pages') {
780
            $currentPageId = $recordId;
781
        } else {
782
            $currentPageId = MathUtility::convertToPositiveInteger($this->popViewId);
783
        }
784
785
        $pageTsConfig = BackendUtility::getPagesTSconfig($currentPageId);
786
        $previewConfiguration = isset($pageTsConfig['TCEMAIN.']['preview.'][$table . '.'])
787
            ? $pageTsConfig['TCEMAIN.']['preview.'][$table . '.']
788
            : [];
789
790
        $recordArray = BackendUtility::getRecord($table, $recordId);
791
792
        // find the right preview page id
793
        $previewPageId = 0;
794
        if (isset($previewConfiguration['previewPageId'])) {
795
            $previewPageId = $previewConfiguration['previewPageId'];
796
        }
797
        // if no preview page was configured
798
        if (!$previewPageId) {
799
            $rootPageData = null;
800
            $rootLine = BackendUtility::BEgetRootLine($currentPageId);
801
            $currentPage = reset($rootLine);
802
            // Allow all doktypes below 200
803
            // This makes custom doktype work as well with opening a frontend page.
804
            if ((int)$currentPage['doktype'] <= PageRepository::DOKTYPE_SPACER) {
805
                // try the current page
806
                $previewPageId = $currentPageId;
807
            } else {
808
                // or search for the root page
809
                foreach ($rootLine as $page) {
810
                    if ($page['is_siteroot']) {
811
                        $rootPageData = $page;
812
                        break;
813
                    }
814
                }
815
                $previewPageId = isset($rootPageData)
816
                    ? (int)$rootPageData['uid']
817
                    : $currentPageId;
818
            }
819
        }
820
821
        $linkParameters = [
822
            'no_cache' => 1,
823
        ];
824
825
        // language handling
826
        $languageField = isset($GLOBALS['TCA'][$table]['ctrl']['languageField'])
827
            ? $GLOBALS['TCA'][$table]['ctrl']['languageField']
828
            : '';
829
        if ($languageField && !empty($recordArray[$languageField])) {
830
            $l18nPointer = isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])
831
                ? $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
832
                : '';
833
            if ($l18nPointer && !empty($recordArray[$l18nPointer])
834
                && isset($previewConfiguration['useDefaultLanguageRecord'])
835
                && !$previewConfiguration['useDefaultLanguageRecord']
836
            ) {
837
                // use parent record
838
                $recordId = $recordArray[$l18nPointer];
839
            }
840
            $linkParameters['L'] = $recordArray[$languageField];
841
        }
842
843
        // map record data to GET parameters
844
        if (isset($previewConfiguration['fieldToParameterMap.'])) {
845
            foreach ($previewConfiguration['fieldToParameterMap.'] as $field => $parameterName) {
846
                $value = $recordArray[$field];
847
                if ($field === 'uid') {
848
                    $value = $recordId;
849
                }
850
                $linkParameters[$parameterName] = $value;
851
            }
852
        }
853
854
        // add/override parameters by configuration
855
        if (isset($previewConfiguration['additionalGetParameters.'])) {
856
            $additionalGetParameters = [];
857
            $this->parseAdditionalGetParameters(
858
                $additionalGetParameters,
859
                $previewConfiguration['additionalGetParameters.']
860
            );
861
            $linkParameters = array_replace($linkParameters, $additionalGetParameters);
862
        }
863
864
        // anchor with uid of content element]
865
        $anchorSection = $table === 'tt_content' ? '#c' . $recordId : '';
866
867
        $this->popViewId = $previewPageId;
868
        $this->popViewId_addParams = GeneralUtility::implodeArrayForUrl('', $linkParameters, '', false, true);
869
870
        $previewPageRootline = BackendUtility::BEgetRootLine($this->popViewId);
871
        return '
872
				if (window.opener) {
873
				'
874
            . BackendUtility::viewOnClick(
875
                $this->popViewId,
876
                '',
877
                $previewPageRootline,
878
                $anchorSection,
879
                $this->viewUrl,
880
                $this->popViewId_addParams,
881
                false
882
            )
883
            . '
884
				} else {
885
				'
886
            . BackendUtility::viewOnClick(
887
                $this->popViewId,
888
                '',
889
                $previewPageRootline,
890
                $anchorSection,
891
                $this->viewUrl,
892
                $this->popViewId_addParams
893
            )
894
            . '
895
				}';
896
    }
897
898
    /**
899
     * Migrates a set of (possibly nested) GET parameters in TypoScript syntax to a plain array
900
     *
901
     * This basically removes the trailing dots of sub-array keys in TypoScript.
902
     * The result can be used to create a query string with GeneralUtility::implodeArrayForUrl().
903
     *
904
     * @param array $parameters Should be an empty array by default
905
     * @param array $typoScript The TypoScript configuration
906
     */
907 View Code Duplication
    protected function parseAdditionalGetParameters(array &$parameters, array $typoScript)
908
    {
909
        foreach ($typoScript as $key => $value) {
910
            if (is_array($value)) {
911
                $key = rtrim($key, '.');
912
                $parameters[$key] = [];
913
                $this->parseAdditionalGetParameters($parameters[$key], $value);
914
            } else {
915
                $parameters[$key] = $value;
916
            }
917
        }
918
    }
919
920
    /**
921
     * Main module operation
922
     */
923
    public function main()
924
    {
925
        $body = '';
926
        // Begin edit:
927
        if (is_array($this->editconf)) {
928
            /** @var FormResultCompiler formResultCompiler */
929
            $this->formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
930
931
            // Creating the editing form, wrap it with buttons, document selector etc.
932
            $editForm = $this->makeEditForm();
933
            if ($editForm) {
934
                $this->firstEl = reset($this->elementsData);
935
                // Checking if the currently open document is stored in the list of "open documents" - if not, add it:
936
                if (($this->docDat[1] !== $this->storeUrlMd5
937
                        || !isset($this->docHandler[$this->storeUrlMd5]))
938
                    && !$this->dontStoreDocumentRef
939
                ) {
940
                    $this->docHandler[$this->storeUrlMd5] = [
941
                        $this->storeTitle,
942
                        $this->storeArray,
943
                        $this->storeUrl,
944
                        $this->firstEl
945
                    ];
946
                    $this->getBackendUser()->pushModuleData('FormEngine', [$this->docHandler, $this->storeUrlMd5]);
947
                    BackendUtility::setUpdateSignal('OpendocsController::updateNumber', count($this->docHandler));
948
                }
949
                // Module configuration
950
                $this->modTSconfig = $this->viewId ? BackendUtility::getModTSconfig(
951
                    $this->viewId,
952
                    'mod.xMOD_alt_doc'
953
                ) : [];
954
                $body = $this->formResultCompiler->addCssFiles();
955
                $body .= $this->compileForm($editForm);
956
                $body .= $this->formResultCompiler->printNeededJSFunctions();
957
                $body .= '</form>';
958
            }
959
        }
960
        // Access check...
961
        // The page will show only if there is a valid page and if this page may be viewed by the user
962
        $this->pageinfo = BackendUtility::readPageAccess($this->viewId, $this->perms_clause);
963
        if ($this->pageinfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->pageinfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
964
            $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
965
        }
966
        // Setting up the buttons and markers for docheader
967
        $this->getButtons();
968
        $this->languageSwitch($this->firstEl['table'], $this->firstEl['uid'], $this->firstEl['pid']);
969
        $this->moduleTemplate->setContent($body);
970
    }
971
972
    /***************************
973
     *
974
     * Sub-content functions, rendering specific parts of the module content.
975
     *
976
     ***************************/
977
    /**
978
     * Creates the editing form with FormEnigne, based on the input from GPvars.
979
     *
980
     * @return string HTML form elements wrapped in tables
981
     */
982
    public function makeEditForm()
983
    {
984
        // Initialize variables:
985
        $this->elementsData = [];
986
        $this->errorC = 0;
987
        $this->newC = 0;
988
        $editForm = '';
989
        $trData = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $trData is dead and can be removed.
Loading history...
990
        $beUser = $this->getBackendUser();
991
        // Traverse the GPvar edit array
992
        // Tables:
993
        foreach ($this->editconf as $table => $conf) {
994
            if (is_array($conf) && $GLOBALS['TCA'][$table] && $beUser->check('tables_modify', $table)) {
995
                // Traverse the keys/comments of each table (keys can be a commalist of uids)
996
                foreach ($conf as $cKey => $command) {
997
                    if ($command === 'edit' || $command === 'new') {
998
                        // Get the ids:
999
                        $ids = GeneralUtility::trimExplode(',', $cKey, true);
1000
                        // Traverse the ids:
1001
                        foreach ($ids as $theUid) {
1002
                            // Don't save this document title in the document selector if the document is new.
1003
                            if ($command === 'new') {
1004
                                $this->dontStoreDocumentRef = 1;
0 ignored issues
show
Documentation Bug introduced by
The property $dontStoreDocumentRef was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1005
                            }
1006
1007
                            /** @var TcaDatabaseRecord $formDataGroup */
1008
                            $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
1009
                            /** @var FormDataCompiler $formDataCompiler */
1010
                            $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
0 ignored issues
show
Bug introduced by
$formDataGroup of type TYPO3\CMS\Backend\Form\F...Group\TcaDatabaseRecord is incompatible with the type array<integer,mixed> expected by parameter $constructorArguments of TYPO3\CMS\Core\Utility\G...Utility::makeInstance(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1010
                            $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, /** @scrutinizer ignore-type */ $formDataGroup);
Loading history...
1011
                            /** @var NodeFactory $nodeFactory */
1012
                            $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
1013
1014
                            try {
1015
                                // Reset viewId - it should hold data of last entry only
1016
                                $this->viewId = 0;
1017
                                $this->viewId_addParams = '';
1018
1019
                                $formDataCompilerInput = [
1020
                                    'tableName' => $table,
1021
                                    'vanillaUid' => (int)$theUid,
1022
                                    'command' => $command,
1023
                                    'returnUrl' => $this->R_URI,
1024
                                ];
1025
                                if (is_array($this->overrideVals) && is_array($this->overrideVals[$table])) {
1026
                                    $formDataCompilerInput['overrideValues'] = $this->overrideVals[$table];
1027
                                }
1028
1029
                                $formData = $formDataCompiler->compile($formDataCompilerInput);
1030
1031
                                // Set this->viewId if possible
1032
                                if ($command === 'new'
1033
                                    && $table !== 'pages'
1034
                                    && !empty($formData['parentPageRow']['uid'])
1035
                                ) {
1036
                                    $this->viewId = $formData['parentPageRow']['uid'];
1037
                                } else {
1038
                                    if ($table === 'pages') {
1039
                                        $this->viewId = $formData['databaseRow']['uid'];
1040
                                    } elseif (!empty($formData['parentPageRow']['uid'])) {
1041
                                        $this->viewId = $formData['parentPageRow']['uid'];
1042
                                        // Adding "&L=xx" if the record being edited has a languageField with a value larger than zero!
1043
                                        if (!empty($formData['processedTca']['ctrl']['languageField'])
1044
                                            && is_array($formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']])
1045
                                            && $formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']][0] > 0
1046
                                        ) {
1047
                                            $this->viewId_addParams = '&L=' . $formData['databaseRow'][$formData['processedTca']['ctrl']['languageField']][0];
1048
                                        }
1049
                                    }
1050
                                }
1051
1052
                                // Determine if delete button can be shown
1053
                                $deleteAccess = false;
1054
                                if ($command === 'edit') {
1055
                                    $permission = $formData['userPermissionOnPage'];
1056
                                    if ($formData['tableName'] === 'pages') {
1057
                                        $deleteAccess = $permission & Permission::PAGE_DELETE ? true : false;
1058
                                    } else {
1059
                                        $deleteAccess = $permission & Permission::CONTENT_EDIT ? true : false;
1060
                                    }
1061
                                }
1062
1063
                                // Display "is-locked" message:
1064
                                if ($command === 'edit') {
1065
                                    $lockInfo = BackendUtility::isRecordLocked($table, $formData['databaseRow']['uid']);
1066 View Code Duplication
                                    if ($lockInfo) {
1067
                                        /** @var $flashMessage \TYPO3\CMS\Core\Messaging\FlashMessage */
1068
                                        $flashMessage = GeneralUtility::makeInstance(
1069
                                            FlashMessage::class,
1070
                                            $lockInfo['msg'],
1071
                                            '',
0 ignored issues
show
Bug introduced by
'' of type string is incompatible with the type array<integer,mixed> expected by parameter $constructorArguments of TYPO3\CMS\Core\Utility\G...Utility::makeInstance(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1071
                                            /** @scrutinizer ignore-type */ '',
Loading history...
1072
                                            FlashMessage::WARNING
0 ignored issues
show
Bug introduced by
TYPO3\CMS\Core\Messaging\FlashMessage::WARNING of type integer is incompatible with the type array<integer,mixed> expected by parameter $constructorArguments of TYPO3\CMS\Core\Utility\G...Utility::makeInstance(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1072
                                            /** @scrutinizer ignore-type */ FlashMessage::WARNING
Loading history...
1073
                                        );
1074
                                        /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
1075
                                        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1076
                                        /** @var $defaultFlashMessageQueue FlashMessageQueue */
1077
                                        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1078
                                        $defaultFlashMessageQueue->enqueue($flashMessage);
1079
                                    }
1080
                                }
1081
1082
                                // Record title
1083
                                if (!$this->storeTitle) {
1084
                                    $this->storeTitle = $this->recTitle
1085
                                        ? htmlspecialchars($this->recTitle)
1086
                                        : BackendUtility::getRecordTitle($table, FormEngineUtility::databaseRowCompatibility($formData['databaseRow']), true);
1087
                                }
1088
1089
                                $this->elementsData[] = [
1090
                                    'table' => $table,
1091
                                    'uid' => $formData['databaseRow']['uid'],
1092
                                    'pid' => $formData['databaseRow']['pid'],
1093
                                    'cmd' => $command,
1094
                                    'deleteAccess' => $deleteAccess
1095
                                ];
1096
1097
                                if ($command !== 'new') {
1098
                                    BackendUtility::lockRecords($table, $formData['databaseRow']['uid'], $table === 'tt_content' ? $formData['databaseRow']['pid'] : 0);
1099
                                }
1100
1101
                                // Set list if only specific fields should be rendered. This will trigger
1102
                                // ListOfFieldsContainer instead of FullRecordContainer in OuterWrapContainer
1103
                                if ($this->columnsOnly) {
1104
                                    if (is_array($this->columnsOnly)) {
1105
                                        $formData['fieldListToRender'] = $this->columnsOnly[$table];
1106
                                    } else {
1107
                                        $formData['fieldListToRender'] = $this->columnsOnly;
1108
                                    }
1109
                                }
1110
1111
                                $formData['renderType'] = 'outerWrapContainer';
1112
                                $formResult = $nodeFactory->create($formData)->render();
1113
1114
                                $html = $formResult['html'];
1115
1116
                                $formResult['html'] = '';
1117
                                $formResult['doSaveFieldName'] = 'doSave';
1118
1119
                                // @todo: Put all the stuff into FormEngine as final "compiler" class
1120
                                // @todo: This is done here for now to not rewrite addCssFiles()
1121
                                // @todo: and printNeededJSFunctions() now
1122
                                $this->formResultCompiler->mergeResult($formResult);
1123
1124
                                // Seems the pid is set as hidden field (again) at end?!
1125
                                if ($command === 'new') {
1126
                                    // @todo: looks ugly
1127
                                    $html .= LF
1128
                                        . '<input type="hidden"'
1129
                                        . ' name="data[' . htmlspecialchars($table) . '][' . htmlspecialchars($formData['databaseRow']['uid']) . '][pid]"'
1130
                                        . ' value="' . (int)$formData['databaseRow']['pid'] . '" />';
1131
                                    $this->newC++;
1132
                                }
1133
1134
                                $editForm .= $html;
1135
                            } catch (AccessDeniedException $e) {
1136
                                $this->errorC++;
1137
                                // Try to fetch error message from "recordInternals" be user object
1138
                                // @todo: This construct should be logged and localized and de-uglified
1139
                                $message = $beUser->errorMsg;
1140
                                if (empty($message)) {
1141
                                    // Create message from exception.
1142
                                    $message = $e->getMessage() . ' ' . $e->getCode();
1143
                                }
1144
                                $editForm .= htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noEditPermission'))
1145
                                    . '<br /><br />' . htmlspecialchars($message) . '<br /><br />';
1146
                            }
1147
                        } // End of for each uid
1148
                    }
1149
                }
1150
            }
1151
        }
1152
        return $editForm;
1153
    }
1154
1155
    /**
1156
     * Create the panel of buttons for submitting the form or otherwise perform operations.
1157
     *
1158
     * @return array All available buttons as an assoc. array
1159
     */
1160
    protected function getButtons()
1161
    {
1162
        $lang = $this->getLanguageService();
1163
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1164
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1165
        // Render SAVE type buttons:
1166
        // The action of each button is decided by its name attribute. (See doProcessData())
1167
        $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
1168
        if (!$this->errorC && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']) {
1169
            $saveSplitButton = $buttonBar->makeSplitButton();
1170
            // SAVE button:
1171
            $saveButton = $buttonBar->makeInputButton()
1172
                ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'))
1173
                ->setName('_savedok')
1174
                ->setValue('1')
1175
                ->setForm('EditDocumentController')
1176
                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL));
1177
            $saveSplitButton->addItem($saveButton, true);
1178
1179
            // SAVE / VIEW button:
1180
            if ($this->viewId && !$this->noView && $this->getNewIconMode($this->firstEl['table'], 'saveDocView')) {
1181
                $pagesTSconfig = BackendUtility::getPagesTSconfig($this->pageinfo['uid']);
1182 View Code Duplication
                if (isset($pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
1183
                    $excludeDokTypes = GeneralUtility::intExplode(
1184
                        ',',
1185
                        $pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
1186
                        true
1187
                    );
1188
                } else {
1189
                    // exclude sysfolders, spacers and recycler by default
1190
                    $excludeDokTypes = [
1191
                        PageRepository::DOKTYPE_RECYCLER,
1192
                        PageRepository::DOKTYPE_SYSFOLDER,
1193
                        PageRepository::DOKTYPE_SPACER
1194
                    ];
1195
                }
1196
                if (!in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, true)
1197
                    || isset($pagesTSconfig['TCEMAIN.']['preview.'][$this->firstEl['table'] . '.']['previewPageId'])
1198
                ) {
1199
                    $saveAndOpenButton = $buttonBar->makeInputButton()
1200
                        ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveDocShow'))
1201
                        ->setName('_savedokview')
1202
                        ->setValue('1')
1203
                        ->setForm('EditDocumentController')
1204
                        ->setOnClick("window.open('', 'newTYPO3frontendWindow');")
1205
                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1206
                            'actions-document-save-view',
1207
                            Icon::SIZE_SMALL
1208
                        ));
1209
                    $saveSplitButton->addItem($saveAndOpenButton);
1210
                }
1211
            }
1212
            // SAVE / NEW button:
1213 View Code Duplication
            if (count($this->elementsData) === 1 && $this->getNewIconMode($this->firstEl['table'])) {
1214
                $saveAndNewButton = $buttonBar->makeInputButton()
1215
                    ->setName('_savedoknew')
1216
                    ->setClasses('t3js-editform-submitButton')
1217
                    ->setValue('1')
1218
                    ->setForm('EditDocumentController')
1219
                    ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveNewDoc'))
1220
                    ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1221
                        'actions-document-save-new',
1222
                        Icon::SIZE_SMALL
1223
                    ));
1224
                $saveSplitButton->addItem($saveAndNewButton);
1225
            }
1226
            // SAVE / CLOSE
1227
            $saveAndCloseButton = $buttonBar->makeInputButton()
1228
                ->setName('_saveandclosedok')
1229
                ->setClasses('t3js-editform-submitButton')
1230
                ->setValue('1')
1231
                ->setForm('EditDocumentController')
1232
                ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveCloseDoc'))
1233
                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1234
                    'actions-document-save-close',
1235
                    Icon::SIZE_SMALL
1236
                ));
1237
            $saveSplitButton->addItem($saveAndCloseButton);
1238
            // FINISH TRANSLATION / SAVE / CLOSE
1239
            if ($GLOBALS['TYPO3_CONF_VARS']['BE']['explicitConfirmationOfTranslation']) {
1240
                $saveTranslationButton = $buttonBar->makeInputButton()
1241
                    ->setName('_translation_savedok')
1242
                    ->setValue('1')
1243
                    ->setForm('EditDocumentController')
1244
                    ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.translationSaveDoc'))
1245
                    ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1246
                        'actions-document-save-cleartranslationcache',
1247
                        Icon::SIZE_SMALL
1248
                    ));
1249
                $saveSplitButton->addItem($saveTranslationButton);
1250
                $saveAndClearTranslationButton = $buttonBar->makeInputButton()
1251
                    ->setName('_translation_savedokclear')
1252
                    ->setValue('1')
1253
                    ->setForm('EditDocumentController')
1254
                    ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.translationSaveDocClear'))
1255
                    ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1256
                        'actions-document-save-cleartranslationcache',
1257
                        Icon::SIZE_SMALL
1258
                    ));
1259
                $saveSplitButton->addItem($saveAndClearTranslationButton);
1260
            }
1261
            $buttonBar->addButton($saveSplitButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
1262
        }
1263
        // CLOSE button:
1264
        $closeButton = $buttonBar->makeLinkButton()
1265
            ->setHref('#')
1266
            ->setClasses('t3js-editform-close')
1267
            ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'))
1268
            ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1269
                'actions-close',
1270
                Icon::SIZE_SMALL
1271
            ));
1272
        $buttonBar->addButton($closeButton);
1273
        // DELETE + UNDO buttons:
1274
        if (!$this->errorC
1275
            && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']
1276
            && count($this->elementsData) === 1
1277
        ) {
1278
            if ($this->firstEl['cmd'] !== 'new' && MathUtility::canBeInterpretedAsInteger($this->firstEl['uid'])) {
1279
                // Delete:
1280
                if ($this->firstEl['deleteAccess']
1281
                    && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']
1282
                    && !$this->getNewIconMode($this->firstEl['table'], 'disableDelete')
1283
                ) {
1284
                    $returnUrl = $this->retUrl;
1285
                    if ($this->firstEl['table'] === 'pages') {
1286
                        parse_str((string)parse_url($returnUrl, PHP_URL_QUERY), $queryParams);
1287
                        if (isset($queryParams['route'])
1288
                            && isset($queryParams['id'])
1289
                            && (string)$this->firstEl['uid'] === (string)$queryParams['id']
1290
                        ) {
1291
                            // TODO: Use the page's pid instead of 0, this requires a clean API to manipulate the page
1292
                            // tree from the outside to be able to mark the pid as active
1293
                            $returnUrl = (string)$uriBuilder->buildUriFromRoutePath($queryParams['route'], ['id' => 0]);
1294
                        }
1295
                    }
1296
                    $deleteButton = $buttonBar->makeLinkButton()
1297
                        ->setHref('#')
1298
                        ->setClasses('t3js-editform-delete-record')
1299
                        ->setTitle($lang->getLL('deleteItem'))
1300
                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1301
                            'actions-edit-delete',
1302
                            Icon::SIZE_SMALL
1303
                        ))
1304
                        ->setDataAttributes([
1305
                            'return-url' => $returnUrl,
1306
                            'uid' => $this->firstEl['uid'],
1307
                            'table' => $this->firstEl['table']
1308
                        ]);
1309
                    $buttonBar->addButton($deleteButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1310
                }
1311
                // Undo:
1312
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1313
                    ->getQueryBuilderForTable('sys_history');
1314
1315
                $undoButtonR = $queryBuilder->select('tstamp')
1316
                    ->from('sys_history')
1317
                    ->where(
1318
                        $queryBuilder->expr()->eq(
1319
                            'tablename',
1320
                            $queryBuilder->createNamedParameter($this->firstEl['table'], \PDO::PARAM_STR)
1321
                        ),
1322
                        $queryBuilder->expr()->eq(
1323
                            'recuid',
1324
                            $queryBuilder->createNamedParameter($this->firstEl['uid'], \PDO::PARAM_INT)
1325
                        )
1326
                    )
1327
                    ->orderBy('tstamp', 'DESC')
1328
                    ->setMaxResults(1)
1329
                    ->execute()
1330
                    ->fetch();
1331
1332
                if ($undoButtonR !== false) {
1333
                    $aOnClick = 'window.location.href=' .
1334
                        GeneralUtility::quoteJSvalue(
1335
                            (string)$uriBuilder->buildUriFromRoute(
1336
                                'record_history',
1337
                                [
1338
                                    'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
1339
                                    'revert' => 'ALL_FIELDS',
1340
                                    'returnUrl' => $this->R_URI,
1341
                                ]
1342
                            )
1343
                        ) . '; return false;';
1344
1345
                    $undoButton = $buttonBar->makeLinkButton()
1346
                        ->setHref('#')
1347
                        ->setOnClick($aOnClick)
1348
                        ->setTitle(
1349
                            sprintf(
1350
                                $lang->getLL('undoLastChange'),
1351
                                BackendUtility::calcAge(
1352
                                    ($GLOBALS['EXEC_TIME'] - $undoButtonR['tstamp']),
1353
                                    $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
1354
                                )
1355
                            )
1356
                        )
1357
                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1358
                            'actions-document-history-open',
1359
                            Icon::SIZE_SMALL
1360
                        ));
1361
                    $buttonBar->addButton($undoButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1362
                }
1363
                if ($this->getNewIconMode($this->firstEl['table'], 'showHistory')) {
1364
                    $aOnClick = 'window.location.href=' .
1365
                        GeneralUtility::quoteJSvalue(
1366
                            (string)$uriBuilder->buildUriFromRoute(
1367
                                'record_history',
1368
                                [
1369
                                    'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
1370
                                    'returnUrl' => $this->R_URI,
1371
                                ]
1372
                            )
1373
                        ) . '; return false;';
1374
1375
                    $historyButton = $buttonBar->makeLinkButton()
1376
                        ->setHref('#')
1377
                        ->setOnClick($aOnClick)
1378
                        ->setTitle('Open history of this record')
1379
                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1380
                            'actions-document-history-open',
1381
                            Icon::SIZE_SMALL
1382
                        ));
1383
                    $buttonBar->addButton($historyButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1384
                }
1385
                // If only SOME fields are shown in the form, this will link the user to the FULL form:
1386
                if ($this->columnsOnly) {
1387
                    $columnsOnlyButton = $buttonBar->makeLinkButton()
1388
                        ->setHref($this->R_URI . '&columnsOnly=')
1389
                        ->setTitle($lang->getLL('editWholeRecord'))
1390
                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
1391
                            'actions-open',
1392
                            Icon::SIZE_SMALL
1393
                        ));
1394
                    $buttonBar->addButton($columnsOnlyButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1395
                }
1396
            }
1397
        }
1398
        $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('TCEforms');
1399
        $buttonBar->addButton($cshButton);
1400
        $this->shortCutLink();
1401
        $this->openInNewWindowLink();
1402
    }
1403
1404
    /**
1405
     * Put together the various elements (buttons, selectors, form) into a table
1406
     *
1407
     * @param string $editForm HTML form.
1408
     * @return string Composite HTML
1409
     */
1410
    public function compileForm($editForm)
1411
    {
1412
        $formContent = '
1413
			<!-- EDITING FORM -->
1414
			<form
1415
            action="' . htmlspecialchars($this->R_URI) . '"
1416
            method="post"
1417
            enctype="multipart/form-data"
1418
            name="editform"
1419
            id="EditDocumentController"
1420
            onsubmit="TBE_EDITOR.checkAndDoSubmit(1); return false;">
1421
			' . $editForm . '
1422
1423
			<input type="hidden" name="returnUrl" value="' . htmlspecialchars($this->retUrl) . '" />
1424
			<input type="hidden" name="viewUrl" value="' . htmlspecialchars($this->viewUrl) . '" />';
1425
        if ($this->returnNewPageId) {
1426
            $formContent .= '<input type="hidden" name="returnNewPageId" value="1" />';
1427
        }
1428
        $formContent .= '<input type="hidden" name="popViewId" value="' . htmlspecialchars($this->viewId) . '" />';
1429
        if ($this->viewId_addParams) {
1430
            $formContent .= '<input type="hidden" name="popViewId_addParams" value="' . htmlspecialchars($this->viewId_addParams) . '" />';
1431
        }
1432
        $formContent .= '
1433
			<input type="hidden" name="closeDoc" value="0" />
1434
			<input type="hidden" name="doSave" value="0" />
1435
			<input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />
1436
			<input type="hidden" name="_scrollPosition" value="" />';
1437
        return $formContent;
1438
    }
1439
1440
    /**
1441
     * Create shortcut icon
1442
     */
1443
    public function shortCutLink()
1444
    {
1445
        if ($this->returnUrl !== $this->getCloseUrl()) {
1446
            $shortCutButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton();
1447
            $shortCutButton->setModuleName($this->MCONF['name'])
1448
                ->setGetVariables([
1449
                    'returnUrl',
1450
                    'edit',
1451
                    'defVals',
1452
                    'overrideVals',
1453
                    'columnsOnly',
1454
                    'returnNewPageId',
1455
                    'noView']);
1456
            $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton($shortCutButton);
1457
        }
1458
    }
1459
1460
    /**
1461
     * Creates open-in-window link
1462
     */
1463
    public function openInNewWindowLink()
1464
    {
1465
        $closeUrl = $this->getCloseUrl();
1466
        if ($this->returnUrl !== $closeUrl) {
1467
            $aOnClick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue(GeneralUtility::linkThisScript(
1468
                ['returnUrl' => $closeUrl]
1469
            ))
1470
                . ','
1471
                . GeneralUtility::quoteJSvalue(md5($this->R_URI))
1472
                . ',\'width=670,height=500,status=0,menubar=0,scrollbars=1,resizable=1\');vHWin.focus();return false;';
1473
            $openInNewWindowButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()
1474
                ->makeLinkButton()
1475
                ->setHref('#')
1476
                ->setTitle($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.openInNewWindow'))
1477
                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-window-open', Icon::SIZE_SMALL))
1478
                ->setOnClick($aOnClick);
1479
            $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton(
1480
                $openInNewWindowButton,
1481
                ButtonBar::BUTTON_POSITION_RIGHT
1482
            );
1483
        }
1484
    }
1485
1486
    /**
1487
     * Returns the URL (usually for the "returnUrl") which closes the current window.
1488
     * Used when editing a record in a popup.
1489
     *
1490
     * @return string
1491
     */
1492
    protected function getCloseUrl(): string
1493
    {
1494
        $closeUrl = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/Close.html');
1495
        return PathUtility::getAbsoluteWebPath($closeUrl);
1496
    }
1497
1498
    /***************************
1499
     *
1500
     * Localization stuff
1501
     *
1502
     ***************************/
1503
    /**
1504
     * Make selector box for creating new translation for a record or switching to edit the record in an existing
1505
     * language.
1506
     * Displays only languages which are available for the current page.
1507
     *
1508
     * @param string $table Table name
1509
     * @param int $uid Uid for which to create a new language
1510
     * @param int $pid Pid of the record
1511
     */
1512
    public function languageSwitch($table, $uid, $pid = null)
1513
    {
1514
        $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
1515
        $transOrigPointerField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
1516
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1517
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1518
1519
        // Table editable and activated for languages?
1520
        if ($this->getBackendUser()->check('tables_modify', $table)
1521
            && $languageField
1522
            && $transOrigPointerField
1523
        ) {
1524
            if (is_null($pid)) {
1525
                $row = BackendUtility::getRecord($table, $uid, 'pid');
1526
                $pid = $row['pid'];
1527
            }
1528
            // Get all available languages for the page
1529
            // If editing a page, the translations of the current UID need to be fetched
1530
            if ($table === 'pages') {
1531
                $row = BackendUtility::getRecord($table, $uid, $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']);
1532
                // Ensure the check is always done against the default language page
1533
                $langRows = $this->getLanguages($row[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']] ?: $uid);
1534
            } else {
1535
                $langRows = $this->getLanguages($pid);
1536
            }
1537
            // Page available in other languages than default language?
1538
            if (is_array($langRows) && count($langRows) > 1) {
1539
                $rowsByLang = [];
1540
                $fetchFields = 'uid,' . $languageField . ',' . $transOrigPointerField;
1541
                // Get record in current language
1542
                $rowCurrent = BackendUtility::getLiveVersionOfRecord($table, $uid, $fetchFields);
1543
                if (!is_array($rowCurrent)) {
1544
                    $rowCurrent = BackendUtility::getRecord($table, $uid, $fetchFields);
1545
                }
1546
                $currentLanguage = (int)$rowCurrent[$languageField];
1547
                // Disabled for records with [all] language!
1548
                if ($currentLanguage > -1) {
1549
                    // Get record in default language if needed
1550
                    if ($currentLanguage && $rowCurrent[$transOrigPointerField]) {
1551
                        $rowsByLang[0] = BackendUtility::getLiveVersionOfRecord(
1552
                            $table,
1553
                            $rowCurrent[$transOrigPointerField],
1554
                            $fetchFields
1555
                        );
1556
                        if (!is_array($rowsByLang[0])) {
1557
                            $rowsByLang[0] = BackendUtility::getRecord(
1558
                                $table,
1559
                                $rowCurrent[$transOrigPointerField],
1560
                                $fetchFields
1561
                            );
1562
                        }
1563
                    } else {
1564
                        $rowsByLang[$rowCurrent[$languageField]] = $rowCurrent;
1565
                    }
1566
                    if ($rowCurrent[$transOrigPointerField] || $currentLanguage === 0) {
1567
                        // Get record in other languages to see what's already available
1568
1569
                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1570
                            ->getQueryBuilderForTable($table);
1571
1572
                        $queryBuilder->getRestrictions()
1573
                            ->removeAll()
1574
                            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1575
                            ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1576
1577
                        $result = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fetchFields, true))
1578
                            ->from($table)
1579
                            ->where(
1580
                                $queryBuilder->expr()->eq(
1581
                                    'pid',
1582
                                    $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)
1583
                                ),
1584
                                $queryBuilder->expr()->gt(
1585
                                    $languageField,
1586
                                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1587
                                ),
1588
                                $queryBuilder->expr()->eq(
1589
                                    $transOrigPointerField,
1590
                                    $queryBuilder->createNamedParameter($rowsByLang[0]['uid'], \PDO::PARAM_INT)
1591
                                )
1592
                            )
1593
                            ->execute();
1594
1595
                        while ($row = $result->fetch()) {
1596
                            $rowsByLang[$row[$languageField]] = $row;
1597
                        }
1598
                    }
1599
                    $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1600
                    $languageMenu->setIdentifier('_langSelector');
1601
                    foreach ($langRows as $lang) {
1602
                        if ($this->getBackendUser()->checkLanguageAccess($lang['uid'])) {
1603
                            $newTranslation = isset($rowsByLang[$lang['uid']]) ? '' : ' [' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.new')) . ']';
1604
                            // Create url for creating a localized record
1605
                            $addOption = true;
1606
                            if ($newTranslation) {
1607
                                $redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', [
1608
                                    'justLocalized' => $table . ':' . $rowsByLang[0]['uid'] . ':' . $lang['uid'],
1609
                                    'returnUrl' => $this->retUrl
1610
                                ]);
1611
1612
                                if (array_key_exists(0, $rowsByLang)) {
1613
                                    $href = BackendUtility::getLinkToDataHandlerAction(
1614
                                        '&cmd[' . $table . '][' . $rowsByLang[0]['uid'] . '][localize]=' . $lang['uid'],
1615
                                        $redirectUrl
1616
                                    );
1617
                                } else {
1618
                                    $addOption = false;
1619
                                }
1620
                            } else {
1621
                                $href = (string)$uriBuilder->buildUriFromRoute('record_edit', [
1622
                                    'edit[' . $table . '][' . $rowsByLang[$lang['uid']]['uid'] . ']' => 'edit',
1623
                                    'returnUrl' => $this->retUrl
1624
                                ]);
1625
                            }
1626
                            if ($addOption) {
1627
                                $menuItem = $languageMenu->makeMenuItem()
1628
                                                         ->setTitle($lang['title'] . $newTranslation)
1629
                                                         ->setHref($href);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $href does not seem to be defined for all execution paths leading up to this point.
Loading history...
1630
                                if ((int)$lang['uid'] === $currentLanguage) {
1631
                                    $menuItem->setActive(true);
1632
                                }
1633
                                $languageMenu->addMenuItem($menuItem);
1634
                            }
1635
                        }
1636
                    }
1637
                    $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1638
                }
1639
            }
1640
        }
1641
    }
1642
1643
    /**
1644
     * Redirects to FormEngine with new parameters to edit a just created localized record
1645
     *
1646
     * @param string $justLocalized String passed by GET &justLocalized=
1647
     */
1648
    public function localizationRedirect($justLocalized)
1649
    {
1650
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1651
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1652
1653
        list($table, $origUid, $language) = explode(':', $justLocalized);
1654
        if ($GLOBALS['TCA'][$table]
1655
            && $GLOBALS['TCA'][$table]['ctrl']['languageField']
1656
            && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
1657
        ) {
1658
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1659
                ->getQueryBuilderForTable($table);
1660
            $queryBuilder->getRestrictions()
1661
                ->removeAll()
1662
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1663
                ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1664
1665
            $localizedRecord = $queryBuilder->select('uid')
1666
                ->from($table)
1667
                ->where(
1668
                    $queryBuilder->expr()->eq(
1669
                        $GLOBALS['TCA'][$table]['ctrl']['languageField'],
1670
                        $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT)
1671
                    ),
1672
                    $queryBuilder->expr()->eq(
1673
                        $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
1674
                        $queryBuilder->createNamedParameter($origUid, \PDO::PARAM_INT)
1675
                    )
1676
                )
1677
                ->execute()
1678
                ->fetch();
1679
1680
            if (is_array($localizedRecord)) {
1681
                // Create parameters and finally run the classic page module for creating a new page translation
1682
                $location = (string)$uriBuilder->buildUriFromRoute('record_edit', [
1683
                    'edit[' . $table . '][' . $localizedRecord['uid'] . ']' => 'edit',
1684
                    'returnUrl' => GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'))
1685
                ]);
1686
                HttpUtility::redirect($location);
1687
            }
1688
        }
1689
    }
1690
1691
    /**
1692
     * Returns sys_language records available for record translations on given page.
1693
     *
1694
     * @param int $id Page id: If zero, the query will select all sys_language records from root level which are NOT
1695
     *                hidden. If set to another value, the query will select all sys_language records that has a
1696
     *                translation record on that page (and is not hidden, unless you are admin user)
1697
     * @return array Language records including faked record for default language
1698
     */
1699
    public function getLanguages($id)
1700
    {
1701
        $modSharedTSconfig = BackendUtility::getModTSconfig($id, 'mod.SHARED');
1702
        // Fallback non sprite-configuration
1703
        if (preg_match('/\\.gif$/', $modSharedTSconfig['properties']['defaultLanguageFlag'])) {
1704
            $modSharedTSconfig['properties']['defaultLanguageFlag'] = str_replace(
1705
                '.gif',
1706
                '',
1707
                $modSharedTSconfig['properties']['defaultLanguageFlag']
1708
            );
1709
        }
1710
        $languages = [
1711
            0 => [
1712
                'uid' => 0,
1713
                'pid' => 0,
1714
                'hidden' => 0,
1715
                'title' => $modSharedTSconfig['properties']['defaultLanguageLabel'] !== ''
1716
                        ? $modSharedTSconfig['properties']['defaultLanguageLabel'] . ' (' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf:defaultLanguage') . ')'
1717
                        : $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf:defaultLanguage'),
1718
                'flag' => $modSharedTSconfig['properties']['defaultLanguageFlag']
1719
            ]
1720
        ];
1721
1722
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1723
            ->getQueryBuilderForTable('sys_language');
1724
1725
        $queryBuilder->select('s.uid', 's.pid', 's.hidden', 's.title', 's.flag')
1726
            ->from('sys_language', 's')
1727
            ->groupBy('s.uid', 's.pid', 's.hidden', 's.title', 's.flag', 's.sorting')
1728
            ->orderBy('s.sorting');
1729
1730
        if ($id) {
1731
            $queryBuilder->getRestrictions()
1732
                ->removeAll()
1733
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1734
                ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1735
1736
            if (!$this->getBackendUser()->isAdmin()) {
1737
                $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
1738
            }
1739
1740
            // Add join with pages translations to only show active languages
1741
            $queryBuilder->from('pages', 'o')
1742
                ->where(
1743
                    $queryBuilder->expr()->eq('o.' . $GLOBALS['TCA']['pages']['ctrl']['languageField'], $queryBuilder->quoteIdentifier('s.uid')),
1744
                    $queryBuilder->expr()->eq('o.' . $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'], $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT))
1745
                );
1746
        }
1747
1748
        $result = $queryBuilder->execute();
1749
        while ($row = $result->fetch()) {
1750
            $languages[$row['uid']] = $row;
1751
        }
1752
1753
        return $languages;
1754
    }
1755
1756
    /***************************
1757
     *
1758
     * Other functions
1759
     *
1760
     ***************************/
1761
    /**
1762
     * Fix $this->editconf if versioning applies to any of the records
1763
     *
1764
     * @param array|bool $mapArray Mapping between old and new ids if auto-versioning has been performed.
1765
     */
1766
    public function fixWSversioningInEditConf($mapArray = false)
1767
    {
1768
        // Traverse the editConf array
1769
        if (is_array($this->editconf)) {
1770
            // Tables:
1771
            foreach ($this->editconf as $table => $conf) {
1772
                if (is_array($conf) && $GLOBALS['TCA'][$table]) {
1773
                    // Traverse the keys/comments of each table (keys can be a commalist of uids)
1774
                    $newConf = [];
1775
                    foreach ($conf as $cKey => $cmd) {
1776
                        if ($cmd === 'edit') {
1777
                            // Traverse the ids:
1778
                            $ids = GeneralUtility::trimExplode(',', $cKey, true);
1779
                            foreach ($ids as $idKey => $theUid) {
1780
                                if (is_array($mapArray)) {
1781
                                    if ($mapArray[$table][$theUid]) {
1782
                                        $ids[$idKey] = $mapArray[$table][$theUid];
1783
                                    }
1784
                                } else {
1785
                                    // Default, look for versions in workspace for record:
1786
                                    $calcPRec = $this->getRecordForEdit($table, $theUid);
1787
                                    if (is_array($calcPRec)) {
1788
                                        // Setting UID again if it had changed, eg. due to workspace versioning.
1789
                                        $ids[$idKey] = $calcPRec['uid'];
1790
                                    }
1791
                                }
1792
                            }
1793
                            // Add the possibly manipulated IDs to the new-build newConf array:
1794
                            $newConf[implode(',', $ids)] = $cmd;
1795
                        } else {
1796
                            $newConf[$cKey] = $cmd;
1797
                        }
1798
                    }
1799
                    // Store the new conf array:
1800
                    $this->editconf[$table] = $newConf;
1801
                }
1802
            }
1803
        }
1804
    }
1805
1806
    /**
1807
     * Get record for editing.
1808
     *
1809
     * @param string $table Table name
1810
     * @param int $theUid Record UID
1811
     * @return array Returns record to edit, FALSE if none
1812
     */
1813
    public function getRecordForEdit($table, $theUid)
1814
    {
1815
        // Fetch requested record:
1816
        $reqRecord = BackendUtility::getRecord($table, $theUid, 'uid,pid');
1817
        if (is_array($reqRecord)) {
1818
            // If workspace is OFFLINE:
1819
            if ($this->getBackendUser()->workspace != 0) {
1820
                // Check for versioning support of the table:
1821
                if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1822
                    // If the record is already a version of "something" pass it by.
1823
                    if ($reqRecord['pid'] == -1) {
1824
                        // (If it turns out not to be a version of the current workspace there will be trouble, but
1825
                        // that is handled inside DataHandler then and in the interface it would clearly be an error of
1826
                        // links if the user accesses such a scenario)
1827
                        return $reqRecord;
1828
                    }
1829
                    // The input record was online and an offline version must be found or made:
1830
                    // Look for version of this workspace:
1831
                    $versionRec = BackendUtility::getWorkspaceVersionOfRecord(
1832
                            $this->getBackendUser()->workspace,
1833
                            $table,
1834
                            $reqRecord['uid'],
1835
                            'uid,pid,t3ver_oid'
1836
                        );
1837
                    return is_array($versionRec) ? $versionRec : $reqRecord;
1838
                }
1839
                // This means that editing cannot occur on this record because it was not supporting versioning
1840
                // which is required inside an offline workspace.
1841
                return false;
1842
            }
1843
            // In ONLINE workspace, just return the originally requested record:
1844
            return $reqRecord;
1845
        }
1846
        // Return FALSE because the table/uid was not found anyway.
1847
        return false;
1848
    }
1849
1850
    /**
1851
     * Populates the variables $this->storeArray, $this->storeUrl, $this->storeUrlMd5
1852
     *
1853
     * @see makeDocSel()
1854
     */
1855
    public function compileStoreDat()
1856
    {
1857
        $this->storeArray = GeneralUtility::compileSelectedGetVarsFromArray(
1858
            'edit,defVals,overrideVals,columnsOnly,noView,workspace',
1859
            $this->R_URL_getvars
1860
        );
1861
        $this->storeUrl = GeneralUtility::implodeArrayForUrl('', $this->storeArray);
1862
        $this->storeUrlMd5 = md5($this->storeUrl);
1863
    }
1864
1865
    /**
1866
     * Function used to look for configuration of buttons in the form: Fx. disabling buttons or showing them at various
1867
     * positions.
1868
     *
1869
     * @param string $table The table for which the configuration may be specific
1870
     * @param string $key The option for look for. Default is checking if the saveDocNew button should be displayed.
1871
     * @return string Return value fetched from USER TSconfig
1872
     */
1873
    public function getNewIconMode($table, $key = 'saveDocNew')
1874
    {
1875
        $TSconfig = $this->getBackendUser()->getTSConfig('options.' . $key);
1876
        $output = trim(isset($TSconfig['properties'][$table]) ? $TSconfig['properties'][$table] : $TSconfig['value']);
1877
        return $output;
1878
    }
1879
1880
    /**
1881
     * Handling the closing of a document
1882
     * The argument $mode can be one of this values:
1883
     * - 0/1 will redirect to $this->retUrl [self::DOCUMENT_CLOSE_MODE_DEFAULT || self::DOCUMENT_CLOSE_MODE_REDIRECT]
1884
     * - 3 will clear the docHandler (thus closing all documents) [self::DOCUMENT_CLOSE_MODE_CLEAR_ALL]
1885
     * - 4 will do no redirect [self::DOCUMENT_CLOSE_MODE_NO_REDIRECT]
1886
     * - other values will call setDocument with ->retUrl
1887
     *
1888
     * @param int $mode the close mode: one of self::DOCUMENT_CLOSE_MODE_*
1889
     */
1890
    public function closeDocument($mode = self::DOCUMENT_CLOSE_MODE_DEFAULT)
1891
    {
1892
        $mode = (int)$mode;
1893
        // If current document is found in docHandler,
1894
        // then unset it, possibly unset it ALL and finally, write it to the session data
1895
        if (isset($this->docHandler[$this->storeUrlMd5])) {
1896
            // add the closing document to the recent documents
1897
            $recentDocs = $this->getBackendUser()->getModuleData('opendocs::recent');
1898
            if (!is_array($recentDocs)) {
1899
                $recentDocs = [];
1900
            }
1901
            $closedDoc = $this->docHandler[$this->storeUrlMd5];
1902
            $recentDocs = array_merge([$this->storeUrlMd5 => $closedDoc], $recentDocs);
1903
            if (count($recentDocs) > 8) {
1904
                $recentDocs = array_slice($recentDocs, 0, 8);
1905
            }
1906
            // remove it from the list of the open documents
1907
            unset($this->docHandler[$this->storeUrlMd5]);
1908
            if ($mode === self::DOCUMENT_CLOSE_MODE_CLEAR_ALL) {
1909
                $recentDocs = array_merge($this->docHandler, $recentDocs);
1910
                $this->docHandler = [];
1911
            }
1912
            $this->getBackendUser()->pushModuleData('opendocs::recent', $recentDocs);
1913
            $this->getBackendUser()->pushModuleData('FormEngine', [$this->docHandler, $this->docDat[1]]);
1914
            BackendUtility::setUpdateSignal('OpendocsController::updateNumber', count($this->docHandler));
1915
        }
1916
        if ($mode !== self::DOCUMENT_CLOSE_MODE_NO_REDIRECT) {
1917
            /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1918
            $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1919
            // If ->returnEditConf is set, then add the current content of editconf to the ->retUrl variable: (used by
1920
            // other scripts, like wizard_add, to know which records was created or so...)
1921
            if ($this->returnEditConf && $this->retUrl != (string)$uriBuilder->buildUriFromRoute('dummy')) {
1922
                $this->retUrl .= '&returnEditConf=' . rawurlencode(json_encode($this->editconf));
1923
            }
1924
1925
            // If mode is NOT set (means 0) OR set to 1, then make a header location redirect to $this->retUrl
1926
            if ($mode === self::DOCUMENT_CLOSE_MODE_DEFAULT || $mode === self::DOCUMENT_CLOSE_MODE_REDIRECT) {
1927
                HttpUtility::redirect($this->retUrl);
1928
            } else {
1929
                $this->setDocument('', $this->retUrl);
1930
            }
1931
        }
1932
    }
1933
1934
    /**
1935
     * Redirects to the document pointed to by $currentDocFromHandlerMD5 OR $retUrl (depending on some internal
1936
     * calculations).
1937
     * Most likely you will get a header-location redirect from this function.
1938
     *
1939
     * @param string $currentDocFromHandlerMD5 Pointer to the document in the docHandler array
1940
     * @param string $retUrl Alternative/Default retUrl
1941
     */
1942
    public function setDocument($currentDocFromHandlerMD5 = '', $retUrl = '')
1943
    {
1944
        if ($retUrl === '') {
1945
            return;
1946
        }
1947
        if (!$this->modTSconfig['properties']['disableDocSelector']
1948
            && is_array($this->docHandler)
1949
            && !empty($this->docHandler)
1950
        ) {
1951
            if (isset($this->docHandler[$currentDocFromHandlerMD5])) {
1952
                $setupArr = $this->docHandler[$currentDocFromHandlerMD5];
1953
            } else {
1954
                $setupArr = reset($this->docHandler);
1955
            }
1956
            if ($setupArr[2]) {
1957
                $sParts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
1958
                $retUrl = $sParts['path'] . '?' . $setupArr[2] . '&returnUrl=' . rawurlencode($retUrl);
1959
            }
1960
        }
1961
        HttpUtility::redirect($retUrl);
1962
    }
1963
1964
    /**
1965
     * Injects the request object for the current request or subrequest
1966
     *
1967
     * @param ServerRequestInterface $request the current request
1968
     * @param ResponseInterface $response
1969
     * @return ResponseInterface the response with the content
1970
     */
1971
    public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1971
    public function mainAction(/** @scrutinizer ignore-unused */ ServerRequestInterface $request, ResponseInterface $response)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1972
    {
1973
        BackendUtility::lockRecords();
1974
1975
        // Preprocessing, storing data if submitted to
1976
        $this->preInit();
1977
1978
        // Checks, if a save button has been clicked (or the doSave variable is sent)
1979
        if ($this->doProcessData()) {
1980
            $this->processData();
1981
        }
1982
1983
        $this->init();
1984
        $this->main();
1985
1986
        $response->getBody()->write($this->moduleTemplate->renderContent());
1987
        return $response;
1988
    }
1989
1990
    /**
1991
     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1992
     */
1993
    protected function getBackendUser()
1994
    {
1995
        return $GLOBALS['BE_USER'];
1996
    }
1997
1998
    /**
1999
     * Returns LanguageService
2000
     *
2001
     * @return \TYPO3\CMS\Core\Localization\LanguageService
2002
     */
2003
    protected function getLanguageService()
2004
    {
2005
        return $GLOBALS['LANG'];
2006
    }
2007
}
2008