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