[BUGFIX] Restore registers before early returns
[Packages/TYPO3.CMS.git] / typo3 / sysext / css_styled_content / Classes / Controller / CssStyledContentController.php
1 <?php
2 namespace TYPO3\CMS\CssStyledContent\Controller;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the text file GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Core\Utility\MathUtility;
32
33 /**
34 * Plugin class - instantiated from TypoScript.
35 * Rendering some content elements from tt_content table.
36 *
37 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
38 */
39 class CssStyledContentController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
40
41 /**
42 * Same as class name
43 *
44 * @todo Define visibility
45 */
46 public $prefixId = 'tx_cssstyledcontent_pi1';
47
48 /**
49 * Path to this script relative to the extension dir.
50 *
51 * @todo Define visibility
52 */
53 public $scriptRelPath = 'pi1/class.tx_cssstyledcontent_pi1.php';
54
55 /**
56 * The extension key
57 *
58 * @todo Define visibility
59 */
60 public $extKey = 'css_styled_content';
61
62 /**
63 * @todo Define visibility
64 */
65 public $conf = array();
66
67 /***********************************
68 * Rendering of Content Elements:
69 ***********************************/
70
71 /**
72 * Rendering the "Bulletlist" type content element, called from TypoScript (tt_content.bullets.20)
73 *
74 * @param string $content Content input. Not used, ignore.
75 * @param array $conf TypoScript configuration
76 * @return string HTML output.
77 */
78 public function render_bullets($content, $conf) {
79 // Look for hook before running default code for function
80 if ($hookObj = $this->hookRequest('render_bullets')) {
81 return $hookObj->render_bullets($content, $conf);
82 } else {
83 // Get bodytext field content, returning blank if empty:
84 $field = isset($conf['field']) && trim($conf['field']) ? trim($conf['field']) : 'bodytext';
85 $content = trim($this->cObj->data[$field]);
86 if ($content === '') {
87 return '';
88 }
89 // Split into single lines:
90 $lines = GeneralUtility::trimExplode(LF, $content);
91 foreach ($lines as &$val) {
92 $val = '<li>' . $this->cObj->stdWrap($val, $conf['innerStdWrap.']) . '</li>';
93 }
94 unset($val);
95 // Set header type:
96 $type = (int)$this->cObj->data['layout'];
97 // Compile list:
98 $out = '
99 <ul class="csc-bulletlist csc-bulletlist-' . $type . '">' . implode('', $lines) . '
100 </ul>';
101 // Return value
102 return $out;
103 }
104 }
105
106 /**
107 * Rendering the "Table" type content element, called from TypoScript (tt_content.table.20)
108 *
109 * @param string $content Content input. Not used, ignore.
110 * @param array $conf TypoScript configuration
111 * @return string HTML output.
112 */
113 public function render_table($content, $conf) {
114 // Look for hook before running default code for function
115 if ($hookObj = $this->hookRequest('render_table')) {
116 return $hookObj->render_table($content, $conf);
117 } else {
118 // Init FlexForm configuration
119 $this->pi_initPIflexForm();
120 // Get bodytext field content
121 $field = isset($conf['field']) && trim($conf['field']) ? trim($conf['field']) : 'bodytext';
122 $content = trim($this->cObj->data[$field]);
123 if ($content === '') {
124 return '';
125 }
126 // get flexform values
127 $caption = trim(htmlspecialchars($this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'acctables_caption')));
128 $useTfoot = trim($this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'acctables_tfoot'));
129 $headerPos = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'acctables_headerpos');
130 $noStyles = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'acctables_nostyles');
131 $tableClass = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'acctables_tableclass');
132 $delimiter = trim($this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'tableparsing_delimiter', 's_parsing'));
133 if ($delimiter) {
134 $delimiter = chr((int)$delimiter);
135 } else {
136 $delimiter = '|';
137 }
138 $quotedInput = trim($this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'tableparsing_quote', 's_parsing'));
139 if ($quotedInput) {
140 $quotedInput = chr((int)$quotedInput);
141 } else {
142 $quotedInput = '';
143 }
144 // Generate id prefix for accessible header
145 $headerScope = $headerPos == 'top' ? 'col' : 'row';
146 $headerIdPrefix = $headerScope . $this->cObj->data['uid'] . '-';
147 // Split into single lines (will become table-rows):
148 $rows = GeneralUtility::trimExplode(LF, $content);
149 reset($rows);
150 // Find number of columns to render:
151 $cols = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange(
152 $this->cObj->data['cols'] ? $this->cObj->data['cols'] : count(str_getcsv(current($rows), $delimiter, $quotedInput)),
153 0,
154 100
155 );
156 // Traverse rows (rendering the table here)
157 $rCount = count($rows);
158 foreach ($rows as $k => $v) {
159 $cells = str_getcsv($v, $delimiter, $quotedInput);
160 $newCells = array();
161 for ($a = 0; $a < $cols; $a++) {
162 if (trim($cells[$a]) === '') {
163 $cells[$a] = '&nbsp;';
164 }
165 $cellAttribs = $noStyles ? '' : ($a > 0 && $cols - 1 == $a ? ' class="td-last td-' . $a . '"' : ' class="td-' . $a . '"');
166 if ($headerPos == 'top' && !$k || $headerPos == 'left' && !$a) {
167 $scope = ' scope="' . $headerScope . '"';
168 $scope .= ' id="' . $headerIdPrefix . ($headerScope == 'col' ? $a : $k) . '"';
169 $newCells[$a] = '
170 <th' . $cellAttribs . $scope . '>' . $this->cObj->stdWrap($cells[$a], $conf['innerStdWrap.']) . '</th>';
171 } else {
172 if (empty($headerPos)) {
173 $accessibleHeader = '';
174 } else {
175 $accessibleHeader = ' headers="' . $headerIdPrefix . ($headerScope == 'col' ? $a : $k) . '"';
176 }
177 $newCells[$a] = '
178 <td' . $cellAttribs . $accessibleHeader . '>' . $this->cObj->stdWrap($cells[$a], $conf['innerStdWrap.']) . '</td>';
179 }
180 }
181 if (!$noStyles) {
182 $oddEven = $k % 2 ? 'tr-odd' : 'tr-even';
183 $rowAttribs = $k > 0 && $rCount - 1 == $k ? ' class="' . $oddEven . ' tr-last"' : ' class="' . $oddEven . ' tr-' . $k . '"';
184 }
185 $rows[$k] = '
186 <tr' . $rowAttribs . '>' . implode('', $newCells) . '
187 </tr>';
188 }
189 $addTbody = 0;
190 $tableContents = '';
191 if ($caption) {
192 $tableContents .= '
193 <caption>' . $caption . '</caption>';
194 }
195 if ($headerPos == 'top' && $rows[0]) {
196 $tableContents .= '<thead>' . $rows[0] . '
197 </thead>';
198 unset($rows[0]);
199 $addTbody = 1;
200 }
201 if ($useTfoot) {
202 $tableContents .= '
203 <tfoot>' . $rows[($rCount - 1)] . '</tfoot>';
204 unset($rows[$rCount - 1]);
205 $addTbody = 1;
206 }
207 $tmpTable = implode('', $rows);
208 if ($addTbody) {
209 $tmpTable = '<tbody>' . $tmpTable . '</tbody>';
210 }
211 $tableContents .= $tmpTable;
212 // Set header type:
213 $type = (int)$this->cObj->data['layout'];
214 // Table tag params.
215 $tableTagParams = $this->getTableAttributes($conf, $type);
216 if (!$noStyles) {
217 $tableTagParams['class'] = 'contenttable contenttable-' . $type . ($tableClass ? ' ' . $tableClass : '') . $tableTagParams['class'];
218 } elseif ($tableClass) {
219 $tableTagParams['class'] = $tableClass;
220 }
221 // Compile table output:
222 $out = '
223 <table ' . GeneralUtility::implodeAttributes($tableTagParams) . '>' . $tableContents . '
224 </table>';
225 // Return value
226 return $out;
227 }
228 }
229
230 /**
231 * Rendering the "Filelinks" type content element, called from TypoScript (tt_content.uploads.20)
232 *
233 * @param string $content Content input. Not used, ignore.
234 * @param array $conf TypoScript configuration
235 * @return string HTML output.
236 */
237 public function render_uploads($content, $conf) {
238 // Look for hook before running default code for function
239 if ($hookObj = $this->hookRequest('render_uploads')) {
240 return $hookObj->render_uploads($content, $conf);
241 } else {
242 // Loading language-labels
243 $this->pi_loadLL();
244 $out = '';
245 // Set layout type:
246 $type = (int)$this->cObj->data['layout'];
247 // See if the file path variable is set, this takes precedence
248 $filePathConf = $this->cObj->stdWrap($conf['filePath'], $conf['filePath.']);
249 if ($filePathConf) {
250 $fileList = $this->cObj->filelist($filePathConf);
251 list($path) = explode('|', $filePathConf);
252 } else {
253 // Get the list of files from the field
254 $field = trim($conf['field']) ?: 'media';
255 $fileList = $this->cObj->data[$field];
256 $path = 'uploads/media/';
257 if (
258 is_array($GLOBALS['TCA']['tt_content']['columns'][$field]) &&
259 !empty($GLOBALS['TCA']['tt_content']['columns'][$field]['config']['uploadfolder'])
260 ) {
261 // In TCA-Array folders are saved without trailing slash, so $path.$fileName won't work
262 $path = $GLOBALS['TCA']['tt_content']['columns'][$field]['config']['uploadfolder'] . '/';
263 }
264 }
265 $path = trim($path);
266 // Explode into an array:
267 $fileArray = GeneralUtility::trimExplode(',', $fileList, TRUE);
268 // If there were files to list...:
269 if (count($fileArray)) {
270 // Get the descriptions for the files (if any):
271 $descriptions = GeneralUtility::trimExplode(LF, $this->cObj->data['imagecaption']);
272 // Get the titles for the files (if any)
273 $titles = GeneralUtility::trimExplode(LF, $this->cObj->data['titleText']);
274 // Get the alternative text for icons/thumbnails
275 $altTexts = GeneralUtility::trimExplode(LF, $this->cObj->data['altText']);
276 // Add the target to linkProc when explicitly set
277 if ($this->cObj->data['target']) {
278 $conf['linkProc.']['target'] = $this->cObj->data['target'];
279 unset($conf['linkProc.']['target.']);
280 }
281 // Adding hardcoded TS to linkProc configuration:
282 $conf['linkProc.']['path.']['current'] = 1;
283 if ($conf['linkProc.']['combinedLink']) {
284 $conf['linkProc.']['icon'] = $type > 0 ? 1 : 0;
285 } else {
286 // Always render icon - is inserted by PHP if needed.
287 $conf['linkProc.']['icon'] = 1;
288 // Temporary, internal split-token!
289 $conf['linkProc.']['icon.']['wrap'] = ' | //**//';
290 // ALways link the icon
291 $conf['linkProc.']['icon_link'] = 1;
292 }
293 $conf['linkProc.']['icon_image_ext_list'] = $type == 2 || $type == 3 ? $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] : '';
294 // If the layout is type 2 or 3 we will render an image based icon if possible.
295 if ($conf['labelStdWrap.']) {
296 $conf['linkProc.']['labelStdWrap.'] = $conf['labelStdWrap.'];
297 }
298 if ($conf['useSpacesInLinkText'] || $conf['stripFileExtensionFromLinkText']) {
299 $conf['linkProc.']['removePrependedNumbers'] = 0;
300 }
301 // Traverse the files found:
302 $filesData = array();
303 foreach ($fileArray as $key => $fileName) {
304 $absPath = GeneralUtility::getFileAbsFileName(GeneralUtility::resolveBackPath($path . $fileName));
305 if (@is_file($absPath)) {
306 $fI = pathinfo($fileName);
307 $filesData[$key] = array();
308 $currentPath = $path;
309 if (GeneralUtility::isFirstPartOfStr($fileName, '../../')) {
310 $currentPath = '';
311 $fileName = substr($fileName, 6);
312 }
313 $filesData[$key]['filename'] = $fileName;
314 $filesData[$key]['path'] = $currentPath;
315 $filesData[$key]['filesize'] = filesize($absPath);
316 $filesData[$key]['fileextension'] = strtolower($fI['extension']);
317 $filesData[$key]['description'] = trim($descriptions[$key]);
318 $filesData[$key]['titletext'] = trim($titles[$key]);
319 $filesData[$key]['alttext'] = trim($altTexts[$key]);
320 $conf['linkProc.']['title'] = trim($titles[$key]);
321 if (isset($altTexts[$key]) && !empty($altTexts[$key])) {
322 $altText = trim($altTexts[$key]);
323 } else {
324 $altText = sprintf($this->pi_getLL('uploads.icon'), $fileName);
325 }
326 $conf['linkProc.']['altText'] = ($conf['linkProc.']['iconCObject.']['altText'] = $altText);
327 $this->cObj->setCurrentVal($currentPath);
328 $GLOBALS['TSFE']->register['ICON_REL_PATH'] = $currentPath . $fileName;
329 $GLOBALS['TSFE']->register['filename'] = $filesData[$key]['filename'];
330 $GLOBALS['TSFE']->register['path'] = $filesData[$key]['path'];
331 $GLOBALS['TSFE']->register['fileSize'] = $filesData[$key]['filesize'];
332 $GLOBALS['TSFE']->register['fileExtension'] = $filesData[$key]['fileextension'];
333 $GLOBALS['TSFE']->register['description'] = $filesData[$key]['description'];
334 $GLOBALS['TSFE']->register['titleText'] = $filesData[$key]['titletext'];
335 $GLOBALS['TSFE']->register['altText'] = $filesData[$key]['alttext'];
336
337 $filesData[$key]['linkedFilenameParts'] = $this->beautifyFileLink(
338 explode('//**//', $this->cObj->filelink($fileName, $conf['linkProc.'])),
339 $fileName,
340 $conf['useSpacesInLinkText'],
341 $conf['stripFileExtensionFromLinkText']
342 );
343 }
344 }
345 // optionSplit applied to conf to allow differnt settings per file
346 $splitConf = $GLOBALS['TSFE']->tmpl->splitConfArray($conf, count($filesData));
347 // Now, lets render the list!
348 $outputEntries = array();
349 foreach ($filesData as $key => $fileData) {
350 $GLOBALS['TSFE']->register['linkedIcon'] = $fileData['linkedFilenameParts'][0];
351 $GLOBALS['TSFE']->register['linkedLabel'] = $fileData['linkedFilenameParts'][1];
352 $GLOBALS['TSFE']->register['filename'] = $fileData['filename'];
353 $GLOBALS['TSFE']->register['path'] = $fileData['path'];
354 $GLOBALS['TSFE']->register['description'] = $fileData['description'];
355 $GLOBALS['TSFE']->register['fileSize'] = $fileData['filesize'];
356 $GLOBALS['TSFE']->register['fileExtension'] = $fileData['fileextension'];
357 $GLOBALS['TSFE']->register['titleText'] = $fileData['titletext'];
358 $GLOBALS['TSFE']->register['altText'] = $fileData['alttext'];
359 $outputEntries[] = $this->cObj->cObjGetSingle($splitConf[$key]['itemRendering'], $splitConf[$key]['itemRendering.']);
360 }
361 if (isset($conf['outerWrap'])) {
362 // Wrap around the whole content
363 $outerWrap = $this->cObj->stdWrap($conf['outerWrap'], $conf['outerWrap.']);
364 } else {
365 // Table tag params
366 $tableTagParams = $this->getTableAttributes($conf, $type);
367 $tableTagParams['class'] = 'csc-uploads csc-uploads-' . $type;
368 $outerWrap = '<table ' . GeneralUtility::implodeAttributes($tableTagParams) . '>|</table>';
369 }
370 // Compile it all into table tags:
371 $out = $this->cObj->wrap(implode('', $outputEntries), $outerWrap);
372 }
373 // Return value
374 return $out;
375 }
376 }
377
378 /**
379 * Returns an array containing width relations for $colCount columns.
380 *
381 * Tries to use "colRelations" setting given by TS.
382 * uses "1:1" column relations by default.
383 *
384 * @param array $conf TS configuration for img
385 * @param integer $colCount number of columns
386 * @return array
387 */
388 protected function getImgColumnRelations($conf, $colCount) {
389 $relations = array();
390 $equalRelations = array_fill(0, $colCount, 1);
391 $colRelationsTypoScript = trim($this->cObj->stdWrap($conf['colRelations'], $conf['colRelations.']));
392 if ($colRelationsTypoScript) {
393 // Try to use column width relations given by TS
394 $relationParts = explode(':', $colRelationsTypoScript);
395 // Enough columns defined?
396 if (count($relationParts) >= $colCount) {
397 $out = array();
398 for ($a = 0; $a < $colCount; $a++) {
399 $currentRelationValue = (int)$relationParts[$a];
400 if ($currentRelationValue >= 1) {
401 $out[$a] = $currentRelationValue;
402 } else {
403 GeneralUtility::devLog('colRelations used with a value smaller than 1 therefore colRelations setting is ignored.', $this->extKey, 2);
404 unset($out);
405 break;
406 }
407 }
408 if (max($out) / min($out) <= 10) {
409 $relations = $out;
410 } else {
411 GeneralUtility::devLog(
412 'The difference in size between the largest and smallest colRelation was not within' .
413 ' a factor of ten therefore colRelations setting is ignored..',
414 $this->extKey,
415 2
416 );
417 }
418 }
419 }
420 return $relations ?: $equalRelations;
421 }
422
423 /**
424 * Returns an array containing the image widths for an image row with $colCount columns.
425 *
426 * @param array $conf TS configuration of img
427 * @param integer $colCount number of columns
428 * @param integer $netW max usable width for images (without spaces and borders)
429 * @return array
430 */
431 protected function getImgColumnWidths($conf, $colCount, $netW) {
432 $columnWidths = array();
433 $colRelations = $this->getImgColumnRelations($conf, $colCount);
434 $accumWidth = 0;
435 $accumDesiredWidth = 0;
436 $relUnitCount = array_sum($colRelations);
437 for ($a = 0; $a < $colCount; $a++) {
438 // This much width is available for the remaining images in this row (int)
439 $availableWidth = $netW - $accumWidth;
440 // Theoretical width of resized image. (float)
441 $desiredWidth = $netW / $relUnitCount * $colRelations[$a];
442 // Add this width. $accumDesiredWidth becomes the desired horizontal position
443 $accumDesiredWidth += $desiredWidth;
444 // Calculate width by comparing actual and desired horizontal position.
445 // this evenly distributes rounding errors across all images in this row.
446 $suggestedWidth = round($accumDesiredWidth - $accumWidth);
447 // finalImgWidth may not exceed $availableWidth
448 $finalImgWidth = (int)min($availableWidth, $suggestedWidth);
449 $accumWidth += $finalImgWidth;
450 $columnWidths[$a] = $finalImgWidth;
451 }
452 return $columnWidths;
453 }
454
455 /**
456 * Rendering the IMGTEXT content element, called from TypoScript (tt_content.textpic.20)
457 *
458 * @param string $content Content input. Not used, ignore.
459 * @param array $conf TypoScript configuration. See TSRef "IMGTEXT". This function aims to be compatible.
460 * @return string HTML output.
461 * @coauthor Ernesto Baschny <ernst@cron-it.de>
462 * @coauthor Patrick Broens <patrick@patrickbroens.nl>
463 */
464 public function render_textpic($content, $conf) {
465 // Look for hook before running default code for function
466 if (method_exists($this, 'hookRequest') && ($hookObj = $this->hookRequest('render_textpic'))) {
467 return $hookObj->render_textpic($content, $conf);
468 }
469 $renderMethod = $this->cObj->stdWrap($conf['renderMethod'], $conf['renderMethod.']);
470 // Render using the default IMGTEXT code (table-based)
471 if (!$renderMethod || $renderMethod == 'table') {
472 return $this->cObj->IMGTEXT($conf);
473 }
474 $restoreRegisters = FALSE;
475 if (isset($conf['preRenderRegisters.'])) {
476 $restoreRegisters = TRUE;
477 $this->cObj->LOAD_REGISTER($conf['preRenderRegisters.'], 'LOAD_REGISTER');
478 }
479 // Specific configuration for the chosen rendering method
480 if (is_array($conf['rendering.'][$renderMethod . '.'])) {
481 $conf = array_replace_recursive($conf, $conf['rendering.'][$renderMethod . '.']);
482 }
483 // Image or Text with Image?
484 if (is_array($conf['text.'])) {
485 $content = $this->cObj->stdWrap($this->cObj->cObjGet($conf['text.'], 'text.'), $conf['text.']);
486 }
487 $imgList = trim($this->cObj->stdWrap($conf['imgList'], $conf['imgList.']));
488 if (!$imgList) {
489 // No images, that's easy
490 if ($restoreRegisters) {
491 $this->cObj->LOAD_REGISTER(array(), 'RESTORE_REGISTER');
492 }
493 return $content;
494 }
495 $imgs = GeneralUtility::trimExplode(',', $imgList, TRUE);
496 if (count($imgs) === 0) {
497 // The imgList was not empty but did only contain empty values
498 if ($restoreRegisters) {
499 $this->cObj->LOAD_REGISTER(array(), 'RESTORE_REGISTER');
500 }
501 return $content;
502 }
503 $imgStart = (int)$this->cObj->stdWrap($conf['imgStart'], $conf['imgStart.']);
504 $imgCount = count($imgs) - $imgStart;
505 $imgMax = (int)$this->cObj->stdWrap($conf['imgMax'], $conf['imgMax.']);
506 if ($imgMax) {
507 $imgCount = MathUtility::forceIntegerInRange($imgCount, 0, $imgMax);
508 }
509 $imgPath = $this->cObj->stdWrap($conf['imgPath'], $conf['imgPath.']);
510 // Does we need to render a "global caption" (below the whole image block)?
511 $renderGlobalCaption = !$conf['captionSplit'] && !$conf['imageTextSplit'] && is_array($conf['caption.']);
512 if ($imgCount == 1) {
513 // If we just have one image, the caption relates to the image, so it is not "global"
514 $renderGlobalCaption = FALSE;
515 }
516 $imgListContainsReferenceUids = (bool)(isset($conf['imgListContainsReferenceUids.'])
517 ? $this->cObj->stdWrap($conf['imgListContainsReferenceUids'], $conf['imgListContainsReferenceUids.'])
518 : $conf['imgListContainsReferenceUids']);
519 // Use the calculated information (amount of images, if global caption is wanted) to choose a different rendering method for the images-block
520 $GLOBALS['TSFE']->register['imageCount'] = $imgCount;
521 $GLOBALS['TSFE']->register['renderGlobalCaption'] = $renderGlobalCaption;
522 $fallbackRenderMethod = '';
523 if ($conf['fallbackRendering']) {
524 $fallbackRenderMethod = $this->cObj->cObjGetSingle($conf['fallbackRendering'], $conf['fallbackRendering.']);
525 }
526 if ($fallbackRenderMethod && is_array($conf['rendering.'][$fallbackRenderMethod . '.'])) {
527 $conf = array_replace_recursive($conf, $conf['rendering.'][$fallbackRenderMethod . '.']);
528 }
529 // Set the accessibility mode which uses a different type of markup, used 4.7+
530 $accessibilityMode = FALSE;
531 if (strpos(strtolower($renderMethod), 'caption') || strpos(strtolower($fallbackRenderMethod), 'caption')) {
532 $accessibilityMode = TRUE;
533 }
534 // Global caption
535 $globalCaption = '';
536 if ($renderGlobalCaption) {
537 $globalCaption = $this->cObj->stdWrap($this->cObj->cObjGet($conf['caption.'], 'caption.'), $conf['caption.']);
538 }
539 // Positioning
540 $position = $this->cObj->stdWrap($conf['textPos'], $conf['textPos.']);
541 // 0,1,2 = center,right,left
542 $imagePosition = $position & 7;
543 // 0,8,16,24 (above,below,intext,intext-wrap)
544 $contentPosition = $position & 24;
545 $textMargin = (int)$this->cObj->stdWrap($conf['textMargin'], $conf['textMargin.']);
546 if (!$conf['textMargin_outOfText'] && $contentPosition < 16) {
547 $textMargin = 0;
548 }
549 $colspacing = (int)$this->cObj->stdWrap($conf['colSpace'], $conf['colSpace.']);
550 $border = (int)$this->cObj->stdWrap($conf['border'], $conf['border.']) ? 1 : 0;
551 $borderThickness = (int)$this->cObj->stdWrap($conf['borderThick'], $conf['borderThick.']);
552 $borderThickness = $borderThickness ?: 1;
553 $borderSpace = $conf['borderSpace'] && $border ? (int)$conf['borderSpace'] : 0;
554 // Generate cols
555 $cols = (int)$this->cObj->stdWrap($conf['cols'], $conf['cols.']);
556 $colCount = $cols > 1 ? $cols : 1;
557 if ($colCount > $imgCount) {
558 $colCount = $imgCount;
559 }
560 $rowCount = ceil($imgCount / $colCount);
561 // Generate rows
562 $rows = (int)$this->cObj->stdWrap($conf['rows'], $conf['rows.']);
563 if ($rows > 1) {
564 $rowCount = $rows;
565 if ($rowCount > $imgCount) {
566 $rowCount = $imgCount;
567 }
568 $colCount = $rowCount > 1 ? ceil($imgCount / $rowCount) : $imgCount;
569 }
570 // Max Width
571 $maxW = (int)$this->cObj->stdWrap($conf['maxW'], $conf['maxW.']);
572 $maxWInText = (int)$this->cObj->stdWrap($conf['maxWInText'], $conf['maxWInText.']);
573 $fiftyPercentWidthInText = round($maxW / 100 * 50);
574 // in Text
575 if ($contentPosition >= 16) {
576 if (!$maxWInText) {
577 // If maxWInText is not set, it's calculated to the 50% of the max
578 $maxW = $fiftyPercentWidthInText;
579 } else {
580 $maxW = $maxWInText;
581 }
582 }
583 // max usuable width for images (without spacers and borders)
584 $netW = $maxW - $colspacing * ($colCount - 1) - $colCount * $border * ($borderThickness + $borderSpace) * 2;
585 // Specify the maximum width for each column
586 $columnWidths = $this->getImgColumnWidths($conf, $colCount, $netW);
587 $image_compression = (int)$this->cObj->stdWrap($conf['image_compression'], $conf['image_compression.']);
588 $image_effects = (int)$this->cObj->stdWrap($conf['image_effects'], $conf['image_effects.']);
589 $image_frames = (int)$this->cObj->stdWrap($conf['image_frames.']['key'], $conf['image_frames.']['key.']);
590 // EqualHeight
591 $equalHeight = (int)$this->cObj->stdWrap($conf['equalH'], $conf['equalH.']);
592 if ($equalHeight) {
593 $relations_cols = array();
594 // contains the individual width of all images after scaling to $equalHeight
595 $imgWidths = array();
596 for ($a = 0; $a < $imgCount; $a++) {
597 $imgKey = $a + $imgStart;
598
599 /** @var $file \TYPO3\CMS\Core\Resource\File */
600 if (MathUtility::canBeInterpretedAsInteger($imgs[$imgKey])) {
601 if ($imgListContainsReferenceUids) {
602 $file = $this->getResourceFactory()->getFileReferenceObject((int)$imgs[$imgKey])->getOriginalFile();
603 } else {
604 $file = $this->getResourceFactory()->getFileObject((int)$imgs[$imgKey]);
605 }
606
607 } else {
608 $file = $this->getResourceFactory()->getFileObjectFromCombinedIdentifier($imgPath . $imgs[$imgKey]);
609 }
610
611 // relationship between the original height and the wished height
612 $rel = $file->getProperty('height') / $equalHeight;
613 // if relations is zero, then the addition of this value is omitted as the image is not expected to display because of some error.
614 if ($rel) {
615 $imgWidths[$a] = $file->getProperty('width') / $rel;
616 // counts the total width of the row with the new height taken into consideration.
617 $relations_cols[(int)floor($a / $colCount)] += $imgWidths[$a];
618 }
619 }
620 }
621 // Fetches pictures
622 $splitArr = array();
623 $splitArr['imgObjNum'] = $conf['imgObjNum'];
624 $splitArr = $GLOBALS['TSFE']->tmpl->splitConfArray($splitArr, $imgCount);
625 // Contains the width of every image row
626 $imageRowsFinalWidths = array();
627 // Array index of $imgsTag will be the same as in $imgs, but $imgsTag only contains the images that are actually shown
628 $imgsTag = array();
629 $origImages = array();
630 $rowIdx = 0;
631 for ($a = 0; $a < $imgCount; $a++) {
632 $imgKey = $a + $imgStart;
633 // If the image cannot be interpreted as integer (therefore filename and no FAL id), add the image path
634 if (MathUtility::canBeInterpretedAsInteger($imgs[$imgKey])) {
635 $totalImagePath = (int)$imgs[$imgKey];
636 $this->initializeCurrentFileInContentObjectRenderer($totalImagePath, $imgListContainsReferenceUids);
637 } else {
638 $totalImagePath = $imgPath . $imgs[$imgKey];
639 }
640 // register IMG_NUM is kept for backwards compatibility
641 $GLOBALS['TSFE']->register['IMAGE_NUM'] = $imgKey;
642 $GLOBALS['TSFE']->register['IMAGE_NUM_CURRENT'] = $imgKey;
643 $GLOBALS['TSFE']->register['ORIG_FILENAME'] = $totalImagePath;
644 $this->cObj->data[$this->cObj->currentValKey] = $totalImagePath;
645 $imgObjNum = (int)$splitArr[$a]['imgObjNum'];
646 $imgConf = $conf[$imgObjNum . '.'];
647 if ($equalHeight) {
648 if ($a % $colCount == 0) {
649 // A new row starts
650 // Reset accumulated net width
651 $accumWidth = 0;
652 // Reset accumulated desired width
653 $accumDesiredWidth = 0;
654 $rowTotalMaxW = $relations_cols[$rowIdx];
655 if ($rowTotalMaxW > $netW && $netW > 0) {
656 $scale = $rowTotalMaxW / $netW;
657 } else {
658 $scale = 1;
659 }
660 $desiredHeight = $equalHeight / $scale;
661 $rowIdx++;
662 }
663 // This much width is available for the remaining images in this row (int)
664 $availableWidth = $netW - $accumWidth;
665 // Theoretical width of resized image. (float)
666 $desiredWidth = $imgWidths[$a] / $scale;
667 // Add this width. $accumDesiredWidth becomes the desired horizontal position
668 $accumDesiredWidth += $desiredWidth;
669 // Calculate width by comparing actual and desired horizontal position.
670 // this evenly distributes rounding errors across all images in this row.
671 $suggestedWidth = round($accumDesiredWidth - $accumWidth);
672 // finalImgWidth may not exceed $availableWidth
673 $finalImgWidth = (int)min($availableWidth, $suggestedWidth);
674 $accumWidth += $finalImgWidth;
675 $imgConf['file.']['width'] = $finalImgWidth;
676 $imgConf['file.']['height'] = round($desiredHeight);
677 // other stuff will be calculated accordingly:
678 unset($imgConf['file.']['maxW']);
679 unset($imgConf['file.']['maxH']);
680 unset($imgConf['file.']['minW']);
681 unset($imgConf['file.']['minH']);
682 unset($imgConf['file.']['width.']);
683 unset($imgConf['file.']['maxW.']);
684 unset($imgConf['file.']['maxH.']);
685 unset($imgConf['file.']['minW.']);
686 unset($imgConf['file.']['minH.']);
687 } else {
688 $imgConf['file.']['maxW'] = $columnWidths[$a % $colCount];
689 }
690 $titleInLink = $this->cObj->stdWrap($imgConf['titleInLink'], $imgConf['titleInLink.']);
691 $titleInLinkAndImg = $this->cObj->stdWrap($imgConf['titleInLinkAndImg'], $imgConf['titleInLinkAndImg.']);
692 $oldATagParms = $GLOBALS['TSFE']->ATagParams;
693 if ($titleInLink) {
694 // Title in A-tag instead of IMG-tag
695 $titleText = trim($this->cObj->stdWrap($imgConf['titleText'], $imgConf['titleText.']));
696 if ($titleText) {
697 // This will be used by the IMAGE call later:
698 $GLOBALS['TSFE']->ATagParams .= ' title="' . htmlspecialchars($titleText) . '"';
699 }
700 }
701
702 // hook to allow custom rendering of a single element
703 // This hook is needed to render alternative content which is not just a plain image,
704 // like showing other FAL content, like videos, things which need to be embedded as JS, ...
705 $customRendering = '';
706 if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['css_styled_content']['pi1_hooks']['render_singleMediaElement'])
707 && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['css_styled_content']['pi1_hooks']['render_singleMediaElement'])) {
708 $hookParameters = array(
709 'file' => $totalImagePath,
710 'imageConfiguration' => $imgConf
711 );
712
713 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['css_styled_content']['pi1_hooks']['render_singleMediaElement'] as $reference) {
714 $customRendering = \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($reference, $hookParameters, $this);
715 // if there is a renderer found, don't run through the other renderers
716 if (!empty($customRendering)) {
717 break;
718 }
719 }
720 }
721
722 if (!empty($customRendering)) {
723 $imgsTag[$imgKey] = $customRendering;
724 } elseif ($imgConf || $imgConf['file']) {
725 if ($this->cObj->image_effects[$image_effects]) {
726 $imgConf['file.']['params'] .= ' ' . $this->cObj->image_effects[$image_effects];
727 }
728 if ($image_frames) {
729 if (is_array($conf['image_frames.'][$image_frames . '.'])) {
730 $imgConf['file.']['m.'] = $conf['image_frames.'][$image_frames . '.'];
731 }
732 }
733 if ($image_compression && $imgConf['file'] != 'GIFBUILDER') {
734 if ($image_compression == 1) {
735 $tempImport = $imgConf['file.']['import'];
736 $tempImport_dot = $imgConf['file.']['import.'];
737 $tempTreatIdAsReference = $imgConf['file.']['treatIdAsReference'];
738 unset($imgConf['file.']);
739 $imgConf['file.']['import'] = $tempImport;
740 $imgConf['file.']['import.'] = $tempImport_dot;
741 $imgConf['file.']['treatIdAsReference'] = $tempTreatIdAsReference;
742 } elseif (isset($this->cObj->image_compression[$image_compression])) {
743 $imgConf['file.']['params'] .= ' ' . $this->cObj->image_compression[$image_compression]['params'];
744 $imgConf['file.']['ext'] = $this->cObj->image_compression[$image_compression]['ext'];
745 unset($imgConf['file.']['ext.']);
746 }
747 }
748 if ($titleInLink && !$titleInLinkAndImg) {
749 // Check if the image will be linked
750 $link = $this->cObj->imageLinkWrap('', $this->cObj->getCurrentFile() ?: $totalImagePath, $imgConf['imageLinkWrap.']);
751 if ($link) {
752 // Title in A-tag only (set above: ATagParams), not in IMG-tag
753 unset($imgConf['titleText']);
754 unset($imgConf['titleText.']);
755 $imgConf['emptyTitleHandling'] = 'removeAttr';
756 }
757 }
758 $imgsTag[$imgKey] = $this->cObj->IMAGE($imgConf);
759 } else {
760 // currentValKey !!!
761 $imgsTag[$imgKey] = $this->cObj->IMAGE(array('file' => $totalImagePath));
762 }
763 // Restore our ATagParams
764 $GLOBALS['TSFE']->ATagParams = $oldATagParms;
765 // Store the original filepath
766 $origImages[$imgKey] = $GLOBALS['TSFE']->lastImageInfo;
767 if ($GLOBALS['TSFE']->lastImageInfo[0] == 0) {
768 $imageRowsFinalWidths[(int)floor($a / $colCount)] += $this->cObj->data['imagewidth'];
769 } else {
770 $imageRowsFinalWidths[(int)floor($a / $colCount)] += $GLOBALS['TSFE']->lastImageInfo[0];
771 }
772 }
773 // How much space will the image-block occupy?
774 $imageBlockWidth = max($imageRowsFinalWidths) + $colspacing * ($colCount - 1) + $colCount * $border * ($borderSpace + $borderThickness) * 2;
775 $GLOBALS['TSFE']->register['rowwidth'] = $imageBlockWidth;
776 $GLOBALS['TSFE']->register['rowWidthPlusTextMargin'] = $imageBlockWidth + $textMargin;
777 // noRows is in fact just one ROW, with the amount of columns specified, where the images are placed in.
778 // noCols is just one COLUMN, each images placed side by side on each row
779 $noRows = $this->cObj->stdWrap($conf['noRows'], $conf['noRows.']);
780 $noCols = $this->cObj->stdWrap($conf['noCols'], $conf['noCols.']);
781 // noRows overrides noCols. They cannot exist at the same time.
782 if ($noRows) {
783 $noCols = 0;
784 $rowCount = 1;
785 }
786 if ($noCols) {
787 $colCount = 1;
788 }
789 // Edit icons:
790 if (!is_array($conf['editIcons.'])) {
791 $conf['editIcons.'] = array();
792 }
793 $editIconsHTML = $conf['editIcons'] && $GLOBALS['TSFE']->beUserLogin ? $this->cObj->editIcons('', $conf['editIcons'], $conf['editIcons.']) : '';
794 // If noRows, we need multiple imagecolumn wraps
795 $imageWrapCols = 1;
796 if ($noRows) {
797 $imageWrapCols = $colCount;
798 }
799 // User wants to separate the rows, but only do that if we do have rows
800 $separateRows = $this->cObj->stdWrap($conf['separateRows'], $conf['separateRows.']);
801 if ($noRows) {
802 $separateRows = 0;
803 }
804 if ($rowCount == 1) {
805 $separateRows = 0;
806 }
807 if ($accessibilityMode) {
808 $imagesInColumns = round($imgCount / ($rowCount * $colCount), 0, PHP_ROUND_HALF_UP);
809 // Apply optionSplit to the list of classes that we want to add to each column
810 $addClassesCol = $conf['addClassesCol'];
811 if (isset($conf['addClassesCol.'])) {
812 $addClassesCol = $this->cObj->stdWrap($addClassesCol, $conf['addClassesCol.']);
813 }
814 $addClassesColConf = $GLOBALS['TSFE']->tmpl->splitConfArray(array('addClassesCol' => $addClassesCol), $colCount);
815 // Apply optionSplit to the list of classes that we want to add to each image
816 $addClassesImage = $conf['addClassesImage'];
817 if (isset($conf['addClassesImage.'])) {
818 $addClassesImage = $this->cObj->stdWrap($addClassesImage, $conf['addClassesImage.']);
819 }
820 $addClassesImageConf = $GLOBALS['TSFE']->tmpl->splitConfArray(array('addClassesImage' => $addClassesImage), $imagesInColumns);
821 $rows = array();
822 $currentImage = 0;
823 // Set the class for the caption (split or global)
824 $classCaptionAlign = array(
825 'center' => 'csc-textpic-caption-c',
826 'right' => 'csc-textpic-caption-r',
827 'left' => 'csc-textpic-caption-l'
828 );
829 $captionAlign = $this->cObj->stdWrap($conf['captionAlign'], $conf['captionAlign.']);
830 // Iterate over the rows
831 for ($rowCounter = 1; $rowCounter <= $rowCount; $rowCounter++) {
832 $rowColumns = array();
833 // Iterate over the columns
834 for ($columnCounter = 1; $columnCounter <= $colCount; $columnCounter++) {
835 $columnImages = array();
836 // Iterate over the amount of images allowed in a column
837 for ($imagesCounter = 1; $imagesCounter <= $imagesInColumns; $imagesCounter++) {
838 $image = NULL;
839 $splitCaption = NULL;
840 $imageMarkers = ($captionMarkers = array());
841 $single = '&nbsp;';
842 // Set the key of the current image
843 $imageKey = $currentImage + $imgStart;
844 // Register IMAGE_NUM_CURRENT for the caption
845 $GLOBALS['TSFE']->register['IMAGE_NUM_CURRENT'] = $imageKey;
846 $this->cObj->data[$this->cObj->currentValKey] = $origImages[$imageKey]['origFile'];
847 if (MathUtility::canBeInterpretedAsInteger($imgs[$imageKey])) {
848 $this->initializeCurrentFileInContentObjectRenderer((int)$imgs[$imageKey], $imgListContainsReferenceUids);
849 } elseif (!isset($imgs[$imageKey])) {
850 // If not all columns in the last row are filled $imageKey gets larger than
851 // the array. In that case we clear the current file.
852 $this->cObj->setCurrentFile(NULL);
853 }
854 // Get the image if not an empty cell
855 if (isset($imgsTag[$imageKey])) {
856 $image = $this->cObj->stdWrap($imgsTag[$imageKey], $conf['imgTagStdWrap.']);
857 // Add the edit icons
858 if ($editIconsHTML) {
859 $image .= $this->cObj->stdWrap($editIconsHTML, $conf['editIconsStdWrap.']);
860 }
861 // Wrap the single image
862 $single = $this->cObj->stdWrap($image, $conf['singleStdWrap.']);
863 // Get the caption
864 if (!$renderGlobalCaption) {
865 $imageMarkers['caption'] = $this->cObj->stdWrap($this->cObj->cObjGet($conf['caption.'], 'caption.'), $conf['caption.']);
866 if ($captionAlign) {
867 $captionMarkers['classes'] = ' ' . $classCaptionAlign[$captionAlign];
868 }
869 $imageMarkers['caption'] = $this->cObj->substituteMarkerArray($imageMarkers['caption'], $captionMarkers, '###|###', 1, 1);
870 }
871 if ($addClassesImageConf[$imagesCounter - 1]['addClassesImage']) {
872 $imageMarkers['classes'] = ' ' . $addClassesImageConf[($imagesCounter - 1)]['addClassesImage'];
873 }
874 }
875 $columnImages[] = $this->cObj->substituteMarkerArray($single, $imageMarkers, '###|###', 1, 1);
876 $currentImage++;
877 }
878 $rowColumn = $this->cObj->stdWrap(implode(LF, $columnImages), $conf['columnStdWrap.']);
879 // Start filling the markers for columnStdWrap
880 $columnMarkers = array();
881 if ($addClassesColConf[$columnCounter - 1]['addClassesCol']) {
882 $columnMarkers['classes'] = ' ' . $addClassesColConf[($columnCounter - 1)]['addClassesCol'];
883 }
884 $rowColumns[] = $this->cObj->substituteMarkerArray($rowColumn, $columnMarkers, '###|###', 1, 1);
885 }
886 if ($noRows) {
887 $rowConfiguration = $conf['noRowsStdWrap.'];
888 } elseif ($rowCounter == $rowCount) {
889 $rowConfiguration = $conf['lastRowStdWrap.'];
890 } else {
891 $rowConfiguration = $conf['rowStdWrap.'];
892 }
893 $row = $this->cObj->stdWrap(implode(LF, $rowColumns), $rowConfiguration);
894 // Start filling the markers for columnStdWrap
895 $rowMarkers = array();
896 $rows[] = $this->cObj->substituteMarkerArray($row, $rowMarkers, '###|###', 1, 1);
897 }
898 $images = $this->cObj->stdWrap(implode(LF, $rows), $conf['allStdWrap.']);
899 // Start filling the markers for allStdWrap
900 $allMarkers = array();
901 $classes = array();
902 // Add the global caption to the allStdWrap marker array if set
903 if ($globalCaption) {
904 $allMarkers['caption'] = $globalCaption;
905 if ($captionAlign) {
906 $classes[] = $classCaptionAlign[$captionAlign];
907 }
908 }
909 // Set the margin for image + text, no wrap always to avoid multiple stylesheets
910 $noWrapMargin = (int)(($maxWInText ? $maxWInText : $fiftyPercentWidthInText) + (int)$this->cObj->stdWrap($conf['textMargin'], $conf['textMargin.']));
911 $this->addPageStyle('.csc-textpic-intext-right-nowrap .csc-textpic-text', 'margin-right: ' . $noWrapMargin . 'px;');
912 $this->addPageStyle('.csc-textpic-intext-left-nowrap .csc-textpic-text', 'margin-left: ' . $noWrapMargin . 'px;');
913 // Beside Text where the image block width is not equal to maxW
914 if ($contentPosition == 24 && $maxW != $imageBlockWidth) {
915 $noWrapMargin = $imageBlockWidth + $textMargin;
916 // Beside Text, Right
917 if ($imagePosition == 1) {
918 $this->addPageStyle('.csc-textpic-intext-right-nowrap-' . $noWrapMargin . ' .csc-textpic-text', 'margin-right: ' . $noWrapMargin . 'px;');
919 $classes[] = 'csc-textpic-intext-right-nowrap-' . $noWrapMargin;
920 } elseif ($imagePosition == 2) {
921 $this->addPageStyle('.csc-textpic-intext-left-nowrap-' . $noWrapMargin . ' .csc-textpic-text', 'margin-left: ' . $noWrapMargin . 'px;');
922 $classes[] = 'csc-textpic-intext-left-nowrap-' . $noWrapMargin;
923 }
924 }
925 // Add the border class if needed
926 if ($border) {
927 $classes[] = $conf['borderClass'] ?: 'csc-textpic-border';
928 }
929 // Add the class for equal height if needed
930 if ($equalHeight) {
931 $classes[] = 'csc-textpic-equalheight';
932 }
933 $addClasses = $this->cObj->stdWrap($conf['addClasses'], $conf['addClasses.']);
934 if ($addClasses) {
935 $classes[] = $addClasses;
936 }
937 if ($classes) {
938 $class = ' ' . implode(' ', $classes);
939 }
940 // Fill the markers for the allStdWrap
941 $images = $this->cObj->substituteMarkerArray($images, $allMarkers, '###|###', 1, 1);
942 } else {
943 // Apply optionSplit to the list of classes that we want to add to each image
944 $addClassesImage = $conf['addClassesImage'];
945 if (isset($conf['addClassesImage.'])) {
946 $addClassesImage = $this->cObj->stdWrap($addClassesImage, $conf['addClassesImage.']);
947 }
948 $addClassesImageConf = $GLOBALS['TSFE']->tmpl->splitConfArray(array('addClassesImage' => $addClassesImage), $colCount);
949 // Render the images
950 $images = '';
951 for ($c = 0; $c < $imageWrapCols; $c++) {
952 $tmpColspacing = $colspacing;
953 if ($c == $imageWrapCols - 1 && $imagePosition == 2 || $c == 0 && ($imagePosition == 1 || $imagePosition == 0)) {
954 // Do not add spacing after column if we are first column (left) or last column (center/right)
955 $tmpColspacing = 0;
956 }
957 $thisImages = '';
958 $allRows = '';
959 $maxImageSpace = 0;
960 $imgsTagCount = count($imgsTag);
961 for ($i = $c; $i < $imgsTagCount; $i = $i + $imageWrapCols) {
962 $imgKey = $i + $imgStart;
963 $colPos = $i % $colCount;
964 if ($separateRows && $colPos == 0) {
965 $thisRow = '';
966 }
967 // Render one image
968 if ($origImages[$imgKey][0] == 0) {
969 $imageSpace = $this->cObj->data['imagewidth'] + $border * ($borderSpace + $borderThickness) * 2;
970 } else {
971 $imageSpace = $origImages[$imgKey][0] + $border * ($borderSpace + $borderThickness) * 2;
972 }
973 $GLOBALS['TSFE']->register['IMAGE_NUM'] = $imgKey;
974 $GLOBALS['TSFE']->register['IMAGE_NUM_CURRENT'] = $imgKey;
975 $GLOBALS['TSFE']->register['ORIG_FILENAME'] = $origImages[$imgKey]['origFile'];
976 $GLOBALS['TSFE']->register['imagewidth'] = $origImages[$imgKey][0];
977 $GLOBALS['TSFE']->register['imagespace'] = $imageSpace;
978 $GLOBALS['TSFE']->register['imageheight'] = $origImages[$imgKey][1];
979 if (MathUtility::canBeInterpretedAsInteger($imgs[$imgKey])) {
980 $this->initializeCurrentFileInContentObjectRenderer(intval($imgs[$imgKey]), $imgListContainsReferenceUids);
981 }
982 if ($imageSpace > $maxImageSpace) {
983 $maxImageSpace = $imageSpace;
984 }
985 $thisImage = '';
986 $thisImage .= $this->cObj->stdWrap($imgsTag[$imgKey], $conf['imgTagStdWrap.']);
987 if (!$renderGlobalCaption) {
988 $thisImage .= $this->cObj->stdWrap($this->cObj->cObjGet($conf['caption.'], 'caption.'), $conf['caption.']);
989 }
990 if ($editIconsHTML) {
991 $thisImage .= $this->cObj->stdWrap($editIconsHTML, $conf['editIconsStdWrap.']);
992 }
993 $thisImage = $this->cObj->stdWrap($thisImage, $conf['oneImageStdWrap.']);
994 $classes = '';
995 if ($addClassesImageConf[$colPos]['addClassesImage']) {
996 $classes = ' ' . $addClassesImageConf[$colPos]['addClassesImage'];
997 }
998 $thisImage = str_replace('###CLASSES###', $classes, $thisImage);
999 if ($separateRows) {
1000 $thisRow .= $thisImage;
1001 } else {
1002 $allRows .= $thisImage;
1003 }
1004 $GLOBALS['TSFE']->register['columnwidth'] = $maxImageSpace + $tmpColspacing;
1005 // Close this row at the end (colCount), or the last row at the final end
1006 if ($separateRows && $i + 1 == count($imgsTag)) {
1007 // Close the very last row with either normal configuration or lastRow stdWrap
1008 $allRows .= $this->cObj->stdWrap(
1009 $thisRow,
1010 is_array($conf['imageLastRowStdWrap.']) ? $conf['imageLastRowStdWrap.'] : $conf['imageRowStdWrap.']
1011 );
1012 } elseif ($separateRows && $colPos == $colCount - 1) {
1013 $allRows .= $this->cObj->stdWrap($thisRow, $conf['imageRowStdWrap.']);
1014 }
1015 }
1016 if ($separateRows) {
1017 $thisImages .= $allRows;
1018 } else {
1019 $thisImages .= $this->cObj->stdWrap($allRows, $conf['noRowsStdWrap.']);
1020 }
1021 if ($noRows) {
1022 // Only needed to make columns, rather than rows:
1023 $images .= $this->cObj->stdWrap($thisImages, $conf['imageColumnStdWrap.']);
1024 } else {
1025 $images .= $thisImages;
1026 }
1027 }
1028 // Add the global caption, if not split
1029 if ($globalCaption) {
1030 $images .= $globalCaption;
1031 }
1032 // CSS-classes
1033 $captionClass = '';
1034 $classCaptionAlign = array(
1035 'center' => 'csc-textpic-caption-c',
1036 'right' => 'csc-textpic-caption-r',
1037 'left' => 'csc-textpic-caption-l'
1038 );
1039 $captionAlign = $this->cObj->stdWrap($conf['captionAlign'], $conf['captionAlign.']);
1040 if ($captionAlign) {
1041 $captionClass = $classCaptionAlign[$captionAlign];
1042 }
1043 $borderClass = '';
1044 if ($border) {
1045 $borderClass = $conf['borderClass'] ?: 'csc-textpic-border';
1046 }
1047 // Multiple classes with all properties, to be styled in CSS
1048 $class = '';
1049 $class .= $borderClass ? ' ' . $borderClass : '';
1050 $class .= $captionClass ? ' ' . $captionClass : '';
1051 $class .= $equalHeight ? ' csc-textpic-equalheight' : '';
1052 $addClasses = $this->cObj->stdWrap($conf['addClasses'], $conf['addClasses.']);
1053 $class .= $addClasses ? ' ' . $addClasses : '';
1054 // Do we need a width in our wrap around images?
1055 $imgWrapWidth = '';
1056 if ($position == 0 || $position == 8) {
1057 // For 'center' we always need a width: without one, the margin:auto trick won't work
1058 $imgWrapWidth = $imageBlockWidth;
1059 }
1060 if ($rowCount > 1) {
1061 // For multiple rows we also need a width, so that the images will wrap
1062 $imgWrapWidth = $imageBlockWidth;
1063 }
1064 if ($globalCaption) {
1065 // If we have a global caption, we need the width so that the caption will wrap
1066 $imgWrapWidth = $imageBlockWidth;
1067 }
1068 // Wrap around the whole image block
1069 $GLOBALS['TSFE']->register['totalwidth'] = $imgWrapWidth;
1070 if ($imgWrapWidth) {
1071 $images = $this->cObj->stdWrap($images, $conf['imageStdWrap.']);
1072 } else {
1073 $images = $this->cObj->stdWrap($images, $conf['imageStdWrapNoWidth.']);
1074 }
1075 }
1076
1077 $output = str_replace(
1078 array(
1079 '###TEXT###',
1080 '###IMAGES###',
1081 '###CLASSES###'
1082 ),
1083 array(
1084 $content,
1085 $images,
1086 $class
1087 ),
1088 $this->cObj->cObjGetSingle($conf['layout'], $conf['layout.'])
1089 );
1090
1091 if ($restoreRegisters) {
1092 $this->cObj->LOAD_REGISTER(array(), 'RESTORE_REGISTER');
1093 }
1094
1095 return $output;
1096 }
1097
1098 /**
1099 * Loads the file / file reference object and sets it in the
1100 * currentFile property of the ContentObjectRenderer.
1101 *
1102 * This makes the file data available during image rendering.
1103 *
1104 * @param int $fileUid The UID of the file or file reference (depending on $treatAsReference) that should be loaded.
1105 * @param bool $treatAsReference If TRUE the given UID will be used to load a file reference otherwise it will be used to load a regular file.
1106 * @return void
1107 */
1108 protected function initializeCurrentFileInContentObjectRenderer($fileUid, $treatAsReference) {
1109 $resourceFactory = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance();
1110 if ($treatAsReference) {
1111 $imageFile = $resourceFactory->getFileReferenceObject($fileUid);
1112 } else {
1113 $imageFile = $resourceFactory->getFileObject($fileUid);
1114 }
1115 $this->cObj->setCurrentFile($imageFile);
1116 }
1117
1118 /***********************************
1119 * Rendering of Content Element properties
1120 ***********************************/
1121
1122 /**
1123 * Add top or bottom margin to the content element
1124 *
1125 * Constructs and adds a class to the content element. This class selector
1126 * and its declaration are added to the specific page styles.
1127 *
1128 * @param string $content Content input. Not used, ignore.
1129 * @param array $configuration TypoScript configuration
1130 * @return string The class name
1131 */
1132 public function renderSpace($content, array $configuration) {
1133 // Look for hook before running default code for function
1134 if (method_exists($this, 'hookRequest') && ($hookObject = $this->hookRequest('renderSpace'))) {
1135 return $hookObject->renderSpace($content, $configuration);
1136 }
1137 if (isset($configuration['space']) && in_array($configuration['space'], array('before', 'after'))) {
1138 $constant = (int)$configuration['constant'];
1139 if ($configuration['space'] === 'before') {
1140 $value = $constant + $this->cObj->data['spaceBefore'];
1141 $declaration = 'margin-top: ' . $value . 'px !important;';
1142 } else {
1143 $value = $constant + $this->cObj->data['spaceAfter'];
1144 $declaration = 'margin-bottom: ' . $value . 'px !important;';
1145 }
1146 if (!empty($value)) {
1147 if ($configuration['classStdWrap.']) {
1148 $className = $this->cObj->stdWrap($value, $configuration['classStdWrap.']);
1149 } else {
1150 $className = $value;
1151 }
1152 $selector = '.' . trim($className);
1153 $this->addPageStyle($selector, $declaration);
1154 return $className;
1155 }
1156 }
1157 }
1158
1159 /************************************
1160 * Helper functions
1161 ************************************/
1162
1163 /**
1164 * Returns a link text string which replaces underscores in filename with
1165 * blanks.
1166 *
1167 * Has the possibility to cut off FileType.
1168 *
1169 * @param array $links
1170 * @param string $fileName
1171 * @param boolean $useSpaces
1172 * @param boolean $cutFileExtension
1173 * @return array modified array with new link text
1174 */
1175 protected function beautifyFileLink(array $links, $fileName, $useSpaces = FALSE, $cutFileExtension = FALSE) {
1176 $linkText = $fileName;
1177 if ($useSpaces) {
1178 $linkText = str_replace('_', ' ', $linkText);
1179 }
1180 if ($cutFileExtension) {
1181 $pos = strrpos($linkText, '.');
1182 $linkText = substr($linkText, 0, $pos);
1183 }
1184 $links[1] = str_replace('>' . $fileName . '<', '>' . htmlspecialchars($linkText) . '<', $links[1]);
1185 return $links;
1186 }
1187
1188 /**
1189 * Returns table attributes for uploads / tables.
1190 *
1191 * @param array $conf TypoScript configuration array
1192 * @param integer $type The "layout" type
1193 * @return array Array with attributes inside.
1194 * @todo Define visibility
1195 */
1196 public function getTableAttributes($conf, $type) {
1197 // Initializing:
1198 $tableTagParams_conf = $conf['tableParams_' . $type . '.'];
1199 $border = $this->cObj->data['table_border'] ? (int)$this->cObj->data['table_border'] : $tableTagParams_conf['border'];
1200 $cellSpacing = $this->cObj->data['table_cellspacing'] ? (int)$this->cObj->data['table_cellspacing'] : $tableTagParams_conf['cellspacing'];
1201 $cellPadding = $this->cObj->data['table_cellpadding'] ? (int)$this->cObj->data['table_cellpadding'] : $tableTagParams_conf['cellpadding'];
1202 $summary = trim(htmlspecialchars($this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'acctables_summary')));
1203 // Create table attributes and classes array:
1204 $tableTagParams = ($classes = array());
1205 // Table attributes for all doctypes except HTML5
1206 if ($GLOBALS['TSFE']->config['config']['doctype'] !== 'html5') {
1207 $tableTagParams['border'] = $border;
1208 $tableTagParams['cellspacing'] = $cellSpacing;
1209 $tableTagParams['cellpadding'] = $cellPadding;
1210 if ($summary) {
1211 $tableTagParams['summary'] = $summary;
1212 }
1213 } else {
1214 if ($border) {
1215 // Border property has changed, now with class
1216 $borderClass = 'contenttable-border-' . $border;
1217 $borderDeclaration = 'border-width: ' . $border . 'px; border-style: solid;';
1218 $this->addPageStyle('.' . $borderClass, $borderDeclaration);
1219 $classes[] = $borderClass;
1220 }
1221 if ($cellSpacing) {
1222 // Border attribute for HTML5 is 1 when there is cell spacing
1223 $tableTagParams['border'] = 1;
1224 // Use CSS3 border-spacing in class to have cell spacing
1225 $cellSpacingClass = 'contenttable-cellspacing-' . $cellSpacing;
1226 $cellSpacingDeclaration = 'border-spacing: ' . $cellSpacing . 'px;';
1227 $this->addPageStyle('.' . $cellSpacingClass, $cellSpacingDeclaration);
1228 $classes[] = $cellSpacingClass;
1229 }
1230 if ($cellPadding) {
1231 // Cell padding property has changed, now with class
1232 $cellPaddingClass = 'contenttable-cellpadding-' . $cellPadding;
1233 $cellSpacingSelector = '.' . $cellPaddingClass . ' td, .' . $cellPaddingClass . ' th';
1234 $cellPaddingDeclaration = 'padding: ' . $cellPadding . 'px;';
1235 $this->addPageStyle($cellSpacingSelector, $cellPaddingDeclaration);
1236 $classes[] = $cellPaddingClass;
1237 }
1238 }
1239 // Background color is class
1240 if (isset($conf['color.'][$this->cObj->data['table_bgColor']]) && !empty($conf['color.'][$this->cObj->data['table_bgColor']])) {
1241 $classes[] = 'contenttable-color-' . $this->cObj->data['table_bgColor'];
1242 }
1243 if (!empty($classes)) {
1244 $tableTagParams['class'] = ' ' . implode(' ', $classes);
1245 }
1246 // Return result:
1247 return $tableTagParams;
1248 }
1249
1250 /**
1251 * Add a style to the page, specific for this page
1252 *
1253 * The selector can be a contextual selector, like '#id .class p'
1254 * The presence of the selector is checked to avoid multiple entries of the
1255 * same selector.
1256 *
1257 * @param string $selector The selector
1258 * @param string $declaration The declaration
1259 * @return void
1260 */
1261 protected function addPageStyle($selector, $declaration) {
1262 if (!isset($GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_cssstyledcontent.']['_CSS_PAGE_STYLE'])) {
1263 $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_cssstyledcontent.']['_CSS_PAGE_STYLE'] = array();
1264 }
1265 if (!isset($GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_cssstyledcontent.']['_CSS_PAGE_STYLE'][$selector])) {
1266 $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_cssstyledcontent.']['_CSS_PAGE_STYLE'][$selector] = TAB . $selector . ' { ' . $declaration . ' }';
1267 }
1268 }
1269
1270 /**
1271 * Returns an object reference to the hook object if any
1272 *
1273 * @param string $functionName Name of the function you want to call / hook key
1274 * @return object Hook object, if any. Otherwise NULL.
1275 * @todo Define visibility
1276 */
1277 public function hookRequest($functionName) {
1278 global $TYPO3_CONF_VARS;
1279 // Hook: menuConfig_preProcessModMenu
1280 if ($TYPO3_CONF_VARS['EXTCONF']['css_styled_content']['pi1_hooks'][$functionName]) {
1281 $hookObj = GeneralUtility::getUserObj($TYPO3_CONF_VARS['EXTCONF']['css_styled_content']['pi1_hooks'][$functionName]);
1282 if (method_exists($hookObj, $functionName)) {
1283 $hookObj->pObj = $this;
1284 return $hookObj;
1285 }
1286 }
1287 }
1288
1289 /**
1290 * Get the ResourceFactory
1291 *
1292 * @return \TYPO3\CMS\Core\Resource\ResourceFactory
1293 */
1294 protected function getResourceFactory() {
1295 return \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance();
1296 }
1297 }