Newer
Older
/*
imagecache.module - dynamic image resizer and cache
imagecache allows you to setup specialized identifiers for dynamic image processing
Darrel O'Pry
committed
and caching. It abuses the rewrite rules used for drupal's clean urls to provide
dynamic image generation.
Darrel O'Pry
committed
Eariler security flaws were overcome by using 'rulesets' similar to image.module.
We don't fuss with the database, and creating data that we have to keep up with in drupal.
We do it on the fly and flush it when we want to, or when a ruleset changes.
$get[q] = files/imagecache/<ruleset>/path
@todo: improve image resizing, reduce distortion.
@todo: add watermarking capabilities.
@todo: split action/handlers into their own little .inc files.
@todo: enforce permissions.
**
to add a new handler,
add the button & fields in the _imagecache_actions_form
add a switch to add it in imagecache_admin_list
add handling code to imagecache_cache
**
*/
function imagecache_help($section) {
switch($section) {
case 'admin/modules#description': return t('enable dynamic image manipulator');
function imagecache_perm() {
return array('administer imagecache','flush imagecache');
}
function imagecache_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array( 'path' => 'files/imagecache',
'callback' => 'imagecache_cache',
'access' => TRUE,
'type' => MENU_CALLBACK
);
$items[] = array( 'path' => 'admin/imagecache',
'title' => t('imagecache'),
'access' => user_access('administer imagecache'),
'callback' => 'imagecache_admin_list',
);
}
return $items;
}
function imagecache_cache() {
Darrel O'Pry
committed
$generated = FALSE;
Darrel O'Pry
committed
$ruleset = array_shift($args);
$ruleset = _imagecache_ruleset_load_by_name($ruleset);
$actions = _imagecache_actions_get_by_rulesetid($ruleset['rulesetid']);
$path = implode('/', $args);
// verify that the source exists, if not exit. Maybe display a missing image.
$source = file_create_path($path);
if (!is_file($source)) { drupal_set_message('$source does not exist');
return 'source does not exist';
//deliver not found image and exit.
}
$destination = file_create_path() . '/imagecache/'. $ruleset['rulesetname'] .'/'. $path;
$tmpdestination = file_directory_temp() .'/'. str_replace(dirname($path), '', $path);
$dir = dirname($destination);
drupal_set_message('$tmpdestination: '. $tmpdestination);
drupal_set_message('$destination: '. $destination);
if(!file_check_directory($dir)) {
$folders = explode("/",$dir);
$path = array();
foreach($folders as $folder) {
$tpath[] = $folder;
if(!file_check_directory(implode("/", $tpath))) {
mkdir(implode("/", $tpath));
}
Darrel O'Pry
committed
}
}
if(!file_check_directory($dir)) {
return 'could not create destination: '. $destination;
}
//check if file exists to prevent multiple apache children from trying to generate.
if (!is_file($tmpdestination)) {
$generated = TRUE;
file_copy($source, $tmpdestination);
foreach($actions as $action) {
switch($action['data']['function']) {
case 'resize':
if (!image_resize($tmpdestination, $tmpdestination, $action['data']['width'], $action['data']['height'])) {
$generated[$action['actionid']] = FALSE;
Darrel O'Pry
committed
}
break;
case 'scale':
if (!image_scale($tmpdestination, $tmpdestination, $action['data']['width'], $action['data']['height'])) {
$generated[$action['actionid']] = FALSE;
}
break;
Darrel O'Pry
committed
}
}
file_move($tmpdestination, $destination);
}
Darrel O'Pry
committed
if ($generated) {
if (function_exists('mime_content_type')) {
$mime = mime_content_type($destination);
}
else {
$size = getimagesize($destination);
$mime = $size['mime'];
}
file_transfer($destination, array('Content-Type: ' . mime_header_encode($mime), 'Content-Length: ' . filesize($destination)));
//generation error.
//@todo watchdog an error.
function _imagecache_get_rulesets() {
$rulesets = array();
$result = db_query('SELECT rulesetid, rulesetname FROM {imagecache_rulesets} ORDER BY rulesetname');
while($row = db_fetch_array($result)) {
$rulesets[$row['rulesetid']] = $row;
}
return $rulesets;
}
function _imagecache_actions_get_by_rulesetid($rulesetid) {
$actions = array();
$result = db_query('SELECT actionid, weight, data FROM {imagecache_actions} where rulesetid=%d order by weight',$rulesetid);
while($row = db_fetch_array($result)) {
$row['data'] = unserialize($row['data']);
$actions[$row['actionid']] = $row;
}
return $actions;
}
function imagecache_admin_list() {
// screw fapi. Its inability to handled nested submit buttons or any sort of additional data on a submit
// button is frustrating.. So we're gonna do it ourself.
if (is_array($_POST['ruleset-op'])) {
foreach($_POST['ruleset-op'] as $rulesetid => $op) {
$rulesetid = check_plain($rulesetid);
switch($op) {
case t('Create Ruleset'):
_imagecache_ruleset_create(check_plain($_POST['edit']['newruleset']['name']));
break;
case t('Update Ruleset'):
_imagecache_ruleset_update($rulesetid, check_plain($_POST['edit']['rulesets'][$rulesetid]['name']));
break;
case t('Delete Ruleset'):
_imagecache_ruleset_delete($rulesetid);
break;
case t('Flush Ruleset'):
_imagecache_ruleset_flush($rulesetid);
break;
case t('Add Scale'):
$action['data'] = array('function' => 'scale');
$action['rulesetid'] = $rulesetid;
$action['weight'] = 0;
_imagecache_action_create($action);
break;
case t('Add Resize'):
$action['data'] = array('function' => 'resize');
$action['rulesetid'] = $rulesetid;
$action['weight'] = 0;
_imagecache_action_create($action);
break;
case t('Add Crop'):
$action['data'] = array('function' => 'crop');
$action['rulesetid'] = $rulesetid;
$action['weight'] = 0;
_imagecache_action_create($action);
if (is_array($_POST['action-op'])) {
foreach($_POST['action-op'] as $actionid => $op) {
$actionid = check_plain($actionid);
$action = _imagecache_action_load($actionid);
switch($op) {
case t('Update Action'):
$action['data'] = $_POST['edit']['rulesets'][$action['rulesetid']]['handlers'][$actionid]['data'];
$action['weight'] = $_POST['edit']['rulesets'][$action['rulesetid']]['handlers'][$actionid]['weight'];
if (count($action['data'])) {
foreach($action['data'] as $key => $value) {
$action['data'][$key] = check_plain($value);
}
}
//debug_msg($action,'action b4 update');
_imagecache_action_update($action);
break;
case t('Delete Action'):
_imagecache_action_delete($action);
break;
}
}
}
$rulesets = _imagecache_get_rulesets();
drupal_set_title('Imagecache Administration');
$form['title'] = array('#type' => 'markup', '#value' => 'Rulesets', '#theme' => 'imagecache_admin_title');
$form['rulesets']['#tree'] = TRUE;
if (count($rulesets)) {
foreach($rulesets as $rulesetid => $ruleset) {
$form['rulesets'][$rulesetid] = array(
'#type' => 'fieldset',
'#title' => t($ruleset['rulesetname']),
'#collapsible' => TRUE,
);
$form['rulesets'][$rulesetid]['name'] = array(
'#type' => 'textfield',
'#title' => 'NameSpace',
'#default_value' => $ruleset['rulesetname'],
'#description' => t('string that will be used as an identifier in the url for this set of handlers. Final urls will look like http://example.com/files/imagecache/%namespace/<path to orig>', array('%namespace' => theme('placeholder',$ruleset['rulesetname']))),
);
$form['rulesets'][$rulesetid]['handlers'] = array (
'#type' => 'fieldset',
'#title' => t('image handlers'),
);
$form['rulesets'][$rulesetid]['handlers']['#tree'] = FALSE;
$form['rulesets'][$rulesetid]['handlers'] = _imagecache_actions_form($ruleset['rulesetid']);
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
$form['rulesets'][$rulesetid]['ops']['#tree'] = FALSE;
$form['rulesets'][$rulesetid]['ops']['update'] = array(
'#type' => 'submit',
'#name' => 'ruleset-op['.$rulesetid.']',
'#value' => t('Update Ruleset'),
);
$form['rulesets'][$rulesetid]['ops']['delete'] = array(
'#type' => 'submit',
'#name' => 'ruleset-op['.$rulesetid.']',
'#value' => t('Delete Ruleset'),
);
$form['rulesets'][$rulesetid]['ops']['flush'] = array(
'#type' => 'submit',
'#name' => 'ruleset-op['.$rulesetid.']',
'#value' => t('Flush Ruleset'),
);
}
}
$form['newruleset'] = array(
'#type' => 'fieldset',
'#title' => t('New Ruleset'),
'#tree' => TRUE,
);
$form['newruleset']['name'] = array(
'#type' => 'textfield',
'#size' => '64',
'#title' => t('Name'),
'#default_value' => '',
'#description' => t('The name of an imagecache ruleset. It represents a series of actions to be performed when imagecache dynamically generates an image. This will also be used in the url for an image. Please no spaces.'),
);
$form['newruleset']['create'] = array(
'#type' => 'submit',
'#name' => 'ruleset-op[new]',
'#value' => 'Create Ruleset',
'#weight' => 10,
);
$output .= drupal_get_form('imagecache_admin',$form);
return $output;
}
/**
* load a ruleset by id.
* @param id
* ruleset id.
*/
function _imagecache_ruleset_load($id) {
$result = db_query('SELECT rulesetid, rulesetname FROM {imagecache_rulesets} WHERE rulesetid = %d', $id);
return db_fetch_array($result);
Darrel O'Pry
committed
/**
* load a ruleset by name
* @param name
* ruleset name
*/
function _imagecache_ruleset_load_by_name($name) {
$result = db_query('SELECT rulesetid, rulesetname FROM {imagecache_rulesets} WHERE rulesetname = \'%s\'', $name);
return db_fetch_array($result);
}
/**
* create a ruleset
* @param name
* name of the ruleset to be created.
*/
function _imagecache_ruleset_create($name) {
db_query('INSERT INTO {imagecache_rulesets} (rulesetname) VALUES (\'%s\')', $name);
$path = file_directory_path(). '/imagecache/'.$name;
//debug_msg($path);
}
/**
* update a ruleset
* @param id
* ruleset id
* @param name
* new name for the ruleset
*/
function _imagecache_ruleset_update($id, $name) {
$name = check_plain($name);
$id = (int)$id;
db_query('UPDATE {imagecache_rulesets} SET rulesetname =\'%s\' WHERE rulesetid = %d', $name, $id);
drupal_set_message('Updated ruleset '. $id);
}
function _imagecache_ruleset_delete($id) {
db_query('DELETE FROM {imagecache_actions} where rulesetid = %d', $id);
db_query('DELETE FROM {imagecache_rulesets} where rulesetid = %d', $id);
drupal_set_message('Ruleset '. (int)$id . ' deleted.');
/**
* flush cached media for a ruleset.
* @param id
* a ruleset id.
*/
function _imagecache_ruleset_flush($id) {
if (user_access('imagecache flush')) {
drupal_set_message('Flush Ruleset: '. $id);
$ruleset = _imagecache_ruleset_load($id);
$rulesetdir = file_directory_path().'/imagecache/'.$ruleset['rulesetname'] .'/'. file_directory_path();
drupal_set_message('rulesetdir: '. $rulesetdir);
$dir = opendir($rulesetdir);
while ($file = readdir($dir)) {
if ($file != '.' && $file != '..') {
unlink($rulesetdir .'/'. $file);
}
}
//unlink(file_directory_path().'/imagecache/'.$ruleset['rulesetname']);
}
function _imagecache_action_load($id) {
$result = db_query('SELECT actionid, rulesetid, weight, data FROM {imagecache_actions} WHERE actionid = %d', $id);
$action = db_fetch_array($result);
$action['data'] = unserialize($action['data']);
return $action;
function _imagecache_action_create($action) {
//debug_msg($action, 'action@create: ');
db_query('INSERT INTO {imagecache_actions} (rulesetid, weight, data) VALUES (%d, %d, "%s")', $action['rulesetid'], $action['weight'], serialize($action['data']));
}
function _imagecache_action_update($action) {
//debug_msg($action, 'action@update');
db_query('UPDATE {imagecache_actions} SET weight = %d, data = \'%s\' WHERE actionid = %d', $action['weight'], serialize($action['data']), $action['actionid']);
function _imagecache_action_delete($action) {
_imagecache_ruleset_flush($action['rulesetid']);
db_query('DELETE FROM {imagecache_actions} WHERE actionid = %d', $action['actionid']);
}
function _imagecache_actions_form($rulesetid) {
$form = array(
'#type' => 'fieldset',
$actions = _imagecache_actions_get_by_rulesetid($rulesetid);
if (count($actions)) {
foreach($actions as $actionid => $action) {
$form[$actionid] = array (
'#type' => 'fieldset',
'#title' => t($action['data']['function']),
);
$form[$actionid]['data']['function'] = array(
'#type' => 'hidden',
'#value' => $action['data']['function'],
);
$form[$actionid]['weight'] = array(
'#type' => 'select',
'#title' => t('Weight'),
'#options' => array(0,1,2,3,4,5,6,7,8,9,10),
'#default_value' => 0,
);
switch($action['data']['function']) {
case 'scale':
case 'resize':
$form[$actionid]['data']['height'] = array(
'#type' => 'textfield',
'#title' => t('Height'),
'#default_value' => $action['data']['height'],
);
$form[$actionid]['data']['width'] = array(
'#type' => 'textfield',
'#title' => t('Width'),
'#default_value' => $action['data']['width'],
);
break;
$form[$actionid]['data']['height'] = array(
'#type' => 'textfield',
'#title' => t('Height'),
'#default_value' => $action['data']['height'],
);
$form[$actionid]['data']['width'] = array(
'#type' => 'textfield',
'#title' => t('Width'),
'#default_value' => $action['data']['width'],
);
$form[$actionid]['data']['xoffset'] = array(
'#type' => 'textfield',
'#title' => t('X Offset'),
'#default_value' => $action['data']['xoffset'],
);
$form[$actionid]['data']['yoffset'] = array(
'#type' => 'textfield',
'#title' => t('Y Offset'),
'#default_value' => $action['data']['yoffset'],
);
break;
case 'watermark':
//Think about this one...
}
$form[$actionid]['delete'] = array(
'#type' => 'button',
'#name' => 'action-op['. $actionid .']',
'#value' => 'Delete Action',
);
$form[$actionid]['update'] = array(
'#type' => 'button',
'#name' => 'action-op['. $actionid .']',
'#value' => 'Update Action',
);
}
}
$form['newaction'] = array(
'#type' => 'fieldset',
'#title' => t('Add a New Action'),
);
$form['newaction']['scale'] = array(
'#type' => 'submit',
'#value' => 'Add Scale',
'#name' => 'ruleset-op['.$rulesetid .']',
$form['newaction']['resize'] = array(
'#type' => 'submit',
'#value' => 'Add Resize',
'#name' => 'ruleset-op['.$rulesetid .']',
);
//begin crop add form
$form['newaction']['crop'] = array(
'#type' => 'submit',
'#value' => 'Add Crop',
'#name' => 'ruleset-op['.$rulesetid .']',
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
);
return $form;
}
/**
* Theme an img tag for displaying the image.
*/
function theme_imagecache_display($node, $label, $url, $attributes) {
return '<img src="'. check_url($url) .'" alt="'. check_plain($node->title) .'" title="'. check_plain($node->title) .'" '. drupal_attributes($attributes) .' />';
}
/**
* Verify the image module and toolkit settings.
*/
function _imagecache_check_settings() {
// Sanity check : make sure we've got a working toolkit
if (!image_get_toolkit()) {
drupal_set_message(t('Make sure you have a working image toolkit installed and enabled, for more information see: %settings', array('%settings' => l(t('the settings page'), 'admin/settings'))), 'error');
return false;
}
return true;
}
function theme_imagecache_admin_title($element) {
return '<h2>'.$element['value'].'</h2>';
}