'Imagecache javascript crop', 'page callback' => 'drupal_get_form', 'page arguments' => array('imagecrop_settings'), 'access arguments' => array('administer imagecrop'), 'file' => 'imagecrop.admin.inc', ); $items['imagecrop/showcrop'] = array( 'page callback' => 'imagecrop_showcrop', 'type' => MENU_CALLBACK, 'access arguments' => array('crop images with toolbox'), 'file' => 'imagecrop.admin.inc', ); $items['imagecrop/docrop'] = array( 'page callback' => 'imagecrop_docrop', 'type' => MENU_CALLBACK, 'access arguments' => array('crop images with toolbox'), 'file' => 'imagecrop.admin.inc', ); return $items; } /** * Implementation of hook_theme(). */ function imagecrop_theme() { return array( 'imagecrop_javascript' => array( 'arguments' => array('element' => NULL), ), 'imagecrop' => array( 'arguments' => array('url' => NULL, 'width' => NULL, 'height' => NULL, 'resize' => NULL), ), 'imagecrop_result' => array( 'arguments' => array('presetname' => NULL, 'filepath' => NULL, 'alt' => NULL, 'attributes' => NULL), ), 'presettabs' => array( 'arguments' => array('presets' => array(), 'fid' => NULL, 'presetid' => NULL, 'module' => NULL, 'field' => NULL, 'node_type' => NULL), ), ); } /** * Implementation of hook_theme_registry_alter(). */ function imagecrop_theme_registry_alter(&$theme_registry) { array_unshift($theme_registry['page']['theme paths'], drupal_get_path('module', 'imagecrop')); } /** * Implementation of hook_imagecrop_popups(). */ function imagecrop_imagecrop_popups() { $popups = array(); if (module_exists('thickbox')) { $popups['imagecrop_thickbox'] = t('Thickbox'); } if (module_exists('modalframe')) { $popups['imagecrop_modalframe'] = t('Modalframe'); } if (module_exists('colorbox')) { $popups['imagecrop_colorbox'] = t('Colorbox'); } if (module_exists('shadowbox')) { $popups['imagecrop_shadowbox'] = t('Shadowbox'); } return $popups; } /** * Implementation of hook_cron(). * Delete all references in imagecrop table when * a) file doesn't exist anymore. * b) when preset has been deleted. * c) when javascrip_crop action is removed from a preset. */ function imagecrop_cron() { // get all files which do not exist anymore from the files table $result = db_query("SELECT ic.fid,ic.presetid FROM {imagecrop} ic WHERE NOT EXISTS (SELECT fid FROM {files} f WHERE ic.fid = f.fid) AND ic.reference = 'files'"); while ($row = db_fetch_object($result)) { $records[] = array('fid' => $row->fid, 'presetid' => $row->presetid, 'reference' => 'files'); } // get all files which do not exist anymore from the node_images table if (module_exists('node_images')) { $result = db_query("SELECT ic.fid,presetid FROM {imagecrop} ic WHERE NOT EXISTS (SELECT id FROM {node_images} ni WHERE ic.fid = ni.id) AND ic.reference = 'node_images'"); while ($row = db_fetch_object($result)) { $records[] = array('fid' => $row->fid, 'presetid' => $row->presetid, 'reference' => 'node_images'); } } /* * Get all records * a) from presets which do not exist anymore. * b) and/or from presets with no imagecrop_javascript action anymore. */ // files table $result = db_query("SELECT ic.fid,ic.presetid FROM {imagecrop} ic WHERE NOT EXISTS (SELECT presetid FROM {imagecache_action} ia where ic.presetid = ia.presetid AND ia.action = 'imagecrop_javascript') AND ic.reference = 'files'"); while ($row = db_fetch_object($result)) { $records[] = array('fid' => $row->fid, 'presetid' => $row->presetid, 'reference' => 'files'); } // node_images table if (module_exists('node_images')) { $result = db_query("SELECT ic.fid,ic.presetid FROM {imagecrop} ic WHERE NOT EXISTS (SELECT presetid FROM {imagecache_action} ia where ic.presetid = ia.presetid AND ia.action = 'imagecrop_javascript') AND ic.reference = 'node_images'"); while ($row = db_fetch_object($result)) { $records[] = array('fid' => $row->fid, 'presetid' => $row->presetid, 'reference' => 'node_images'); } } if (!empty($records)) { while (list($key, $val) = each($records)) { db_query("DELETE FROM {imagecrop} WHERE fid=%d AND presetid=%d AND reference = '%s'", $val['fid'], $val['presetid'], $val['reference']); } } } /** * Implementation of filefield.module's hook_file_delete(). * * Remove imagecrop settings + temp files after a file has been deleted. */ function imagecrop_file_delete($file) { db_query("DELETE FROM {imagecrop} WHERE fid = %d", $file->fid); file_delete(imagecache_create_path('_imagecrop_temp', $file->filepath)); } /** * Implementation of hook_imagecache_actions(). */ function imagecrop_imagecache_actions() { $actions = array( 'imagecrop_javascript' => array( 'name' => 'Javascript crop', 'description' => 'Create a crop with a javascript toolbox.', 'file' => 'imagecrop_actions.inc', ), ); return $actions; } /** * Implementation of hook_widget_settings_alter(). */ function imagecrop_widget_settings_alter(&$settings, $op, $widget) { // Only support modules that implement hook_insert_widgets(). $widget_type = isset($widget['widget_type']) ? $widget['widget_type'] : $widget['type']; if ($widget_type != 'imagefield_widget' && $widget_type != 'image_fupload_imagefield_widget' && $widget_type != 'linkimagefield_widget') { return; } // Add our new options to the list of settings to be saved. if ($op == 'save') { $settings = array_merge($settings, imagecrop_widget_settings()); } // Add the additional settings to the form. if ($op == 'form') { $settings = array_merge($settings, imagecrop_widget_form($widget)); } } /** * A list of settings needed by Imagecrop module on widgets. */ function imagecrop_widget_settings() { return array( 'imagecrop', 'imagecrop_presets', ); } /** * Configuration form for editing Imagecrop settings for a field instance. */ function imagecrop_widget_form($widget) { $form['imagecrop'] = array( '#type' => 'fieldset', '#title' => t('Imagecrop'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#description' => t('These options allow the user to alter JavaScript crops for specific ImageCache presets.'), '#weight' => 15, ); $presets = imagecrop_presets_list(); if (count($presets) > 0) { $form['imagecrop']['imagecrop'] = array( '#type' => 'checkbox', '#title' => t('Enable JavaScript crop'), '#default_value' => (bool) $widget['imagecrop'], '#description' => t('Enable JavaScript image crop tool for this widget.'), ); $form['imagecrop']['imagecrop_presets'] = array( '#title' => t('Enabled imagecrop presets'), '#type' => 'checkboxes', '#options' => $presets, '#default_value' => (array) $widget['imagecrop_presets'], '#description' => t('Select which Imagecache presets should be available for cropping. If no presets are selected, the option to crop the image is not displayed.'), ); } else { $form['imagecrop']['imagecrop_warning'] = array( '#value' => t('No preset is found with the javascript_crop action so far. If you want to take advantage of this module, you will need to create at least one preset with that action.'), ); } return $form; } /** * Get a list of styles suitable for an #options array. */ function imagecrop_presets_list() { $presets = array(); foreach (imagecache_presets() as $preset) { foreach ($preset['actions'] as $action) { if ($action['action'] == 'imagecrop_javascript') { $presets[$preset['presetname']] = $preset['presetname']; } } } return $presets; } /** * Implementation of hook_elements(). */ function imagecrop_elements() { // Add our function to the form element declared by imagefield. $elements = array(); $elements['imagefield_widget'] = array('#after_build' => array('imagecrop_process_cck_field')); $elements['image_fupload_imagefield_widget'] = $elements['imagefield_widget']; $elements['linkimagefield_widget'] = $elements['imagefield_widget']; return $elements; } /** * Process function for imagecrop-enabled cck fields. */ function imagecrop_process_cck_field($element) { $field = content_fields($element['#field_name'], $element['#type_name']); // Bail out if user does not have permission to crop images. if (!user_access('crop images with toolbox')) { return $element; } // Bail out of imagecrop is not enabled on this field. if (!$field['widget']['imagecrop']) { return $element; } $imagecache_presets = array_filter((array) $field['widget']['imagecrop_presets']); if (empty($imagecache_presets)) { return $element; } $element['imagecrop'] = array( '#type' => 'markup', '#widget' => $field['widget'], '#weight' => 10, '#suffix' => '', ); if ($element['fid']['#value']) { $element['imagecrop']['#prefix'] = '
'; $element['imagecrop']['#value'] = imagecrop_linkitem($element['fid']['#value'], 'imagefield', $element); } else { $element['imagecrop']['#prefix'] = '
'; $element['imagecrop']['#value'] = t('After uploading an image you\'ll be able to crop it.'); } return $element; } /** * Implementation of hook_form_alter(). * Hook into several existing image modules/fields. */ function imagecrop_form_alter(&$form, $form_state, $form_id) { // No need to do checks on forms, other then node form. if ($form['#id'] != 'node-form' && $form_id != '_node_images_edit_page' && $form_id != 'user_profile_form') { return; } // Only show cropping link when editing a node. if ($form['#id'] == 'node-form' && $form['nid']['#value'] == NULL) { return; } $access = user_access('crop images with toolbox'); if (!$access) { return; } // get array of available modules $hooks = variable_get('imagecrop_modules', array()); // hook into image module if (isset($form['images']['thumbnail']) && module_exists('image') && in_array('image', $hooks)) { // do we have presets with javascript_crop ? if (count(imagecrop_presets_list()) == 0) { return; } // it's anonying we have to make a database call to get the right fid. $fid = db_result(db_query("SELECT i.fid FROM {image} i LEFT JOIN {files} f on f.fid = i.fid WHERE i.nid=%d AND filepath='%s' AND filename = '_original'", $form['nid']['#value'], $form['images']['_original']['#value'])); $form['croptab'] = array( '#type' => 'item', '#value' => imagecrop_linkitem($fid), '#weight' => -10, ); } // hook into node_images module elseif ($form_id == '_node_images_edit_page' && module_exists('node_images')) { // do we have presets with javascript_crop ? if (count(return_presets()) == 0) { return; } $type = $form['#parameters'][2]->type; if (variable_get('node_images_position_'. $type, 'hide') != 'hide' && in_array('node_images_position_'. $type, $hooks)) { $form['node_images']['wrapper']['node_images']['imagecrop'] = array('#type' => 'hidden', '#value' => '1'); } } // hook into profile picture elseif ($form_id == 'user_profile_form' && isset($form['picture']['current_picture']) && in_array('profile_picture', $hooks)) { $form['picture']['current_picture']['#value'] .= imagecrop_linkitem($form['_account']['#value']->uid, 'user'); } } /** * Implementation of hook_form_{form_id}_alter(). */ function imagecrop_form_imagecache_ui_action_form_alter($form, $form_state) { if ($form['action']['#value'] == 'imagecrop_javascript') { $form['reset-crops'] = array( '#type' => 'checkbox', '#title' => t('Reset the already cropped images to the new width and height'), '#description' => t('All crop selections that have the same width / height as old settings, will be updated to the new width and height.'), '#weight' => 0, ); $form['old-height'] = array( '#type' => 'hidden', '#value' => $form['data']['height']['#default_value'], ); $form['old-width'] = array( '#type' => 'hidden', '#value' => $form['data']['width']['#default_value'], ); $form['#submit'][] = 'imagecrop_javascript_submit'; } } /** * Helper function to add click link * * @return $form form markup */ function imagecrop_linkitem($fid, $module = '', $element = '') { $popup_link_function = variable_get('imagecrop_popup', 'basic'); $width = variable_get('imagecrop_popup_width', 700); $height = variable_get('imagecrop_popup_height', 600); $module = $module ? ('/0/'. $module) : ''; $field = $element['#field_name'] ? ('/'. $element['#field_name']) : ''; $node_type = $element['#type_name'] ? ('/'. $element['#type_name']) : ''; $url = url('imagecrop/showcrop/'. $fid . $module . $field . $node_type, array('absolute' => TRUE)); if ($popup_link_function != 'basic' && function_exists($popup_link_function) && $link = $popup_link_function($url, $width, $height)) { return $link; } else { return '['. t('Crop this image') .']'; } } /** * Helper function to determine which preset exists and which to load * * @param $presetid id of preset * @return $presets array with presetid to load and list of all other possible presets */ function return_presets($presetid = '', $module = '', $field = '', $node_type = '') { $filter = array(); // get possible presets for current imagefield if ($module == 'imagefield' && $field) { $element = content_fields($field, $node_type); if ($element['widget']['imagecrop']) { $filter = $element['widget']['imagecrop_presets']; } } $all_presets = imagecache_presets(); $presets = array(); foreach ($all_presets as $preset) { foreach ($preset['actions'] as $action) { if ($action['action'] == 'imagecrop_javascript') { if (!count($filter) || !empty($filter[$preset['presetname']])) { $presets['tabs'][] = array('id' => $preset['presetid'], 'name' => $preset['presetname']); if ($presetid == $preset['presetid']) { $presets['presetid'] = $preset['presetid']; } } } } } if (!isset($presets['presetid']) && count($presets) > 0) { $presets['presetid'] = $presets['tabs'][0]['id']; } return $presets; } /** * Render the imagecrop links for thickbox. */ function imagecrop_thickbox($url, $width, $height) { if (!module_exists('thickbox')) { return FALSE; } return '['. t('Crop this image') .']'; } /** * Render the imagecrop links for colorbox. */ function imagecrop_colorbox($url, $width, $height) { if (!module_exists('colorbox')) { return FALSE; } return '['. t('Crop this image') .']'; } /** * Render the imagecrop links for modalframe. */ function imagecrop_modalframe($url, $width, $height) { if (!module_exists('modalframe')) { return FALSE; } // Send the Modal Frame javascript for parent windows to the page. modalframe_parent_js(); $modalFrame = "Drupal.modalFrame.open({url:'$url',width:'$width',height:'$height'});"; return '['. t('Crop this image') .']'; } /** * Render the imagecrop links for shadowbox. */ function imagecrop_shadowbox($url, $width, $height) { if (!module_exists('shadowbox')) { return FALSE; } return '['. t('Crop this image') .']'; } /** * Helper function to create image * * @param $fid file id in files table * @param $presetid id of the preset * @param $cutoff delete the javascript crop action when user wants to define the offsets * @return $file with file, javascript crop and preset properties */ function create_image_object($fid, $presetid, $module = '', $cutoff = FALSE) { $file = _imagecrop_file_load($fid, $module); if ($file != FALSE) { $preset = imagecache_preset($presetid); if ($cutoff == FALSE) { // get the actions from the preset and and throw out the javascript_crop action // and every other action which comes after it. $break = FALSE; while (list($key, $val) = each($preset['actions'])) { if ($val['action'] == 'imagecrop_javascript') { $crop_width = $preset['actions'][$key]['data']['width']; $crop_height = $preset['actions'][$key]['data']['height']; $resizable = $preset['actions'][$key]['data']['resizable']; $aspect = $preset['actions'][$key]['data']['aspect']; $downscaling = $preset['actions'][$key]['data']['downscaling']; $break = TRUE; } if ($break == TRUE) { unset($preset['actions'][$key]); } } // see if we have stored values already for this file $file->xoffset = 0; $file->yoffset = 0; $file->crop_width = $crop_width; $file->crop_height = $crop_height; if ($downscaling) { $file->min_width = $crop_width; $file->min_height = $crop_height; } else { $file->min_width = 1; $file->min_height = 1; } $file->preset_width = $crop_width; $file->preset_height = $crop_height; $reference = ($module == 'node_images' || $module == 'user') ? $module : 'files'; $row = db_fetch_object(db_query( "SELECT xoffset, yoffset, width, height, scale FROM {imagecrop} ic WHERE ic.fid = %d AND ic.presetid = %d AND ic.reference = '%s'", $fid, $presetid, $reference)); $firstscale = FALSE; if (!empty($row)) { $file->xoffset = $row->xoffset; $file->yoffset = $row->yoffset; $file->crop_width = $row->width; $file->crop_height = $row->height; $file->scale = $row->scale; $firstscale = TRUE; } // resizable or not $file->resizable = $resizable; // start with original image size for upcoming actions. $src = $file->filepath; $download_method = variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC); $dst = imagecache_create_path(($cutoff || ($download_method == FILE_DOWNLOADS_PRIVATE)) ? $preset['presetname'] : '_imagecrop_temp', $file->filepath); file_delete($dst); imagecache_build_derivative($preset['actions'], $src, $dst); $orig = getimagesize($dst); $file->orig_width = $orig[0]; $file->orig_height = $orig[1]; // aspect ratio if ($aspect == 'CROP') { $aspect = $crop_width / $crop_height; } elseif ($aspect == 'KEEP') { $aspect = $file->orig_width / $file->orig_height; } $file->aspect = $aspect; // add scale action if necessary if ($row->scale != 'original' && $firstscale == TRUE) { $preset['actions'][] = array( 'action' => 'imagecache_scale', 'data' => array('width' => $row->scale, 'height' => '', 'upscale' => 'false')); } } $file->presetname = $preset['presetname']; $file->dst = $dst; $file->preset_destination = imagecache_create_path($file->presetname, $file->filepath); // create the file to display for the crop, // we also set a global presetid variable, so I can use this in // the javascript_crop action $GLOBALS['imagecrop_presetid'] = $presetid; // if we have a dst, flush the image with all the needed imagecache actions. if (!empty($dst)) { file_delete($dst); imagecache_build_derivative($preset['actions'], $src, $dst); } return $file; } else { return FALSE; } } /** * Helper function to load a file into an object * * @param $fid file id * @param $module specific module which does not use the files table * @return $file with properties of the file or false */ function _imagecrop_file_load($fid, $module) { global $user; if (empty($module) || $module == 'imagefield') { $file = db_fetch_object(db_query('SELECT * FROM {files} WHERE fid = %d', $fid)); } elseif ($module == 'node_images') { $file = db_fetch_object(db_query('SELECT * FROM {node_images} WHERE id = %d', $fid)); } elseif ($module == 'user') { $filepath = db_result(db_query('SELECT picture FROM {users} WHERE uid = %d', $fid)); if ($filepath) { $file = new stdClass(); $file->uid = $fid; $file->filepath = $filepath; $file->filemime = file_get_mimetype($filepath); } } if ($file) { // make sure it's an image. Any other mime extensions possible? // return false if it's not the right mime type $filemime = array('image/jpeg', 'image/gif', 'image/png', 'image/pjpeg'); if (!in_array($file->filemime, $filemime)) return FALSE; // access denied if current user hasn't enough permissions if (!user_access('crop any image with toolbox')) { if ($module != 'user' && !user_access('administer nodes') && $user->uid != $file->uid) { drupal_access_denied(); exit(); } elseif ($user->uid != $file->uid) { drupal_access_denied(); exit(); } } // all seems ok, return file return $file; } // return false if no file was found. return FALSE; } /** * Add imagecrop css & javascript */ function imagecrop_markup($js, $css) { $path = drupal_get_path('module', 'imagecrop'); if ($js == TRUE) drupal_add_js($path .'/imagecrop.js'); if ($css == TRUE) drupal_add_css($path .'/imagecrop.css'); } /** * Theme image crop. * * @param $url url of image * @param $width width of image * @param $height height of image * @param $resize wether the cropping box is resizeble or not * @return $output html of the javascript crop area */ function theme_imagecrop($url, $width, $height, $resize = 0) { $url = str_replace("'", "\\'", $url); $output = '
'; $output .= '
'; return $output; } /** * Theme cropped image result. * * @param string $presetname * @param string $filepath * @param string $alt * @param string $attributes * @return image */ function theme_imagecrop_result($presetname, $filepath, $alt = '', $attributes = NULL) { $url = imagecache_create_url($presetname, $filepath, TRUE); return ''. check_plain($alt) .''; } /** * Theme preset tabs * * @param $tabs array of available presets * @param fid file id * @param $presetid preset to highlight * @return $output html of the tabs */ function theme_presettabs($presets, $fid, $presetid, $module = '', $field = '', $node_type = '') { $module = $module ? ('/'. $module) : ''; $field = $field ? ('/'. $field) : ''; $node_type = $node_type ? ('/'. $node_type) : ''; $tabs[0] = array( 'data' => t('Select a preset to crop »'), 'class' => 'preset-label' ); foreach ($presets['tabs'] as $key => $value) { $key++; $class = ($value['id'] == $presetid) ? 'imagecrop_highlight' : ''; $url = 'imagecrop/showcrop/'. $fid .'/'. $value['id'] . $module . $field . $node_type; $tabs[$key] = array( 'data' => l($value['name'], $url), ); if ($value['id'] == $presetid) { $tabs[$key]['class'] = 'active'; } } return '
'. theme('item_list', $tabs, NULL, 'ul', array('id' => 'preset-tabs')) .'
'; }