media_crop.module in Media crop 7
Media crop primary module file.
Contains all the server side functionality for creating/storing/retrieving media crop instances.
File
media_crop.moduleView source
<?php
/**
* @file
* Media crop primary module file.
*
* Contains all the server side functionality for creating/storing/retrieving
* media crop instances.
*/
define('MEDIA_CROP_TOKEN_VALUE', 'media_crop_image');
/**
* Implements hook_help().
*/
function media_crop_help($path, $args) {
switch ($path) {
case "admin/help#media_crop":
$output = '';
$output .= '<h3>' . t('About', array(), array(
'context' => 'Media crop',
)) . '</h3>';
$output .= '<p>' . t("The Media Crop module allows users to crop, rotate, and scale images added with the WYSIWYG editor, CKeditor. Crop, rotate, and scale actions are performed on copies (instances) of the source image, so the source image located in the media library is not affected.", array(), array(
'context' => 'Media crop',
)) . '</p>';
$output .= '<p>' . t("This module provides control to manually override the following effects:", array(), array(
'context' => 'Media crop',
));
$output .= '<ul>';
$output .= '<li>' . t("<strong>Positioning</strong> and <strong>dimensions</strong> of the image style's crop effect.", array(), array(
'context' => 'Media crop',
)) . '</li>';
$output .= '<li>' . t("<strong>Degree</strong> of the image style's rotation effect.", array(), array(
'context' => 'Media crop',
)) . '</li>';
$output .= '</ul></p>';
$output .= '<p>' . t("If an image style also includes crop and rotation effects, users may experience unexpected results. For example, the image style’s rotation effect will be applied over any modifications performed real time by the <insert name> module. Generally, an image style works transparently to end users when the image style effects do not change image dimensions, such as desaturation.", array(), array(
'context' => 'Media crop',
)) . '</p>';
$output .= '<p>' . t("If crop and rotation are needed at the image-style level, the following effects result in predictable outcomes:", array(), array(
'context' => 'Media crop',
));
$output .= '<ul>';
$output .= '<li>' . t("A <strong>single rotation</strong> (90, 180, 270 degrees in either direction) <em>OR</em>", array(), array(
'context' => 'Media crop',
)) . '</li>';
$output .= '<li>' . t("A <strong>single</strong> crop <em>OR</em>", array(), array(
'context' => 'Media crop',
)) . '</li>';
$output .= '<li>' . t("A <strong>single rotation</strong> followed by a single crop", array(), array(
'context' => 'Media crop',
)) . '</li>';
$output .= '</ul></p>';
return $output;
break;
}
}
/**
* Implements hook_menu().
*/
function media_crop_menu() {
$items = array();
// style, fid, token (receives other necessary data via _POST)
$items['media_crop/%/%file/%'] = array(
'title' => '',
'page callback' => 'media_crop_create_instance',
'page arguments' => array(
1,
2,
3,
),
'access callback' => 'media_crop_image_access',
'access arguments' => array(
2,
),
'type' => MENU_CALLBACK,
);
$directory_path = file_stream_wrapper_get_instance_by_scheme('public')
->getDirectoryPath();
$items[$directory_path . '/media_crop/%media_crop_instance'] = array(
'title' => 'Generate media crop',
'page callback' => 'media_crop_get_image',
'page arguments' => array(
count(explode('/', $directory_path)) + 1,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['system/files/media_crop/%media_crop_instance'] = array(
'title' => 'Generate media crop private',
'page callback' => 'media_crop_get_image',
'page arguments' => array(
3,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_theme().
*/
function media_crop_theme() {
return array(
'media_crop_image' => array(
'variables' => array(
'file' => NULL,
'attributes' => array(),
'media_crop' => array(),
),
),
);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function media_crop_form_image_style_form_alter(&$form, &$form_state) {
$effects = isset($form_state['image_style']['effects']) ? $form_state['image_style']['effects'] : array();
$compatible = _media_crop_effects_are_compatible($effects);
$not_safe_description = t("Effects have been added that could produce unexpected results with the Media Crop module's real-time crop, rotate, and scale feature. <a href=\"@url\" target=\"_blank\">Learn more</a>", array(
'@url' => url('admin/help/media_crop'),
), array(
'context' => 'Media crop',
));
if (empty($form_state['input']) && !$compatible) {
drupal_set_message($not_safe_description, 'warning');
}
}
/**
* Implements hook_media_format_form_prepare_alter().
*/
function media_crop_media_format_form_prepare_alter(&$form, $form_state, $file) {
// See access callback implementation.
if (!media_crop_image_access($file)) {
return;
}
// Alter only if file is not remote and is of type image.
if (!_media_crop_is_file_remote($file) && $file->type === 'image') {
$form['token'] = array(
'#type' => 'hidden',
'#value' => drupal_get_token(MEDIA_CROP_TOKEN_VALUE),
);
$form['template'] = array(
'#type' => 'hidden',
'#value' => theme('image', array(
'path' => _media_crop_get_path('{MCIID}', $file->uri),
'attributes' => array(
'id' => 'ID_PLACEHOLDER',
),
)),
);
$top_container_key = 'format_and_crop_top';
$bottom_container_key = 'format_and_crop_bottom';
$image = image_load($file->uri);
// Remove original format select, we bypass this with a media crop specific
// fake view mode.
unset($form['options']['format']);
$form['format_and_crop']['format'] = array(
'#type' => 'hidden',
'#value' => 'media_crop',
);
$form['image_edit_elements'] = array(
'#type' => 'container',
'#weight' => 0,
'#attributes' => array(
'class' => array(
'image-edit-elements-container',
),
),
);
unset($form['preview']);
// Form elements after this appear above the image area.
$form['image_edit_elements'][$top_container_key] = array(
'#type' => 'container',
'#weight' => 0,
'#attributes' => array(
'class' => array(
'format-and-crop-container-top clearfix',
),
),
);
$image_style_options = image_style_options(FALSE);
$form['image_edit_elements'][$top_container_key]['image_style'] = array(
'#type' => 'select',
'#title' => t('Current format is:', array(), array(
'context' => 'Media crop',
)),
'#weight' => -2,
'#options' => $image_style_options + array(
'-1' => t('None'),
),
'#default_value' => variable_get('media_crop_default_style', -1),
'#attributes' => array(
'class' => array(
'media-crop-special-select',
),
),
);
$image_style_keys = array_keys($image_style_options);
foreach ($image_style_keys as $style_key) {
$image_styles = image_styles();
$style_def = NULL;
if (isset($image_styles[$style_key])) {
$style_def = $image_styles[$style_key];
}
$style_html = theme('image', array(
'path' => "media_crop/{$style_key}/{$file->fid}/0/0/0/0/0",
));
$form['image_edit_elements'][$top_container_key]['image_style']['#attached']['js'][] = array(
'data' => array(
'media_crop' => array(
'imageStyleHtml' => array(
$style_key => $style_html,
),
),
),
'type' => 'setting',
);
$is_safe = FALSE;
if ($style_def !== NULL) {
$is_safe = _media_crop_effects_are_compatible($style_def['effects']);
}
$safe_style_rotation_crop_info = array();
if ($is_safe) {
$safe_style_rotation_crop_info = _media_crop_extract_rotation_and_crop($style_def['effects'], $image->info['width'], $image->info['height']);
}
$form['image_edit_elements'][$top_container_key]['image_style']['#attached']['js'][] = array(
'data' => array(
'media_crop' => array(
'styles' => array(
$style_key => array(
'safe' => $is_safe,
) + $safe_style_rotation_crop_info,
),
),
),
'type' => 'setting',
);
}
$form['image_edit_elements'][$top_container_key]['image_style_description'] = array(
'#markup' => t('Includes effects that may produce <a href="@url" target="_blank">unexpected results</a>.', array(
'@url' => url('admin/help/media_crop'),
), array(
'context' => 'Media crop',
)),
'#prefix' => '<div class="image-style-description">',
'#suffix' => '</div>',
'#weight' => -1,
);
// Form elements after this appear under the image area.
$form['image_edit_elements']['enable_interface'] = array(
'#markup' => theme('image', array(
'path' => drupal_get_path('module', 'media_crop') . '/images/crop-rotate-scale.png',
'attributes' => array(
'class' => 'enable-interface',
'title' => t('Enable Image Editor UI', array(), array(
'context' => 'Media crop',
)),
),
)),
'#weight' => 2,
);
$form['image_edit_elements'][$bottom_container_key] = array(
'#type' => 'container',
'#weight' => 3,
'#attributes' => array(
'class' => array(
'format-and-crop-container-bottom clearfix',
),
),
);
$crop_warning = theme('image', array(
'path' => drupal_get_path('module', 'media_crop') . '/images/warning.png',
'attributes' => array(
'class' => 'crop-warning',
'title' => t('Overrides the Image style’s cropping.', array(), array(
'context' => 'Media crop',
)),
),
));
list($crop_title, $crop_desc) = explode('@sep', t('Crop @sep (click / drag)', array(), array(
'context' => 'Media crop',
)));
$form['image_edit_elements'][$bottom_container_key]['crop_dimensions_container'] = array(
'#type' => 'fieldset',
'#title' => $crop_warning . $crop_title . '<span class="crop-title-description">' . $crop_desc . '</span>',
'#weight' => -3,
'#attributes' => array(
'class' => array(
'crop-dimensions-container',
),
),
);
$form['image_edit_elements'][$bottom_container_key]['crop_dimensions_container']['crop_aspect_ratio_lock'] = array(
'#markup' => theme('image', array(
'path' => drupal_get_path('module', 'media_crop') . '/images/lock.png',
'attributes' => array(
'class' => 'crop-aspect-ratio-lock',
'title' => t('Lock aspect ratio', array(), array(
'context' => 'Media crop',
)),
),
)),
'#weight' => -3,
);
$form['image_edit_elements'][$bottom_container_key]['crop_dimensions_container']['crop_aspect_ratio_unlock'] = array(
'#markup' => theme('image', array(
'path' => drupal_get_path('module', 'media_crop') . '/images/unlock.png',
'attributes' => array(
'class' => 'crop-aspect-ratio-unlock',
'title' => t('Unlock aspect ratio', array(), array(
'context' => 'Media crop',
)),
),
)),
'#weight' => -3.1,
);
$form['image_edit_elements'][$bottom_container_key]['crop_dimensions_container']['crop_dimensions'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array(
'crop-dimensions',
),
),
);
$form['image_edit_elements'][$bottom_container_key]['crop_dimensions_container']['crop_dimensions']['crop_width'] = array(
'#type' => 'textfield',
'#title' => t('Width:', array(), array(
'context' => 'Media crop',
)),
'#field_suffix' => 'px',
'#element_validata' => 'element_validate_integer_positive',
'#size' => 4,
'#maxlength' => 4,
'#weight' => -2,
);
$form['image_edit_elements'][$bottom_container_key]['crop_dimensions_container']['crop_dimensions']['crop_height'] = array(
'#type' => 'textfield',
'#title' => t('Height:', array(), array(
'context' => 'Media crop',
)),
'#field_suffix' => 'px',
'#element_validata' => 'element_validate_integer_positive',
'#size' => 4,
'#maxlength' => 4,
'#weight' => -1,
);
$scale_warning = theme('image', array(
'path' => drupal_get_path('module', 'media_crop') . '/images/warning.png',
'attributes' => array(
'class' => 'scale-warning',
'title' => t('Upscaling may produce poor image quality.', array(), array(
'context' => 'Media crop',
)),
),
));
$form['image_edit_elements'][$bottom_container_key]['scale_image_container'] = array(
'#type' => 'fieldset',
'#title' => $scale_warning . t('Scale', array(), array(
'context' => 'Media crop',
)),
'#weight' => -2,
'#attributes' => array(
'class' => array(
'scale-image-container',
),
),
);
$form['image_edit_elements'][$bottom_container_key]['scale_image_container']['scale_width'] = array(
'#type' => 'textfield',
'#title' => t('Width:', array(), array(
'context' => 'Media crop',
)),
'#field_suffix' => 'px',
'#element_validata' => 'element_validate_integer_positive',
'#size' => 4,
'#maxlength' => 4,
'#weight' => -2,
);
$form['image_edit_elements'][$bottom_container_key]['scale_image_container']['scale_height'] = array(
'#type' => 'textfield',
'#title' => t('Height:', array(), array(
'context' => 'Media crop',
)),
'#field_suffix' => 'px',
'#element_validata' => 'element_validate_integer_positive',
'#size' => 4,
'#maxlength' => 4,
'#weight' => -1,
);
$form['image_edit_elements'][$bottom_container_key]['rotate_image_container'] = array(
'#type' => 'fieldset',
'#title' => t('Rotate', array(), array(
'context' => 'Media crop',
)),
'#weight' => -1,
'#attributes' => array(
'class' => array(
'rotate-image-container',
),
),
);
$form['image_edit_elements'][$bottom_container_key]['rotate_image_container']['rotate_right'] = array(
'#markup' => '<a href="#" class="rotate-right-button"><span>' . t('Right', array(), array(
'context' => 'Media crop',
)) . '</span></a>',
'#weight' => -2,
);
$form['image_edit_elements'][$bottom_container_key]['rotate_image_container']['rotate_left'] = array(
'#markup' => '<a href="#" class="rotate-left-button"><span>' . t('Left', array(), array(
'context' => 'Media crop',
)) . '</span></a>',
'#weight' => -1,
);
$form['image_edit_elements'][$bottom_container_key]['disable_interface'] = array(
'#markup' => theme('image', array(
'path' => drupal_get_path('module', 'media_crop') . '/images/close-crop.png',
'attributes' => array(
'class' => 'disable-interface',
'title' => t('Disable Image Editor UI', array(), array(
'context' => 'Media crop',
)),
),
)),
'#weight' => 0,
);
$form['options']['#title'] = '';
$form['options']['#weight'] = 5;
$form['heading']['#weight'] = -5;
// Create width, height, x, y fields.
foreach (array(
'width',
'height',
'x',
'y',
'rotate',
) as $name) {
$form[$name] = array(
'#title' => t(ucfirst($name), array(), array(
'context' => 'Media crop',
)),
'#title_display' => 'invisible',
'#type' => 'textfield',
'#attributes' => array(
'class' => array(
'media-crop-invisible',
),
),
'#theme_wrappers' => array(),
);
}
$form['#attached']['css'][] = drupal_get_path('module', 'media_crop') . "/css/media_crop.base.css";
$form['#attached']['css'][] = drupal_get_path('module', 'media_crop') . "/css/media_crop.theme.css";
$form['#attached']['js'][] = drupal_get_path('module', 'media_crop') . "/js/media_crop.js";
$preview = file_view_file($file, 'media_original');
$preview['#theme_wrappers'][] = 'media_thumbnail';
$form['image_edit_elements']['preview']['#markup'] = drupal_render($preview);
$form['image_edit_elements']['preview']['#weight'] = 1;
$form['image_edit_elements']['rotated-images'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array(
'rotated-images',
),
),
);
for ($deg = 90; $deg < 360; $deg += 90) {
$form['image_edit_elements']['rotated-images']["preview_{$deg}"] = array(
'#markup' => theme('image_style', array(
'style_name' => "media_crop_rotate_{$deg}",
'path' => $file->uri,
'attributes' => array(
'class' => "rotated rotated-{$deg}",
),
)),
);
}
$form['#attached']['js'][] = array(
'type' => 'setting',
'data' => array(
'media_format_form' => array(),
),
);
$form['fid'] = array(
'#type' => 'hidden',
'#value' => $file->fid,
);
$form['#attached']['library'][] = array(
'media_crop',
'imgareaselect',
);
}
}
/**
* Implements hook_library().
*/
function media_crop_library() {
$libpath = libraries_get_path('jquery.imgareaselect');
return array(
'imgareaselect' => array(
'title' => t('imgAreaSelect'),
'website' => 'http://odyniec.net/projects/imgareaselect/',
'version' => '0.9.8',
'js' => array(
"{$libpath}/scripts/jquery.imgareaselect.min.js" => array(),
),
'css' => array(
"{$libpath}/css/imgareaselect-default.css" => array(),
),
'dependencies' => array(
array(
'system',
'jquery',
),
),
),
);
}
/**
* Implements hook_image_default_styles().
*/
function media_crop_image_default_styles() {
$styles = array();
for ($deg = 90; $deg < 360; $deg += 90) {
$styles["media_crop_rotate_{$deg}"] = array(
'effects' => array(
array(
'name' => 'image_scale',
'data' => array(
'width' => 350,
'height' => 350,
'upscale' => TRUE,
),
'weight' => 0,
),
array(
'name' => 'image_rotate',
'data' => array(
'degrees' => $deg,
'random' => FALSE,
),
'weight' => 1,
),
),
);
}
return $styles;
}
/**
* Access callback for media_crop/*.
*/
function media_crop_image_access($file, $account = NULL) {
if ($account === NULL) {
global $user;
$account = $user;
}
// Anyone can crop their own files, but to crop other people's private files,
// they need to be an admin, and to crop other people's public files,
// they need to be able to both view and edit media.
return $file->uid === $account->uid || (strpos($file->uri, 'private://') === 0 ? user_access('administer media') : media_access('view') && media_access('edit'));
}
/**
* Page callback, for creating a (or retrieving an existing)
* cropped media instance.
*/
function media_crop_create_instance($style_name, $file, $token) {
if (!drupal_valid_token($token, MEDIA_CROP_TOKEN_VALUE)) {
watchdog('media_crop', 'Invalid token. ( %token )', array(
'%token' => $token,
));
drupal_access_denied();
}
if (!is_array($_POST['media_crop'])) {
watchdog('media_crop', 'Missing necessary _POST data.');
drupal_access_denied();
}
$angle = (int) $_POST['media_crop']['angle'];
$crop_x_coordinate = (int) $_POST['media_crop']['x'];
$crop_y_coordinate = (int) $_POST['media_crop']['y'];
$crop_width = (int) $_POST['media_crop']['w'];
$crop_height = (int) $_POST['media_crop']['h'];
$scale_width = _is_positive_integer($_POST['media_crop']['scale_w']) ? (int) $_POST['media_crop']['scale_w'] : 0;
$scale_height = _is_positive_integer($_POST['media_crop']['scale_h']) ? (int) $_POST['media_crop']['scale_h'] : 0;
$mciid = db_query("\n SELECT mciid FROM {media_crop_instance} WHERE\n fid = :fid AND\n image_style_name = :style AND\n angle = :angle AND\n crop_x = :x AND\n crop_y = :y AND\n crop_w = :w AND\n crop_h = :h AND\n crop_scale_w = :scale_w AND\n crop_scale_h = :scale_h\n ", array(
':fid' => $file->fid,
':style' => $style_name,
':angle' => $angle,
':x' => $crop_x_coordinate,
':y' => $crop_y_coordinate,
':w' => $crop_width,
':h' => $crop_height,
':scale_w' => $scale_width,
':scale_h' => $scale_height,
))
->fetchField();
if (!$mciid) {
$instance = (object) array(
'fid' => $file->fid,
'image_style_name' => $style_name,
'angle' => $angle,
'crop_x' => $crop_x_coordinate,
'crop_y' => $crop_y_coordinate,
'crop_w' => $crop_width,
'crop_h' => $crop_height,
'crop_scale_w' => $scale_width,
'crop_scale_h' => $scale_height,
);
drupal_write_record('media_crop_instance', $instance);
$mciid = $instance->mciid;
}
drupal_json_output($mciid);
return NULL;
}
/**
* Page callback for image generation.
*/
function media_crop_get_image($media_crop_instance) {
if (!$media_crop_instance) {
drupal_goto(drupal_get_path('module', 'media_crop') . '/images/loading.gif');
}
$angle = (int) $media_crop_instance->angle;
$file = file_load($media_crop_instance->fid);
$style_name = $media_crop_instance->image_style_name;
$crop_x_coordinate = (int) $media_crop_instance->crop_x;
$crop_y_coordinate = (int) $media_crop_instance->crop_y;
$crop_width = (int) $media_crop_instance->crop_w;
$crop_height = (int) $media_crop_instance->crop_h;
$scale_width = (int) $media_crop_instance->crop_scale_w;
$scale_height = (int) $media_crop_instance->crop_scale_h;
$args = func_get_args();
array_shift($args);
$req_uri = implode('/', $args);
$orig_uri = str_replace('://', '/', $file->uri);
if ($req_uri != $orig_uri) {
watchdog('media_crop', 'Request URI not equals to original URI.');
drupal_access_denied();
}
unset($args);
$style = image_style_load($style_name);
if (!$style) {
$style = array(
'name' => $style_name,
'effects' => array(),
);
}
$derivative_uri = _media_crop_get_path($media_crop_instance->mciid, $file->uri);
if (($scheme = file_uri_scheme($derivative_uri)) == 'private') {
if (file_exists($derivative_uri)) {
file_download($scheme, file_uri_target($derivative_uri));
}
else {
$headers = module_invoke_all('file_download', $file->uri);
if (in_array(-1, $headers) || empty($headers)) {
watchdog('media_crop', 'No headers for file download.');
return drupal_access_denied();
}
if (count($headers)) {
foreach ($headers as $name => $value) {
drupal_add_http_header($name, $value);
}
}
}
}
$lock = drupal_hash_base64($file->uri . implode('/', array(
$angle,
$crop_x_coordinate,
$crop_y_coordinate,
$crop_width,
$crop_height,
)));
if (!file_exists($derivative_uri)) {
$lock_acquired = lock_acquire($lock);
if (!$lock_acquired) {
drupal_add_http_header('Status', '503 Service Unavailable');
drupal_add_http_header('Retry-After', 3);
print t('Image generation in progress. Try again shortly.', array(), array(
'context' => 'Media crop',
));
drupal_exit();
}
}
$crop_object = (object) array(
'uri' => $file->uri,
'angle' => $angle,
'style' => $style,
'crop_x_coordinate' => $crop_x_coordinate,
'crop_y_coordinate' => $crop_y_coordinate,
'crop_width' => $crop_width,
'crop_height' => $crop_height,
'scale_height' => $scale_height,
'scale_width' => $scale_width,
'mciid' => $media_crop_instance->mciid,
);
$success = file_exists($derivative_uri) || media_crop_create_derivative($crop_object);
if (!empty($lock_acquired)) {
lock_release($lock);
}
if ($success) {
$image = image_load($derivative_uri);
file_transfer($image->source, array(
'Content-Type' => $image->info['mime_type'],
'Content-Length' => $image->info['file_size'],
));
}
else {
watchdog('media_crop', 'Unable to generate the derived image located at %path.', array(
'%path' => $derivative_uri,
));
drupal_add_http_header('Status', '500 Internal Server Error');
print t('Error generating image.', array(), array(
'context' => 'Media crop',
));
drupal_exit();
}
return NULL;
}
/**
* Helper function to create a derivative of an image.
*
* @param object $crop_object
* Anonymous object, contains data for image generation:
* - uri: (string) URI of the image.
* - angle: (int) Angle in degrees to rotate.
* - style: (string) Name of the style to apply after rotation and cropping.
* - crop_x_coordinate: (int) X coordinate of the crop.
* - crop_y_coordinate: (int) Y coordinate of the crop.
* - crop_width: (int) Width of the crop.
* - crop_height: (int) Height of the crop.
* - scale_width: (int) Width of image scale.
* - scale_height: (int) Height of image scale.
* - mciid: (int) Media crop instance id.
*
* @return bool
* TRUE if the image generation is successful, FALSE otherwise.
*/
function media_crop_create_derivative($crop_object) {
$uri = $crop_object->uri;
$angle = $crop_object->angle;
$style = $crop_object->style;
$crop_x_coordinate = $crop_object->crop_x_coordinate;
$crop_y_coordinate = $crop_object->crop_y_coordinate;
$crop_width = $crop_object->crop_width;
$crop_height = $crop_object->crop_height;
$scale_width = $crop_object->scale_width;
$scale_height = $crop_object->scale_height;
$destination = _media_crop_get_path($crop_object->mciid, $uri);
$directory = drupal_dirname($destination);
if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
watchdog('media_crop', 'Failed to create style directory: %directory', array(
'%directory' => $directory,
), WATCHDOG_ERROR);
return FALSE;
}
if (!($image = image_load($uri))) {
return FALSE;
}
_media_crop_apply_rotation($angle, $image);
_media_crop_apply_crop($crop_width, $crop_height, $crop_x_coordinate, $crop_y_coordinate, $image);
_media_crop_apply_image_style($style, $image);
_media_crop_apply_scale($scale_width, $scale_height, $image);
if (!image_save($image, $destination)) {
if (file_exists($destination)) {
watchdog('media_crop', 'Cached image file %destination already exists. There may be an issue with your rewrite configuration.', array(
'%destination' => $destination,
), WATCHDOG_ERROR);
}
return FALSE;
}
return TRUE;
}
/**
* Helper function to apply rotation to an image.
*
* @param int $angle
* Angle in degrees to rotate.
* @param object $image
* Image object to modify.
*/
function _media_crop_apply_rotation($angle, $image) {
if ($angle) {
image_effect_apply($image, array(
'name' => 'image_rotate',
'data' => array(
'degrees' => $angle,
'random' => FALSE,
),
) + image_effect_definition_load('image_rotate'));
}
}
/**
* Helper function to apply crop to an image.
*
* @param int $crop_width
* Width of the crop.
* @param int $crop_height
* Height of the crop.
* @param int $crop_x_coordinate
* X coordinate of the crop.
* @param int $crop_y_coordinate
* Y coordinate of the crop.
* @param object $image
* Image object to modify.
*/
function _media_crop_apply_crop($crop_width, $crop_height, $crop_x_coordinate, $crop_y_coordinate, $image) {
if ($crop_width && $crop_height) {
$image_width = $image->info['width'];
$image_height = $image->info['height'];
image_effect_apply($image, array(
'name' => 'image_crop',
'data' => array(
'width' => (int) _media_crop_scale($image_width, $image_height, $crop_width),
'height' => (int) _media_crop_scale($image_width, $image_height, $crop_height),
'anchor' => (int) _media_crop_scale($image_width, $image_height, $crop_x_coordinate) . '-' . (int) _media_crop_scale($image_width, $image_height, $crop_y_coordinate),
),
) + image_effect_definition_load('image_crop'));
}
}
/**
* Helper function to apply image style to an image.
*
* @param string $style
* Name of the style to apply after rotation and cropping.
* @param object $image
* Image object to modify.
*/
function _media_crop_apply_image_style($style, $image) {
if (!empty($style) && $style !== NULL) {
$safe = _media_crop_effects_are_compatible($style['effects']);
foreach ($style['effects'] as $effect) {
if (!($safe && ($effect['name'] == 'image_rotate' || $effect['name'] == 'image_crop'))) {
image_effect_apply($image, $effect);
}
}
}
}
/**
* Helper function to apply scale to an image.
*
* @param int $scale_width
* Width of image scale.
* @param int $scale_height
* Height of image scale.
* @param object $image
* Image object to modify.
*/
function _media_crop_apply_scale($scale_width, $scale_height, $image) {
if ($scale_width || $scale_height) {
$scale_width = (int) $scale_width === 0 ? NULL : (int) $scale_width;
$scale_height = (int) $scale_height === 0 ? NULL : (int) $scale_height;
image_scale($image, $scale_width, $scale_height, TRUE);
}
}
/**
* Generates a path for a derivative image.
*
* @param int $mciid
* Media crop instance id.
* @param string $uri
* URI of the image.
*
* @return string
* Path to the derivative image.
*/
function _media_crop_get_path($mciid, $uri) {
$scheme = file_uri_scheme($uri);
if ($scheme) {
$path = file_uri_target($uri);
}
else {
$path = $uri;
$scheme = file_default_scheme();
}
return $scheme . "://media_crop/{$mciid}/{$scheme}/{$path}";
}
/**
* Scaling helper.
*
* @param double $image_width
* Original image width.
* @param double $image_height
* Original image height.
* @param double $val
* Value to scale.
* @param bool $invert
* TRUE if you want to convert from original size to UI units.
* FALSE if you want to convert from UI units to original size.
*
* @return string
* Scaled value.
*/
function _media_crop_scale($image_width, $image_height, $val, $invert = FALSE) {
static $precision = 4096;
$cache =& drupal_static(__FUNCTION__, array());
$cache_index = "{$image_width}x{$image_height}";
if (!isset($cache[$cache_index])) {
$cache[$cache_index] = function_exists('bcdiv') ? bcdiv(350, max($image_width, $image_height), $precision) : 350 / max($image_width, $image_height);
}
return $invert ? function_exists('bcmul') ? bcmul($val, $cache[$cache_index], $precision) : $cache[$cache_index] * $val : (function_exists('bcdiv') ? bcdiv($val, $cache[$cache_index], $precision) : $cache[$cache_index] / $val);
}
/**
* Checks if a list of effects are compatible with the cropping interface.
*
* @param array $effects
* List of image effects.
*
* @return bool
* Compatible or not.
*/
function _media_crop_effects_are_compatible($effects) {
static $safe_rotations = array(
0,
90,
180,
270,
);
$crop = FALSE;
$rotate = FALSE;
foreach ($effects as $effect) {
switch ($effect['name']) {
case 'image_rotate':
if ($crop || $rotate || !in_array($effect['data']['degrees'], $safe_rotations)) {
return FALSE;
}
$rotate = TRUE;
break;
case 'image_crop':
if ($crop) {
return FALSE;
}
$crop = TRUE;
break;
default:
if (_media_crop_is_effect_changes_dimension($effect)) {
return FALSE;
}
}
}
return TRUE;
}
/**
* Checks if an effect changes the image dimensions.
*
* This function does not attempt to manually try it out. Instead it has a
* database of "safe" effects, which can be expanded by implementing
* hook_media_crop_safe_effects().
*
* @param array $effect
* Effect instance definition.
*
* @return bool
* Changes image dimensions or not.
*/
function _media_crop_is_effect_changes_dimension($effect) {
$whitelist =& drupal_static(__FUNCTION__);
if ($whitelist === NULL) {
$whitelist = module_invoke_all('media_crop_safe_effects');
}
return empty($whitelist[$effect['name']]);
}
/**
* Implements hook_media_crop_safe_effects().
*/
function media_crop_media_crop_safe_effects() {
return array(
'image_desaturate' => TRUE,
);
}
/**
* Extracts the rotation and crop information from an image effect list.
*
* It is assumed that the given effect list passed on
* _media_crop_effects_are_compatible().
*
* @param array $effects
* List of image effects.
* @param double $image_width
* Width of original image.
* @param double $image_height
* Height of original image.
*
* @return array
* Rotation and crop information. The result is an associative array:
* - rotation: rotation in degrees
* - crop: crop information
* - width: crop width
* - height: crop height
* - anchor: crop anchor
*/
function _media_crop_extract_rotation_and_crop(array $effects, $image_width, $image_height) {
$rotation = 0;
$crop = NULL;
foreach ($effects as $effect) {
switch ($effect['name']) {
case 'image_rotate':
$rotation = $effect['data']['degrees'];
break;
case 'image_crop':
$crop = $effect['data'];
if ($image_width && $image_height) {
$crop = array(
'width' => (double) _media_crop_scale($image_width, $image_height, $effect['data']['width'], TRUE),
'height' => (double) _media_crop_scale($image_width, $image_height, $effect['data']['height'], TRUE),
'anchor' => $effect['data']['anchor'],
);
}
break;
}
}
return array(
'rotation' => $rotation,
'crop' => $crop,
);
}
/**
* Implements hook_media_token_to_markup_alter().
*/
function media_crop_media_token_to_markup_alter(&$element, $tag_info, $settings) {
if ($tag_info['view_mode'] === 'media_crop' && $tag_info['file']->type === 'image') {
$element['#attributes']['class'] = array(
'media-image',
);
$media_crop_settings = $tag_info['attributes'];
foreach ($media_crop_settings as $name => $value) {
if (_startsWith($name, 'media_crop_')) {
$element['#attributes']['class'][] = 'attr__' . $name . '__' . $value;
$element['#media_crop'][$name] = $value;
}
}
$element['#theme'] = 'media_crop_image';
}
}
/**
* Wildcald loader, for loading a media crop instance object.
*/
function media_crop_instance_load($mciid) {
return is_numeric($mciid) ? db_query('SELECT * FROM {media_crop_instance} WHERE mciid = :mciid', array(
':mciid' => (int) $mciid,
))
->fetch() : NULL;
}
/**
* For rendering the media (image), with applied crop/rotation settings.
*/
function theme_media_crop_image($variables) {
$file = $variables['file'];
$mciid = $variables['media_crop']['media_crop_instance'];
$image_variables = array(
'path' => _media_crop_get_path($mciid, $file->uri),
'attributes' => $variables['attributes'],
);
return theme('image', $image_variables);
}
/**
* Implements hook_element_info_alter().
*/
function media_crop_element_info_alter(&$types) {
$types['text_format']['#pre_render'][] = 'media_crop_pre_render_text_format';
}
/**
* Process function for adding necessary js to the editor.
*/
function media_crop_pre_render_text_format($element) {
// filter_process_format() copies properties to the expanded 'value' child
// element. Skip this text format widget, if it contains no 'format' or when
// the current user does not have access to edit the value.
if (!isset($element['format']) || !empty($element['value']['#disabled'])) {
return $element;
}
// Allow modules to programmatically enforce no client-side editor by setting
// the #wysiwyg property to FALSE.
if (isset($element['#wysiwyg']) && !$element['#wysiwyg']) {
return $element;
}
$path = drupal_get_path('module', 'media_crop');
drupal_add_js($path . '/js/media_crop.image_replace.js');
return $element;
}
/**
* Implements hook_wysiwyg_plugin().
*/
function media_crop_wysiwyg_plugin($editor, $version) {
switch ($editor) {
case 'ckeditor':
if ($version > 3) {
return array(
'media_crop_ckeditor' => array(
'path' => drupal_get_path('module', 'media_crop') . '/plugins/ckeditor',
'extensions' => array(
'media_crop_edit_instance' => t('Media crop edit instance', array(), array(
'context' => 'Media crop',
)),
),
'load' => TRUE,
),
);
}
break;
}
}
/**
* Implements hook_wysiwyg_editor_settings_alter().
*/
function media_crop_wysiwyg_editor_settings_alter(&$settings, $context) {
if ($context['profile']->editor === 'ckeditor') {
if (isset($settings['extraPlugins'])) {
$extra_plugins = explode(',', $settings['extraPlugins']);
if (in_array('media_crop_ckeditor', $extra_plugins)) {
if (isset($settings['menu_groups'])) {
$settings['menu_groups'] .= ',mediaCrop';
}
else {
$settings['menu_groups'] = 'clipboard,form,tablecell,tablecellproperties,tablerow,tablecolumn,table,anchor,link,image,flash,checkbox,radio,textfield,hiddenfield,imagebutton,button,select,textarea,mediaCrop';
}
}
}
}
}
/**
* Helper function for checking if a value is a positive integer.
*/
function _is_positive_integer($value) {
return !($value !== '' && (!is_numeric($value) || intval($value) != $value || $value <= 0));
}
/**
* Helper function for checking if a string starts with a substring.
*/
function _startsWith($haystack, $needle) {
$length = strlen($needle);
return substr($haystack, 0, $length) === $needle;
}
/**
* Checks if file is remote.
*
* @param stdClass $file
* File object.
*
* @return bool
* TRUE if file is remote, FALSE otherwise.
*/
function _media_crop_is_file_remote($file) {
$scheme = file_uri_scheme($file->uri);
$is_remote_stream_wrapper = module_exists('remote_stream_wrapper') ? file_is_scheme_remote($scheme) : FALSE;
$is_oembed = $scheme === 'oembed';
return $is_remote_stream_wrapper || $is_oembed;
}
Functions
Name![]() |
Description |
---|---|
media_crop_create_derivative | Helper function to create a derivative of an image. |
media_crop_create_instance | Page callback, for creating a (or retrieving an existing) cropped media instance. |
media_crop_element_info_alter | Implements hook_element_info_alter(). |
media_crop_form_image_style_form_alter | Implements hook_form_FORM_ID_alter(). |
media_crop_get_image | Page callback for image generation. |
media_crop_help | Implements hook_help(). |
media_crop_image_access | Access callback for media_crop/*. |
media_crop_image_default_styles | Implements hook_image_default_styles(). |
media_crop_instance_load | Wildcald loader, for loading a media crop instance object. |
media_crop_library | Implements hook_library(). |
media_crop_media_crop_safe_effects | Implements hook_media_crop_safe_effects(). |
media_crop_media_format_form_prepare_alter | Implements hook_media_format_form_prepare_alter(). |
media_crop_media_token_to_markup_alter | Implements hook_media_token_to_markup_alter(). |
media_crop_menu | Implements hook_menu(). |
media_crop_pre_render_text_format | Process function for adding necessary js to the editor. |
media_crop_theme | Implements hook_theme(). |
media_crop_wysiwyg_editor_settings_alter | Implements hook_wysiwyg_editor_settings_alter(). |
media_crop_wysiwyg_plugin | Implements hook_wysiwyg_plugin(). |
theme_media_crop_image | For rendering the media (image), with applied crop/rotation settings. |
_is_positive_integer | Helper function for checking if a value is a positive integer. |
_media_crop_apply_crop | Helper function to apply crop to an image. |
_media_crop_apply_image_style | Helper function to apply image style to an image. |
_media_crop_apply_rotation | Helper function to apply rotation to an image. |
_media_crop_apply_scale | Helper function to apply scale to an image. |
_media_crop_effects_are_compatible | Checks if a list of effects are compatible with the cropping interface. |
_media_crop_extract_rotation_and_crop | Extracts the rotation and crop information from an image effect list. |
_media_crop_get_path | Generates a path for a derivative image. |
_media_crop_is_effect_changes_dimension | Checks if an effect changes the image dimensions. |
_media_crop_is_file_remote | Checks if file is remote. |
_media_crop_scale | Scaling helper. |
_startsWith | Helper function for checking if a string starts with a substring. |
Constants
Name![]() |
Description |
---|---|
MEDIA_CROP_TOKEN_VALUE | @file Media crop primary module file. |