[TASK] Call explicit render() on icon objects
[Packages/TYPO3.CMS.git] / typo3 / sysext / recordlist / Classes / Browser / ElementBrowser.php
1 <?php
2 namespace TYPO3\CMS\Recordlist\Browser;
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 TYPO3\CMS\Backend\Form\FormEngine;
18 use TYPO3\CMS\Backend\RecordList\ElementBrowserRecordList;
19 use TYPO3\CMS\Backend\Routing\Router;
20 use TYPO3\CMS\Backend\Routing\UriBuilder;
21 use TYPO3\CMS\Backend\Template\DocumentTemplate;
22 use TYPO3\CMS\Backend\Tree\View\ElementBrowserFolderTreeView;
23 use TYPO3\CMS\Backend\Tree\View\ElementBrowserPageTreeView;
24 use TYPO3\CMS\Backend\Utility\BackendUtility;
25 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
26 use TYPO3\CMS\Core\Database\DatabaseConnection;
27 use TYPO3\CMS\Core\ElementBrowser\ElementBrowserHookInterface;
28 use TYPO3\CMS\Core\Imaging\Icon;
29 use TYPO3\CMS\Core\Imaging\IconFactory;
30 use TYPO3\CMS\Core\Messaging\FlashMessage;
31 use TYPO3\CMS\Core\Resource\FileRepository;
32 use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry;
33 use TYPO3\CMS\Core\Page\PageRenderer;
34 use TYPO3\CMS\Core\Resource\Exception;
35 use TYPO3\CMS\Core\Resource\File;
36 use TYPO3\CMS\Core\Resource\FileInterface;
37 use TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter;
38 use TYPO3\CMS\Core\Resource\Folder;
39 use TYPO3\CMS\Core\Resource\InaccessibleFolder;
40 use TYPO3\CMS\Core\Resource\ProcessedFile;
41 use TYPO3\CMS\Core\Resource\ResourceFactory;
42 use TYPO3\CMS\Core\Utility\File\BasicFileUtility;
43 use TYPO3\CMS\Core\Utility\GeneralUtility;
44 use TYPO3\CMS\Core\Utility\StringUtility;
45 use TYPO3\CMS\Core\Utility\MathUtility;
46 use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
47 use TYPO3\CMS\Lang\LanguageService;
48
49 /**
50 * class for the Element Browser window.
51 */
52 class ElementBrowser {
53
54 /**
55 * Optional instance of a record list that TBE_expandPage() should
56 * use to render the records in a page
57 *
58 * @var ElementBrowserRecordList
59 */
60 protected $recordList = NULL;
61
62 /**
63 * Current site URL (Frontend)
64 *
65 * @var string
66 * @internal
67 */
68 public $siteURL;
69
70 /**
71 * the script to link to
72 *
73 * @var string
74 */
75 public $thisScript;
76
77 /**
78 * RTE configuration
79 *
80 * @var array
81 */
82 protected $RTEProperties = array();
83
84 /**
85 * Target (RTE specific)
86 *
87 * @var string
88 */
89 public $setTarget;
90
91 /**
92 * CSS Class (RTE specific)
93 *
94 * @var string
95 */
96 public $setClass;
97
98 /**
99 * title (RTE specific)
100 *
101 * @var string
102 */
103 public $setTitle;
104
105 /**
106 * @var string
107 */
108 public $setParams;
109
110 /**
111 * Backend template object
112 *
113 * @var DocumentTemplate
114 */
115 public $doc;
116
117 /**
118 * Holds information about files
119 *
120 * @var mixed[][]
121 */
122 public $elements = array();
123
124 /**
125 * The mode determines the main kind of output from the element browser.
126 * There are these options for values: rte, db, file, filedrag, wizard.
127 * "rte" will show the link selector for the Rich Text Editor (see main_rte())
128 * "db" will allow you to browse for pages or records in the page tree (for TCEforms, see main_db())
129 * "file"/"filedrag" will allow you to browse for files or folders in the folder mounts (for TCEforms, main_file())
130 * "wizard" will allow you to browse for links (like "rte") which are passed back to TCEforms (see main_rte(1))
131 *
132 * @see main()
133 * @var string
134 */
135 public $mode;
136
137 /**
138 * Link selector action.
139 * page,file,url,mail are allowed values.
140 * These are only important with the link selector function and in that case they switch
141 * between the various menu options.
142 *
143 * @var string
144 */
145 public $act;
146
147 /**
148 * When you click a page title/expand icon to see the content of a certain page, this
149 * value will contain that value (the ID of the expanded page). If the value is NOT set,
150 * then it will be restored from the module session data (see main(), mode="db")
151 *
152 * @var NULL|int
153 */
154 public $expandPage;
155
156 /**
157 * When you click a folder name/expand icon to see the content of a certain file folder,
158 * this value will contain that value (the path of the expanded file folder). If the
159 * value is NOT set, then it will be restored from the module session data (see main(),
160 * mode="file"/"filedrag"). Example value: "/www/htdocs/typo3/32/3dsplm/fileadmin/css/"
161 *
162 * @var string
163 */
164 public $expandFolder;
165
166 /**
167 * the folder object of a parent folder that was selected
168 *
169 * @var Folder
170 */
171 protected $selectedFolder;
172
173 /**
174 * TYPO3 Element Browser, wizard mode parameters. There is a heap of parameters there,
175 * better debug() them out if you need something... :-)
176 *
177 * @var array[]
178 */
179 public $P;
180
181 /**
182 * Active with TYPO3 Element Browser: Contains the name of the form field for which this window
183 * opens - thus allows us to make references back to the main window in which the form is.
184 * Example value: "data[pages][39][bodytext]|||tt_content|"
185 * or "data[tt_content][NEW3fba56fde763d][image]|||gif,jpg,jpeg,tif,bmp,pcx,tga,png,pdf,ai|"
186 *
187 * Values:
188 * 0: form field name reference, eg. "data[tt_content][123][image]"
189 * 1: htmlArea RTE parameters: editorNo:contentTypo3Language
190 * 2: RTE config parameters: RTEtsConfigParams
191 * 3: allowed types. Eg. "tt_content" or "gif,jpg,jpeg,tif,bmp,pcx,tga,png,pdf,ai"
192 * 4: IRRE uniqueness: target level object-id to perform actions/checks on, eg. "data[79][tt_address][1][<field>][<foreign_table>]"
193 * 5: IRRE uniqueness: name of function in opener window that checks if element is already used, eg. "inline.checkUniqueElement"
194 * 6: IRRE uniqueness: name of function in opener window that performs some additional(!) action, eg. "inline.setUniqueElement"
195 * 7: IRRE uniqueness: name of function in opener window that performs action instead of using addElement/insertElement, eg. "inline.importElement"
196 *
197 * $pArr = explode('|', $this->bparams);
198 * $formFieldName = $pArr[0];
199 * $allowedTablesOrFileTypes = $pArr[3];
200 *
201 * @var string
202 */
203 public $bparams;
204
205 /**
206 * Used with the Rich Text Editor.
207 * Example value: "tt_content:NEW3fba58c969f5c:bodytext:23:text:23:"
208 *
209 * @var string
210 */
211 public $RTEtsConfigParams;
212
213 /**
214 * Plus/Minus icon value. Used by the tree class to open/close notes on the trees.
215 *
216 * @var string
217 */
218 public $PM;
219
220 /**
221 * Pointer, used when browsing a long list of records etc.
222 *
223 * @var int
224 */
225 public $pointer;
226
227 /**
228 * Used with the link selector: Contains the GET input information about the CURRENT link
229 * in the RTE/TCEform field. This consists of "href", "target" and "title" keys.
230 * This information is passed around in links.
231 *
232 * @var array[]
233 */
234 public $curUrlArray;
235
236 /**
237 * Used with the link selector: Contains a processed version of the input values from curUrlInfo.
238 * This is splitted into pageid, content element id, label value etc.
239 * This is used for the internal processing of that information.
240 *
241 * @var array[]
242 */
243 public $curUrlInfo;
244
245 /**
246 * array which holds hook objects (initialised in init())
247 *
248 * @var ElementBrowserHookInterface[]
249 */
250 protected $hookObjects = array();
251
252 /**
253 * @var BasicFileUtility
254 */
255 public $fileProcessor;
256
257 /**
258 * @var PageRenderer
259 */
260 protected $pageRenderer = NULL;
261
262 /**
263 * @var IconFactory
264 */
265 protected $iconFactory;
266
267 /**
268 * @var string
269 */
270 protected $hookName = 'typo3/class.browse_links.php';
271
272 /**
273 * @var string
274 */
275 protected $searchWord;
276
277 /**
278 * @var FileRepository
279 */
280 protected $fileRepository;
281
282 /**
283 * Construct
284 */
285 public function __construct() {
286 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
287 $this->fileRepository = GeneralUtility::makeInstance(FileRepository::class);
288 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
289 $this->pageRenderer->loadJquery();
290 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Recordlist/FieldSelectBox');
291 }
292
293 /**
294 * Sets the script url depending on being a module or script request
295 */
296 protected function determineScriptUrl() {
297 if ($routePath = GeneralUtility::_GP('route')) {
298 $router = GeneralUtility::makeInstance(Router::class);
299 $route = $router->match($routePath);
300 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
301 $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
302 } elseif ($moduleName = GeneralUtility::_GP('M')) {
303 $this->thisScript = BackendUtility::getModuleUrl($moduleName);
304 } else {
305 $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
306 }
307 }
308
309 /**
310 * Calculate path to this script.
311 * This method is public, to be used in hooks of this class only.
312 *
313 * @return string
314 */
315 public function getThisScript() {
316 return strpos($this->thisScript, '?') === FALSE ? $this->thisScript . '?' : $this->thisScript . '&';
317 }
318
319 /**
320 * Constructor:
321 * Initializes a lot of variables, setting JavaScript functions in header etc.
322 *
323 * @return void
324 * @throws \UnexpectedValueException
325 */
326 public function init() {
327 $this->initVariables();
328 $this->initDocumentTemplate();
329
330 // Initializing hooking browsers
331 $this->initHookObjects();
332
333 $this->initCurrentUrl();
334
335 // Determine nature of current url:
336 $this->act = GeneralUtility::_GP('act');
337 if (!$this->act) {
338 $this->act = $this->curUrlInfo['act'];
339 }
340
341 $this->initLinkAttributes();
342
343 // Finally, add the accumulated JavaScript to the template object:
344 // also unset the default jumpToUrl() function before
345 unset($this->doc->JScodeArray['jumpToUrl']);
346 $this->doc->JScode .= $this->doc->wrapScriptTags($this->getJSCode());
347 }
348
349 /**
350 * Initialize class variables
351 *
352 * @return void
353 */
354 public function initVariables() {
355 // Main GPvars:
356 $this->pointer = GeneralUtility::_GP('pointer');
357 $this->bparams = GeneralUtility::_GP('bparams');
358 $this->P = GeneralUtility::_GP('P');
359 $this->expandPage = GeneralUtility::_GP('expandPage');
360 $this->expandFolder = GeneralUtility::_GP('expandFolder');
361 $this->PM = GeneralUtility::_GP('PM');
362 $this->RTEtsConfigParams = GeneralUtility::_GP('RTEtsConfigParams');
363 $this->searchWord = (string)GeneralUtility::_GP('searchWord');
364
365 // Site URL
366 // Current site url
367 $this->siteURL = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
368 $this->determineScriptUrl();
369
370 // Default mode is RTE
371 $this->mode = GeneralUtility::_GP('mode');
372 if (!$this->mode) {
373 $this->mode = 'rte';
374 }
375
376 // Init fileProcessor
377 $this->fileProcessor = GeneralUtility::makeInstance(BasicFileUtility::class);
378 $this->fileProcessor->init(array(), $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']);
379
380 // Rich Text Editor specific configuration:
381 if ($this->mode === 'rte') {
382 $this->RTEProperties = $this->getRTEConfig();
383 }
384 }
385
386 /**
387 * Initialize document template object
388 *
389 * @return void
390 */
391 protected function initDocumentTemplate() {
392 // Creating backend template object:
393 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
394 $this->doc->bodyTagId = 'typo3-browse-links-php';
395 $this->doc->getContextMenuCode();
396
397 $pageRenderer = $this->getPageRenderer();
398 $pageRenderer->loadJquery();
399 $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/BrowseLinks');
400 $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LegacyTree');
401 }
402
403 /**
404 * Initialize hook objects implementing the interface
405 *
406 * @throws \UnexpectedValueException
407 * @return void
408 */
409 protected function initHookObjects() {
410 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['browseLinksHook'])) {
411 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['browseLinksHook'] as $classData) {
412 $processObject = GeneralUtility::getUserObj($classData);
413 if (!$processObject instanceof ElementBrowserHookInterface) {
414 throw new \UnexpectedValueException('$processObject must implement interface ' . ElementBrowserHookInterface::class, 1195039394);
415 }
416 $parameters = array();
417 $processObject->init($this, $parameters);
418 $this->hookObjects[] = $processObject;
419 }
420 }
421 }
422
423 /**
424 * Initialize $this->curUrlArray and $this->curUrlInfo based on script parameters
425 *
426 * @return void
427 */
428 protected function initCurrentUrl() {
429 // CurrentUrl - the current link url must be passed around if it exists
430 if ($this->mode === 'wizard') {
431 $currentValues = GeneralUtility::trimExplode(LF, trim($this->P['currentValue']));
432 if (!empty($currentValues)) {
433 $currentValue = array_pop($currentValues);
434 } else {
435 $currentValue = '';
436 }
437 $currentLinkParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($currentValue);
438
439 $initialCurUrlArray = array(
440 'href' => $currentLinkParts['url'],
441 'target' => $currentLinkParts['target'],
442 'class' => $currentLinkParts['class'],
443 'title' => $currentLinkParts['title'],
444 'params' => $currentLinkParts['additionalParams']
445 );
446 $this->curUrlArray = is_array(GeneralUtility::_GP('curUrl'))
447 ? array_merge($initialCurUrlArray, GeneralUtility::_GP('curUrl'))
448 : $initialCurUrlArray;
449 // Additional fields for page links
450 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['extendUrlArray'])
451 && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['extendUrlArray'])
452 ) {
453 $conf = array();
454 $_params = array(
455 'conf' => &$conf,
456 'linkParts' => [
457 // the hook expects old numerical indexes
458 $currentLinkParts['url'],
459 $currentLinkParts['target'],
460 $currentLinkParts['class'],
461 $currentLinkParts['title'],
462 $currentLinkParts['additionalParams']
463 ]
464 );
465 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['extendUrlArray'] as $objRef) {
466 $processor =& GeneralUtility::getUserObj($objRef);
467 $processor->extendUrlArray($_params, $this);
468 }
469 }
470 $this->curUrlInfo = $this->parseCurUrl($this->siteURL . '?id=' . $this->curUrlArray['href'], $this->siteURL);
471 // pageid == 0 means that this is not an internal (page) link
472 if ($this->curUrlInfo['pageid'] == 0 && $this->curUrlArray['href']) {
473 // Check if there is the FAL API
474 if (GeneralUtility::isFirstPartOfStr($this->curUrlArray['href'], 'file:')) {
475 $this->curUrlInfo = $this->parseCurUrl($this->curUrlArray['href'], $this->siteURL);
476 } elseif (file_exists(PATH_site . rawurldecode($this->curUrlArray['href']))) {
477 $this->curUrlInfo = $this->parseCurUrl($this->siteURL . $this->curUrlArray['href'], $this->siteURL);
478 } elseif (strstr($this->curUrlArray['href'], '@')) {
479 $this->curUrlInfo = $this->parseCurUrl('mailto:' . $this->curUrlArray['href'], $this->siteURL);
480 } else {
481 // nothing of the above. this is an external link
482 if (strpos($this->curUrlArray['href'], '://') === FALSE) {
483 $currentLinkParts['url'] = 'http://' . $this->curUrlArray['href'];
484 }
485 $this->curUrlInfo = $this->parseCurUrl($currentLinkParts['url'], $this->siteURL);
486 }
487 } elseif (!$this->curUrlArray['href']) {
488 $this->curUrlInfo = array();
489 $this->act = 'page';
490 } else {
491 $this->curUrlInfo = $this->parseCurUrl($this->siteURL . '?id=' . $this->curUrlArray['href'], $this->siteURL);
492 }
493 } else {
494 $this->curUrlArray = GeneralUtility::_GP('curUrl');
495 if ($this->curUrlArray['all']) {
496 $this->curUrlArray = GeneralUtility::get_tag_attributes($this->curUrlArray['all']);
497 $this->curUrlArray['href'] = htmlspecialchars_decode($this->curUrlArray['href']);
498 }
499 // Note: parseCurUrl will invoke the hooks
500 $this->curUrlInfo = $this->parseCurUrl($this->curUrlArray['href'], $this->siteURL);
501 if (isset($this->curUrlArray['data-htmlarea-external']) && $this->curUrlArray['data-htmlarea-external'] === '1' && $this->curUrlInfo['act'] !== 'mail') {
502 $this->curUrlInfo['act'] = 'url';
503 $this->curUrlInfo['info'] = $this->curUrlArray['href'];
504 }
505 }
506 }
507
508 /**
509 * Initialize the current or default values of the link attributes (RTE)
510 *
511 * @return void
512 */
513 protected function initLinkAttributes() {
514 $this->setTarget = $this->curUrlArray['target'] != '-' ? $this->curUrlArray['target'] : '';
515 if ($this->RTEProperties['default.']['defaultLinkTarget'] && !isset($this->curUrlArray['target'])) {
516 $this->setTarget = $this->RTEProperties['default.']['defaultLinkTarget'];
517 }
518 $this->setClass = $this->curUrlArray['class'] != '-' ? $this->curUrlArray['class'] : '';
519 $this->setTitle = $this->curUrlArray['title'] != '-' ? $this->curUrlArray['title'] : '';
520 $this->setParams = $this->curUrlArray['params'] != '-' ? $this->curUrlArray['params'] : '';
521 }
522
523 /**
524 * Get the RTE configuration from Page TSConfig
525 *
526 * @return array[] RTE configuration array
527 */
528 protected function getRTEConfig() {
529 $RTEtsConfigParts = explode(':', $this->RTEtsConfigParams);
530 $RTEsetup = $this->getBackendUser()->getTSConfig('RTE', BackendUtility::getPagesTSconfig($RTEtsConfigParts[5]));
531 $RTEsetup['properties']['default.'] = BackendUtility::RTEsetup($RTEsetup['properties'], $RTEtsConfigParts[0], $RTEtsConfigParts[2], $RTEtsConfigParts[4]);
532 return $RTEsetup['properties'];
533 }
534
535 /**
536 * Generate JS code to be used on the link insert/modify dialogue
537 *
538 * @return string the generated JS code
539 */
540 public function getJsCode() {
541 // BEGIN accumulation of header JavaScript:
542 $JScode = '
543 // This JavaScript is primarily for RTE/Link. jumpToUrl is used in the other cases as well...
544 var add_href=' . GeneralUtility::quoteJSvalue($this->curUrlArray['href'] ? '&curUrl[href]=' . rawurlencode($this->curUrlArray['href']) : '') . ';
545 var add_target=' . GeneralUtility::quoteJSvalue($this->setTarget ? '&curUrl[target]=' . rawurlencode($this->setTarget) : '') . ';
546 var add_class=' . GeneralUtility::quoteJSvalue($this->setClass ? '&curUrl[class]=' . rawurlencode($this->setClass) : '') . ';
547 var add_title=' . GeneralUtility::quoteJSvalue($this->setTitle ? '&curUrl[title]=' . rawurlencode($this->setTitle) : '') . ';
548 var add_params=' . GeneralUtility::quoteJSvalue($this->bparams ? '&bparams=' . rawurlencode($this->bparams) : '') . ';
549
550 var cur_href=' . GeneralUtility::quoteJSvalue($this->curUrlArray['href'] ?: '') . ';
551 var cur_target=' . GeneralUtility::quoteJSvalue($this->setTarget ?: '') . ';
552 var cur_class=' . GeneralUtility::quoteJSvalue($this->setClass ?: '') . ';
553 var cur_title=' . GeneralUtility::quoteJSvalue($this->setTitle ?: '') . ';
554 var cur_params=' . GeneralUtility::quoteJSvalue($this->setParams ?: '') . ';
555
556 function browse_links_setTarget(target) {
557 cur_target=target;
558 add_target="&curUrl[target]="+encodeURIComponent(target);
559 }
560 function browse_links_setClass(cssClass) {
561 cur_class = cssClass;
562 add_class = "&curUrl[class]="+encodeURIComponent(cssClass);
563 }
564 function browse_links_setTitle(title) {
565 cur_title=title;
566 add_title="&curUrl[title]="+encodeURIComponent(title);
567 }
568 function browse_links_setValue(value) {
569 cur_href=value;
570 add_href="&curUrl[href]="+value;
571 }
572 function browse_links_setParams(params) {
573 cur_params=params;
574 add_params="&curUrl[params]="+encodeURIComponent(params);
575 }
576 ' . $this->doc->redirectUrls();
577
578 // Functions used, if the link selector is in wizard mode (= TCEforms fields)
579 $addPassOnParams = '';
580 if ($this->mode === 'rte') {
581 // Rich Text Editor specific configuration
582 $addPassOnParams .= '&RTEtsConfigParams=' . rawurlencode($this->RTEtsConfigParams);
583 }
584 $update = '';
585 if ($this->mode === 'wizard') {
586 if (!$this->areFieldChangeFunctionsValid() && !$this->areFieldChangeFunctionsValid(TRUE)) {
587 $this->P['fieldChangeFunc'] = array();
588 }
589 unset($this->P['fieldChangeFunc']['alert']);
590 foreach ($this->P['fieldChangeFunc'] as $v) {
591 $update .= '
592 window.opener.' . $v;
593 }
594 $P2 = array();
595 $P2['uid'] = $this->P['uid'];
596 $P2['pid'] = $this->P['pid'];
597 $P2['itemName'] = $this->P['itemName'];
598 $P2['formName'] = $this->P['formName'];
599 $P2['fieldChangeFunc'] = $this->P['fieldChangeFunc'];
600 $P2['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($this->P['fieldChangeFunc']));
601 $P2['params']['allowedExtensions'] = isset($this->P['params']['allowedExtensions']) ? $this->P['params']['allowedExtensions'] : '';
602 $P2['params']['blindLinkOptions'] = isset($this->P['params']['blindLinkOptions']) ? $this->P['params']['blindLinkOptions'] : '';
603 $P2['params']['blindLinkFields'] = isset($this->P['params']['blindLinkFields']) ? $this->P['params']['blindLinkFields']: '';
604 $addPassOnParams .= GeneralUtility::implodeArrayForUrl('P', $P2);
605 $JScode .= '
606 function link_typo3Page(id,anchor) { //
607 updateValueInMainForm(id + (anchor ? anchor : ""));
608 close();
609 return false;
610 }
611 function link_folder(folder) { //
612 updateValueInMainForm(folder);
613 close();
614 return false;
615 }
616 function link_current() { //
617 if (cur_href!="http://" && cur_href!="mailto:") {
618 returnBeforeCleaned = cur_href;
619 if (returnBeforeCleaned.substr(0, 7) == "http://") {
620 returnToMainFormValue = returnBeforeCleaned.substr(7);
621 } else if (returnBeforeCleaned.substr(0, 7) == "mailto:") {
622 if (returnBeforeCleaned.substr(0, 14) == "mailto:mailto:") {
623 returnToMainFormValue = returnBeforeCleaned.substr(14);
624 } else {
625 returnToMainFormValue = returnBeforeCleaned.substr(7);
626 }
627 } else {
628 returnToMainFormValue = returnBeforeCleaned;
629 }
630 updateValueInMainForm(returnToMainFormValue);
631 close();
632 }
633 return false;
634 }
635 function checkReference() { //
636 if (window.opener && window.opener.document && window.opener.document.querySelector(\'form[name="'
637 . $this->P['formName'] . '"] [data-formengine-input-name="' . $this->P['itemName'] . '"]\')) {
638 return window.opener.document.querySelector(\'form[name="' . $this->P['formName'] . '"] [data-formengine-input-name="' . $this->P['itemName'] . '"]\');
639 } else {
640 close();
641 }
642 }
643 function updateValueInMainForm(input) { //
644 var field = checkReference();
645 if (field) {
646 if (cur_target == "" && (cur_class != "" || cur_title != "" || cur_params != "")) {
647 cur_target = "-";
648 }
649 if (cur_class == "" && (cur_title != "" || cur_params != "")) {
650 cur_class = "-";
651 }
652 cur_class = cur_class.replace(/[\'\\"]/g, "");
653 if (cur_class.indexOf(" ") != -1) {
654 cur_class = "\\"" + cur_class + "\\"";
655 }
656 if (cur_title == "" && cur_params != "") {
657 cur_title = "-";
658 }
659 // replace each \ with \\
660 cur_title = cur_title.replace(/\\\\/g, "\\\\\\\\");
661 // replace each " with \"
662 cur_title = cur_title.replace(/\\"/g, "\\\\\\"");
663 if (cur_title.indexOf(" ") != -1) {
664 cur_title = "\\"" + cur_title + "\\"";
665 }
666 if (cur_params) {
667 cur_params = cur_params.replace(/\\bid\\=.*?(\\&|$)/, "");
668 }
669 input = input + " " + cur_target + " " + cur_class + " " + cur_title + " " + cur_params;
670 input = input.replace(/^\s+|\s+$/g, "");
671 if(field.value && field.className.search(/textarea/) != -1) {
672 field.value += "\\n" + input;
673 } else {
674 field.value = input;
675 }
676 if (typeof field.onchange === \'function\') {
677 field.onchange();
678 }
679 ' . $update . '
680 }
681 }
682 ';
683 }
684 // General "jumpToUrl" function:
685 $JScode .= '
686 function jumpToUrl(URL,anchor) { //
687 if (URL.charAt(0) === \'?\') {
688 URL = ' . GeneralUtility::quoteJSvalue($this->getThisScript()) . ' + URL.substring(1);
689 }
690 var add_act = URL.indexOf("act=")==-1 ? "&act=' . $this->act . '" : "";
691 var add_mode = URL.indexOf("mode=")==-1 ? "&mode=' . $this->mode . '" : "";
692 window.location.href = URL + add_act + add_mode + add_href + add_target + add_class + add_title + add_params'
693 . ($addPassOnParams ? '+' . GeneralUtility::quoteJSvalue($addPassOnParams) : '')
694 . '+(typeof(anchor) === "string" ? anchor : "");
695 return false;
696 }
697 ';
698
699 $JScode .= $this->getBParamJSCode();
700
701 // extends JavaScript code
702 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['extendJScode'])
703 && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['extendJScode'])
704 ) {
705 $_params = array(
706 'conf' => [],
707 'wizardUpdate' => $update,
708 'addPassOnParams' => $addPassOnParams
709 );
710 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['extendJScode'] as $objRef) {
711 $processor =& GeneralUtility::getUserObj($objRef);
712 $JScode .= $processor->extendJScode($_params, $this);
713 }
714 }
715 return $JScode;
716 }
717
718 /**
719 * Splits parts of $this->bparams and returns needed JS
720 *
721 * @return string JavaScript code
722 */
723 protected function getBParamJSCode() {
724 $pArr = explode('|', $this->bparams);
725 // This is JavaScript especially for the TBE Element Browser!
726 $formFieldName = 'data[' . $pArr[0] . '][' . $pArr[1] . '][' . $pArr[2] . ']';
727 // insertElement - Call check function (e.g. for uniqueness handling):
728 $JScodeCheck = '';
729 if ($pArr[4] && $pArr[5]) {
730 $JScodeCheck = '
731 // Call a check function in the opener window (e.g. for uniqueness handling):
732 if (parent.window.opener) {
733 var res = parent.window.opener.' . $pArr[5] . '("' . addslashes($pArr[4]) . '",table,uid,type);
734 if (!res.passed) {
735 if (res.message) alert(res.message);
736 performAction = false;
737 }
738 } else {
739 alert("Error - reference to main window is not set properly!");
740 parent.close();
741 }
742 ';
743 }
744 // insertElement - Call helper function:
745 $JScodeHelper = '';
746 if ($pArr[4] && $pArr[6]) {
747 $JScodeHelper = '
748 // Call helper function to manage data in the opener window:
749 if (parent.window.opener) {
750 parent.window.opener.' . $pArr[6] . '("' . addslashes($pArr[4]) . '",table,uid,type,"' . addslashes($pArr[0]) . '");
751 } else {
752 alert("Error - reference to main window is not set properly!");
753 parent.close();
754 }
755 ';
756 }
757 // insertElement - perform action commands:
758 $JScodeActionMultiple = '';
759 if ($pArr[4] && $pArr[7]) {
760 // Call user defined action function:
761 $JScodeAction = '
762 if (parent.window.opener) {
763 parent.window.opener.' . $pArr[7] . '("' . addslashes($pArr[4]) . '",table,uid,type);
764 if (close) { focusOpenerAndClose(close); }
765 } else {
766 alert("Error - reference to main window is not set properly!");
767 if (close) { parent.close(); }
768 }
769 ';
770 $JScodeActionMultiple = '
771 // Call helper function to manage data in the opener window:
772 if (parent.window.opener) {
773 parent.window.opener.' . $pArr[7] . 'Multiple("' . addslashes($pArr[4]) . '",table,uid,type,"'
774 . addslashes($pArr[0]) . '");
775 } else {
776 alert("Error - reference to main window is not set properly!");
777 parent.close();
778 }
779 ';
780 } elseif ($pArr[0] && !$pArr[1] && !$pArr[2]) {
781 $JScodeAction = '
782 addElement(filename,table+"_"+uid,fp,close);
783 ';
784 } else {
785 $JScodeAction = '
786 if (setReferences()) {
787 parent.window.opener.group_change("add","' . $pArr[0] . '","' . $pArr[1] . '","' . $pArr[2]
788 . '",elRef,targetDoc);
789 } else {
790 alert("Error - reference to main window is not set properly!");
791 }
792 focusOpenerAndClose(close);
793 ';
794 }
795 return '
796 var elRef="";
797 var targetDoc="";
798
799 function setReferences() { //
800 if (parent.window.opener && parent.window.opener.content && parent.window.opener.content.document.editform'
801 . '&& parent.window.opener.content.document.editform["' . $formFieldName . '"]) {
802 targetDoc = parent.window.opener.content.document;
803 elRef = targetDoc.editform["' . $formFieldName . '"];
804 return true;
805 } else {
806 return false;
807 }
808 }
809 function insertElement(table, uid, type, filename, fp, filetype, imagefile, action, close) { //
810 var performAction = true;
811 ' . $JScodeCheck . '
812 // Call performing function and finish this action:
813 if (performAction) {
814 ' . $JScodeHelper . $JScodeAction . '
815 }
816 return false;
817 }
818 var _hasActionMultipleCode = ' . (!empty($JScodeActionMultiple) ? 'true' : 'false') . ';
819 function insertMultiple(table, uid) {
820 var type = "";
821 ' . $JScodeActionMultiple . '
822 return false;
823 }
824 function addElement(elName, elValue, altElValue, close) { //
825 if (parent.window.opener && parent.window.opener.setFormValueFromBrowseWin) {
826 parent.window.opener.setFormValueFromBrowseWin("' . $pArr[0] . '",altElValue?altElValue:elValue,elName);
827 focusOpenerAndClose(close);
828 } else {
829 alert("Error - reference to main window is not set properly!");
830 parent.close();
831 }
832 }
833 function focusOpenerAndClose(close) { //
834 BrowseLinks.focusOpenerAndClose(close);
835 }
836 ';
837 }
838
839 /**
840 * Session data for this class can be set from outside with this method.
841 * Call after init()
842 *
843 * @param mixed[] $data Session data array
844 * @return array[] Session data and boolean which indicates that data needs to be stored in session because it's changed
845 */
846 public function processSessionData($data) {
847 $store = FALSE;
848 switch ($this->mode) {
849 case 'db':
850 if (isset($this->expandPage)) {
851 $data['expandPage'] = $this->expandPage;
852 $store = TRUE;
853 } else {
854 $this->expandPage = $data['expandPage'];
855 }
856 break;
857 case 'file':
858 case 'filedrag':
859 case 'folder':
860 if (isset($this->expandFolder)) {
861 $data['expandFolder'] = $this->expandFolder;
862 $store = TRUE;
863 } else {
864 $this->expandFolder = $data['expandFolder'];
865 }
866 break;
867 default:
868 // intentionally empty
869 }
870 return array($data, $store);
871 }
872
873 /******************************************************************
874 *
875 * Main functions
876 *
877 ******************************************************************/
878
879 /**
880 * Main entry point
881 *
882 * @return string HTML output
883 */
884 public function render() {
885 // Output the correct content according to $this->mode
886 switch ($this->mode) {
887 case 'rte':
888 return $this->main_rte();
889 case 'db':
890 return $this->main_db();
891 case 'file':
892 case 'filedrag':
893 return $this->main_file();
894 case 'folder':
895 return $this->main_folder();
896 case 'wizard':
897 return $this->main_rte(TRUE);
898 }
899 return '';
900 }
901
902 /**
903 * Rich Text Editor (RTE) link selector (MAIN function)
904 * Generates the link selector for the Rich Text Editor.
905 * Can also be used to select links for the TCEforms (see $wiz)
906 *
907 * @param bool $wiz If set, the "remove link" is not shown in the menu: Used for the "Select link" wizard which is used by the TCEforms
908 * @return string Modified content variable.
909 */
910 protected function main_rte($wiz = FALSE) {
911 // needs to be executed before doc->startPage()
912 if (in_array($this->act, array('file', 'folder'))) {
913 $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "sc_alt_file_navframe_expandtoggle"');
914 } elseif ($this->act === 'page') {
915 $this->doc->getDragDropCode('pages');
916 }
917 // Starting content:
918 $content = $this->doc->startPage('RTE link');
919 // Add the FlashMessages if any
920 $content .= $this->doc->getFlashMessages();
921
922 $content .= $this->doc->getTabMenuRaw($this->buildMenuArray($wiz, $this->getAllowedItems('page,file,folder,url,mail')));
923 // Adding the menu and header to the top of page:
924 $content .= $this->printCurrentUrl($this->curUrlInfo['info']) . '<br />';
925 // Depending on the current action we will create the actual module content for selecting a link:
926 switch ($this->act) {
927 case 'mail':
928 $content .= $this->getEmailSelectorHtml();
929 break;
930 case 'url':
931 $content .= $this->getExternalUrlSelectorHtml();
932 break;
933 case 'file':
934 case 'folder':
935 $content .= $this->getFileSelectorHtml();
936 break;
937 case 'page':
938 $content .= $this->getPageSelectorHtml();
939 break;
940 default:
941 // Call hook
942 foreach ($this->hookObjects as $hookObject) {
943 $content .= $hookObject->getTab($this->act);
944 }
945 }
946 $lang = $this->getLanguageService();
947
948 // Removing link fields if configured
949 $blindLinkFields = isset($this->RTEProperties['default.']['blindLinkFields'])
950 ? GeneralUtility::trimExplode(',', $this->RTEProperties['default.']['blindLinkFields'], TRUE)
951 : array();
952 $pBlindLinkFields = isset($this->P['params']['blindLinkFields'])
953 ? GeneralUtility::trimExplode(',', $this->P['params']['blindLinkFields'], TRUE)
954 : array();
955 $allowedFields = array_diff(array('target', 'title', 'class', 'params'), $blindLinkFields, $pBlindLinkFields);
956
957 if (in_array('params', $allowedFields, TRUE) && $this->act !== 'url') {
958 $content .= '
959 <!--
960 Selecting params for link:
961 -->
962 <form action="" name="lparamsform" id="lparamsform">
963 <table border="0" cellpadding="2" cellspacing="1" id="typo3-linkParams">
964 <tr>
965 <td style="width: 96px;">' . $lang->getLL('params', TRUE) . '</td>
966 <td><input type="text" name="lparams" class="typo3-link-input" onchange="'
967 . 'browse_links_setParams(this.value);" value="' . htmlspecialchars($this->setParams)
968 . '" /></td>
969 </tr>
970 </table>
971 </form>
972 ';
973 }
974 if (in_array('class', $allowedFields, TRUE)) {
975 $content .= '
976 <!--
977 Selecting class for link:
978 -->
979 <form action="" name="lclassform" id="lclassform">
980 <table border="0" cellpadding="2" cellspacing="1" id="typo3-linkClass">
981 <tr>
982 <td style="width: 96px;">' . $lang->getLL('class', TRUE) . '</td>
983 <td><input type="text" name="lclass" class="typo3-link-input" onchange="'
984 . 'browse_links_setClass(this.value);" value="' . htmlspecialchars($this->setClass)
985 . '" /></td>
986 </tr>
987 </table>
988 </form>
989 ';
990 }
991 if (in_array('title', $allowedFields, TRUE)) {
992 $content .= '
993 <!--
994 Selecting title for link:
995 -->
996 <form action="" name="ltitleform" id="ltitleform">
997 <table border="0" cellpadding="2" cellspacing="1" id="typo3-linkTitle">
998 <tr>
999 <td style="width: 96px;">' . $lang->getLL('title', TRUE) . '</td>
1000 <td><input type="text" name="ltitle" class="typo3-link-input" onchange="'
1001 . 'browse_links_setTitle(this.value);" value="' . htmlspecialchars($this->setTitle)
1002 . '" /></td>
1003 </tr>
1004 </table>
1005 </form>
1006 ';
1007 }
1008 // additional fields for page links
1009 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['addFields_PageLink'])
1010 && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['addFields_PageLink'])
1011 ) {
1012 $conf = array();
1013 $_params = array(
1014 'conf' => &$conf
1015 );
1016 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$this->hookName]['addFields_PageLink'] as $objRef) {
1017 $processor =& GeneralUtility::getUserObj($objRef);
1018 $content .= $processor->addFields($_params, $this);
1019 }
1020 }
1021 // Target:
1022 if ($this->act !== 'mail' && in_array('target', $allowedFields, TRUE)) {
1023 $ltarget = '
1024
1025 <!--
1026 Selecting target for link:
1027 -->
1028 <form action="" name="ltargetform" id="ltargetform">
1029 <table border="0" cellpadding="2" cellspacing="1" id="typo3-linkTarget">
1030 <tr>
1031 <td>' . $lang->getLL('target', TRUE) . ':</td>
1032 <td><input type="text" name="ltarget" onchange="browse_links_setTarget(this.value);" value="'
1033 . htmlspecialchars($this->setTarget) . '"' . $this->doc->formWidth(10) . ' /></td>
1034 <td>
1035 <select name="ltarget_type" onchange="browse_links_setTarget('
1036 . 'this.options[this.selectedIndex].value);document.ltargetform.ltarget.value='
1037 . 'this.options[this.selectedIndex].value;this.selectedIndex=0;">
1038 <option></option>
1039 <option value="_top">' . $lang->getLL('top', TRUE) . '</option>
1040 <option value="_blank">' . $lang->getLL('newWindow', TRUE) . '</option>
1041 </select>
1042 </td>
1043 <td>';
1044 if (($this->curUrlInfo['act'] === 'page' || $this->curUrlInfo['act'] === 'file' || $this->curUrlInfo['act'] === 'folder')
1045 && $this->curUrlArray['href'] && $this->curUrlInfo['act'] === $this->act
1046 ) {
1047 $ltarget .= '
1048 <input class="btn btn-default" type="submit" value="' . $lang->getLL('update', TRUE)
1049 . '" onclick="return link_current();" />';
1050 }
1051 $ltarget .= ' </td>
1052 </tr>
1053 </table>
1054 </form>';
1055 // Add "target selector" box to content:
1056 $content .= $ltarget;
1057 // Add some space
1058 $content .= '<br /><br />';
1059 }
1060 // End page, return content:
1061 $content .= $this->doc->endPage();
1062 $content = $this->doc->insertStylesAndJS($content);
1063 return $content;
1064 }
1065
1066 /**
1067 * Get the allowed items or tabs
1068 *
1069 * @param string $items initial list of possible items
1070 * @return array the allowed items
1071 */
1072 public function getAllowedItems($items) {
1073 $allowedItems = explode(',', $items);
1074 // Call hook for extra options
1075 foreach ($this->hookObjects as $hookObject) {
1076 $allowedItems = $hookObject->addAllowedItems($allowedItems);
1077 }
1078
1079 // Initializing the action value, possibly removing blinded values etc:
1080 $blindLinkOptions = isset($this->RTEProperties['default.']['blindLinkOptions'])
1081 ? GeneralUtility::trimExplode(',', $this->RTEProperties['default.']['blindLinkOptions'], TRUE)
1082 : array();
1083 $pBlindLinkOptions = isset($this->P['params']['blindLinkOptions'])
1084 ? GeneralUtility::trimExplode(',', $this->P['params']['blindLinkOptions'])
1085 : array();
1086 $allowedItems = array_diff($allowedItems, $blindLinkOptions, $pBlindLinkOptions);
1087
1088 reset($allowedItems);
1089 if (!in_array($this->act, $allowedItems)) {
1090 $this->act = current($allowedItems);
1091 }
1092 return $allowedItems;
1093 }
1094
1095 /**
1096 * Returns an array definition of the top menu
1097 *
1098 * @param bool $wiz
1099 * @param array $allowedItems
1100 * @return mixed[][]
1101 */
1102 protected function buildMenuArray($wiz, $allowedItems) {
1103 $lang = $this->getLanguageService();
1104
1105 $menuDef = array();
1106 if (!$wiz && $this->curUrlArray['href']) {
1107 $menuDef['removeLink']['isActive'] = $this->act === 'removeLink';
1108 $menuDef['removeLink']['label'] = $lang->getLL('removeLink', TRUE);
1109 $menuDef['removeLink']['url'] = '#';
1110 $menuDef['removeLink']['addParams'] = 'onclick="plugin.unLink();return false;"';
1111 }
1112 if (in_array('page', $allowedItems, TRUE)) {
1113 $menuDef['page']['isActive'] = $this->act === 'page';
1114 $menuDef['page']['label'] = $lang->getLL('page', TRUE);
1115 $menuDef['page']['url'] = '#';
1116 $menuDef['page']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue('?act=page&mode=' . $this->mode . '&bparams=' . $this->bparams) . ');return false;"';
1117 }
1118 if (in_array('file', $allowedItems, TRUE)) {
1119 $menuDef['file']['isActive'] = $this->act === 'file';
1120 $menuDef['file']['label'] = $lang->getLL('file', TRUE);
1121 $menuDef['file']['url'] = '#';
1122 $menuDef['file']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue('?act=file&mode=' . $this->mode . '&bparams=' . $this->bparams) . ');return false;"';
1123 }
1124 if (in_array('folder', $allowedItems, TRUE)) {
1125 $menuDef['folder']['isActive'] = $this->act === 'folder';
1126 $menuDef['folder']['label'] = $lang->getLL('folder', TRUE);
1127 $menuDef['folder']['url'] = '#';
1128 $menuDef['folder']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue('?act=folder&mode=' . $this->mode . '&bparams=' . $this->bparams) . ');return false;"';
1129 }
1130 if (in_array('url', $allowedItems, TRUE)) {
1131 $menuDef['url']['isActive'] = $this->act === 'url';
1132 $menuDef['url']['label'] = $lang->getLL('extUrl', TRUE);
1133 $menuDef['url']['url'] = '#';
1134 $menuDef['url']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue('?act=url&mode=' . $this->mode . '&bparams=' . $this->bparams) . ');return false;"';
1135 }
1136 if (in_array('mail', $allowedItems, TRUE)) {
1137 $menuDef['mail']['isActive'] = $this->act === 'mail';
1138 $menuDef['mail']['label'] = $lang->getLL('email', TRUE);
1139 $menuDef['mail']['url'] = '#';
1140 $menuDef['mail']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue('?act=mail&mode=' . $this->mode . '&bparams=' . $this->bparams) . ');return false;"';
1141 }
1142 // Call hook for extra options
1143 foreach ($this->hookObjects as $hookObject) {
1144 $menuDef = $hookObject->modifyMenuDefinition($menuDef);
1145 }
1146 return $menuDef;
1147 }
1148
1149 /**
1150 * Returns HTML of the email link from
1151 *
1152 * @return string
1153 */
1154 protected function getEmailSelectorHtml() {
1155 $lang = $this->getLanguageService();
1156 $extUrl = '
1157 <!--
1158 Enter mail address:
1159 -->
1160 <form action="" name="lurlform" id="lurlform">
1161 <table border="0" cellpadding="2" cellspacing="1" id="typo3-linkMail">
1162 <tr>
1163 <td style="width: 96px;">
1164 ' . $lang->getLL('emailAddress', TRUE) . ':
1165 </td>
1166 <td>
1167 <input type="text" name="lemail"' . $this->doc->formWidth(20) . ' value="'
1168 . htmlspecialchars(($this->curUrlInfo['act'] === 'mail' ? $this->curUrlInfo['info'] : ''))
1169 . '" />
1170 <input class="btn btn-default" type="submit" value="' . $lang->getLL('setLink', TRUE)
1171 . '" onclick="browse_links_setTarget(\'\');browse_links_setValue(\'mailto:\'+'
1172 . 'document.lurlform.lemail.value); return link_current();" />
1173 </td>
1174 </tr>
1175 </table>
1176 </form>';
1177 return $extUrl;
1178 }
1179
1180 /**
1181 * Returns HTML of the external url link from
1182 *
1183 * @return string
1184 */
1185 protected function getExternalUrlSelectorHtml() {
1186 $extUrl = '
1187
1188 <!--
1189 Enter External URL:
1190 -->
1191 <form action="" name="lurlform" id="lurlform">
1192 <table border="0" cellpadding="2" cellspacing="1" id="typo3-linkURL">
1193 <tr>
1194 <td style="width: 96px;">URL:</td>
1195 <td><input type="text" name="lurl"' . $this->doc->formWidth(30) . ' value="'
1196 . htmlspecialchars(($this->curUrlInfo['act'] === 'url' ? $this->curUrlInfo['info'] : 'http://'))
1197 . '" /> ' . '<input class="btn btn-default" type="submit" value="' . $this->getLanguageService()->getLL('setLink', TRUE)
1198 . '" onclick="browse_links_setValue(document.lurlform.lurl.value); return link_current();" /></td>
1199 </tr>
1200 </table>
1201 </form>';
1202 return $extUrl;
1203 }
1204
1205 /**
1206 * Returns HTML of the file/folder link selector
1207 *
1208 * @param string $treeClassName
1209 * @return string
1210 */
1211 protected function getFileSelectorHtml($treeClassName = ElementBrowserFolderTreeView::class) {
1212 /** @var ElementBrowserFolderTreeView $folderTree */
1213 $folderTree = GeneralUtility::makeInstance($treeClassName);
1214 $folderTree->setElementBrowser($this);
1215 $folderTree->thisScript = $this->thisScript;
1216 $tree = $folderTree->getBrowsableTree();
1217 $backendUser = $this->getBackendUser();
1218 if ($this->curUrlInfo['value'] && $this->curUrlInfo['act'] === $this->act) {
1219 $cmpPath = $this->curUrlInfo['value'];
1220 if (!isset($this->expandFolder)) {
1221 $this->expandFolder = $cmpPath;
1222 }
1223 }
1224 // Create upload/create folder forms, if a path is given
1225 $selectedFolder = FALSE;
1226 if ($this->expandFolder) {
1227 $fileOrFolderObject = NULL;
1228 try {
1229 $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->expandFolder);
1230 } catch (\Exception $e) {
1231 // No path is selected
1232 }
1233
1234 if ($fileOrFolderObject instanceof Folder) {
1235 // It's a folder
1236 $selectedFolder = $fileOrFolderObject;
1237 } elseif ($fileOrFolderObject instanceof FileInterface) {
1238 // It's a file
1239 try {
1240 $selectedFolder = $fileOrFolderObject->getParentFolder();
1241 } catch (\Exception $e) {
1242 // Accessing the parent folder failed for some reason. e.g. permissions
1243 }
1244 }
1245 }
1246 // If no folder is selected, get the user's default upload folder
1247 if (!$selectedFolder) {
1248 try {
1249 $selectedFolder = $backendUser->getDefaultUploadFolder();
1250 } catch (\Exception $e) {
1251 // The configured default user folder does not exist
1252 }
1253 }
1254 // Build the file upload and folder creation form
1255 $uploadForm = '';
1256 $createFolder = '';
1257 $content = '';
1258 if ($selectedFolder) {
1259 $uploadForm = ($this->act === 'file') ? $this->uploadForm($selectedFolder) : '';
1260 $createFolder = $this->createFolder($selectedFolder);
1261 }
1262 // Insert the upload form on top, if so configured
1263 if ($backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
1264 $content .= $uploadForm;
1265 }
1266
1267 // Render the filelist if there is a folder selected
1268 $files = '';
1269 if ($selectedFolder) {
1270 $allowedExtensions = isset($this->P['params']['allowedExtensions']) ? $this->P['params']['allowedExtensions'] : '';
1271 $files = $this->expandFolder($selectedFolder, $allowedExtensions);
1272 }
1273 // Create folder tree:
1274 $content .= '
1275 <!--
1276 Wrapper table for folder tree / file/folder list:
1277 -->
1278 <table border="0" cellpadding="0" cellspacing="0" id="typo3-linkFiles">
1279 <tr>
1280 <td class="c-wCell" valign="top">'
1281 . $this->barheader(($this->getLanguageService()->getLL('folderTree') . ':')) . $tree . '</td>
1282 <td class="c-wCell" valign="top">' . $files . '</td>
1283 </tr>
1284 </table>
1285 ';
1286 // Adding create folder + upload form if applicable
1287 if (!$backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
1288 $content .= $uploadForm;
1289 }
1290 $content .= '<br />' . $createFolder . '<br />';
1291 return $content;
1292 }
1293
1294 /**
1295 * Returns HTML of the page link selector
1296 *
1297 * @param string $treeClassName name of the class used for page tree rendering
1298 * @return string
1299 */
1300 protected function getPageSelectorHtml($treeClassName = ElementBrowserPageTreeView::class) {
1301 $backendUser = $this->getBackendUser();
1302
1303 /** @var ElementBrowserPageTreeView $pageTree */
1304 $pageTree = GeneralUtility::makeInstance($treeClassName);
1305 $pageTree->setElementBrowser($this);
1306 $pageTree->thisScript = $this->thisScript;
1307 $pageTree->ext_showPageId = (bool)$backendUser->getTSConfigVal('options.pageTree.showPageIdWithTitle');
1308 $pageTree->ext_showNavTitle = (bool)$backendUser->getTSConfigVal('options.pageTree.showNavTitle');
1309 $pageTree->addField('nav_title');
1310 $tree = $pageTree->getBrowsableTree();
1311 $cElements = $this->expandPage();
1312 $dbmount = $this->getTemporaryTreeMountCancelNotice();
1313 $content = '
1314
1315 <!--
1316 Wrapper table for page tree / record list:
1317 -->
1318 <table border="0" cellpadding="0" cellspacing="0" id="typo3-linkPages">
1319 <tr>
1320 <td class="c-wCell" valign="top">'
1321 . $this->barheader(($this->getLanguageService()->getLL('pageTree') . ':'))
1322 . $dbmount
1323 . $tree . '</td>
1324 <td class="c-wCell" valign="top">' . $cElements . '</td>
1325 </tr>
1326 </table>
1327 ';
1328 return $content;
1329 }
1330
1331 /**
1332 * TYPO3 Element Browser: Showing a page tree and allows you to browse for records
1333 *
1334 * @return string HTML content for the module
1335 */
1336 protected function main_db() {
1337 // Starting content:
1338 $content = $this->doc->startPage('TBE record selector');
1339 // Init variable:
1340 $pArr = explode('|', $this->bparams);
1341 $tables = $pArr[3];
1342 $backendUser = $this->getBackendUser();
1343
1344 // Making the browsable pagetree:
1345 /** @var \TYPO3\CMS\Recordlist\Tree\View\ElementBrowserPageTreeView $pageTree */
1346 $pageTree = GeneralUtility::makeInstance(\TYPO3\CMS\Recordlist\Tree\View\ElementBrowserPageTreeView::class);
1347 $pageTree->setElementBrowser($this);
1348 $pageTree->thisScript = $this->thisScript;
1349 $pageTree->ext_pArrPages = $tables === 'pages';
1350 $pageTree->ext_showNavTitle = (bool)$backendUser->getTSConfigVal('options.pageTree.showNavTitle');
1351 $pageTree->ext_showPageId = (bool)$backendUser->getTSConfigVal('options.pageTree.showPageIdWithTitle');
1352 $pageTree->addField('nav_title');
1353
1354 $withTree = TRUE;
1355 if (($tables !== '') && ($tables !== '*')) {
1356 $tablesArr = GeneralUtility::trimExplode(',', $tables, TRUE);
1357 $onlyRootLevel = TRUE;
1358 foreach ($tablesArr as $currentTable) {
1359 $tableTca = $GLOBALS['TCA'][$currentTable];
1360 if (isset($tableTca)) {
1361 if (!isset($tableTca['ctrl']['rootLevel']) || ((int)$tableTca['ctrl']['rootLevel']) != 1) {
1362 $onlyRootLevel = FALSE;
1363 }
1364 }
1365 }
1366 if ($onlyRootLevel) {
1367 $withTree = FALSE;
1368 // page to work on will be root
1369 $this->expandPage = 0;
1370 }
1371 }
1372
1373 $tree = $pageTree->getBrowsableTree();
1374 // Making the list of elements, if applicable:
1375 $cElements = $this->TBE_expandPage($tables);
1376 // Putting the things together, side by side:
1377 $content .= '
1378
1379 <!--
1380 Wrapper table for page tree / record list:
1381 -->
1382 <table border="0" cellpadding="0" cellspacing="0" id="typo3-EBrecords">
1383 <tr>';
1384 if ($withTree) {
1385 $content .= '<td class="c-wCell" valign="top">'
1386 . $this->barheader(($this->getLanguageService()->getLL('pageTree') . ':'))
1387 . $this->getTemporaryTreeMountCancelNotice()
1388 . $tree . '</td>';
1389 }
1390 $content .= '<td class="c-wCell" valign="top">' . $cElements . '</td>
1391 </tr>
1392 </table>
1393 ';
1394 // Add some space
1395 $content .= '<br /><br />';
1396 // End page, return content:
1397 $content .= $this->doc->endPage();
1398 $content = $this->doc->insertStylesAndJS($content);
1399 return $content;
1400 }
1401
1402 /**
1403 * TYPO3 Element Browser: Showing a folder tree, allowing you to browse for files.
1404 *
1405 * @return string HTML content for the module
1406 */
1407 protected function main_file() {
1408 // include JS files and set prefs for foldertree
1409 $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "sc_alt_file_navframe_expandtoggle"');
1410 // Starting content:
1411 $content = $this->doc->startPage('TBE file selector');
1412 // Add the FlashMessages if any
1413 $content .= $this->doc->getFlashMessages();
1414 // Init variable:
1415 $pArr = explode('|', $this->bparams);
1416 // The key number 3 of the pArr contains the "allowed" string. Disallowed is not passed to
1417 // the element browser at all but only filtered out in TCEMain afterwards
1418 $allowed = $pArr[3];
1419 if ($allowed !== 'sys_file' && $allowed !== '*' && !empty($allowed)) {
1420 $allowedFileExtensions = $allowed;
1421 }
1422 $backendUser = $this->getBackendUser();
1423
1424 if (isset($allowedFileExtensions)) {
1425 // Create new filter object
1426 $filterObject = GeneralUtility::makeInstance(FileExtensionFilter::class);
1427 $filterObject->setAllowedFileExtensions($allowedFileExtensions);
1428 // Set file extension filters on all storages
1429 $storages = $backendUser->getFileStorages();
1430 /** @var $storage \TYPO3\CMS\Core\Resource\ResourceStorage */
1431 foreach ($storages as $storage) {
1432 $storage->addFileAndFolderNameFilter(array($filterObject, 'filterFileList'));
1433 }
1434 }
1435 // Create upload/create folder forms, if a path is given
1436 $this->selectedFolder = FALSE;
1437 if ($this->expandFolder) {
1438 $fileOrFolderObject = NULL;
1439
1440 // Try to fetch the folder the user had open the last time he browsed files
1441 // Fallback to the default folder in case the last used folder is not existing
1442 try {
1443 $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->expandFolder);
1444 } catch (Exception $accessException) {
1445 // We're just catching the exception here, nothing to be done if folder does not exist or is not accessible.
1446 }
1447
1448 if ($fileOrFolderObject instanceof Folder) {
1449 // It's a folder
1450 $this->selectedFolder = $fileOrFolderObject;
1451 } elseif ($fileOrFolderObject instanceof FileInterface) {
1452 // It's a file
1453 $this->selectedFolder = $fileOrFolderObject->getParentFolder();
1454 }
1455 }
1456 // Or get the user's default upload folder
1457 if (!$this->selectedFolder) {
1458 try {
1459 $this->selectedFolder = $backendUser->getDefaultUploadFolder();
1460 } catch (\Exception $e) {
1461 // The configured default user folder does not exist
1462 }
1463 }
1464 // Build the file upload and folder creation form
1465 $uploadForm = '';
1466 $createFolder = '';
1467 if ($this->selectedFolder) {
1468 $uploadForm = $this->uploadForm($this->selectedFolder);
1469 $createFolder = $this->createFolder($this->selectedFolder);
1470 }
1471 // Insert the upload form on top, if so configured
1472 if ($backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
1473 $content .= $uploadForm;
1474 }
1475 // Getting flag for showing/not showing thumbnails:
1476 $noThumbs = $backendUser->getTSConfigVal('options.noThumbsInEB');
1477 $_MOD_SETTINGS = array();
1478 if (!$noThumbs) {
1479 // MENU-ITEMS, fetching the setting for thumbnails from File>List module:
1480 $_MOD_MENU = array('displayThumbs' => '');
1481 $_MCONF['name'] = 'file_list';
1482 $_MOD_SETTINGS = BackendUtility::getModuleData($_MOD_MENU, GeneralUtility::_GP('SET'), $_MCONF['name']);
1483 }
1484 $noThumbs = $noThumbs ?: !$_MOD_SETTINGS['displayThumbs'];
1485 // Create folder tree:
1486 /** @var ElementBrowserFolderTreeView $folderTree */
1487 $folderTree = GeneralUtility::makeInstance(ElementBrowserFolderTreeView::class);
1488 $folderTree->setElementBrowser($this);
1489 $folderTree->thisScript = $this->thisScript;
1490 $folderTree->ext_noTempRecyclerDirs = $this->mode === 'filedrag';
1491 $tree = $folderTree->getBrowsableTree();
1492 if ($this->selectedFolder) {
1493 if ($this->mode === 'filedrag') {
1494 $files = $this->TBE_dragNDrop($this->selectedFolder, $pArr[3]);
1495 } else {
1496 $files = $this->TBE_expandFolder($this->selectedFolder, $pArr[3], $noThumbs);
1497 }
1498 } else {
1499 $files = '';
1500 }
1501
1502 // Putting the parts together, side by side:
1503 $content .= '
1504
1505 <!--
1506 Wrapper table for folder tree / filelist:
1507 -->
1508 <table border="0" cellpadding="0" cellspacing="0" id="typo3-EBfiles">
1509 <tr>
1510 <td class="c-wCell" valign="top">' . $this->barheader(($this->getLanguageService()->getLL('folderTree') . ':'))
1511 . $tree . '</td>
1512 <td class="c-wCell" valign="top">' . $files . '</td>
1513 </tr>
1514 </table>
1515 ';
1516
1517
1518 // Adding create folder + upload forms if applicable:
1519 if (!$backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
1520 $content .= $uploadForm;
1521 }
1522 $content .= $createFolder;
1523 // Add some space
1524 $content .= '<br /><br />';
1525 // Setup indexed elements:
1526 $this->doc->JScode .= $this->doc->wrapScriptTags('
1527 require(["TYPO3/CMS/Backend/BrowseLinks"], function(BrowseLinks) {
1528 BrowseLinks.addElements(' . json_encode($this->elements) . ');
1529 });');
1530 // Ending page, returning content:
1531 $content .= $this->doc->endPage();
1532 $content = $this->doc->insertStylesAndJS($content);
1533 return $content;
1534 }
1535
1536 /**
1537 * TYPO3 Element Browser: Showing a folder tree, allowing you to browse for folders.
1538 *
1539 * @return string HTML content for the module
1540 */
1541 protected function main_folder() {
1542 // include JS files
1543 // Setting prefs for foldertree
1544 $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "sc_alt_file_navframe_expandtoggle";');
1545 // Starting content:
1546 $content = $this->doc->startPage('TBE folder selector');
1547 // Add the FlashMessages if any
1548 $content .= $this->doc->getFlashMessages();
1549 // Init variable:
1550 $parameters = explode('|', $this->bparams);
1551 if ($this->expandFolder) {
1552 $this->selectedFolder = ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($this->expandFolder);
1553 }
1554 if ($this->selectedFolder) {
1555 $createFolder = $this->createFolder($this->selectedFolder);
1556 } else {
1557 $createFolder = '';
1558 }
1559 // Create folder tree:
1560 /** @var ElementBrowserFolderTreeView $folderTree */
1561 $folderTree = GeneralUtility::makeInstance(ElementBrowserFolderTreeView::class);
1562 $folderTree->setElementBrowser($this);
1563 $folderTree->thisScript = $this->thisScript;
1564 $folderTree->ext_noTempRecyclerDirs = $this->mode === 'filedrag';
1565 $tree = $folderTree->getBrowsableTree();
1566 $folders = '';
1567 if ($this->selectedFolder) {
1568 if ($this->mode === 'filedrag') {
1569 $folders = $this->TBE_dragNDrop($this->selectedFolder, $parameters[3]);
1570 } else {
1571 $folders = $this->TBE_expandSubFolders($this->selectedFolder);
1572 }
1573 }
1574 // Putting the parts together, side by side:
1575 $content .= '
1576
1577 <!--
1578 Wrapper table for folder tree / folder list:
1579 -->
1580 <table border="0" cellpadding="0" cellspacing="0" id="typo3-EBfiles">
1581 <tr>
1582 <td class="c-wCell" valign="top">' . $this->barheader(($this->getLanguageService()->getLL('folderTree') . ':'))
1583 . $tree . '</td>
1584 <td class="c-wCell" valign="top">' . $folders . '</td>
1585 </tr>
1586 </table>
1587 ';
1588 // Adding create folder if applicable:
1589 $content .= $createFolder;
1590 // Add some space
1591 $content .= '<br /><br />';
1592 // Ending page, returning content:
1593 $content .= $this->doc->endPage();
1594 $content = $this->doc->insertStylesAndJS($content);
1595 return $content;
1596 }
1597
1598 /******************************************************************
1599 *
1600 * Record listing
1601 *
1602 ******************************************************************/
1603 /**
1604 * For RTE: This displays all content elements on a page and lets you create a link to the element.
1605 *
1606 * @return string HTML output. Returns content only if the ->expandPage value is set (pointing to a page uid to show tt_content records from ...)
1607 */
1608 public function expandPage() {
1609 $out = '';
1610 // Set page id (if any) to expand
1611 $expPageId = $this->expandPage;
1612 // If there is an anchor value (content element reference) in the element reference, then force an ID to expand:
1613 if (!$this->expandPage && $this->curUrlInfo['cElement']) {
1614 // Set to the current link page id.
1615 $expPageId = $this->curUrlInfo['pageid'];
1616 }
1617 // Draw the record list IF there is a page id to expand:
1618 if (
1619 $expPageId
1620 && MathUtility::canBeInterpretedAsInteger($expPageId)
1621 && $this->getBackendUser()->isInWebMount($expPageId)
1622 ) {
1623 // Set header:
1624 $out .= $this->barheader($this->getLanguageService()->getLL('contentElements') . ':');
1625 // Create header for listing, showing the page title/icon:
1626 $mainPageRec = BackendUtility::getRecordWSOL('pages', $expPageId);
1627 $db = $this->getDatabaseConnection();
1628 $out .= '
1629 <ul class="list-tree list-tree-root list-tree-root-clean">
1630 <li class="list-tree-control-open">
1631 <span class="list-tree-group">
1632 <span class="list-tree-icon">' . $this->iconFactory->getIconForRecord('pages', $mainPageRec, Icon::SIZE_SMALL)->render() . '</span>
1633 <span class="list-tree-title">' . htmlspecialchars(BackendUtility::getRecordTitle('pages', $mainPageRec, TRUE)) . '</span>
1634 </span>
1635 <ul>
1636 ';
1637
1638 // Look up tt_content elements from the expanded page:
1639 $res = $db->exec_SELECTquery(
1640 'uid,header,hidden,starttime,endtime,fe_group,CType,colPos,bodytext',
1641 'tt_content',
1642 'pid=' . (int)$expPageId . BackendUtility::deleteClause('tt_content')
1643 . BackendUtility::versioningPlaceholderClause('tt_content'),
1644 '',
1645 'colPos,sorting'
1646 );
1647 // Traverse list of records:
1648 $c = 0;
1649 while ($row = $db->sql_fetch_assoc($res)) {
1650 $c++;
1651 $icon = $this->iconFactory->getIconForRecord('tt_content', $row, Icon::SIZE_SMALL)->render();
1652 $selected = '';
1653 if ($this->curUrlInfo['act'] == 'page' && $this->curUrlInfo['cElement'] == $row['uid']) {
1654 $selected = ' class="active"';
1655 }
1656 // Putting list element HTML together:
1657 $out .= '
1658 <li' . $selected . '>
1659 <span class="list-tree-group">
1660 <span class="list-tree-icon">
1661 ' . $icon . '
1662 </span>
1663 <span class="list-tree-title">
1664 <a href="#" onclick="return link_typo3Page(\'' . $expPageId . '\',\'#' . $row['uid'] . '\');">
1665 ' . htmlspecialchars(BackendUtility::getRecordTitle('tt_content', $row, TRUE)) . '
1666 </a>
1667 </span>
1668 </span>
1669 </li>
1670 ';
1671 }
1672 $out .= '
1673 </ul>
1674 </li>
1675 </ul>
1676 ';
1677 }
1678 return $out;
1679 }
1680
1681 /**
1682 * For TYPO3 Element Browser: This lists all content elements from the given list of tables
1683 *
1684 * @param string $tables Comma separated list of tables. Set to "*" if you want all tables.
1685 * @return string HTML output.
1686 */
1687 public function TBE_expandPage($tables) {
1688 $backendUser = $this->getBackendUser();
1689 if (!MathUtility::canBeInterpretedAsInteger($this->expandPage)
1690 || $this->expandPage < 0
1691 || !$backendUser->isInWebMount($this->expandPage)
1692 ) {
1693 return '';
1694 }
1695 // Set array with table names to list:
1696 if (trim($tables) === '*') {
1697 $tablesArr = array_keys($GLOBALS['TCA']);
1698 } else {
1699 $tablesArr = GeneralUtility::trimExplode(',', $tables, TRUE);
1700 }
1701 reset($tablesArr);
1702 // Headline for selecting records:
1703 $out = $this->barheader($this->getLanguageService()->getLL('selectRecords') . ':');
1704 // Create the header, showing the current page for which the listing is.
1705 // Includes link to the page itself, if pages are amount allowed tables.
1706 $titleLen = (int)$backendUser->uc['titleLen'];
1707 $mainPageRec = BackendUtility::getRecordWSOL('pages', $this->expandPage);
1708 $ATag = '';
1709 $ATag_e = '';
1710 $ATag2 = '';
1711 $picon = '';
1712 if (is_array($mainPageRec)) {
1713 $picon = $this->iconFactory->getIconForRecord('pages', $mainPageRec, Icon::SIZE_SMALL)->render();
1714 if (in_array('pages', $tablesArr)) {
1715 $ATag = '<a href="#" onclick="return insertElement(\'pages\', \'' . $mainPageRec['uid'] . '\', \'db\', '
1716 . GeneralUtility::quoteJSvalue($mainPageRec['title']) . ', \'\', \'\', \'\',\'\',1);">';
1717 $ATag2 = '<a href="#" onclick="return insertElement(\'pages\', \'' . $mainPageRec['uid'] . '\', \'db\', '
1718 . GeneralUtility::quoteJSvalue($mainPageRec['title']) . ', \'\', \'\', \'\',\'\',0);">';
1719 $ATag_e = '</a>';
1720 }
1721 }
1722 $pBicon = $ATag2 ? $this->iconFactory->getIcon('actions-edit-add', Icon::SIZE_SMALL)->render() : '';
1723 $pText = htmlspecialchars(GeneralUtility::fixed_lgd_cs($mainPageRec['title'], $titleLen));
1724 $out .= $picon . $ATag2 . $pBicon . $ATag_e . $ATag . $pText . $ATag_e . '<br />';
1725 // Initialize the record listing:
1726 $id = $this->expandPage;
1727 $pointer = MathUtility::forceIntegerInRange($this->pointer, 0, 100000);
1728 $perms_clause = $backendUser->getPagePermsClause(1);
1729 $pageInfo = BackendUtility::readPageAccess($id, $perms_clause);
1730 // Generate the record list:
1731 /** @var $dbList ElementBrowserRecordList */
1732 if (is_object($this->recordList)) {
1733 $dbList = $this->recordList;
1734 } else {
1735 $dbList = GeneralUtility::makeInstance(ElementBrowserRecordList::class);
1736 }
1737 $dbList->setElementBrowser($this);
1738 $dbList->thisScript = $this->thisScript;
1739 $dbList->thumbs = 0;
1740 $dbList->localizationView = 1;
1741 $dbList->setIsEditable(FALSE);
1742 $dbList->calcPerms = $backendUser->calcPerms($pageInfo);
1743 $dbList->noControlPanels = 1;
1744 $dbList->clickMenuEnabled = 0;
1745 $dbList->tableList = implode(',', $tablesArr);
1746 $pArr = explode('|', $this->bparams);
1747 // a string like "data[pages][79][storage_pid]"
1748 $fieldPointerString = $pArr[0];
1749 // parts like: data, pages], 79], storage_pid]
1750 $fieldPointerParts = explode('[', $fieldPointerString);
1751 $relatingTableName = substr($fieldPointerParts[1], 0, -1);
1752 $relatingFieldName = substr($fieldPointerParts[3], 0, -1);
1753 if ($relatingTableName && $relatingFieldName) {
1754 $dbList->setRelatingTableAndField($relatingTableName, $relatingFieldName);
1755 }
1756 $dbList->start($id, GeneralUtility::_GP('table'), $pointer, GeneralUtility::_GP('search_field'),
1757 GeneralUtility::_GP('search_levels'), GeneralUtility::_GP('showLimit')
1758 );
1759 $dbList->setDispFields();
1760 $dbList->generateList();
1761 $out .= $dbList->getSearchBox();
1762 $out .= "<script>document.getElementById('db_list-searchbox-toolbar').style.display = 'block';document.getElementById('db_list-searchbox-toolbar').style.position = 'relative';</script>";
1763
1764 // Add the HTML for the record list to output variable:
1765 $out .= $dbList->HTMLcode;
1766 // Add support for fieldselectbox in singleTableMode
1767 if ($dbList->table) {
1768 $out .= $dbList->fieldSelectBox($dbList->table);
1769 }
1770
1771 // Return accumulated content:
1772 return $out;
1773 }
1774
1775 /**
1776 * Render list of folders inside a folder.
1777 *
1778 * @param Folder $folder Folder
1779 * @return string HTML output
1780 */
1781 public function TBE_expandSubFolders(Folder $folder) {
1782 $content = '';
1783 if ($folder->checkActionPermission('read')) {
1784 $content .= $this->folderList($folder);
1785 }
1786 // Return accumulated content for folderlisting:
1787 return $content;
1788 }
1789
1790 /******************************************************************
1791 *
1792 * Filelisting
1793 *
1794 ******************************************************************/
1795 /**
1796 * For RTE: This displays all files from folder. No thumbnails shown
1797 *
1798 * @param Folder $folder The folder path to expand
1799 * @param string $extensionList List of file extensions to show
1800 * @return string HTML output
1801 */
1802 public function expandFolder(Folder $folder, $extensionList = '') {
1803 if (!$folder->checkActionPermission('read')) {
1804 return '';
1805 }
1806 $lang = $this->getLanguageService();
1807 $renderFolders = $this->act === 'folder';
1808 // Create header for file/folder listing:
1809 if ($renderFolders) {
1810 $out = $this->barheader($lang->getLL('folders') . ':');
1811 } else {
1812 $out = $this->barheader($lang->getLL('files') . ':');
1813 }
1814 // Prepare current path value for comparison (showing red arrow)
1815 $currentIdentifier = '';
1816 if ($this->curUrlInfo['value']) {
1817 $currentIdentifier = $this->curUrlInfo['info'];
1818 }
1819 // Create header element; The folder from which files are listed.
1820 $titleLen = (int)$this->getBackendUser()->uc['titleLen'];
1821 $folderIcon = $this->iconFactory->getIconForResource($folder, Icon::SIZE_SMALL)->render();
1822 $folderIcon .= htmlspecialchars(GeneralUtility::fixed_lgd_cs($folder->getIdentifier(), $titleLen));
1823 $selected = '';
1824 if ($this->curUrlInfo['act'] == 'folder' && $currentIdentifier == $folder->getCombinedIdentifier()) {
1825 $selected = ' class="bg-success"';
1826 }
1827 $out .= '
1828 <a href="#"' . $selected . ' title="' . htmlspecialchars($folder->getIdentifier()) . '" onclick="return link_folder(\'file:' . $folder->getCombinedIdentifier() . '\');">
1829 ' . $folderIcon . '
1830 </a>
1831 ';
1832 // Get files from the folder:
1833 if ($renderFolders) {
1834 $items = $folder->getSubfolders();
1835 } else {
1836 $items = $this->getFilesInFolder($folder, $extensionList);
1837 }
1838 $c = 0;
1839
1840 if (!empty($items)) {
1841 $out .= '<ul class="list-tree list-tree-root">';
1842 foreach ($items as $fileOrFolderObject) {
1843 $c++;
1844 if ($renderFolders) {
1845 $fileIdentifier = $fileOrFolderObject->getCombinedIdentifier();
1846 $overlay = NULL;
1847 if ($fileOrFolderObject instanceof InaccessibleFolder) {
1848 $overlay = array('status-overlay-locked' => array());
1849 }
1850 $icon = '<span title="' . htmlspecialchars($fileOrFolderObject->getName()) . '">'
1851 . $this->iconFactory->getIcon('apps-filetree-folder-default', Icon::SIZE_SMALL, $overlay)->render()
1852 . '</span>';
1853 $itemUid = 'file:' . $fileIdentifier;
1854 } else {
1855 $fileIdentifier = $fileOrFolderObject->getUid();
1856 // Get size and icon:
1857 $size = ' (' . GeneralUtility::formatSize($fileOrFolderObject->getSize()) . 'bytes)';
1858 $icon = '<span title="' . htmlspecialchars($fileOrFolderObject->getName() . $size) . '">' . $this->iconFactory->getIconForResource($fileOrFolderObject, Icon::SIZE_SMALL)->render() . '</span>';
1859 $itemUid = 'file:' . $fileIdentifier;
1860 }
1861 $selected = '';
1862 if (($this->curUrlInfo['act'] == 'file' || $this->curUrlInfo['act'] == 'folder')
1863 && $currentIdentifier == $fileIdentifier
1864 ) {
1865 $selected = ' class="active"';
1866 }
1867 // Put it all together for the file element:
1868 $out .=
1869 '<li' . $selected . '>
1870 <a href="#"title="' . htmlspecialchars($fileOrFolderObject->getName()) . '" onclick="return link_folder(\'' . $itemUid . '\');">
1871 ' . $icon . '
1872 ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileOrFolderObject->getName(), $titleLen)) . '
1873 </a>
1874 </li>';
1875 }
1876 $out .= '</ul>';
1877 }
1878 return $out;
1879 }
1880
1881 /**
1882 * For TYPO3 Element Browser: Expand folder of files.
1883 *
1884 * @param Folder $folder The folder path to expand
1885 * @param string $extensionList List of fileextensions to show
1886 * @param bool $noThumbs Whether to show thumbnails or not. If set, no thumbnails are shown.
1887 * @return string HTML output
1888 */
1889 public function TBE_expandFolder(Folder $folder, $extensionList = '', $noThumbs = FALSE) {
1890 if (!$folder->checkActionPermission('read')) {
1891 return '';
1892 }
1893 $extensionList = $extensionList == '*' ? '' : $extensionList;
1894 if ($this->searchWord !== '') {
1895 $files = $this->fileRepository->searchByName($folder, $this->searchWord);
1896 } else {
1897 $files = $this->getFilesInFolder($folder, $extensionList);
1898 }
1899
1900 return $this->fileList($files, $folder, $noThumbs);
1901 }
1902
1903 /**
1904 * Render list of files.
1905 *
1906 * @param File[] $files List of files
1907 * @param Folder $folder If set a header with a folder icon and folder name are shown
1908 * @param bool $noThumbs Whether to show thumbnails or not. If set, no thumbnails are shown.
1909 * @return string HTML output
1910 */
1911 protected function fileList(array $files, Folder $folder = NULL, $noThumbs = FALSE) {
1912 $out = '';
1913
1914 $lang = $this->getLanguageService();
1915 $lines = array();
1916 // Create headline (showing number of files):
1917 $filesCount = count($files);
1918 $out .= $this->barheader(sprintf($lang->getLL('files') . ' (%s):', $filesCount));
1919 $out .= $this->getFileSearchField();
1920 $out .= '<div id="filelist">';
1921 $out .= $this->getBulkSelector($filesCount);
1922 $titleLen = (int)$this->getBackendUser()->uc['titleLen'];
1923 // Create the header of current folder:
1924 if ($folder) {
1925 $folderIcon = $this->iconFactory->getIconForResource($folder, Icon::SIZE_SMALL);
1926 $lines[] = '<tr class="t3-row-header">
1927 <td colspan="4">' . $folderIcon->render()
1928 . htmlspecialchars(GeneralUtility::fixed_lgd_cs($folder->getIdentifier(), $titleLen)) . '</td>
1929 </tr>';
1930 }
1931 if ($filesCount == 0) {
1932 $lines[] = '
1933 <tr class="file_list_normal">
1934 <td colspan="4">No files found.</td>
1935 </tr>';
1936 }
1937 // Traverse the filelist:
1938 /** @var $fileObject File */
1939 foreach ($files as $fileObject) {
1940 $fileExtension = $fileObject->getExtension();
1941 // Thumbnail/size generation:
1942 $imgInfo = array();
1943 if (GeneralUtility::inList(strtolower($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] . ',' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']), strtolower($fileExtension)) && !$noThumbs) {
1944 $processedFile = $fileObject->process(
1945 ProcessedFile::CONTEXT_IMAGEPREVIEW,
1946 array('width' => 64, 'height' => 64)
1947 );
1948 $imageUrl = $processedFile->getPublicUrl(TRUE);
1949 $imgInfo = array(
1950 $fileObject->getProperty('width'),
1951 $fileObject->getProperty('height')
1952 );
1953 $pDim = $imgInfo[0] . 'x' . $imgInfo[1] . ' pixels';
1954 $clickIcon = '<img src="' . $imageUrl . '" ' .
1955 'width="' . $processedFile->getProperty('width') . '" ' .
1956 'height="' . $processedFile->getProperty('height') . '" ' .
1957 'hspace="5" vspace="5" border="1" />';
1958 } else {
1959 $clickIcon = '';
1960 $pDim = '';
1961 }
1962 // Create file icon:
1963 $size = ' (' . GeneralUtility::formatSize($fileObject->getSize()) . 'bytes' . ($pDim ? ', ' . $pDim : '') . ')';
1964 $icon = '<span title="' . htmlspecialchars($fileObject->getName() . $size) . '">' . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
1965 // Create links for adding the file:
1966 $filesIndex = count($this->elements);
1967 $this->elements['file_' . $filesIndex] = array(
1968 'type' => 'file',
1969 'table' => 'sys_file',
1970 'uid' => $fileObject->getUid(),
1971 'fileName' => $fileObject->getName(),
1972 'filePath' => $fileObject->getUid(),
1973 'fileExt' => $fileExtension,
1974 'fileIcon' => $icon
1975 );
1976 if ($this->fileIsSelectableInFileList($fileObject, $imgInfo)) {
1977 $ATag = '<a href="#" title="' . htmlspecialchars($fileObject->getName()) . '" onclick="return BrowseLinks.File.insertElement(\'file_' . $filesIndex . '\');">';
1978 $ATag_alt = substr($ATag, 0, -4) . ',1);">';
1979 $bulkCheckBox = '<input type="checkbox" class="typo3-bulk-item" name="file_' . $filesIndex . '" value="0" /> ';
1980 $ATag_e = '</a>';
1981 } else {
1982 $ATag = '';
1983 $ATag_alt = '';
1984 $ATag_e = '';
1985 $bulkCheckBox = '';
1986 }
1987 // Create link to showing details about the file in a window:
1988 $Ahref = BackendUtility::getModuleUrl('show_item', array(
1989 'type' => 'file',
1990 'table' => '_FILE',
1991 'uid' => $fileObject->getCombinedIdentifier(),
1992 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1993 ));
1994 $ATag2_e = '</a>';
1995 // Combine the stuff:
1996 $filenameAndIcon = $bulkCheckBox . $ATag_alt . $icon
1997 . htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileObject->getName(), $titleLen)) . $ATag_e;
1998 // Show element:
1999 if ($pDim) {
2000 // Image...
2001 $lines[] = '
2002 <tr class="file_list_normal">
2003 <td nowrap="nowrap">' . $filenameAndIcon . '&nbsp;</td>
2004 <td>' . $ATag . '<span title="' . $lang->getLL('addToList', TRUE) . '">' . $this->iconFactory->getIcon('actions-edit-add', Icon::SIZE_SMALL)->render() . '</span>' . $ATag_e . '</td>
2005 <td nowrap="nowrap"><a href="' . htmlspecialchars($Ahref) . '" title="' . $lang->getLL('info', TRUE) . '">' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . $lang->getLL('info', TRUE) . $ATag2_e . '</td>
2006 <td nowrap="nowrap">&nbsp;' . $pDim . '</td>
2007 </tr>';
2008 $lines[] = '
2009 <tr>
2010 <td class="filelistThumbnail" colspan="4">' . $ATag_alt . $clickIcon . $ATag_e . '</td>
2011 </tr>';
2012 } else {
2013 $lines[] = '
2014 <tr class="file_list_normal">
2015 <td nowrap="nowrap">' . $filenameAndIcon . '&nbsp;</td>
2016 <td>' . $ATag . '<span title="' . $lang->getLL('addToList', TRUE) . '">' . $this->iconFactory->getIcon('actions-edit-add', Icon::SIZE_SMALL)->render() . '</span>' . $ATag_e . '</td>
2017 <td nowrap="nowrap"><a href="' . htmlspecialchars($Ahref) . '" title="' . $lang->getLL('info', TRUE) . '">' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . $lang->getLL('info', TRUE) . $ATag2_e . '</td>
2018 <td>&nbsp;</td>
2019 </tr>';
2020 }
2021 }
2022 // Wrap all the rows in table tags:
2023 $out .= '
2024
2025 <!--
2026 Filelisting
2027 -->
2028 <table cellpadding="0" cellspacing="0" id="typo3-filelist">
2029 ' . implode('', $lines) . '
2030 </table>';
2031 // Return accumulated content for filelisting:
2032 $out .= '</div>';
2033 return $out;
2034 }
2035
2036 /**
2037 * Checks if the given file is selectable in the filelist.
2038 *
2039 * By default all files are selectable. This method may be overwritten in child classes.
2040 *
2041 * @param FileInterface $file
2042 * @param mixed[] $imgInfo Image dimensions from \TYPO3\CMS\Core\Imaging\GraphicalFunctions::getImageDimensions()
2043 * @return bool TRUE if file is selectable.
2044 */
2045 protected function fileIsSelectableInFileList(FileInterface $file, array $imgInfo) {
2046 return TRUE;
2047 }
2048
2049 /**
2050 * Render list of folders.
2051 *
2052 * @param Folder $baseFolder
2053 * @return string HTML output
2054 */
2055 public function folderList(Folder $baseFolder) {
2056 $content = '';
2057 $lang = $this->getLanguageService();
2058 $folders = $baseFolder->getSubfolders();
2059 $folderIdentifier = $baseFolder->getCombinedIdentifier();
2060 // Create headline (showing number of folders):
2061 $content .= $this->barheader(sprintf($lang->getLL('folders') . ' (%s):', count($folders)));
2062 $titleLength = (int)$this->getBackendUser()->uc['titleLen'];
2063 // Create the header of current folder:
2064 $aTag = '<a href="#" onclick="return insertElement(\'\',' . GeneralUtility::quoteJSvalue($folderIdentifier)
2065 . ', \'folder\', ' . GeneralUtility::quoteJSvalue($folderIdentifier) . ', ' . GeneralUtility::quoteJSvalue($folderIdentifier)
2066 . ', \'\', \'\',\'\',1);">';
2067 // Add the foder icon
2068 $folderIcon = $aTag;
2069 $folderIcon .= $this->iconFactory->getIcon('apps-filetree-folder-default', Icon::SIZE_SMALL)->render();
2070 $folderIcon .= htmlspecialchars(GeneralUtility::fixed_lgd_cs($baseFolder->getName(), $titleLength));
2071 $folderIcon .= '</a>';
2072 $content .= $folderIcon . '<br />';
2073
2074 $lines = array();
2075 // Traverse the folder list:
2076 foreach ($folders as $subFolder) {
2077 $subFolderIdentifier = $subFolder->getCombinedIdentifier();
2078 // Create folder icon:
2079 $icon = '<span style="width: 16px; height: 16px; display: inline-block;"></span>';
2080 $icon .= '<span title="' . htmlspecialchars($subFolder->getName()) . '">' . $this->iconFactory->getIcon('apps-filetree-folder-default', Icon::SIZE_SMALL)->render() . '</span>';
2081 // Create links for adding the folder:
2082 if ($this->P['itemName'] != '' && $this->P['formName'] != '') {
2083 $aTag = '<a href="#" onclick="return set_folderpath(' . GeneralUtility::quoteJSvalue($subFolderIdentifier)
2084 . ');">';
2085 } else {
2086 $aTag = '<a href="#" onclick="return insertElement(\'\',' . GeneralUtility::quoteJSvalue($subFolderIdentifier)
2087 . ', \'folder\', ' . GeneralUtility::quoteJSvalue($subFolderIdentifier) . ', '
2088 . GeneralUtility::quoteJSvalue($subFolderIdentifier) . ', \'\', \'\');">';
2089 }
2090 if (strstr($subFolderIdentifier, ',') || strstr($subFolderIdentifier, '|')) {
2091 // In case an invalid character is in the filepath, display error message:
2092 $errorMessage = GeneralUtility::quoteJSvalue(sprintf($lang->getLL('invalidChar'), ', |'));
2093 $aTag = ($aTag_alt = '<a href="#" onclick="alert(' . $errorMessage . ');return false;">');
2094 } else {
2095 // If foldername is OK, just add it:
2096 $aTag_alt = substr($aTag, 0, -4) . ',\'\',1);">';
2097 }
2098 $aTag_e = '</a>';
2099 // Combine icon and folderpath:
2100 $foldernameAndIcon = $aTag_alt . $icon
2101 . htmlspecialchars(GeneralUtility::fixed_lgd_cs($subFolder->getName(), $titleLength)) . $aTag_e;
2102 if ($this->P['itemName'] != '') {
2103 $lines[] = '
2104 <tr class="bgColor4">
2105 <td nowrap="nowrap">' . $foldernameAndIcon . '&nbsp;</td>
2106 <td>&nbsp;</td>
2107 </tr>';
2108 } else {
2109 $lines[] = '
2110 <tr class="bgColor4">
2111 <td nowrap="nowrap">' . $foldernameAndIcon . '&nbsp;</td>
2112 <td>' . $aTag . '<span title="' . $lang->getLL('addToList', TRUE) . '">' . $this->iconFactory->getIcon('actions-edit-add', Icon::SIZE_SMALL)->render() . '</span>' . $aTag_e . ' </td>
2113 <td>&nbsp;</td>
2114 </tr>';
2115 }
2116 $lines[] = '
2117 <tr>
2118 <td colspan="3"><span style="width: 1px; height: 3px; display: inline-block;"></span></td>
2119 </tr>';
2120 }
2121 // Wrap all the rows in table tags:
2122 $content .= '
2123
2124 <!--
2125 Folder listing
2126 -->
2127 <table border="0" cellpadding="0" cellspacing="1" id="typo3-folderList">
2128 ' . implode('', $lines) . '
2129 </table>';
2130 // Return accumulated content for folderlisting:
2131 return $content;
2132 }
2133
2134 /**
2135 * For RTE: This displays all IMAGES (gif,png,jpg) (from extensionList) from folder. Thumbnails are shown for images.
2136 * This listing is of images located in the web-accessible paths ONLY - the listing is for drag-n-drop use in the RTE
2137 *
2138 * @param Folder $folder The folder path to expand
2139 * @param string $extensionList List of file extensions to show
2140 * @return string HTML output
2141 */
2142 public function TBE_dragNDrop(Folder $folder, $extensionList = '') {
2143 if (!$folder) {
2144 return '';
2145 }
2146 $lang = $this->getLanguageService();
2147 if (!$folder->getStorage()->isPublic()) {
2148 // Print this warning if the folder is NOT a web folder
2149 return GeneralUtility::makeInstance(FlashMessage::class, $lang->getLL('noWebFolder'), $lang->getLL('files'), FlashMessage::WARNING)
2150 ->render();
2151 }
2152 $out = '';
2153
2154 // Read files from directory:
2155 $extensionList = $extensionList == '*' ? '' : $extensionList;
2156 $files = $this->getFilesInFolder($folder, $extensionList);
2157
2158 $out .= $this->barheader(sprintf($lang->getLL('files') . ' (%s):', count($files)));
2159 $titleLen = (int)$this->getBackendUser()->uc['titleLen'];
2160 $picon = $this->iconFactory->getIcon('apps-filetree-folder-default', Icon::SIZE_SMALL)->render();
2161 $picon .= htmlspecialchars(GeneralUtility::fixed_lgd_cs(basename($folder->getName()), $titleLen));
2162 $out .= $picon . '<br />';
2163 // Init row-array:
2164 $lines = array();
2165 // Add "drag-n-drop" message:
2166 $infoText = GeneralUtility::makeInstance(FlashMessage::class, $lang->getLL('findDragDrop'), '', FlashMessage::INFO)
2167 ->render();
2168 $lines[] = '
2169 <tr>
2170 <td colspan="2">' . $infoText . '</td>
2171 </tr>';
2172 // Traverse files:
2173 foreach ($files as $fileObject) {
2174 // URL of image:
2175 $iUrl = GeneralUtility::rawurlencodeFP($fileObject->getPublicUrl(TRUE));
2176 // Show only web-images
2177 $fileExtension = strtolower($fileObject->getExtension());
2178 if (GeneralUtility::inList('gif,jpeg,jpg,png', $fileExtension)) {
2179 $imgInfo = array(
2180 $fileObject->getProperty('width'),
2181 $fileObject->getProperty('height')
2182 );
2183 $pDim = $imgInfo[0] . 'x' . $imgInfo[1] . ' pixels';
2184 $size = ' (' . GeneralUtility::formatSize($fileObject->getSize()) . 'bytes' . ($pDim ? ', ' . $pDim : '') . ')';
2185 $filenameAndIcon = '<span title="' . htmlspecialchars($fileObject->getName() . $size) . '">' . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
2186 if (GeneralUtility::_GP('noLimit')) {
2187 $maxW = 10000;
2188 $maxH = 10000;
2189 } else {
2190 $maxW = 380;
2191 $maxH = 500;
2192 }
2193 $IW = $imgInfo[0];
2194 $IH = $imgInfo[1];
2195 if ($IW > $maxW) {
2196 $IH = ceil($IH / $IW * $maxW);
2197 $IW = $maxW;
2198 }
2199 if ($IH > $maxH) {
2200 $IW = ceil($IW / $IH * $maxH);
2201 $IH = $maxH;
2202 }
2203 // Make row:
2204 $lines[] = '
2205 <tr class="bgColor4">
2206 <td nowrap="nowrap">' . $filenameAndIcon . '&nbsp;</td>
2207 <td nowrap="nowrap">' . ($imgInfo[0] != $IW
2208 ? '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('noLimit' => '1')))
2209 . '" title="' . $lang->getLL('clickToRedrawFullSize', TRUE) . '">' . $this->iconFactory->getIcon('status-dialog-warning', Icon::SIZE_SMALL)->render()
2210 . '</a>'
2211 : '')
2212 . $pDim . '&nbsp;</td>
2213 </tr>';
2214 $lines[] = '
2215 <tr>
2216 <td colspan="2"><img src="' . htmlspecialchars($iUrl) . '" data-htmlarea-file-uid="' . $fileObject->getUid()
2217 . '" width="' . htmlspecialchars($IW) . '" height="' . htmlspecialchars($IH) . '" border="1" alt="" /></td>
2218 </tr>';
2219 $lines[] = '
2220 <tr>
2221 <td colspan="2"><span style="width: 1px; height: 3px; display: inline-block;"></span></td>
2222 </tr>';
2223 }
2224 }
2225 // Finally, wrap all rows in a table tag:
2226 $out .= '
2227
2228
2229 <!--
2230 Filelisting / Drag-n-drop
2231 -->
2232 <table border="0" cellpadding="0" cellspacing="1" id="typo3-dragBox">
2233 ' . implode('', $lines) . '
2234 </table>';
2235
2236 return $out;
2237 }
2238
2239 /******************************************************************
2240 *
2241 * Miscellaneous functions
2242 *
2243 ******************************************************************/
2244
2245 /**
2246 * Prints a 'header' where string is in a tablecell
2247 *
2248 * @param string $str The string to print in the header. The value is htmlspecialchars()'ed before output.
2249 * @return string The header HTML (wrapped in a table)
2250 */
2251 public function barheader($str) {
2252 return '
2253 <!-- Bar header: -->
2254 <h3>' . htmlspecialchars($str) . '</h3>
2255 ';
2256 }
2257
2258 /**
2259 * For RTE/link: This prints the 'currentUrl'
2260 *
2261 * @param string $str URL value. The value is htmlspecialchars()'ed before output.
2262 * @return string HTML content, wrapped in a table.
2263 */
2264 public function printCurrentUrl($str) {
2265 // Output the folder or file identifier, when working with files
2266 if (isset($str) && MathUtility::canBeInterpretedAsInteger($str)) {
2267 try {
2268 $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($str);
2269 } catch (Exception\FileDoesNotExistException $e) {
2270 $fileObject = NULL;
2271 }
2272 $str = is_object($fileObject) ? $fileObject->getIdentifier() : '';
2273 }
2274 if ($str !== '') {
2275 return '
2276 <!-- Print current URL -->
2277 <table border="0" cellpadding="0" cellspacing="0" id="typo3-curUrl">
2278 <tr>
2279 <td>' . $this->getLanguageService()->getLL('currentLink', TRUE) . ': '
2280 . htmlspecialchars(rawurldecode($str)) . '</td>
2281 </tr>
2282 </table>';
2283 } else {
2284 return '';
2285 }
2286 }
2287
2288 /**
2289 * For RTE/link: Parses the incoming URL and determines if it's a page, file, external or mail address.
2290 *
2291 * @param string $href HREF value tp analyse
2292 * @param string $siteUrl The URL of the current website (frontend)
2293 * @return array[] Array with URL information stored in assoc. keys: value, act (page, file, mail), pageid, cElement, info
2294 */
2295 public function parseCurUrl($href, $siteUrl) {
2296 $lang = $this->getLanguageService();
2297 $href = trim($href);
2298 if ($href) {
2299 $info = array();
2300 // Default is "url":
2301 $info['value'] = $href;
2302 $info['act'] = 'url';
2303 if (!StringUtility::beginsWith($href, 'file://') && strpos($href, 'file:') !== FALSE) {
2304 $rel = substr($href, strpos($href, 'file:') + 5);
2305 $rel = rawurldecode($rel);
2306 try {
2307 // resolve FAL-api "file:UID-of-sys_file-record" and "file:combined-identifier"
2308 $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($rel);
2309 if ($fileOrFolderObject instanceof Folder) {
2310 $info['act'] = 'folder';
2311 $info['value'] = $fileOrFolderObject->getCombinedIdentifier();
2312 } elseif ($fileOrFolderObject instanceof File) {
2313 $info['act'] = 'file';
2314 $info['value'] = $fileOrFolderObject->getUid();
2315 } else {
2316 $info['value'] = $rel;
2317 }
2318 } catch (Exception\FileDoesNotExistException $e) {
2319 // file was deleted or any other reason, don't select any item
2320 if (MathUtility::canBeInterpretedAsInteger($rel)) {
2321 $info['act'] = 'file';
2322 } else {
2323 $info['act'] = 'folder';
2324 }
2325 $info['value'] = '';
2326 }
2327 } elseif (StringUtility::beginsWith($href, $siteUrl)) {
2328 // If URL is on the current frontend website:
2329 // URL is a file, which exists:
2330 if (file_exists(PATH_site . rawurldecode($href))) {
2331 $info['value'] = rawurldecode($href);
2332 if (@is_dir((PATH_site . $info['value']))) {
2333 $info['act'] = 'folder';
2334 } else {
2335 $info['act'] = 'file';
2336 }
2337 } else {
2338 // URL is a page (id parameter)
2339 $uP = parse_url($href);
2340
2341 $pp = preg_split('/^id=/', $uP['query']);
2342 $pp[1] = preg_replace('/&id=[^&]*/', '', $pp[1]);
2343 $parameters = explode('&', $pp[1]);
2344 $id = array_shift($parameters);
2345 if ($id) {
2346 // Checking if the id-parameter is an alias.
2347 if (!MathUtility::canBeInterpretedAsInteger($id)) {
2348 list($idPartR) = BackendUtility::getRecordsByField('pages', 'alias', $id);
2349 $id = (int)$idPartR['uid'];
2350 }
2351 $pageRow = BackendUtility::getRecordWSOL('pages', $id);
2352 $titleLen = (int)$this->getBackendUser()->uc['titleLen'];
2353 $info['value'] = ((((($lang->getLL('page', TRUE) . ' \'')
2354 . htmlspecialchars(GeneralUtility::fixed_lgd_cs($pageRow['title'], $titleLen)))
2355 . '\' (ID:') . $id) . ($uP['fragment'] ? ', #' . $uP['fragment'] : '')) . ')';
2356 $info['pageid'] = $id;
2357 $info['cElement'] = $uP['fragment'];
2358 $info['act'] = 'page';
2359 $info['query'] = $parameters[0] ? '&' . implode('&', $parameters) : '';
2360 }
2361 }
2362 } else {
2363 // Email link:
2364 if (strtolower(substr($href, 0, 7)) === 'mailto:') {
2365 $info['value'] = trim(substr($href, 7));
2366 $info['act'] = 'mail';
2367 }
2368 }
2369 $info['info'] = $info['value'];
2370 } else {
2371 // NO value input:
2372 $info = array();
2373 $info['info'] = $lang->getLL('none');
2374 $info['value'] = '';
2375 $info['act'] = 'page';
2376 }
2377 // let the hook have a look
2378 foreach ($this->hookObjects as $hookObject) {
2379 $info = $hookObject->parseCurrentUrl($href, $siteUrl, $info);
2380 }
2381 return $info;
2382 }
2383
2384 /**
2385 * Setter for the class that should be used by TBE_expandPage() to generate the record list.
2386 * This method is intended to be used by Extensions that implement their own browsing functionality.
2387 *
2388 * @param ElementBrowserRecordList $recordList
2389 * @return void
2390 * @api
2391 */
2392 public function setRecordList(ElementBrowserRecordList $recordList) {
2393 $this->recordList = $recordList;
2394 }
2395
2396 /**
2397 * For TBE: Makes an upload form for uploading files to the filemount the user is browsing.
2398 * The files are uploaded to the tce_file.php script in the core which will handle the upload.
2399 *
2400 * @param Folder $folderObject Absolute filepath on server to which to upload.
2401 * @return string HTML for an upload form.
2402 */
2403 public function uploadForm(Folder $folderObject) {
2404 if (!$folderObject->checkActionPermission('write')) {
2405 return '';
2406 }
2407 // Read configuration of upload field count
2408 $userSetting = $this->getBackendUser()->getTSConfigVal('options.folderTree.uploadFieldsInLinkBrowser');
2409 $count = isset($userSetting) ? $userSetting : 1;
2410 if ($count === '0') {
2411 return '';
2412 }
2413 $pArr = explode('|', $this->bparams);
2414 $allowedExtensions = isset($pArr[3]) ? GeneralUtility::trimExplode(',', $pArr[3], TRUE) : [];
2415
2416 $count = (int)$count === 0 ? 1 : (int)$count;
2417 // Create header, showing upload path:
2418 $header = $folderObject->getIdentifier();
2419 $lang = $this->getLanguageService();
2420 // Create a list of allowed file extensions with the readable format "youtube, vimeo" etc.
2421 $fileExtList = array();
2422 foreach ($allowedExtensions as $fileExt) {
2423 if (GeneralUtility::verifyFilenameAgainstDenyPattern($fileExt)) {
2424 $fileExtList[] = '<span class="label label-success">' . strtoupper(htmlspecialchars($fileExt)) . '</span>';
2425 }
2426 }
2427 $code = '
2428 <br />
2429 <!--
2430 Form, for uploading files:
2431 -->
2432 <form action="' . htmlspecialchars(BackendUtility::getModuleUrl('tce_file')) . '" method="post" name="editform"'
2433 . ' id="typo3-uplFilesForm" enctype="multipart/form-data">
2434 <table border="0" cellpadding="0" cellspacing="0" id="typo3-uplFiles">
2435 <tr>
2436 <td>' . $this->barheader($lang->sL(
2437 'LLL:EXT:lang/locallang_core.xlf:file_upload.php.pagetitle', TRUE) . ':') . '</td>
2438 </tr>
2439 <tr>
2440 <td class="c-wCell c-hCell"><strong>' . $lang->getLL('path', TRUE) . ':</strong> '
2441 . htmlspecialchars($header) . '</td>
2442 </tr>
2443 <tr>
2444 <td class="c-wCell c-hCell">';
2445 // Traverse the number of upload fields (default is 3):
2446 for ($a = 1; $a <= $count; $a++) {
2447 $code .= '<input type="file" multiple="multiple" name="upload_' . $a . '[]"' . $this->doc->formWidth(35)
2448 . ' size="50" />
2449 <input type="hidden" name="file[upload][' . $a . '][target]" value="'
2450 . htmlspecialchars($folderObject->getCombinedIdentifier()) . '" />
2451 <input type="hidden" name="file[upload][' . $a . '][data]" value="' . $a . '" /><br />';
2452 }
2453 // Make footer of upload form, including the submit button:
2454 $redirectValue = $this->getThisScript() . 'act=' . $this->act . '&mode=' . $this->mode
2455 . '&expandFolder=' . rawurlencode($folderObject->getCombinedIdentifier())
2456 . '&bparams=' . rawurlencode($this->bparams)
2457 . (is_array($this->P) ? GeneralUtility::implodeArrayForUrl('P', $this->P) : '');
2458 $code .= '<input type="hidden" name="redirect" value="' . htmlspecialchars($redirectValue) . '" />';
2459
2460 if (!empty($fileExtList)) {
2461 $code .= '
2462 <div class="help-block">
2463 ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:cm.allowedFileExtensions', TRUE) . '<br>
2464 ' . implode(' ', $fileExtList) . '
2465 </div>
2466 ';
2467 }
2468
2469 $code .= '
2470 <div id="c-override">
2471 <label>
2472 <input type="checkbox" name="overwriteExistingFiles" id="overwriteExistingFiles" value="1" /> '
2473 . $lang->sL('LLL:EXT:lang/locallang_misc.xlf:overwriteExistingFiles', TRUE) . '
2474 </label>
2475 </div>
2476 <input class="btn btn-default" type="submit" name="submit" value="'
2477 . $lang->sL('LLL:EXT:lang/locallang_core.xlf:file_upload.php.submit', TRUE) . '" />
2478 ';
2479 $code .= '</td>
2480 </tr>
2481 </table>
2482 </form>';
2483
2484 // Add online media
2485 // Create a list of allowed file extensions in a readable format "youtube, vimeo" etc.
2486 $fileExtList = array();
2487 $onlineMediaFileExt = OnlineMediaHelperRegistry::getInstance()->getSupportedFileExtensions();
2488 foreach ($onlineMediaFileExt as $fileExt) {
2489 if (
2490 GeneralUtility::verifyFilenameAgainstDenyPattern($fileExt)
2491 && (empty($allowedExtensions) || in_array($fileExt, $allowedExtensions, TRUE))
2492 ) {
2493 $fileExtList[] = '<span class="label label-success">' . strtoupper(htmlspecialchars($fileExt)) . '</span>';
2494 }
2495 }
2496 if (!empty($fileExtList)) {
2497 $code .= '
2498 <!--
2499 Form, adding online media urls:
2500 -->
2501 <form action="' . htmlspecialchars(BackendUtility::getModuleUrl('online_media')) . '" method="post" name="editform1"'
2502 . ' id="typo3-addMediaForm">
2503 <table border="0" cellpadding="0" cellspacing="0" id="typo3-uplFiles">
2504 <tr>
2505 <td>' . $this->barheader($lang->sL('LLL:EXT:lang/locallang_core.xlf:online_media.new_media', TRUE) . ':') . '</td>
2506 </tr>
2507 <tr>
2508 <td class="c-wCell c-hCell"><strong>' . $lang->getLL('path', TRUE) . ':</strong> '
2509 . htmlspecialchars($header) . '</td>
2510 </tr>
2511 <tr>
2512 <td class="c-wCell c-hCell">
2513 <input type="text" name="file[newMedia][0][url]"' . $this->doc->formWidth(35)
2514 . ' size="50" placeholder="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:online_media.new_media.placeholder', TRUE) . '" />
2515 <input type="hidden" name="file[newMedia][0][target]" value="'
2516 . htmlspecialchars($folderObject->getCombinedIdentifier()) . '" />
2517 <input type="hidden" name="file[newMedia][0][allowed]" value="'
2518 . htmlspecialchars(implode(',', $allowedExtensions)) . '" />
2519 <button>' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:online_media.new_media.submit', TRUE) . '</button>
2520 <div class="help-block">
2521 ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:online_media.new_media.allowedProviders') . '<br>
2522 ' . implode(' ', $fileExtList) . '
2523 </div>
2524 ';
2525 }
2526
2527 // Make footer of upload form, including the submit button:
2528 $redirectValue = $this->getThisScript()
2529 . 'act=' . $this->act
2530 . '&mode=' . $this->mode
2531 . '&expandFolder=' . rawurlencode($folderObject->getCombinedIdentifier())
2532 . '&bparams=' . rawurlencode($this->bparams)
2533 . (is_array($this->P) ? GeneralUtility::implodeArrayForUrl('P', $this->P) : '');
2534 $code .= '<input type="hidden" name="redirect" value="' . htmlspecialchars($redirectValue) . '" />';
2535
2536 $code .= '</td>
2537 </tr>
2538 </table>
2539 </form><br />';
2540
2541
2542 return $code;
2543 }
2544
2545 /**
2546 * For TBE: Makes a form for creating new folders in the filemount the user is browsing.
2547 * The folder creation request is sent to the tce_file.php script in the core which will handle the creation.
2548 *
2549 * @param Folder $folderObject Absolute filepath on server in which to create the new folder.
2550 * @return string HTML for the create folder form.
2551 */
2552 public function createFolder(Folder $folderObject) {
2553 if (!$folderObject->checkActionPermission('write')) {
2554 return '';
2555 }
2556 $backendUser = $this->getBackendUser();
2557 if (!($backendUser->isAdmin() || $backendUser->getTSConfigVal('options.createFoldersInEB'))) {
2558 return '';
2559 }
2560 // Don't show Folder-create form if it's denied
2561 if ($backendUser->getTSConfigVal('options.folderTree.hideCreateFolder')) {
2562 return '';
2563 }
2564 $lang = $this->getLanguageService();
2565 // Create header, showing upload path:
2566 $header = $folderObject->getIdentifier();
2567 $code = '
2568
2569 <!--
2570 Form, for creating new folders:
2571 -->
2572 <form action="' . htmlspecialchars(BackendUtility::getModuleUrl('tce_file')) . '" method="post" name="editform2" id="typo3-crFolderForm">
2573 <table border="0" cellpadding="0" cellspacing="0" id="typo3-crFolder">
2574 <tr>
2575 <td>' . $this->barheader($lang->sL(
2576 'LLL:EXT:lang/locallang_core.xlf:file_newfolder.php.pagetitle') . ':') . '</td>
2577 </tr>
2578 <tr>
2579 <td class="c-wCell c-hCell"><strong>'
2580 . $lang->getLL('path', TRUE) . ':</strong> ' . htmlspecialchars($header) . '</td>
2581 </tr>
2582 <tr>
2583 <td class="c-wCell c-hCell">';
2584 // Create the new-folder name field:
2585 $a = 1;
2586 $code .= '<input' . $this->doc->formWidth(20) . ' type="text" name="file[newfolder][' . $a . '][data]" />'
2587 . '<input type="hidden" name="file[newfolder][' . $a . '][target]" value="'
2588 . htmlspecialchars($folderObject->getCombinedIdentifier()) . '" />';
2589 // Make footer of upload form, including the submit button:
2590 $redirectValue = $this->getThisScript() . 'act=' . $this->act . '&mode=' . $this->mode
2591 . '&expandFolder=' . rawurlencode($folderObject->getCombinedIdentifier())
2592 . '&bparams=' . rawurlencode($this->bparams)
2593 . (is_array($this->P) ? GeneralUtility::implodeArrayForUrl('P', $this->P) : '');
2594 $code .= '<input type="hidden" name="redirect" value="' . htmlspecialchars($redirectValue) . '" />'
2595 . '<input class="btn btn-default" type="submit" name="submit" value="'
2596 . $lang->sL('LLL:EXT:lang/locallang_core.xlf:file_newfolder.php.submit', TRUE) . '" />';
2597 $code .= '</td>
2598 </tr>
2599 </table>
2600 </form>';
2601 return $code;
2602 }
2603
2604 /**
2605 * Get the HTML data required for a bulk selection of files of the TYPO3 Element Browser.
2606 *
2607 * @param int $filesCount Number of files currently displayed
2608 * @return string HTML data required for a bulk selection of files - if $filesCount is 0, nothing is returned
2609 */
2610 public function getBulkSelector($filesCount) {
2611 if (!$filesCount) {
2612 return '';
2613 }
2614
2615 $lang = $this->getLanguageService();
2616 $labelToggleSelection = $lang->sL('LLL:EXT:lang/locallang_browse_links.xlf:toggleSelection', TRUE);
2617 $labelImportSelection = $lang->sL('LLL:EXT:lang/locallang_browse_links.xlf:importSelection', TRUE);
2618 // Getting flag for showing/not showing thumbnails:
2619 $noThumbsInEB = $this->getBackendUser()->getTSConfigVal('options.noThumbsInEB');
2620 $out = $this->doc->spacer(10) . '<div>' . '<a href="#" onclick="BrowseLinks.Selector.handle()"'
2621 . 'title="' . $labelImportSelection . '">'
2622 . $this->iconFactory->getIcon('actions-document-import-t3d', Icon::SIZE_SMALL)->render()
2623 . $labelImportSelection . '</a>&nbsp;&nbsp;&nbsp;'
2624 . '<a href="#" onclick="BrowseLinks.Selector.toggle()" title="' . $labelToggleSelection . '">'
2625 . $this->iconFactory->getIcon('actions-document-select', Icon::SIZE_SMALL)->render()
2626 . $labelToggleSelection . '</a>' . '</div>';
2627 if (!$noThumbsInEB && $this->selectedFolder) {
2628 // MENU-ITEMS, fetching the setting for thumbnails from File>List module:
2629 $_MOD_MENU = array('displayThumbs' => '');
2630 $_MCONF['name'] = 'file_list';
2631 $_MOD_SETTINGS = BackendUtility::getModuleData($_MOD_MENU, GeneralUtility::_GP('SET'), $_MCONF['name']);
2632 $addParams = '&act=' . $this->act . '&mode=' . $this->mode
2633 . '&expandFolder=' . rawurlencode($this->selectedFolder->getCombinedIdentifier())
2634 . '&bparams=' . rawurlencode($this->bparams);
2635 $thumbNailCheck = '<div class="checkbox"><label for="checkDisplayThumbs">' . BackendUtility::getFuncCheck('', 'SET[displayThumbs]', $_MOD_SETTINGS['displayThumbs'],
2636 GeneralUtility::_GP('M') ? '' : $this->thisScript, $addParams, 'id="checkDisplayThumbs"')
2637 . $lang->sL('LLL:EXT:lang/locallang_mod_file_list.xlf:displayThumbs', TRUE) . '</label></div>';
2638 $out .= $this->doc->spacer(5) . $thumbNailCheck . $this->doc->spacer(15);
2639 } else {
2640 $out .= $this->doc->spacer(15);
2641 }
2642 return $out;
2643 }
2644
2645 /**
2646 * Get the HTML data required for the file search field of the TYPO3 Element Browser.
2647 *
2648 * @return string HTML data required for the search field in the file list of the Element Browser
2649 */
2650 protected function getFileSearchField() {
2651 $action = $this->getThisScript() . 'act=' . $this->act . '&mode=' . $this->mode
2652 . '&bparams=' . rawurlencode($this->bparams)
2653 . (is_array($this->P) ? GeneralUtility::implodeArrayForUrl('P', $this->P) : '');
2654 $out = '
2655 <form method="post" action="' . htmlspecialchars($action) . '">
2656 <div class="input-group">
2657 <input class="form-control" type="text" name="searchWord" value="' . htmlspecialchars($this->searchWord) . '">
2658 <span class="input-group-btn">
2659 <button class="btn btn-default" type="submit">' . $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang.xlf:search', TRUE) .'</button>
2660 </span>
2661 </div>
2662 </form>';
2663 $out .= $this->doc->spacer(15);
2664 return $out;
2665 }
2666
2667 /**
2668 * Determines whether submitted field change functions are valid
2669 * and are coming from the system and not from an external abuse.
2670 *
2671 * @param bool $handleFlexformSections Whether to handle flexform sections differently
2672 * @return bool Whether the submitted field change functions are valid
2673 */
2674 protected function areFieldChangeFunctionsValid($handleFlexformSections = FALSE) {
2675 $result = FALSE;
2676 if (isset($this->P['fieldChangeFunc']) && is_array($this->P['fieldChangeFunc']) && isset($this->P['fieldChangeFuncHash'])) {
2677 $matches = array();
2678 $pattern = '#\\[el\\]\\[(([^]-]+-[^]-]+-)(idx\\d+-)([^]]+))\\]#i';
2679 $fieldChangeFunctions = $this->P['fieldChangeFunc'];
2680 // Special handling of flexform sections:
2681 // Field change functions are modified in JavaScript, thus the hash is always invalid
2682 if ($handleFlexformSections && preg_match($pattern, $this->P['itemName'], $matches)) {
2683 $originalName = $matches[1];
2684 $cleanedName = $matches[2] . $matches[4];
2685 foreach ($fieldChangeFunctions as &$value) {
2686 $value = str_replace($originalName, $cleanedName, $value);
2687 }
2688 unset($value);
2689 }
2690 $result = $this->P['fieldChangeFuncHash'] === GeneralUtility::hmac(serialize($fieldChangeFunctions));
2691 }
2692 return $result;
2693 }
2694
2695 /**
2696 * Check if a temporary tree mount is set and return a cancel button
2697 *
2698 * @return string
2699 */
2700 protected function getTemporaryTreeMountCancelNotice() {
2701 if ((int)$this->getBackendUser()->getSessionData('pageTree_temporaryMountPoint') === 0) {
2702 return '';
2703 }
2704 $link = '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('setTempDBmount' => 0))) . '">'
2705 . $this->getLanguageService()->sl('LLL:EXT:lang/locallang_core.xlf:labels.temporaryDBmount', TRUE) . '</a>';
2706 /** @var FlashMessage $flashMessage */
2707 $flashMessage = GeneralUtility::makeInstance(
2708 FlashMessage::class,
2709 $link,
2710 '',
2711 FlashMessage::INFO
2712 );
2713 return $flashMessage->render();
2714 }
2715
2716 /**
2717 * Get a list of Files in a folder filtered by extension
2718 *
2719 * @param Folder $folder
2720 * @param string $extensionList
2721 * @return File[]
2722 */
2723 protected function getFilesInFolder(Folder $folder, $extensionList) {
2724 if ($extensionList !== '') {
2725 /** @var FileExtensionFilter $filter */
2726 $filter = GeneralUtility::makeInstance(FileExtensionFilter::class);
2727 $filter->setAllowedFileExtensions($extensionList);
2728 $folder->setFileAndFolderNameFilters(array(array($filter, 'filterFileList')));
2729 }
2730 return $folder->getFiles();
2731 }
2732
2733 /**
2734 * @return LanguageService
2735 */
2736 protected function getLanguageService() {
2737 return $GLOBALS['LANG'];
2738 }
2739
2740 /**
2741 * @return BackendUserAuthentication
2742 */
2743 protected function getBackendUser() {
2744 return $GLOBALS['BE_USER'];
2745 }
2746
2747 /**
2748 * @return DatabaseConnection
2749 */
2750 protected function getDatabaseConnection() {
2751 return $GLOBALS['TYPO3_DB'];
2752 }
2753
2754 /**
2755 * @return PageRenderer
2756 */
2757 protected function getPageRenderer() {
2758 if ($this->pageRenderer === NULL) {
2759 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
2760 }
2761 return $this->pageRenderer;
2762 }
2763
2764 }