crop.module in Crop API 8
Same filename and directory in other branches
The Crop API Drupal module.
Provides storage and API for image crops.
File
crop.moduleView source
<?php
/**
* @file
* The Crop API Drupal module.
*
* Provides storage and API for image crops.
*/
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\crop\Entity\Crop;
use Drupal\image\Entity\ImageStyle;
use Drupal\media\Entity\MediaType;
use Drupal\media\MediaTypeInterface;
use Drupal\media_entity\MediaBundleInterface;
use Drupal\file\FileInterface;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_theme().
*/
function crop_theme() {
return [
'crop_crop_summary' => [
'variables' => [
'data' => [],
'effect' => [],
],
],
];
}
/**
* Implements hook_help().
*/
function crop_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.crop':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t("<a href=':crop'>Crop</a> provides basic API for image cropping. This module won't do much by itself. Users should pick one of UI modules that utilize this API.</a>", [
':crop' => 'http://drupal.org/project/crop',
]) . '</p>';
$output .= '<h3>' . t('Configuration') . '</h3>';
$output .= '<p>' . t('This is API module. In order to crop your images you need a UI module. There are currently two UI modules that use Crop API:') . '</p>';
$output .= '<ul>';
$output .= '<li>' . t('<a href=":image_widget_crop">Image widget crop</a>', [
':image_widget_crop' => 'https://www.drupal.org/project/image_widget_crop',
]) . '</li>';
$output .= '<li>' . t('<a href=":focal_point">Focal point</a>', [
':focal_point' => 'https://www.drupal.org/project/focal_point',
]) . '</li>';
$output .= '</ul>';
$output .= '<h3>' . t('Technical details') . '</h3>';
$output .= '<p>' . t('Initial discussion can be found on <a href=":manual">manual crop issue queue</a>.', [
':manual' => 'https://www.drupal.org/node/2368945',
]) . '</p>';
return $output;
}
}
/**
* Prepares variables for crop_crop summary template.
*
* Default template: crop-crop-summary.twig.html.
*/
function template_preprocess_crop_crop_summary(&$variables) {
if (!empty($variables['data']['crop_type'])) {
$type = \Drupal::entityTypeManager()
->getStorage('crop_type')
->load($variables['data']['crop_type']);
$variables['data']['crop_type'] = $type
->label();
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Adds crop configuration fields to media type form.
*/
function crop_form_media_type_edit_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
_crop_media_provider_form($form, $form_state);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Adds crop configuration fields to media bundle form.
*/
function crop_form_media_bundle_edit_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
_crop_media_provider_form($form, $form_state);
}
/**
* Helper function to avoid uneeded code duplication.
*
* @todo Delete this and media entity fallback when media is stable.
*/
function _crop_media_provider_form(array &$form, FormStateInterface $form_state) {
/** @var \Drupal\Core\Config\Entity\ConfigEntityBundleBase $entity_type */
$entity_type = $form_state
->getFormObject()
->getEntity();
$options = [];
$allowed_field_types = [
'file',
'image',
];
/** @var \Drupal\Core\Field\FieldDefinitionInterface[] $fields */
$fields = \Drupal::service('entity_field.manager')
->getFieldDefinitions('media', $entity_type
->id());
foreach ($fields as $field_name => $field) {
if (in_array($field
->getType(), $allowed_field_types) && !$field
->getFieldStorageDefinition()
->isBaseField()) {
$options[$field_name] = $field
->getLabel();
}
}
// Maintain compatibility with Media Entity.
if ($entity_type instanceof MediaType) {
$form['#entity_builders'][] = 'crop_media_type_form_builder';
}
else {
$form['#entity_builders'][] = 'crop_media_bundle_form_builder';
}
$form['crop'] = [
'#type' => 'fieldset',
'#title' => t('Crop configuration'),
'#group' => 'source_dependent',
];
if (empty($options)) {
$form['crop']['image_field'] = [
'#type' => 'value',
'#value' => NULL,
];
$form['crop']['message'] = [
'#markup' => t('There are no file or image fields on this bundle at the moment. In order to configure crop add at least one such field and come back.'),
];
return;
}
$form['crop']['image_field'] = [
'#type' => 'select',
'#title' => t('Image field'),
'#default_value' => $entity_type
->getThirdPartySetting('crop', 'image_field'),
'#options' => $options,
'#empty_option' => t('- Skip field -'),
'#empty_value' => '_none',
'#description' => t('Select field that stores image which needs to be cropped.'),
];
return $form;
}
/**
* Entity builder for Media bundle.
*
* Adds third party settings to Media bundle config entity.
*
* @see crop_form_media_bundle_form_alter()
*/
function crop_media_bundle_form_builder($entity_type, MediaBundleInterface $bundle, &$form, FormStateInterface $form_state) {
$bundle
->setThirdPartySetting('crop', 'image_field', $form_state
->getValue('image_field'));
}
/**
* Entity builder for Media type.
*
* Adds third party settings to Media type config entity.
*
* @see crop_form_media_type_edit_form_alter()
*/
function crop_media_type_form_builder($entity_type, MediaTypeInterface $bundle, array &$form, FormStateInterface $form_state) {
$bundle
->setThirdPartySetting('crop', 'image_field', $form_state
->getValue('image_field'));
}
/**
* Implements hook_ENTITY_TYPE_delete().
*
* Deletes orphaned crops when a file is deleted.
*/
function crop_file_delete(FileInterface $file) {
// Get all crops for the file being deleted.
$crops = \Drupal::entityTypeManager()
->getStorage('crop')
->loadByProperties([
'uri' => $file
->getFileUri(),
]);
foreach ($crops as $crop) {
$crop
->delete();
}
}
/**
* Implements hook_file_url_alter().
*/
function crop_file_url_alter(&$uri) {
// Process only files that are stored in "styles" directory.
if (strpos($uri, '/styles/') !== FALSE && preg_match('/\\/styles\\/(.*?)\\/(.*?)\\/(.+)/', $uri, $match)) {
// Match image style, schema, file subdirectory and file name.
// Get the image style ID.
$image_style = $match[1];
// Get the file path without query parameter.
$parsed_uri = UrlHelper::parse($match[3]);
// Get the file URI using parsed schema and file path.
$file_uri = $match[2] . '://' . $parsed_uri['path'];
// Prevent double hashing, if there is a hash argument already, do not add
// it again.
if (!empty($parsed_uri['query']['h'])) {
return;
}
/** @var \Drupal\image\Entity\ImageStyle $image_style */
if (!($image_style = ImageStyle::load($image_style))) {
return;
}
if ($crop = Crop::getCropFromImageStyle($file_uri, $image_style)) {
// Found a crop for this image, append a hash of it to the URL,
// so that browsers reload the image and CDNs and proxies can be bypassed.
$shortened_hash = substr(md5(implode($crop
->position()) . implode($crop
->anchor())), 0, 8);
// If the URI has a schema and that is not http, https or data, convert
// the URI to the external URL. Otherwise the appended query argument
// will be encoded.
// @see file_create_url()
$scheme = \Drupal::service('file_system')
->uriScheme($uri);
if ($scheme && !in_array($scheme, [
'http',
'https',
'data',
])) {
if ($wrapper = \Drupal::service('stream_wrapper_manager')
->getViaUri($uri)) {
$uri = $wrapper
->getExternalUrl();
}
}
// Append either with a ? or a & if there are existing query arguments.
if (strpos($uri, '?') === FALSE) {
$uri .= '?h=' . $shortened_hash;
}
else {
$uri .= '&h=' . $shortened_hash;
}
}
}
}
Functions
Name | Description |
---|---|
crop_file_delete | Implements hook_ENTITY_TYPE_delete(). |
crop_file_url_alter | Implements hook_file_url_alter(). |
crop_form_media_bundle_edit_form_alter | Implements hook_form_FORM_ID_alter(). |
crop_form_media_type_edit_form_alter | Implements hook_form_FORM_ID_alter(). |
crop_help | Implements hook_help(). |
crop_media_bundle_form_builder | Entity builder for Media bundle. |
crop_media_type_form_builder | Entity builder for Media type. |
crop_theme | Implements hook_theme(). |
template_preprocess_crop_crop_summary | Prepares variables for crop_crop summary template. |
_crop_media_provider_form | Helper function to avoid uneeded code duplication. |