Index: image/image.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/image/image.module,v
retrieving revision 1.209.2.49
diff -u -p -r1.209.2.49 image.module
--- image/image.module	13 Nov 2007 03:23:07 -0000	1.209.2.49
+++ image/image.module	27 Nov 2007 03:53:03 -0000
@@ -89,11 +89,22 @@ function image_admin_settings() {
     '#type' => 'fieldset',
     '#title' => t('File paths')
   );
-  $form['paths']['image_default_path'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Default image path'),
-    '#default_value' => variable_get('image_default_path', 'images'),
-    '#description' => t('Subdirectory in the directory "%dir" where pictures will be stored. Do not include trailing slash.', array('%dir' => variable_get('file_directory_path', 'files'))),
+
+  $example_tokens = array(
+    '%nodepath'   => 'safe/url/example' ,
+    '%filename'   => 'sample', 
+    '%label'      => 'thumb', 
+    '%extension'  => 'jpg', 
+    '%nid'        => 666, 
+    '%uid'        => $GLOBALS['user']->uid
+  );
+  $example_path = variable_get('file_directory_path', 'files') .'/'. strtr(variable_get('image_derivatives_filename_pattern', _image_default_filename_pattern()), $example_tokens );
+
+  $form['paths']['image_derivatives_filename_pattern'] = array(
+    '#type' => 'textfield', 
+    '#title' => t('Filename naming pattern'), 
+    '#default_value' => variable_get('image_derivatives_filename_pattern', _image_default_filename_pattern()), 
+    '#description' => t('Pattern to use when creating derivatives (like thumbnails) for an image. Available tokens are: %tokens.<br/><pre>%example_path</pre>',array('%tokens' => join(', ', array_keys($example_tokens)), '%example_path' => $example_path) )
   );
 
   $form['image_max_upload_size'] = array(
@@ -166,6 +177,15 @@ function image_admin_settings() {
 }
 
 /**
+ * Return the default filename pattern, incorporating the old 'images' directory
+ * path if appropriate.
+ */
+function _image_default_filename_pattern() {
+  // image_default_path is no longer used, but referenced here for backwards compatability
+  return variable_get('image_default_path', 'images') .'/%filename.%label.%extension';
+}
+
+/**
  * Check that the sizes provided have the required amount of information.
  */
 function image_settings_sizes_validate(&$form) {
@@ -336,7 +356,7 @@ function image_prepare(&$node, $field_na
     }
 
     // Save the file to the temp directory.
-    $file = file_save_upload($field_name, _image_filename($file->filename, IMAGE_ORIGINAL, TRUE));
+    $file = file_save_upload($field_name, _image_filename($file->filename, IMAGE_ORIGINAL, TRUE, $node));
     if (!$file) {
       return;
     }
@@ -917,7 +937,7 @@ function _image_build_derivatives(&$node
 
   // Resize for the necessary sizes.
   foreach ($needed_sizes as $key => $size) {
-    $destination = _image_filename($original_path, $key, $temp);
+    $destination = _image_filename($original_path, $key, $temp, $node);
     if (!image_scale($original_path, $destination, $size['width'], $size['height'])) {
       drupal_set_message(t('Unable to create scaled %label image', array('%label' => $size['label'])), 'error');
       return FALSE;
@@ -930,29 +950,53 @@ function _image_build_derivatives(&$node
 /**
  * Creates an image filename.
  */
-function _image_filename($filename, $label = IMAGE_ORIGINAL, $temp = FALSE) {
-  $path = variable_get('image_default_path', 'images') .'/';
-  if ($temp) {
-    $path .= 'temp/';
-  }
-
+function _image_filename($filename, $label = IMAGE_ORIGINAL, $temp = FALSE, $node = NULL) {
   $filename = basename($filename);
+  $pos = strrpos($filename, '.');
 
-  // Insert the resized name in non-original images
-  if ($label && ($label != IMAGE_ORIGINAL)) {
-    $pos = strrpos($filename, '.');
-    if ($pos === false) {
-      // The file had no extension - which happens in really old image.module
-      // versions, so figure out the extension.
-      $image_info = image_get_info(file_create_path($path . $filename));
-      $filename = $filename .'.'. $label .'.'. $image_info['extension'];
-    }
-    else {
-      $filename = substr($filename, 0, $pos) .'.'. $label . substr($filename, $pos);
-    }
+  // Build filename according to token pattern.
+  $tokens = array(
+    '%nodepath'  => $node->path ? $node->path : 'node-'. $node->nid,
+    '%filename'  => substr($filename, 0, strpos($filename, '.')), 
+    '%label'     => $label,
+    '%extension' => substr($filename, $pos+1),
+    '%nid'       => $node->nid,
+    '%uid'       => $node->uid,
+  );
+
+  // Even original images may be renamed, but they won't include the derivative label
+
+  if ($label == IMAGE_ORIGINAL) {
+    $tokens['%label'] = '';
+  }
+
+  if ($pos === false) {
+    // The file had no extension - which happens in really old image.module
+    // versions, so figure out the extension, and construct the filename again.
+    $image_info = image_get_info(file_create_path($path . $filename));
+    $tokens['%filename'] = $filename;
+    $tokens['%extension'] = $image_info['extension'];
+    $filepath = strtr(variable_get('image_derivatives_filename_pattern', _image_default_filename_pattern()), $tokens);
   }
 
-  return file_create_path($path . $filename);
+  $filepath = strtr(variable_get('image_derivatives_filename_pattern',  _image_default_filename_pattern()), $tokens);
+  
+  // Sanitize possible typos and collapse dividers around tokens that are not there.
+  // TODO test edge cases
+  $filepath = preg_replace('|([/_\-\.])[/_\-\.]|', '$1', $filepath);
+  if ($temp) { 
+    $filepath = 'temp/'. $filepath; 
+  } 
+  // TODO repair bad characters & spaces in uploaded filenames?
+
+  $fullpath = file_directory_path() .'/'. $filepath;
+  #drupal_set_message("Returning image full filepath $fullpath");
+
+  // When using advanced filename patterns, we may need to ensure a directory to put this image into exists 
+  $target_dir = dirname($fullpath);
+  if(! is_dir($target_dir)){ image_mkdirs($target_dir); }
+
+  return $fullpath;
 }
 
 /**
@@ -1029,7 +1073,7 @@ function _image_is_required_size($size) 
  */
 function _image_insert(&$node, $size, $image_path) {
   $original_path = $node->images[IMAGE_ORIGINAL];
-  if (file_move($image_path, _image_filename($original_path, $size))) {
+  if (file_move($image_path, _image_filename($original_path, $size, FALSE, $node))) {
     // Update the node to reflect the actual filename, it may have been changed
     // if a file of the same name already existed.
     $node->images[$size] = $image_path;
@@ -1117,3 +1161,11 @@ function image_create_node_from($filepat
   return $node;
 }
 
+/**
+ * Recursive mkdir. Utility func
+ */
+function image_mkdirs($strPath, $mode = 0777) {
+  if(! $strPath){ trigger_error("Null call to image_mkdirs()", E_USER_WARNING); }
+  return is_dir($strPath) or ( image_mkdirs(dirname($strPath), $mode) and mkdir($strPath, $mode) );
+}
+
