[FEATURE] Introduce DataProcessor for media galleries 09/40509/10
authorBenjamin Mack <benni@typo3.org>
Sun, 21 Jun 2015 13:47:51 +0000 (15:47 +0200)
committerPatrick Broens <patrick@patrickbroens.nl>
Tue, 14 Jul 2015 14:54:08 +0000 (16:54 +0200)
The logic for working with galleries and calculating the maximum media
size is done in a separate galleryProcessor.

Resolves: #67663
Releases: master
Change-Id: If00c3b800b2c6723d923cb3e3d427b850f9be7fe
Reviewed-on: http://review.typo3.org/40509
Reviewed-by: Frans Saris <franssaris@gmail.com>
Tested-by: Frans Saris <franssaris@gmail.com>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
Reviewed-by: Patrick Broens <patrick@patrickbroens.nl>
Tested-by: Patrick Broens <patrick@patrickbroens.nl>
typo3/sysext/core/Documentation/Changelog/master/Feature-67663-IntroduceDataProcessorForMediaGalleries.rst [new file with mode: 0644]
typo3/sysext/frontend/Classes/DataProcessing/GalleryProcessor.php [new file with mode: 0644]
typo3/sysext/frontend/Tests/Unit/Processor/GalleryProcessorTest.php [new file with mode: 0644]

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-67663-IntroduceDataProcessorForMediaGalleries.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-67663-IntroduceDataProcessorForMediaGalleries.rst
new file mode 100644 (file)
index 0000000..22db04d
--- /dev/null
@@ -0,0 +1,64 @@
+=============================================================
+Feature: #67663 - Introduce DataProcessor for media galleries
+=============================================================
+
+Description
+===========
+
+The logic for working with galleries and calculating the maximum asset size is done in a separate GalleryProcessor.
+The GalleryProcessor uses the files already present in the processedData array for his calculations. The FilesProcessor can be used to fetch the files.
+
+.. code-block:: typoscript
+
+       tt_content.text_media.20 = FLUIDTEMPLATE
+       tt_content.image.20 {
+               file = EXT:myextension/Resources/Private/Templates/ContentObjects/Image.html
+
+               dataProcessing {
+
+                       # Process files
+                       10 = TYPO3\CMS\Frontend\DataProcessing\FilesProcessor
+
+                       # Calculate gallery info
+                       20 = TYPO3\CMS\Frontend\DataProcessing\GalleryProcessor
+                       20 {
+
+                               # filesProcessedDataKey :: Key in processedData array that holds the files (default: files) + stdWrap
+                               filesProcessedDataKey = files
+
+                               # mediaOrientation :: Media orientation, see: TCA[tt_content][column][imageorient] (default: data.imageorient) + stdWrap
+                               mediaOrientation.field = imageorient
+
+                               # numberOfColumns :: Number of columns (default: data.imagecols) + stdWrap
+                               numberOfColumns.field = imagecols
+
+                               # equalMediaHeight :: Equal media height in pixels (default: data.imageheight) + stdWrap
+                               equalMediaHeight.field = imageheight
+
+                               # equalMediaWidth :: Equal media width in pixels (default: data.imagewidth) + stdWrap
+                               equalMediaWidth.field = imagewidth
+
+                               # maxGalleryWidth :: Max gallery width in pixels (default: 600) + stdWrap
+                               maxGalleryWidth = 1000
+
+                               # maxGalleryWidthInText :: Max gallery width in pixels when orientation intext (default: 300) + stdWrap
+                               maxGalleryWidthInText = 1000
+
+                               # columnSpacing :: Column spacing width in pixels (default: 0) + stdWrap
+                               columnSpacing = 0
+
+                               # borderEnabled :: Border enabled (default: data.imageborder) + stdWrap
+                               borderEnabled.field = imageborder
+
+                               # borderWidth :: Border width in pixels (default: 0) + stdWrap
+                               borderWidth = 0
+
+                               # borderPadding :: Border padding in pixels  (default: 0) + stdWrap
+                               borderPadding = 10
+
+                               # as :: Name of key in processedData array where result is placed (default: gallery) + stdWrap
+                               as = gallery
+                       }
+               }
+
+       }
\ No newline at end of file
diff --git a/typo3/sysext/frontend/Classes/DataProcessing/GalleryProcessor.php b/typo3/sysext/frontend/Classes/DataProcessing/GalleryProcessor.php
new file mode 100644 (file)
index 0000000..5c4765f
--- /dev/null
@@ -0,0 +1,492 @@
+<?php
+namespace TYPO3\CMS\Frontend\DataProcessing;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
+use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException;
+
+/**
+ * This data processor will calculate rows, columns and dimensions for a gallery
+ * based on several settings and can be used for f.i. the CType "textmedia"
+ *
+ * The output will be an array which contains the rows and columns,
+ * including the file references and the calculated width and height for each media element,
+ * but also some more information of the gallery, like position, width and counters
+ *
+ * Example TypoScript configuration:
+ *
+ * 10 = TYPO3\CMS\Frontend\DataProcessing\GalleryProcessor
+ * 10 {
+ *   filesProcessedDataKey = files
+ *   mediaOrientation.field = imageorient
+ *   numberOfColumns.field = imagecols
+ *   equalMediaHeight.field = imageheight
+ *   equalMediaWidth.field = imagewidth
+ *   columnSpacing = 0
+ *   borderEnabled.field = imageborder
+ *   borderPadding = 0
+ *   borderWidth = 0
+ *   maxGalleryWidth = {$styles.content.imgtext.maxW}
+ *   maxGalleryWidthInText = {$styles.content.imgtext.maxWInText}
+ *   as = gallery
+ * }
+ *
+ * Output example:
+ *
+ * gallery {
+ *   position {
+ *     horizontal = center
+ *     vertical = above
+ *     noWrap = FALSE
+ *   }
+ *   width = 600
+ *   count {
+ *     files = 2
+ *     columns = 1
+ *     rows = 2
+ *   }
+ *   rows {
+ *     1 {
+ *       columns {
+ *         1 {
+ *           media = TYPO3\CMS\Core\Resource\FileReference
+ *           dimensions {
+ *             width = 600
+ *             height = 400
+ *           }
+ *         }
+ *       }
+ *     }
+ *     2 {
+ *       columns {
+ *         1 {
+ *           media = TYPO3\CMS\Core\Resource\FileReference
+ *           dimensions {
+ *             width = 600
+ *             height = 400
+ *           }
+ *         }
+ *       }
+ *     }
+ *   }
+ *   columnSpacing = 0
+ *   border {
+ *     enabled = FALSE
+ *     width = 0
+ *     padding = 0
+ *   }
+ * }
+ *
+ */
+class GalleryProcessor implements DataProcessorInterface {
+
+       /**
+        * The content object renderer
+        *
+        * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
+        */
+       protected $contentObjectRenderer;
+
+       /**
+        * The processor configuration
+        *
+        * @var array
+        */
+       protected $processorConfiguration;
+
+       /**
+        * Matching the tt_content field towards the imageOrient option
+        *
+        * @var array
+        */
+       protected $availableGalleryPositions = [
+               'horizontal' => [
+                       'center' => [0, 8],
+                       'right' => [1, 9, 17, 25],
+                       'left' => [2, 10, 18, 26]
+               ],
+               'vertical' => [
+                       'above' => [0, 1, 2],
+                       'intext' => [17, 18, 25, 26],
+                       'below' => [8, 9, 10]
+               ]
+       ];
+
+       /**
+        * Storage for processed data
+        *
+        * @var array
+        */
+       protected $galleryData = [
+               'position' => [
+                       'horizontal' => '',
+                       'vertical' => '',
+                       'noWrap' => FALSE
+               ],
+               'width' => 0,
+               'count' => [
+                       'files' => 0,
+                       'columns' => 0,
+                       'rows' => 0,
+               ],
+               'columnSpacing' => 0,
+               'border' => [
+                       'enabled' => FALSE,
+                       'width' => 0,
+                       'padding' => 0,
+               ],
+               'rows' => []
+       ];
+
+       /**
+        * @var int
+        */
+       protected $numberOfColumns;
+
+       /**
+        * @var int
+        */
+       protected $mediaOrientation;
+
+       /**
+        * @var int
+        */
+       protected $maxGalleryWidth;
+
+       /**
+        * @var int
+        */
+       protected $maxGalleryWidthInText;
+
+       /**
+        * @var int
+        */
+       protected $equalMediaHeight;
+
+       /**
+        * @var int
+        */
+       protected $equalMediaWidth;
+
+       /**
+        * @var int
+        */
+       protected $columnSpacing;
+
+       /**
+        * @var bool
+        */
+       protected $borderEnabled;
+
+       /**
+        * @var int
+        */
+       protected $borderWidth;
+
+       /**
+        * @var int
+        */
+       protected $borderPadding;
+
+       /**
+        * The (filtered) media files to be used in the gallery
+        *
+        * @var fileObjects[]
+        */
+       protected $fileObjects = [];
+
+       /**
+        * The calculated dimensions for each media element
+        *
+        * @var array
+        */
+       protected $mediaDimensions = [];
+
+       /**
+        * Process data for a gallery, for instance the CType "textmedia"
+        *
+        * @param ContentObjectRenderer $cObj The content object renderer, which contains data of the content element
+        * @param array $contentObjectConfiguration The configuration of Content Object
+        * @param array $processorConfiguration The configuration of this processor
+        * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View)
+        * @return array the processed data as key/value store
+        * @throws ContentRenderingException
+        */
+       public function process(
+               ContentObjectRenderer $cObj,
+               array $contentObjectConfiguration,
+               array $processorConfiguration,
+               array $processedData
+       ) {
+               if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) {
+                       return $processedData;
+               }
+
+               $this->contentObjectRenderer = $cObj;
+               $this->processorConfiguration = $processorConfiguration;
+
+               $filesProcessedDataKey = (string)$cObj->stdWrapValue(
+                       'filesProcessedDataKey',
+                       $processorConfiguration,
+                       'files'
+               );
+               if (isset($processedData[$filesProcessedDataKey]) && is_array($processedData[$filesProcessedDataKey])) {
+                       $this->fileObjects = $processedData[$filesProcessedDataKey];
+                       $this->galleryData['count']['files'] = count($this->fileObjects);
+               } else {
+                       throw new ContentRenderingException('No files found for key ' . $filesProcessedDataKey . ' in $processedData.', 1436809789);
+               }
+
+               $this->numberOfColumns = (int)$this->getConfigurationValue('numberOfColumns', 'imagecols');
+               $this->mediaOrientation = (int)$this->getConfigurationValue('mediaOrientation', 'imageorient');
+               $this->maxGalleryWidth = (int)$this->getConfigurationValue('maxGalleryWidth') ?: 600;
+               $this->maxGalleryWidthInText = (int)$this->getConfigurationValue('maxGalleryWidthInText') ?: 300;
+               $this->equalMediaHeight = (int)$this->getConfigurationValue('equalMediaHeight', 'imageheight');
+               $this->equalMediaWidth = (int)$this->getConfigurationValue('equalMediaWidth', 'imagewidth');
+               $this->columnSpacing = (int)$this->getConfigurationValue('columnSpacing');
+               $this->borderEnabled = (bool)$this->getConfigurationValue('borderEnabled', 'imageborder');
+               $this->borderWidth = (int)$this->getConfigurationValue('borderWidth');
+               $this->borderPadding = (int)$this->getConfigurationValue('borderPadding');
+
+               $this->determineGalleryPosition();
+               $this->determineMaximumGalleryWidth();
+
+               $this->calculateRowsAndColumns();
+               $this->calculateMediaWidthsAndHeights();
+
+               $this->prepareGalleryData();
+
+               $targetFieldName = (string)$cObj->stdWrapValue(
+                       'as',
+                       $processorConfiguration,
+                       'gallery'
+               );
+
+               $processedData[$targetFieldName] = $this->galleryData;
+
+               return $processedData;
+       }
+
+       /**
+        * Get configuration value from processorConfiguration
+        * with when $dataArrayKey fallback to value from cObj->data array
+        *
+        * @param string $key
+        * @param string|NULL $dataArrayKey
+        * @return string
+        */
+       protected function getConfigurationValue($key, $dataArrayKey = NULL) {
+               $defaultValue = '';
+               if ($dataArrayKey && isset($this->contentObjectRenderer->data[$dataArrayKey])) {
+                       $defaultValue = $this->contentObjectRenderer->data[$dataArrayKey];
+               }
+               return $this->contentObjectRenderer->stdWrapValue(
+                       $key,
+                       $this->processorConfiguration,
+                       $defaultValue
+               );
+       }
+
+       /**
+        * Define the gallery position
+        *
+        * Gallery has a horizontal and a vertical position towards the text
+        * and a possible wrapping of the text around the gallery.
+        *
+        * @return void
+        */
+       protected function determineGalleryPosition() {
+               foreach ($this->availableGalleryPositions as $positionDirectionKey => $positionDirectionValue) {
+                       foreach ($positionDirectionValue as $positionKey => $positionArray) {
+                               if (in_array($this->mediaOrientation, $positionArray, TRUE)) {
+                                       $this->galleryData['position'][$positionDirectionKey] = $positionKey;
+                               }
+                       }
+               }
+
+               if ($this->mediaOrientation === 25 || $this->mediaOrientation === 26) {
+                       $this->galleryData['position']['noWrap'] = TRUE;
+               }
+       }
+
+       /**
+        * Get the gallery width based on vertical position
+        *
+        * @return void
+        */
+       protected function determineMaximumGalleryWidth() {
+               if ($this->galleryData['position']['vertical'] === 'intext') {
+                       $this->galleryData['width'] = $this->maxGalleryWidthInText;
+               } else {
+                       $this->galleryData['width'] = $this->maxGalleryWidth;
+               }
+       }
+
+       /**
+        * Calculate the amount of rows and columns
+        *
+        * @return void
+        */
+       protected function calculateRowsAndColumns() {
+
+               // If no columns defined, set it to 1
+               $columns = max((int)$this->numberOfColumns, 1);
+
+               // When more columns than media elements, set the columns to the amount of media elements
+               if ($columns > $this->galleryData['count']['files']) {
+                       $columns = $this->galleryData['count']['files'];
+               }
+
+               if ($columns === 0) {
+                       $columns = 1;
+               }
+
+               // Calculate the rows from the amount of files and the columns
+               $rows = ceil($this->galleryData['count']['files'] / $columns);
+
+               $this->galleryData['count']['columns'] = $columns;
+               $this->galleryData['count']['rows'] = (int)$rows;
+       }
+
+       /**
+        * Calculate the width/height of the media elements
+        *
+        * Based on the width of the gallery, defined equal width or height by a user, the spacing between columns and
+        * the use of a border, defined by user, where the border width and padding are taken into account
+        *
+        * File objects MUST already be filtered. They need a height and width to be shown in the gallery
+        *
+        * @return void
+        */
+       protected function calculateMediaWidthsAndHeights() {
+
+               $columnSpacingTotal = ($this->galleryData['count']['columns'] - 1) * $this->columnSpacing;
+
+               $galleryWidthMinusBorderAndSpacing = max($this->galleryData['width'] - $columnSpacingTotal, 1);
+
+               if ($this->borderEnabled) {
+                       $borderPaddingTotal = ($this->galleryData['count']['columns'] * 2) * $this->borderPadding;
+                       $borderWidthTotal = ($this->galleryData['count']['columns'] * 2) * $this->borderWidth;
+                       $galleryWidthMinusBorderAndSpacing = $galleryWidthMinusBorderAndSpacing - $borderPaddingTotal - $borderWidthTotal;
+               }
+
+               // User entered a predefined height
+               if ($this->equalMediaHeight) {
+                       $mediaScalingCorrection = 1;
+                       $maximumRowWidth = 0;
+
+                       // Calculate the scaling correction when the total of media elements is wider than the gallery width
+                       for ($row = 1; $row <= $this->galleryData['count']['rows']; $row++) {
+                               $totalRowWidth = 0;
+                               for ($column = 1; $column <= $this->galleryData['count']['columns']; $column++) {
+                                       $fileKey = (($row - 1) * $this->galleryData['count']['columns']) + $column - 1;
+                                       if ($fileKey > $this->galleryData['count']['files'] - 1) {
+                                               break 2;
+                                       }
+                                       $currentMediaScaling = $this->equalMediaHeight / max($this->fileObjects[$fileKey]->getProperty('height'), 1);
+                                       $totalRowWidth += $this->fileObjects[$fileKey]->getProperty('width') * $currentMediaScaling;
+                               }
+                               $maximumRowWidth = max($totalRowWidth, $maximumRowWidth);
+                               $mediaInRowScaling = $totalRowWidth / $galleryWidthMinusBorderAndSpacing;
+                               $mediaScalingCorrection = max($mediaInRowScaling, $mediaScalingCorrection);
+                       }
+
+                       // Set the corrected dimensions for each media element
+                       foreach ($this->fileObjects as $key => $fileObject) {
+                               $mediaHeight = floor($this->equalMediaHeight / $mediaScalingCorrection);
+                               $mediaWidth = floor(
+                                       $fileObject->getProperty('width') * ($mediaHeight / max($fileObject->getProperty('height'), 1))
+                               );
+                               $this->mediaDimensions[$key] = [
+                                       'width' => $mediaWidth,
+                                       'height' => $mediaHeight
+                               ];
+                       }
+
+                       // Recalculate gallery width
+                       $this->galleryData['width'] = floor($maximumRowWidth / $mediaScalingCorrection);
+
+               // User entered a predefined width
+               } elseif ($this->equalMediaWidth) {
+                       $mediaScalingCorrection = 1;
+
+                       // Calculate the scaling correction when the total of media elements is wider than the gallery width
+                       $totalRowWidth = $this->galleryData['count']['columns'] * $this->equalMediaWidth;
+                       $mediaInRowScaling = $totalRowWidth / $galleryWidthMinusBorderAndSpacing;
+                       $mediaScalingCorrection = max($mediaInRowScaling, $mediaScalingCorrection);
+
+                       // Set the corrected dimensions for each media element
+                       foreach ($this->fileObjects as $key => $fileObject) {
+                               $mediaWidth = floor($this->equalMediaWidth / $mediaScalingCorrection);
+                               $mediaHeight = floor(
+                                       $fileObject->getProperty('height') * ($mediaWidth / max($fileObject->getProperty('width'), 1))
+                               );
+                               $this->mediaDimensions[$key] = [
+                                       'width' => $mediaWidth,
+                                       'height' => $mediaHeight
+                               ];
+                       }
+
+                       // Recalculate gallery width
+                       $this->galleryData['width'] = floor($totalRowWidth / $mediaScalingCorrection);
+
+               // Automatic setting of width and height
+               } else {
+                       $maxMediaWidth = (int)($galleryWidthMinusBorderAndSpacing / $this->galleryData['count']['columns']);
+
+                       foreach ($this->fileObjects as $key => $fileObject) {
+                               $mediaWidth = min($maxMediaWidth, $fileObject->getProperty('width'));
+                               $mediaHeight = floor(
+                                       $fileObject->getProperty('height') * ($mediaWidth / max($fileObject->getProperty('width'), 1))
+                               );
+                               $this->mediaDimensions[$key] = [
+                                       'width' => $mediaWidth,
+                                       'height' => $mediaHeight
+                               ];
+                       }
+               }
+       }
+
+       /**
+        * Prepare the gallery data
+        *
+        * Make an array for rows, columns and configuration
+        *
+        * @return void
+        */
+       protected function prepareGalleryData() {
+               for ($row = 1; $row <= $this->galleryData['count']['rows']; $row++) {
+
+                       for ($column = 1; $column <= $this->galleryData['count']['columns']; $column++) {
+
+                               $fileKey = (($row - 1) * $this->galleryData['count']['columns']) + $column - 1;
+
+                               $this->galleryData['rows'][$row]['columns'][$column] = [
+                                       'media' => $this->fileObjects[$fileKey],
+                                       'dimensions' => [
+                                               'width' => $this->mediaDimensions[$fileKey]['width'],
+                                               'height' => $this->mediaDimensions[$fileKey]['height']
+                                       ]
+                               ];
+                       }
+               }
+
+               $this->galleryData['columnSpacing'] = $this->columnSpacing;
+               $this->galleryData['border']['enabled'] = $this->borderEnabled;
+               $this->galleryData['border']['width'] = $this->borderWidth;
+               $this->galleryData['border']['padding'] = $this->borderPadding;
+       }
+}
diff --git a/typo3/sysext/frontend/Tests/Unit/Processor/GalleryProcessorTest.php b/typo3/sysext/frontend/Tests/Unit/Processor/GalleryProcessorTest.php
new file mode 100644 (file)
index 0000000..8d0184b
--- /dev/null
@@ -0,0 +1,460 @@
+<?php
+namespace TYPO3\CMS\Frontend\Tests\Unit\Processor;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+use TYPO3\CMS\Core\Resource\FileReference;
+use TYPO3\CMS\Core\Tests\UnitTestCase;
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\DataProcessing\GalleryProcessor;
+
+/**
+ * Tests for  GalleryProcessor
+ */
+class GalleryProcessorTest extends UnitTestCase {
+
+       /**
+        * @var ContentObjectRenderer|\PHPUnit_Framework_MockObject_MockObject
+        */
+       protected $contentObjectRenderer;
+
+       /**
+        * Set up
+        */
+       protected function setUp() {
+               $this->contentObjectRenderer = $this->getMock(
+                       ContentObjectRenderer::class,
+                       ['dummy']
+               );
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException
+        */
+       public function processThrowsExceptionWhenFilesProcessedDataKeyIsNotFound() {
+               $processor = new GalleryProcessor();
+               $processor->process(
+                       $this->contentObjectRenderer,
+                       [],
+                       [],
+                       []
+               );
+       }
+
+       /**
+        * Gallery position test data provider
+        */
+       public function galleryPositionDataProvider() {
+               return [
+                       'Default: horizontal above' => [
+                               [],
+                               [
+                                       'horizontal' => 'center',
+                                       'vertical' => 'above',
+                                       'noWrap' => FALSE
+                               ]
+                       ],
+                       'right above' => [
+                               ['mediaOrientation' => 1],
+                               [
+                                       'horizontal' => 'right',
+                                       'vertical' => 'above',
+                                       'noWrap' => FALSE
+                               ]
+                       ],
+                       'left above' => [
+                               ['mediaOrientation' => 2],
+                               [
+                                       'horizontal' => 'left',
+                                       'vertical' => 'above',
+                                       'noWrap' => FALSE
+                               ]
+                       ],
+                       'center below' => [
+                               ['mediaOrientation' => 8],
+                               [
+                                       'horizontal' => 'center',
+                                       'vertical' => 'below',
+                                       'noWrap' => FALSE
+                               ]
+                       ],
+                       'right below' => [
+                               ['mediaOrientation' => 9],
+                               [
+                                       'horizontal' => 'right',
+                                       'vertical' => 'below',
+                                       'noWrap' => FALSE
+                               ]
+                       ],
+                       'left below' => [
+                               ['mediaOrientation' => 10],
+                               [
+                                       'horizontal' => 'left',
+                                       'vertical' => 'below',
+                                       'noWrap' => FALSE
+                               ]
+                       ],
+                       'right intext' => [
+                               ['mediaOrientation' => 17],
+                               [
+                                       'horizontal' => 'right',
+                                       'vertical' => 'intext',
+                                       'noWrap' => FALSE
+                               ]
+                       ],
+                       'left intext' => [
+                               ['mediaOrientation' => 18],
+                               [
+                                       'horizontal' => 'left',
+                                       'vertical' => 'intext',
+                                       'noWrap' => FALSE
+                               ]
+                       ],
+                       'right intext no wrap' => [
+                               ['mediaOrientation' => 25],
+                               [
+                                       'horizontal' => 'right',
+                                       'vertical' => 'intext',
+                                       'noWrap' => TRUE
+                               ]
+                       ],
+                       'left intext no wrap' => [
+                               ['mediaOrientation' => 26],
+                               [
+                                       'horizontal' => 'left',
+                                       'vertical' => 'intext',
+                                       'noWrap' => TRUE
+                               ]
+                       ],
+
+               ];
+       }
+
+       /**
+        * @test
+        * @dataProvider galleryPositionDataProvider
+        */
+       public function galleryPositionTest($processorConfiguration, $expected) {
+               $processor = new GalleryProcessor();
+               $processedData = $processor->process(
+                       $this->contentObjectRenderer,
+                       [],
+                       $processorConfiguration,
+                       ['files' => []]
+               );
+
+               $this->assertEquals($expected, $processedData['gallery']['position']);
+       }
+
+       /**
+        * @test
+        */
+       public function maxGalleryWidthTest() {
+               $processor = new GalleryProcessor();
+               $processedData = $processor->process(
+                       $this->contentObjectRenderer,
+                       [],
+                       ['maxGalleryWidth' => 200, 'maxGalleryWidthInText' => 100],
+                       ['files' => []]
+               );
+
+               $this->assertEquals(200, $processedData['gallery']['width']);
+       }
+
+       /**
+        * @test
+        */
+       public function maxGalleryWidthWhenInTextTest() {
+               $processor = new GalleryProcessor();
+               $processedData = $processor->process(
+                       $this->contentObjectRenderer,
+                       [],
+                       ['maxGalleryWidth' => 200, 'maxGalleryWidthInText' => 100, 'mediaOrientation' => 26],
+                       ['files' => []]
+               );
+
+               $this->assertEquals(100, $processedData['gallery']['width']);
+       }
+
+       /**
+        * Count test data provider
+        * @return array
+        */
+       public function countDataProvider() {
+               return [
+                       'Default settings with 3 files' => [
+                               3,
+                               [],
+                               [],
+                               [
+                                       'files' => 3,
+                                       'columns' => 1,
+                                       'rows' => 3
+                               ]
+                       ],
+                       'NumberOfColumns set by value' => [
+                               3,
+                               [],
+                               ['numberOfColumns' => 2],
+                               [
+                                       'files' => 3,
+                                       'columns' => 2,
+                                       'rows' => 2
+                               ]
+                       ],
+                       'NumberOfColumns set in data' => [
+                               3,
+                               ['imagecols' => 3],
+                               [],
+                               [
+                                       'files' => 3,
+                                       'columns' => 3,
+                                       'rows' => 1
+                               ]
+                       ],
+                       'NumberOfColumns set in custom data field' => [
+                               6,
+                               ['my_imagecols' => 4],
+                               ['numberOfColumns.' => [
+                                       'field' => 'my_imagecols'
+                               ]],
+                               [
+                                       'files' => 6,
+                                       'columns' => 4,
+                                       'rows' => 2
+                               ]
+                       ]
+               ];
+       }
+
+       /**
+        * @test
+        * @dataProvider countDataProvider
+        */
+       public function countResultTest($numberOfFiles, $data, $processorConfiguration, $expected) {
+               $files = [];
+               for ($i = 0; $i < $numberOfFiles; $i++) {
+                       $files[] = $this->getMock(FileReference::class, array(), array(), '', FALSE);
+               }
+               $this->contentObjectRenderer->data = $data;
+               $processor = new GalleryProcessor();
+               $processedData = $processor->process(
+                       $this->contentObjectRenderer,
+                       [],
+                       $processorConfiguration,
+                       ['files' => $files]
+               );
+
+               $this->assertEquals($expected, $processedData['gallery']['count']);
+       }
+
+       /**
+        * Data provider for calculateMediaWidthsAndHeightsTest
+        *
+        * @return array
+        */
+       public function calculateMediaWidthsAndHeightsDataProvider() {
+               return [
+                       'Default settings' => [
+                               [
+                                       [200, 100],
+                                       [200, 100],
+                                       [200, 100],
+                               ],
+                               [],
+                               [
+                                       1 => [
+                                               1 => ['width' => 200, 'height' => 100]
+                                       ],
+                                       2 => [
+                                               1 => ['width' => 200, 'height' => 100]
+                                       ],
+                                       3 => [
+                                               1 => ['width' => 200, 'height' => 100]
+                                       ],
+                               ]
+                       ],
+                       'Max width set + number of columns set' => [
+                               [
+                                       [200, 100],
+                                       [200, 100],
+                                       [200, 100],
+                               ],
+                               ['maxGalleryWidth' => 200, 'numberOfColumns' => 2],
+                               [
+                                       1 => [
+                                               1 => ['width' => 100, 'height' => 50],
+                                               2 => ['width' => 100, 'height' => 50]
+                                       ],
+                                       2 => [
+                                               1 => ['width' => 100, 'height' => 50],
+                                               2 => ['width' => NULL, 'height' => NULL]
+                                       ],
+                               ]
+                       ],
+                       'Max width set, number of columns + border (padding) set' => [
+                               [
+                                       [200, 100],
+                                       [200, 100],
+                                       [200, 100],
+                               ],
+                               [
+                                       'maxGalleryWidth' => 200,
+                                       'numberOfColumns' => 2,
+                                       'borderEnabled' => TRUE,
+                                       'borderPadding' => 4,
+                                       'borderWidth' => 0,
+                               ],
+                               [
+                                       1 => [
+                                               1 => ['width' => 92, 'height' => 46],
+                                               2 => ['width' => 92, 'height' => 46]
+                                       ],
+                                       2 => [
+                                               1 => ['width' => 92, 'height' => 46],
+                                               2 => ['width' => NULL, 'height' => NULL]
+                                       ],
+                               ]
+                       ],
+                       'Max width set, number of columns + border (width) set' => [
+                               [
+                                       [200, 100],
+                                       [200, 100],
+                                       [200, 100],
+                               ],
+                               [
+                                       'maxGalleryWidth' => 200,
+                                       'numberOfColumns' => 2,
+                                       'borderEnabled' => TRUE,
+                                       'borderPadding' => 0,
+                                       'borderWidth' => 4,
+                               ],
+                               [
+                                       1 => [
+                                               1 => ['width' => 92, 'height' => 46],
+                                               2 => ['width' => 92, 'height' => 46]
+                                       ],
+                                       2 => [
+                                               1 => ['width' => 92, 'height' => 46],
+                                               2 => ['width' => NULL, 'height' => NULL]
+                                       ],
+                               ]
+                       ],
+                       'Max width set, number of columns + border (padding + width) set' => [
+                               [
+                                       [200, 100],
+                                       [200, 100],
+                                       [200, 100],
+                               ],
+                               [
+                                       'maxGalleryWidth' => 200,
+                                       'numberOfColumns' => 2,
+                                       'borderEnabled' => TRUE,
+                                       'borderPadding' => 1,
+                                       'borderWidth' => 4,
+                               ],
+                               [
+                                       1 => [
+                                               1 => ['width' => 90, 'height' => 45],
+                                               2 => ['width' => 90, 'height' => 45]
+                                       ],
+                                       2 => [
+                                               1 => ['width' => 90, 'height' => 45],
+                                               2 => ['width' => NULL, 'height' => NULL]
+                                       ],
+                               ]
+                       ],
+                       'Equal height set' => [
+                               [
+                                       [200, 100],
+                                       [200, 300],
+                                       [100, 50],
+                                       [2020, 1000],
+                                       [1000, 1000],
+                               ],
+                               [
+                                       'maxGalleryWidth' => 500,
+                                       'numberOfColumns' => 3,
+                                       'equalMediaHeight' => 75
+                               ],
+                               [
+                                       1 => [
+                                               1 => ['width' => 150, 'height' => 75],
+                                               2 => ['width' => 50, 'height' => 75],
+                                               3 => ['width' => 150, 'height' => 75]
+                                       ],
+                                       2 => [
+                                               1 => ['width' => 151, 'height' => 75],
+                                               2 => ['width' => 75, 'height' => 75],
+                                               3 => ['width' => NULL, 'height' => NULL]
+                                       ],
+                               ]
+                       ],
+                       'Equal width set' => [
+                               [
+                                       [200, 100],
+                                       [200, 300],
+                                       [100, 50],
+                               ],
+                               [
+                                       'maxGalleryWidth' => 200,
+                                       'numberOfColumns' => 3,
+                                       'equalMediaWidth' => 75
+                               ],
+                               [
+                                       1 => [
+                                               1 => ['width' => 66, 'height' => 33],
+                                               2 => ['width' => 66, 'height' => 99],
+                                               3 => ['width' => 66, 'height' => 33]
+                                       ],
+                               ]
+                       ]
+               ];
+       }
+
+       /**
+        * @test
+        * @dataProvider calculateMediaWidthsAndHeightsDataProvider
+        */
+       public function calculateMediaWidthsAndHeightsTest($testFiles, $processorConfiguration, $expected) {
+               $files = [];
+               foreach ($testFiles as $fileConfig) {
+                       $fileReference = $this->getMock(FileReference::class, array(), array(), '', FALSE);
+                       $fileReference->expects($this->any())
+                               ->method('getProperty')
+                               ->will($this->returnValueMap([
+                                       ['width', $fileConfig[0]],
+                                       ['height', $fileConfig[1]]
+                               ]));
+                       $files[] = $fileReference;
+               }
+
+               $processor = new GalleryProcessor();
+               $processedData = $processor->process(
+                       $this->contentObjectRenderer,
+                       [],
+                       $processorConfiguration,
+                       ['files' => $files]
+               );
+
+               foreach ($expected as $row => $columns) {
+                       $this->assertArrayHasKey($row, $processedData['gallery']['rows'], 'Row exists');
+                       foreach ($columns as $column => $dimensions) {
+                               $this->assertArrayHasKey($column, $processedData['gallery']['rows'][$row]['columns'], 'Column exists');
+                               $this->assertEquals($dimensions, $processedData['gallery']['rows'][$row]['columns'][$column]['dimensions'], 'Dimensions match');
+                       }
+               }
+       }
+
+}
\ No newline at end of file