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