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