diff --git a/src/FocalPointEffectBase.php b/src/FocalPointEffectBase.php index 96476a9..9bd9b15 100644 --- a/src/FocalPointEffectBase.php +++ b/src/FocalPointEffectBase.php @@ -49,13 +49,13 @@ abstract class FocalPointEffectBase extends ResizeImageEffect implements Contain * Image logger. * @param \Drupal\crop\CropStorageInterface $crop_storage * Crop storage. - * @param \Drupal\Core\Config\ImmutableConfig $config + * @param \Drupal\Core\Config\ImmutableConfig $focal_point_config * Focal point configuration object. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, CropStorageInterface $crop_storage, ImmutableConfig $config) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, CropStorageInterface $crop_storage, ImmutableConfig $focal_point_config) { parent::__construct($configuration, $plugin_id, $plugin_definition, $logger); $this->cropStorage = $crop_storage; - $this->focalPointConfig = $config; + $this->focalPointConfig = $focal_point_config; } /** @@ -73,9 +73,11 @@ abstract class FocalPointEffectBase extends ResizeImageEffect implements Contain } /** - * Calculate the resize dimensions of an image based on the longest crop - * dimension so that the aspect ratio is preserved and that there is always - * enough image available to the crop. + * Calculate the resize dimensions of an image. + * + * The calculated dimensions are based on the longest crop dimension (length + * or width) so that the aspect ratio is preserved in all cases and that there + * is always enough image available to the crop. * * @param int $image_width * @param int $image_height @@ -122,11 +124,14 @@ abstract class FocalPointEffectBase extends ResizeImageEffect implements Contain */ public function applyCrop(ImageInterface $image) { $crop_type = $this->focalPointConfig->get('crop_type'); + /** @var \Drupal\crop\CropInterface $crop */ if ($crop = Crop::findCrop($image->getSource(), $crop_type)) { + // An existing crop has been found; set the size. $crop->setSize($this->configuration['width'], $this->configuration['height']); } else { + // No existing crop could be found; create a new one using the size. $crop = $this->cropStorage->create([ 'type' => $crop_type, 'x' => (int) round($image->getWidth() / 2), @@ -157,7 +162,7 @@ abstract class FocalPointEffectBase extends ResizeImageEffect implements Contain /** * Calculate the crop anchor. * - * This is based on Crop's anchor function with the additional logic to ensure + * This is based on Crop's anchor function with additional logic to ensure * that crop area doesn't fall outside of the original image. * * @param \Drupal\Core\Image\ImageInterface $image @@ -175,19 +180,19 @@ abstract class FocalPointEffectBase extends ResizeImageEffect implements Contain // Set the minimum number of pixels that must exist between the edge of the // image and the anchor point (in both the x and y directions). $crop_size = $crop->size(); - $crop_offset_x = (int)ceil($crop_size['width'] / 2); - $crop_offset_y = (int)ceil($crop_size['height'] / 2); + $anchor_min_margin_x = (int)ceil($crop_size['width'] / 2); + $anchor_min_margin_y = (int)ceil($crop_size['height'] / 2); - // Ensure that the crop area doesn't fall off the left or right side of the + // Ensure that the crop area doesn't fall off the left or right sides of the // image. - $left_bound = $crop_offset_x; - $right_bound = $image->getWidth() - $crop_offset_x; + $left_bound = $anchor_min_margin_x; + $right_bound = $image->getWidth() - $anchor_min_margin_x; $new_anchor['x'] = max(min($original_anchor['x'], $right_bound), $left_bound); // Ensure that the crop area doesn't fall off the top or bottom of the // image. - $top_bound = $crop_offset_y; - $bottom_bound = $image->getHeight() - $crop_offset_y; + $top_bound = $anchor_min_margin_y; + $bottom_bound = $image->getHeight() - $anchor_min_margin_y; $new_anchor['y'] = max(min($original_anchor['y'], $bottom_bound), $top_bound); return $new_anchor; diff --git a/src/FocalPointManagerInterface.php b/src/FocalPointManagerInterface.php index 2b40361..eedebbf 100644 --- a/src/FocalPointManagerInterface.php +++ b/src/FocalPointManagerInterface.php @@ -40,6 +40,8 @@ interface FocalPointManagerInterface { * @return array * Array containing absolute coordinates of the focal point. 'x' and 'y' are * used for array keys and corresponding coordinates as values. + * + * @see absoluteToRelative */ public function relativeToAbsolute($x, $y, $width, $height); @@ -58,6 +60,8 @@ interface FocalPointManagerInterface { * @return array * Array containing relative coordinates of the focal point. 'x' and 'y' are * used for array keys and corresponding coordinates as values. + * + * @see relativeToAbsolute */ public function absoluteToRelative($x, $y, $width, $height); diff --git a/src/Plugin/Field/FieldWidget/FocalPointImageWidget.php b/src/Plugin/Field/FieldWidget/FocalPointImageWidget.php index 46a1955..5ee8830 100644 --- a/src/Plugin/Field/FieldWidget/FocalPointImageWidget.php +++ b/src/Plugin/Field/FieldWidget/FocalPointImageWidget.php @@ -7,9 +7,7 @@ namespace Drupal\focal_point\Plugin\Field\FieldWidget; -use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\Core\Render\ElementInfoManagerInterface; -use Drupal\Core\StringTranslation\TranslationTrait; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\crop\Entity\Crop; use Drupal\Core\Form\FormStateInterface; use Drupal\image\Plugin\Field\FieldWidget\ImageWidget; @@ -69,7 +67,7 @@ class FocalPointImageWidget extends ImageWidget { $element['focal_point'] = array( '#type' => 'textfield', '#title' => 'Focal point', - '#description' => t('Specify the focus of this image in the form "leftoffset,topoffset" where offsets are in percents. Ex: 25,75'), + '#description' => new TranslatableMarkup('Specify the focus of this image in the form "leftoffset,topoffset" where offsets are in percents. Ex: 25,75'), '#default_value' => isset($item['focal_point']) ? $item['focal_point'] : \Drupal::config('focal_point.settings')->get('default_value'), '#element_validate' => array('\Drupal\focal_point\Plugin\Field\FieldWidget\FocalPointImageWidget::validateFocalPoint'), '#attributes' => array( @@ -105,9 +103,9 @@ class FocalPointImageWidget extends ImageWidget { $crop_type = \Drupal::config('focal_point.settings')->get('crop_type'); $crop = Crop::findCrop($file->getFileUri(), $crop_type); if ($crop) { - $coords = \Drupal::service('focal_point.manager') + $anchor = \Drupal::service('focal_point.manager') ->absoluteToRelative($crop->x->value, $crop->y->value, $return['width'], $return['height']); - $return['focal_point'] = "{$coords['x']},{$coords['y']}"; + $return['focal_point'] = "{$anchor['x']},{$anchor['y']}"; } } return $return; @@ -123,7 +121,7 @@ class FocalPointImageWidget extends ImageWidget { $focal_point_value = $form_state->getValue($field_name); if (!is_null($focal_point_value) && \Drupal::service('focal_point.manager')->validateFocalPoint($focal_point_value)) { - $form_state->setError($element, \Drupal::translation()->translate('The !title field should be in the form "leftoffset,topoffset" where offsets are in percents. Ex: 25,75.', array('!title' => $element['#title']))); + $form_state->setError($element, new TranslatableMarkup('The !title field should be in the form "leftoffset,topoffset" where offsets are in percents. Ex: 25,75.', array('!title' => $element['#title']))); } } diff --git a/tests/src/Unit/FocalPointTest.php b/tests/src/Unit/FocalPointTest.php index bef47f7..ff96c7b 100644 --- a/tests/src/Unit/FocalPointTest.php +++ b/tests/src/Unit/FocalPointTest.php @@ -7,11 +7,15 @@ namespace Drupal\Tests\focal_point\Unit; +use Drupal\crop\CropStorageInterface; +use Drupal\Core\Entity\EntityTypeManager; +use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\focal_point\FocalPointManager; use Drupal\Tests\UnitTestCase; /** * @coversDefaultClass \Drupal\focal_point\FocalPointManager + * * @group Focal Point */ class FocalPointTest extends UnitTestCase { @@ -28,30 +32,21 @@ class FocalPointTest extends UnitTestCase { */ protected function setUp() { parent::setUp(); - $crop_storage = $this->getMockBuilder('Drupal\crop\CropStorage') - ->disableOriginalConstructor() - ->getMock(); - $entity_type_manager = $this->getMockBuilder('Drupal\Core\Entity\EntityTypeManager') - ->disableOriginalConstructor() - ->getMock(); - $entity_type_manager->expects($this->any()) - ->method('getStorage') - ->with('crop') - ->will($this->returnValue($crop_storage)); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->any()) - ->method('get') - ->with('entity_type.manager') - ->will($this->returnValue($entity_type_manager)); - - \Drupal::setContainer($container); + $crop_storage = $this->prophesize(CropStorageInterface::class); + + $entity_type_manager = $this->prophesize(EntityTypeManager::class); + $entity_type_manager->getStorage('crop')->willReturn($crop_storage); + + $container = $this->prophesize(ContainerInterface::class); + $container->get('entity_type.manager')->willReturn($entity_type_manager); + + \Drupal::setContainer($container->reveal()); $this->focalPointManager = new FocalPointManager(\Drupal::service('entity_type.manager')); } /** - * Tests the validateFocalPoint() method. + * @covers ::validateFocalPoint * * @dataProvider providerValidateFocalPoint */ @@ -63,25 +58,31 @@ class FocalPointTest extends UnitTestCase { * Data provider for testFocalPoint(). */ public function providerValidateFocalPoint() { - return [ - ['50,50', TRUE], - ['75,25', TRUE], - ['3,50', TRUE], - ['83,6', TRUE], - ['2,9', TRUE], - ['100,100', TRUE], - ['0,0', TRUE], - ['100,0', TRUE], - ['-20,50', FALSE], - ['18,-3', FALSE], - ['44,101', FALSE], - ['', FALSE], - ['invalid', FALSE], - ]; + $data = []; + $data['default_focal_point_position'] = ['50,50', TRUE]; + $data['basic_focal_point_position_1'] = ['75,25', TRUE]; + $data['basic_focal_point_position_2'] = ['3,50', TRUE]; + $data['basic_focal_point_position_3'] = ['83,6', TRUE]; + $data['basic_focal_point_position_4'] = ['2,9', TRUE]; + $data['extreme_focal_point_position_top_right'] = ['100,0', TRUE]; + $data['extreme_focal_point_position_top_left'] = ['0,0', TRUE]; + $data['extreme_focal_point_position_bottom_right'] = ['100,100', TRUE]; + $data['extreme_focal_point_position_bottom_left'] = ['0,100', TRUE]; + $data['invalid_focal_point_position_negative_x'] = ['-20,50', FALSE]; + $data['invalid_focal_point_position_negative_y'] = ['18,-3', FALSE]; + $data['invalid_focal_point_position_out_of_bounds_x'] = ['101,33', FALSE]; + $data['invalid_focal_point_position_out_of_bounds_y'] = ['44,101', FALSE]; + $data['invalid_focal_point_position_out_of_bounds_xy'] = ['313,512', FALSE]; + $data['invalid_focal_point_position_empty'] = ['', FALSE]; + $data['invalid_focal_point_position_incorrect_format_1'] = ['invalid', FALSE]; + $data['invalid_focal_point_position_incorrect_format_2'] = ['invalid,invalid', FALSE]; + $data['invalid_focal_point_position_incorrect_format_3'] = ['23,invalid', FALSE]; + + return $data; } /** - * Tests the relativeToAbsolute() method. + * @covers ::relativeToAbsolute * * @dataProvider providerCoordinates */ @@ -94,7 +95,7 @@ class FocalPointTest extends UnitTestCase { } /** - * Tests the absoluteToRelative() method. + * @covers ::absoluteToRelative * * @dataProvider providerCoordinates */ @@ -110,43 +111,44 @@ class FocalPointTest extends UnitTestCase { * Data provider for testRelativeToAbsolute() and absoluteToRelative(). */ public function providerCoordinates() { - return [ - [ - ['x' => 0, 'y' => 0], - ['width' => 1000, 'height' => 2000], - ['x' => 0, 'y' => 0] - ], - [ - ['x' => 25, 'y' => 50], - ['width' => 1000, 'height' => 2000], - ['x' => 250, 'y' => 1000] - ], - [ - ['x' => 50, 'y' => 25], - ['width' => 1000, 'height' => 2000], - ['x' => 500, 'y' => 500] - ], - [ - ['x' => 50, 'y' => 50], - ['width' => 1000, 'height' => 2000], - ['x' => 500, 'y' => 1000] - ], - [ - ['x' => 75, 'y' => 50], - ['width' => 1000, 'height' => 2000], - ['x' => 750, 'y' => 1000] - ], - [ - ['x' => 100, 'y' => 75], - ['width' => 1000, 'height' => 2000], - ['x' => 1000, 'y' => 1500] - ], - [ - ['x' => 100, 'y' => 100], - ['width' => 1000, 'height' => 2000], - ['x' => 1000, 'y' => 2000] - ], + $data = []; + $data['top_left'] = [ + ['x' => 0, 'y' => 0], + ['width' => 1000, 'height' => 2000], + ['x' => 0, 'y' => 0], + ]; + $data['basic_case_1'] = [ + ['x' => 25, 'y' => 50], + ['width' => 1000, 'height' => 2000], + ['x' => 250, 'y' => 1000] ]; + $data['basic_case_2'] = [ + ['x' => 50, 'y' => 25], + ['width' => 1000, 'height' => 2000], + ['x' => 500, 'y' => 500], + ]; + $data['basic_case_3'] = [ + ['x' => 50, 'y' => 50], + ['width' => 1000, 'height' => 2000], + ['x' => 500, 'y' => 1000], + ]; + $data['basic_case_4'] = [ + ['x' => 75, 'y' => 50], + ['width' => 1000, 'height' => 2000], + ['x' => 750, 'y' => 1000], + ]; + $data['basic_case_5'] = [ + ['x' => 100, 'y' => 75], + ['width' => 1000, 'height' => 2000], + ['x' => 1000, 'y' => 1500], + ]; + $data['bottom_right'] = [ + ['x' => 100, 'y' => 100], + ['width' => 1000, 'height' => 2000], + ['x' => 1000, 'y' => 2000], + ]; + + return $data; } }