t('Imagecache javascript crop'), 'path' => 'admin/settings/imagecrop', 'callback' => 'drupal_get_form', 'callback arguments' => 'imagecrop_settings', 'access' => $access_settings, ); $items[] = array( 'path' => 'imagecrop/showcrop', 'callback' => 'imagecrop_showcrop', 'type' => MENU_CALLBACK, 'access' => $access_crop, ); $items[] = array( 'path' => 'imagecrop/docrop', 'callback' => 'imagecrop_docrop', 'type' => MENU_CALLBACK, 'access' => $access_crop, ); } return $items; } /** * Imagecrop settings page */ function imagecrop_settings() { // hook into image module if (module_exists('image')) { $options_modules['image'] = t('Hook into image module'); } // hook into node_images module if (module_exists('node_images')) { $result = db_query("SELECT name,value FROM {variable} WHERE name LIKE 'node_images_position_%'"); if (db_num_rows($result) > 0) { drupal_set_message(t('When you want to enable support for the node_images module, please read the README that comes with the imagecrop module.')); while ($row = db_fetch_object($result)) { if (variable_get($row->name, 'hide') != 'hide') { $explode = explode('_', $row->name); $options_modules[$row->name] = t('Hook into node_images module for content type @type', array('@type' => $explode[3])); } } } } // hook into imagefield module if (module_exists('imagefield')) { $result = db_query("SELECT field_name,label FROM {node_field_instance} WHERE widget_type = 'image'"); while ($row = db_fetch_object($result)) { $options_fields[$row->field_name] = t('Hook into imagefield %s', array('%s' => $row->label)); } } // show checkboxes if options are not empty if (!empty($options_modules) || !empty($options_fields)) { if (!empty($options_modules)) { $form['imagecrop_modules'] = array( '#type' => 'checkboxes', '#title' => t('Hook into modules'), '#default_value' => variable_get('imagecrop_modules', array()), '#options' => $options_modules, ); } if (!empty($options_fields)) { $form['imagecrop_fields'] = array( '#type' => 'checkboxes', '#title' => t('Hook into cck fields'), '#default_value' => variable_get('imagecrop_fields', array()), '#options' => $options_fields, ); } $form['array_filter'] = array('#type' => 'hidden'); } else { $form['no_modules_fields'] = array( '#type' => 'item', '#value' => t('No modules or fields are found to hook into.'), ); } // drupal message if no action is found with javascript_crop if (imagecrop_action_exists() == FALSE) { drupal_set_message(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 system_settings_form($form); } /** * 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 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; } /** * Helper function to check if a preset exists with the imagecrop_javascript action. * Needed to determine if we have to display our javascript crop link. * * @return true or false */ function imagecrop_action_exists() { $result = db_result(db_query("SELECT actionid FROM {imagecache_action} WHERE action = 'imagecrop_javascript'")); return $result; } /** * Implementation of hook_form_alter(). * Hook into several existing image modules/fields. */ function imagecrop_form_alter($form_id, &$form) { // do we have presets with javascript_crop ? $exists = imagecrop_action_exists(); // user access $access = user_access('crop images with toolbox'); // build array with available modules/fields $modules = variable_get('imagecrop_modules', array()); $fields = variable_get('imagecrop_fields', array()); $hooks = array_merge($modules, $fields); // hook into imagefield module if (module_exists('imagefield') && $exists != FALSE && $access) { $formfield = imagecrop_find_imagefields($form, $fields); if ($formfield != FALSE) { $count = count($formfield); for ($i=0;$i<$count;$i++) { $a = 0; while ($form[$formfield[$i]][$a]['fid']['#value']) { if (!empty($form[$formfield[$i]][$a]['fid']['#value'])) { $descr = $form[$formfield[$i]][$a]['description']['#value'] .'
'. imagecrop_linkitem($form[$formfield[$i]][$a]['fid']['#value']); $form[$formfield[$i]][$a]['description']['#value'] = $descr; } $a++; } } } } // hook into image module if (module_exists('image') && $exists != FALSE && $access && in_array('image', $hooks) && isset($form['images']['thumbnail'])) { // it's anonying we have to make a database call to get the right fid. $fid = db_result(db_query("SELECT fid FROM {files} WHERE nid=%d AND filename = '_original' AND filepath='%s'", $form['nid']['#value'], $form['images']['_original']['#default_value'])); $form['croptab'] = imagecrop_formitem($fid, -10); } // hook into node_images module if (module_exists('node_images') && $form_id == '_node_images_list' && $exists != FALSE && $access) { $type = $form['#parameters'][1]->type; if (variable_get('node_images_position_'. $type, 'hide') != 'hide' && in_array('node_images_position_'. $type, $hooks)) { $form['imagecrop'] = array('#type' => 'hidden', '#value' => '1'); } } } /** * Helper function to add form item * * @return $form form markup */ function imagecrop_formitem($fid, $weight = 0) { if (!module_exists('thickbox')) { $form = array( '#type' => 'item', '#value' => ''. t('Javascript crop') .'', '#weight' => $weight, ); } else { $form = array( '#type' => 'item', '#value' => ''. t('Javascript crop') .'', '#weight' => $weight, ); } return $form; } /** * Helper function to add click link * * @return $form form markup */ function imagecrop_linkitem($fid) { if (!module_exists('thickbox')) return ''. t('Javascript crop') .''; else return ''. t('Javascript crop') .''; } /** * Helper function to search for a cck field image. * Temporary images not supported at this point. * * @param $form complete form object * @param $fields all available fields from settings * @return ckk imagefields array or false */ function imagecrop_find_imagefields($form, $fields) { $temp_path = file_directory_temp(); $count = count($fields); $return = FALSE; for ($i=0;$i<$count;$i++) { $temppath = strpos($form[$fields[$i]][0]['filepath']['#value'], $temp_path); $deleteflag = $form[$fields[$i]][0]['flags']['delete']['#value']; if (isset($form[$fields[$i]]) && $temppath === FALSE && $deleteflag != 1) { $return[] = $fields[$i]; } } return $return; } /** * Callback with javascript crop. * * @param $fid id of file * @param $presetid id of preset */ function imagecrop_docrop($fid, $presetid, $module = '') { if (module_exists('jquery_interface')) $javascript_library = 'jquery_interface'; if (module_exists('jquery_ui')) $javascript_library = 'jquery_ui'; imagecrop_markup(TRUE, TRUE, $javascript_library); if (imagecrop_action_exists() == TRUE) { $presets = return_presets($presetid); $file = create_image_object($fid, $presetid, $module); if ($file != FALSE) { $size_warning = FALSE; // get size of temporary image $size = getimagesize($file->dst); $width = $size[0]; $height = $size[1]; // return warning message if crop toolbox is too big and not resizable. if (($width < $file->crop_width || $height < $file->crop_height) && $file->resizable == 0) { $size_warning = FALSE; } // add jquery interface or jquery ui if ($javascript_library == 'jquery_interface') jquery_interface_add(); if ($javascript_library == 'jquery_ui') { if ($file->resizable) jquery_ui_add(array('ui.resizable', 'ui.draggable', 'effects.scale')); else jquery_ui_add(array('ui.draggable')); } // output if ($size_warning == FALSE) { $url = file_create_url($file->dst) .'?time='. time(); $output .= '
'. t('Even if you think this crop is ok, press the submit button!') .'
'; $output .= theme('imagecrop', $url, $width, $height, $javascript_library, $file->resizable); $output .= drupal_get_form('imageoffsets', $file->xoffset, $file->yoffset, $file->crop_width, $file->crop_height, $presetid, $fid, $module, $file->orig_width, $file->scale); } else { $output .= '
'. t('The crop toolbox is too big for this image.') .' '. t('Back') .'
'; } return $output; } else { return '
'. t('Image to crop was not found.') .'
'; } } else { return '
'. 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.') .'
'; } } /** * Callback to return offsets, height & width * * @param $xoffset x value of javascript box * @param $yoffset y value of javascript box * @param $crop_width width of javascript box * @param $crop_height height of javascript box * @param $presetid id of preset * @param $fid id of file * @param $module specific module * @return array $form */ function imageoffsets($xoffset, $yoffset, $crop_width, $crop_height, $presetid, $fid, $module, $width, $scale) { $form['buttons']['submit'] = array('#prefix' => '', '#type' => 'submit', '#value' => t('Create crop')); $form['buttons']['scaling'] = array('#prefix' => '', '#type' => 'select', '#default_value' => $scale, '#options' => imagecrop_scale_options($width, $crop_width)); $form['button']['scaledown'] = array('#prefix' => '
', '#suffix' => '', '#suffix' => '', '#suffix' => '
', '#type' => 'submit', '#value' => t('Scale image')); $form['image-crop-x'] = array('#type' => 'hidden', '#default_value' => $xoffset, '#attributes' => array('class' => 'edit-image-crop-x')); $form['image-crop-y'] = array('#type' => 'hidden', '#default_value' => $yoffset, '#attributes' => array('class' => 'edit-image-crop-y')); $form['image-crop-width'] = array('#type' => 'hidden', '#default_value' => $crop_width, '#attributes' => array('class' => 'edit-image-crop-width')); $form['image-crop-height'] = array('#type' => 'hidden', '#default_value' => $crop_height, '#attributes' => array('class' => 'edit-image-crop-height')); $form['fid'] = array('#type' => 'hidden', '#value' => $fid); $form['module'] = array('#type' => 'hidden', '#value' => $module); $form['presetid'] = array('#type' => 'hidden', '#value' => $presetid); return $form; } /** * Add scale options * * @param unknown_type original width of image * @param unknown_type width of give cropboxx * @return possible width options */ function imagecrop_scale_options($width, $cropboxwidth) { $options['original'] = 'Original ('. $width .'px)'; // experimental, we give the width always a bit more $cropboxwidth += 60; while ($width > $cropboxwidth) { $width -= 50; $options[$width] = $width .'px'; } return $options; } /** * Save the offset & size values * * @param $form_id id of the form * @param $form_values submitted values of the imageoffsets form */ function imageoffsets_submit($form_id, $form_values) { if ($form_values['op'] == t('Scale image')) { $form_values['image-crop-x'] = 0; $form_values['image-crop-y'] = 0; } $module = (!empty($form_values['module'])) ? '/'. $form_values['module'] : ''; $reference = (!empty($form_values['module'])) ? $form_values['module'] : 'files'; db_query("DELETE FROM {imagecrop} WHERE fid=%d AND presetid=%d AND reference = '%s'", $form_values['fid'], $form_values['presetid'], $reference); db_query("INSERT INTO {imagecrop} VALUES (%d,%d,'%s',%d,%d,%d,%d,'%s')", $form_values['fid'], $form_values['presetid'], $reference, $form_values['image-crop-x'], $form_values['image-crop-y'], $form_values['image-crop-width'], $form_values['image-crop-height'], $form_values['scaling']); if ($form_values['op'] == t('Scale image')) drupal_goto('imagecrop/docrop/'. $form_values['fid'] .'/'. $form_values['presetid'] . $module); else drupal_goto('imagecrop/showcrop/'. $form_values['fid'] .'/'. $form_values['presetid'] . $module); } /** * Show the cropped image. * * @param $fid file id * @param $presetid id of preset * @return cropped version of chosen image */ function imagecrop_showcrop($fid, $presetid = 0, $module = '') { imagecrop_markup(FALSE, TRUE); if (imagecrop_action_exists() == TRUE) { $presets = return_presets($presetid); $presetid = $presets['presetid']; $file = create_image_object($fid, $presetid, $module, TRUE); if ($file != FALSE) { $module = (!empty($module)) ? '/'. $module : ''; $output = theme('presettabs', $presets, $fid, $presetid, $module); $output .= '
'. l(t('Click here to choose another crop area for this picture'), 'imagecrop/docrop/'. $fid .'/'. $presetid . $module) .'
'; $output .= theme('imagecrop_result', $file->presetname, $file->filepath); return $output; } else { return '
'. t('Image to crop was not found.') .'
'; } } else { return '
'. 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.') .'
'; } } /** * 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) { $result = db_query("SELECT ip.presetid, ip.presetname FROM {imagecache_preset} ip INNER JOIN {imagecache_action} ia on ia.presetid = ip.presetid WHERE action = 'imagecrop_javascript' ORDER by ip.presetname ASC"); while ($row = db_fetch_object($result)) { $presets['tabs'][] = array('id' => $row->presetid, 'name' => $row->presetname); } if (!empty($presetid)) $presets['presetid'] = $presetid; else $presets['presetid'] = $presets['tabs'][0]['id']; return $presets; } /** * 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); imagecache_image_flush($file->filename); 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']; $break = TRUE; } if ($break == TRUE) { unset($preset['actions'][$key]); } } // see if we have stored values allready for this file $file->xoffset = 0; $file->yoffset = 0; $file->crop_width = $crop_width; $file->crop_height = $crop_height; $reference = (!empty($module)) ? $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; // 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')); } } $src = $file->filepath; $file->presetname = $preset['presetname']; $dst = imagecache_create_path($preset['presetname'], $file->filepath); $file->dst = $dst; // original size $orig = getimagesize($file->filepath); $file->orig_width = $orig[0]; // 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; 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) { // standard files table if (empty($module)) $result = db_query('SELECT * FROM {files} WHERE fid = %d', $fid); // node_images table else if ($module == 'node_images') { $result = db_query('SELECT * FROM {node_images} WHERE id = %d', $fid); } $file = db_fetch_object($result); 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 $node = node_load($file->nid); if (!user_access('administer nodes') && !user_access('edit '. $node->type .' content') && !user_access('edit own '. $node->type .' content')) { drupal_access_denied(); exit(); } // all seems ok, return file return $file; } // return false if no file was found. return FALSE; } /** * Theme image crop. * * @param $url url of image * @param $width width of image * @param $height height of image * @param $js_lib wether we are using jquery interface or jquery ui * @param $resize wether the cropping box is resizeble or not * @return $output html of the javascript crop area */ function theme_imagecrop($url, $width, $height, $js, $resize = 0) { $output = '
'; if ($resize == 1 && $js == 'jquery_interface') { $output .= '
'; } $output .= '
'; return $output; } /** * Theme imagecrop. Adds time to the image so we make sure * the brower always loads the latest version of the image. * * @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); 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 = '') { $tab_output = ''; foreach ($presets['tabs'] as $key => $value) { $class = ($value['id'] == $presetid) ? 'imagecrop_tab imagecrop_highlight' : 'imagecrop_tab '; $url = 'imagecrop/showcrop/'. $fid .'/'. $value['id'] . $module; $tab_output .= ''. l($value['name'], $url) .''; } $output = '
'. t('Imagecache presets »') .' '. $tab_output .'
'; return $output; } /** * Add imagecrop css & javascript */ function imagecrop_markup($js, $css, $js_lib = 'jquery_interface') { $path = drupal_get_path('module', 'imagecrop'); $js_lib = ($js_lib == 'jquery_interface') ? '' : '_ui'; if ($js == TRUE) drupal_add_js($path .'/imagecrop'. $js_lib .'.js'); if ($css == TRUE) drupal_add_css($path .'/imagecrop.css'); }