diff --git a/src/Tests/TokenFieldUiTest.php b/src/Tests/TokenFieldUiTest.php index 37c80f4..d2273bc 100644 --- a/src/Tests/TokenFieldUiTest.php +++ b/src/Tests/TokenFieldUiTest.php @@ -3,6 +3,9 @@ namespace Drupal\token\Tests; use Drupal\node\Entity\NodeType; +use Drupal\node\Entity\Node; +use Drupal\file\Entity\File; +use Drupal\image\Entity\ImageStyle; /** * Tests field ui. @@ -60,6 +63,17 @@ class TokenFieldUiTest extends TokenTestBase { 'entity_type' => 'node', 'bundle' => 'article', ))->save(); + entity_create('field_storage_config', array( + 'field_name' => 'field_image_2', + 'entity_type' => 'node', + 'type' => 'image', + ))->save(); + entity_create('field_config', array( + 'field_name' => 'field_image_2', + 'label' => 'Image 2', + 'entity_type' => 'node', + 'bundle' => 'article', + ))->save(); entity_get_form_display('node', 'article', 'default') ->setComponent('field_body', [ @@ -94,4 +108,99 @@ class TokenFieldUiTest extends TokenTestBase { $this->drupalGet('node/add/article'); $this->assertText('The site is called Drupal.'); } + + /** + * Test that tokens are correctly provided and replaced for the image fields. + */ + public function testImageFieldTokens() { + // Generate 2 different test images. + file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example1.png'); + file_unmanaged_copy(\Drupal::root() . '/core/misc/loading.gif', 'public://example2.gif'); + + // Resize the test images so that they will be scaled down during token + // replacement. + $image1 = \Drupal::service('image.factory')->get('public://example1.png'); + $image1->resize(500, 500); + $image1->save(); + $image2 = \Drupal::service('image.factory')->get('public://example2.gif'); + $image2->resize(500, 500); + $image2->save(); + + /** @var \Drupal\file\Entity\File $image1 */ + $image1 = File::create(['uri' => 'public://example1.png']); + $image1->save(); + /** @var \Drupal\file\Entity\File $image2 */ + $image2 = File::create(['uri' => 'public://example2.gif']); + $image2->save(); + + $node = Node::create([ + 'title' => 'Test node title', + 'type' => 'article', + 'field_image' => [ + [ + 'target_id' => $image1->id(), + ], + ], + 'field_image_2' => [ + [ + 'target_id' => $image2->id(), + ], + ], + ]); + $node->save(); + + // Obtain the file size and dimension of the images that will be scaled + // down during token replacement by applying the styles here. + $style = ImageStyle::load('thumbnail'); + $style->createDerivative('public://example1.png', 'public://styles/thumbnail/public/example1-test.png'); + $style->createDerivative('public://example2.gif', 'public://styles/thumbnail/public/example2-test.gif'); + $image_1_thumbnail = \Drupal::service('image.factory')->get('public://styles/thumbnail/public/example1-test.png'); + $image_2_thumbnail = \Drupal::service('image.factory')->get('public://styles/thumbnail/public/example2-test.gif'); + $style = ImageStyle::load('medium'); + $style->createDerivative('public://example1.png', 'public://styles/medium/public/example1-test.png'); + $style->createDerivative('public://example2.gif', 'public://styles/medium/public/example2-test.gif'); + $image_1_medium = \Drupal::service('image.factory')->get('public://styles/medium/public/example1-test.png'); + $image_2_medium = \Drupal::service('image.factory')->get('public://styles/medium/public/example2-test.gif'); + $style = ImageStyle::load('large'); + $style->createDerivative('public://example1.png', 'public://styles/large/public/example1-test.png'); + $style->createDerivative('public://example2.gif', 'public://styles/large/public/example2-test.gif'); + $image_1_large = \Drupal::service('image.factory')->get('public://styles/large/public/example1-test.png'); + $image_2_large = \Drupal::service('image.factory')->get('public://styles/large/public/example2-test.gif'); + + $tokens = [ + // field_image + 'field_image:image_styles:thumbnail:mimetype' => 'image/png', + 'field_image:image_styles:medium:mimetype' => 'image/png', + 'field_image:image_styles:large:mimetype' => 'image/png', + 'field_image:image_styles:thumbnail:filesize' => $image_1_thumbnail->getFileSize(), + 'field_image:image_styles:medium:filesize' => $image_1_medium->getFileSize(), + 'field_image:image_styles:large:filesize' => $image_1_large->getFileSize(), + 'field_image:image_styles:thumbnail:height' => $image_1_thumbnail->getHeight(), + 'field_image:image_styles:medium:height' => $image_1_medium->getHeight(), + 'field_image:image_styles:large:height' => $image_1_large->getHeight(), + 'field_image:image_styles:thumbnail:width' => $image_1_thumbnail->getWidth(), + 'field_image:image_styles:medium:width' => $image_1_medium->getWidth(), + 'field_image:image_styles:large:width' => $image_1_large->getWidth(), + 'field_image:image_styles:thumbnail:uri' => 'public://styles/thumbnail/public/example1.png', + 'field_image:image_styles:medium:uri' => 'public://styles/medium/public/example1.png', + 'field_image:image_styles:large:uri' => 'public://styles/large/public/example1.png', + // field_image_2 + 'field_image_2:image_styles:thumbnail:mimetype' => 'image/gif', + 'field_image_2:image_styles:medium:mimetype' => 'image/gif', + 'field_image_2:image_styles:large:mimetype' => 'image/gif', + 'field_image_2:image_styles:thumbnail:filesize' => $image_2_thumbnail->getFileSize(), + 'field_image_2:image_styles:medium:filesize' => $image_2_medium->getFileSize(), + 'field_image_2:image_styles:large:filesize' => $image_2_large->getFileSize(), + 'field_image_2:image_styles:thumbnail:height' => $image_2_thumbnail->getHeight(), + 'field_image_2:image_styles:medium:height' => $image_2_medium->getHeight(), + 'field_image_2:image_styles:large:height' => $image_2_large->getHeight(), + 'field_image_2:image_styles:thumbnail:width' => $image_2_thumbnail->getWidth(), + 'field_image_2:image_styles:medium:width' => $image_2_medium->getWidth(), + 'field_image_2:image_styles:large:width' => $image_2_large->getWidth(), + 'field_image_2:image_styles:thumbnail:uri' => 'public://styles/thumbnail/public/example2.gif', + 'field_image_2:image_styles:medium:uri' => 'public://styles/medium/public/example2.gif', + 'field_image_2:image_styles:large:uri' => 'public://styles/large/public/example2.gif', + ]; + $this->assertTokens('node', ['node' => $node], $tokens); + } } diff --git a/token.tokens.inc b/token.tokens.inc index 7c30ead..1b200eb 100644 --- a/token.tokens.inc +++ b/token.tokens.inc @@ -22,6 +22,7 @@ use Drupal\user\UserInterface; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Drupal\Core\TypedData\PrimitiveInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\image\Entity\ImageStyle; /** * Implements hook_token_info_alter(). @@ -381,6 +382,60 @@ function token_token_info() { 'dynamic' => TRUE, ); + // Provide image_field tokens for the different image styles. + if (\Drupal::moduleHandler()->moduleExists('image')) { + $info['types']['image_field'] = [ + 'name' => t('Image field'), + 'needs-data' => 'image_field', + 'module' => 'token', + 'nested' => TRUE, + ]; + + $info['tokens']['image_field']['image_styles'] = [ + 'name' => t('Image styles'), + 'description' => t('The image styles.'), + 'type' => 'image_styles', + ]; + + $info['types']['image_styles'] = [ + 'name' => t('Image styles'), + 'needs-data' => 'image_styles', + 'module' => 'token', + 'nested' => TRUE, + ]; + + $image_styles = image_style_options(FALSE); + foreach ($image_styles as $style => $description) { + $info['tokens']['image_styles'][$style] = [ + 'name' => $style, + 'description' => t('@description image.', ['@description' => $description]), + 'type' => 'image_style', + ]; + } + + // Provide additional image_style tokens. + $info['tokens']['image_style']['mimetype'] = [ + 'name' => t('MIME type'), + 'description' => t('The MIME type (image/png, image/bmp, etc.) of the image.'), + ]; + $info['tokens']['image_style']['filesize'] = [ + 'name' => t('File size'), + 'description' => t('The file size of the image.'), + ]; + $info['tokens']['image_style']['height'] = [ + 'name' => t('Height'), + 'description' => t('The height the image, in pixels.'), + ]; + $info['tokens']['image_style']['width'] = [ + 'name' => t('Width'), + 'description' => t('The width of the image, in pixels.'), + ]; + $info['tokens']['image_style']['uri'] = [ + 'name' => t('URI'), + 'description' => t('The URI to the image.'), + ]; + } + return $info; } @@ -436,6 +491,55 @@ function token_tokens($type, array $tokens, array $data = array(), array $option $node = $data['node']; foreach ($tokens as $name => $original) { + $parts = explode(':', $name); + if ($node->hasField($parts[0]) && \Drupal::moduleHandler()->moduleExists('image')) { + $field = $node->get($parts[0]); + $image_styles = image_style_options(FALSE); + if ((count($parts) == 4) && array_key_exists($parts[2], $image_styles) && ($field->getFieldDefinition()->getType() == 'image')) { + // Token is of the form {field_name}:{image_style}:{token}. An image + // derivative of the original has to be created first. Integration of + // Drupal 7's Imagecache Token module. + /** @var \Drupal\file\Entity\File $field_image */ + $field_image = $field->entity; + $original_uri = $field_image->getFileUri(); + $style = ImageStyle::load($parts[2]); + $derivative_uri = $style->buildUri($original_uri); + + if (!file_exists($derivative_uri)) { + // If the image derivative already exists, no need to re-generate it. + $derivative_exists = $style->createDerivative($original_uri, $derivative_uri); + } + else { + $derivative_exists = TRUE; + } + + if ($derivative_exists) { + /** @var \Drupal\Core\Image\Image $image */ + $image = \Drupal::service('image.factory')->get($derivative_uri); + // Generate the replacement token. + switch ($parts[3]) { + case 'mimetype': + $replacements[$original] = $image->getMimeType(); + break; + case 'filesize' : + $replacements[$original] = $image->getFileSize(); + break; + case 'height' : + $replacements[$original] = $image->getHeight(); + break; + case 'width' : + $replacements[$original] = $image->getWidth(); + break; + case 'uri' : + $replacements[$original] = $derivative_uri; + break; + } + } + continue; + } + } + + // Other tokens. switch ($name) { case 'log': $replacements[$original] = (string) $node->revision_log->value; @@ -1244,6 +1348,14 @@ function field_token_info_alter(&$info) { 'needs-data' => $field_token_name, 'nested' => TRUE, ]; + + // For fields of type image, the field's type is changed and field + // properties are not added. + if (($field->getType() == 'image') && \Drupal::moduleHandler()->moduleExists('image')) { + $info['tokens'][$token_type][$field_name]['type'] = 'image_field'; + continue; + } + // Field list token type. if ($cardinality > 1) { $info['types']["list<$field_token_name>"] = array(