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