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