You are here

imagefield.module in ImageField 5

Same filename and directory in other branches
  1. 5.2 imagefield.module
  2. 6.3 imagefield.module

Defines an image field type. imagefield uses content.module to store the fid, and the drupal files table to store the actual file data.

File

imagefield.module
View source
<?php

/**
 * @file
 * Defines an image field type.
 * imagefield uses content.module to store the fid, and the drupal files 
 * table to store the actual file data.
 *
 */
function imagefield_default_item() {
  return array(
    'fid' => 0,
    'title' => '',
    'alt' => '',
  );
}
function imagefield_menu($maycache) {
  $items = array();
  if (!$maycache && $_SESSION['imagefield']) {

    // Add handlers for previewing new uploads.
    foreach ($_SESSION['imagefield'] as $fieldname => $files) {
      if (is_array($files)) {
        foreach ($files as $delta => $file) {
          if ($file['preview']) {
            $items[] = array(
              'path' => $file['preview'],
              'callback' => '_imagefield_preview',
              'access' => TRUE,
              'type' => MENU_CALLBACK,
            );
          }
        }
      }
    }
  }
  return $items;
}

/**
 *  transfer a file that is in a 'preview' state.
 *  @todo  multiple support
 */
function _imagefield_preview() {
  foreach ($_SESSION['imagefield'] as $fieldname => $files) {
    foreach ($files as $delta => $file) {
      if ($file['preview'] == $_GET['q']) {
        file_transfer($file['filepath'], array(
          'Content-Type: ' . mime_header_encode($file['filemime']),
          'Content-Length: ' . $file['filesize'],
        ));
        exit;
      }
    }
  }
}

/**
 * Implementation of hook_field_info().
 */
function imagefield_field_info() {
  return array(
    'image' => array(
      'label' => 'Image',
    ),
  );
}

/**
 * Implementation of hook_field_settings().
 */
function imagefield_field_settings($op, $field) {
  switch ($op) {
    case 'form':
      $form = array();
      return $form;
    case 'validate':
      break;
    case 'save':
      return array();
    case 'database columns':
      $columns = array(
        'fid' => array(
          'type' => 'int',
          'not null' => TRUE,
          'default' => '0',
        ),
        'title' => array(
          'type' => 'varchar',
          length => 255,
          'not null' => TRUE,
          'default' => "''",
          'sortable' => TRUE,
        ),
        'alt' => array(
          'type' => 'varchar',
          length => 255,
          'not null' => TRUE,
          'default' => "''",
          'sortable' => TRUE,
        ),
      );
      return $columns;
  }
}

/**
 * insert a file into the database.
 * @param $node
 *    node object file will be associated with.
 * @param $file
 *    file to be inserted, passed by reference since fid should be attached.
 *    
 */
function imagefield_file_insert($node, &$file, $field) {
  $fieldname = $field['field_name'];
  $filepath = file_create_path($field['widget']['image_path']) . '/' . $file['filename'];
  if (imagefield_check_directory($field['widget']['image_path']) && ($file = file_save_upload((object) $file, $filepath))) {
    $file = (array) $file;
    $file['fid'] = db_next_id('{files}_fid');
    db_query("INSERT into {files} (fid, nid, filename, filepath, filemime, filesize)\n             VALUES (%d, %d, '%s','%s','%s',%d)", $file['fid'], $node->nid, $file['filename'], $file['filepath'], $file['filemime'], $file['filesize']);
    return (array) $file;
  }
  else {

    // Include file name in upload error.
    form_set_error(NULL, t('Image upload was unsuccessful.'));
    return FALSE;
  }
}

/**
 * update the file record if necessary
 * @param $node
 * @param $file
 * @param $field
 */
function imagefield_file_update($node, &$file, $field) {
  $file = (array) $file;
  if ($file['flags']['delete'] == TRUE) {
    if (_imagefield_file_delete($file, $field['field_name'])) {
      return array();
    }
  }
  if ($file['fid'] == 'upload') {
    return imagefield_file_insert($node, $file, $field);
  }
  else {

    // if fid is not numeric here we should complain.
    // else we update the file table.
  }
  return $file;
}

/**
 * Implementation of hook_field().
 */
function imagefield_field($op, $node, $field, &$node_field, $teaser, $page) {
  $fieldname = $field['field_name'];
  switch ($op) {

    // called after content.module loads default data.
    case 'load':
      if (count($node_field)) {
        $values = array();
        foreach ($node_field as $delta => $file) {
          if (!empty($file)) {
            $file = _imagefield_file_load($file['fid']);

            // In certain cases, content.module calls this function with
            // $items[$delta] set, even if the field has not yet been stored at
            // all or has already been deleted. In that case, $file['fid'] == 0
            // and file_load() returns an empty array. When that happens,
            // unset() the delta so that subsequent hooks are not bothered.
            if (empty($file)) {
              unset($node_field[$delta]);
            }
            else {

              // otherwise, merge our info with CCK's, and all is fine.
              $node_field[$delta] = array_merge((array) $node_field[$delta], $file);
            }
          }
        }
        return array(
          $fieldname => $node_field,
        );
      }
      return array();

    // called before content.module defaults.
    case 'insert':
      foreach ($node_field as $delta => $item) {
        $node_field[$delta] = imagefield_file_insert($node, $item, $field);

        // Remove non-existant images from items
        if (empty($node_field[$delta])) {
          unset($node_field[$delta]);
        }
      }
      imagefield_clear_field_session($fieldname);
      break;

    // called before content.module defaults.
    case 'update':
      foreach ($node_field as $delta => $item) {

        // If we're dealing with a single value field, and we just received
        // a new file item, we need to mark the existing (old) one for
        // deletion.  Otherwise, it will become orphaned.
        if (!$field['multiple'] && count($node_field) > 1 && $delta === 0) {
          $item['flags']['delete'] = TRUE;
        }

        // Update each file item.
        $node_field[$delta] = imagefield_file_update($node, $item, $field);

        // If the file has been deleted, unset the file entry so that it's
        // actually deleted from the database, or at least set it to a
        // default item if CCK won't delete it.
        if (empty($node_field[$delta])) {
          if ($field['multiple']) {
            unset($node_field[$delta]);
          }
          else {
            $node_field[$delta] = imagefield_default_item();
          }
        }
      }

      // Compact deltas.
      $node_field = array_values($node_field);
      imagefield_clear_field_session($fieldname);
      break;
    case 'delete':
      foreach ($node_field as $delta => $item) {
        _imagefield_file_delete($item, $field['field_name']);
      }
      break;
  }
}

/**
 * Implementation of hook_widget_info().
 */
function imagefield_widget_info() {
  return array(
    'image' => array(
      'label' => 'Image',
      'field types' => array(
        'image',
      ),
    ),
  );
}

/**
 * Implementation of hook_widget_settings().
 */
function imagefield_widget_settings($op, $widget) {
  switch ($op) {
    case 'callbacks':
      return array(
        'default value' => CONTENT_CALLBACK_CUSTOM,
      );
    case 'form':
      $form = array();
      $form['max_resolution'] = array(
        '#type' => 'textfield',
        '#title' => t('Maximum resolution for Images'),
        '#default_value' => $widget['max_resolution'] ? $widget['max_resolution'] : 0,
        '#size' => 15,
        '#maxlength' => 10,
        '#description' => t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.'),
      );
      $form['image_path'] = array(
        '#type' => 'textfield',
        '#title' => t('Image path'),
        '#default_value' => $widget['image_path'] ? $widget['image_path'] : '',
        '#description' => t('Optional subdirectory within the "%dir" directory where images will be stored. Do not include trailing slash.', array(
          '%dir' => variable_get('file_directory_path', 'files'),
        )),
        '#after_build' => array(
          'imagefield_form_check_directory',
        ),
      );
      $form['custom_alt'] = array(
        '#type' => 'checkbox',
        '#title' => t('Enable custom alternate text'),
        '#default_value' => $widget['custom_alt'] ? $widget['custom_alt'] : 0,
        '#description' => t('Enable custom alternate text for images. Filename will be used if not checked.'),
      );
      $form['custom_title'] = array(
        '#type' => 'checkbox',
        '#title' => t('Enable custom title text'),
        '#default_value' => $widget['custom_title'] ? $widget['custom_title'] : 0,
        '#description' => t('Enable custom title text for images. Filename will be used if not checked.'),
      );
      return $form;
    case 'validate':
      break;
    case 'save':
      return array(
        'max_resolution',
        'image_path',
        'custom_alt',
        'custom_title',
        'teaser_preset',
        'body_preset',
      );
  }
}

/**
 * Wrapper function for imagefield_check_directory that accepts a form element
 * to validate - if user specified one. Won't allow form submit unless the
 * directory exists & is writable
 * 
 * @param $form_element
 *   The form element containing the name of the directory to check.
 */
function imagefield_form_check_directory($form_element) {
  if (!empty($form_element['#value'])) {
    imagefield_check_directory($form_element['#value'], $form_element);
  }
  return $form_element;
}

/**
 * Create the image directory relative to the 'files' dir recursively for every
 * directory in the path.
 * 
 * @param $directory
 *   The directory path under files to check, such as 'photo/path/here'
 * @param $form_element
 *   A form element to throw an error on if the directory is not writable
 */
function imagefield_check_directory($directory, $form_element = array()) {
  foreach (explode('/', $directory) as $dir) {
    $dirs[] = $dir;
    $path = file_create_path(implode($dirs, '/'));
    file_check_directory($path, FILE_CREATE_DIRECTORY, $form_element['#parents'][0]);
  }
  return true;
}
function _imagefield_scale_image($file, $resolution = 0) {
  $info = image_get_info($file['filepath']);
  if ($info) {
    list($width, $height) = explode('x', $resolution);
    if ($width && $height) {
      $result = image_scale($file['filepath'], $file['filepath'], $width, $height);
      if ($result) {
        $file['filesize'] = filesize($file['filepath']);
        drupal_set_message(t('The image was resized to fit within the maximum allowed resolution of %resolution pixels', array(
          '%resolution' => $resolution,
        )));
      }
    }
  }
  return $file;
}
function imagefield_clear_session() {
  if (is_array($_SESSION['imagefield']) && count($_SESSION['imagefield'])) {
    foreach (array_keys($_SESSION['imagefield']) as $fieldname) {
      imagefield_clear_field_session($fieldname);
    }
    unset($_SESSION['imagefield']);
  }
}
function imagefield_clear_field_session($fieldname) {
  if (is_array($_SESSION['imagefield'][$fieldname]) && count($_SESSION['imagefield'][$fieldname])) {
    foreach ($_SESSION['imagefield'][$fieldname] as $delta => $file) {
      if (is_file($file['filepath'])) {
        file_delete($file['filepath']);
      }
    }
    unset($_SESSION['imagefield'][$fieldname]);
  }
}
function _imagefield_file_delete($file, $fieldname) {
  if (is_numeric($file['fid'])) {
    db_query('DELETE FROM {files} WHERE fid = %d', $file['fid']);
  }
  else {
    unset($_SESSION['imagefield'][$fieldname][$file['sessionid']]);
  }
  return file_delete($file['filepath']);
}

/**
 * Implementation of hook_widget().
 */
function imagefield_widget($op, &$node, $field, &$node_field) {
  $fieldname = $field['field_name'];
  $content_type = $field['type_name'];
  switch ($op) {
    case 'default value':
      return array();
    case 'prepare form values':
      _imagefield_widget_prepare_form_values($node, $field, $node_field);
      return;
    case 'form':
      $form = _imagefield_widget_form($node, $field, $node_field);
      return $form;
    case 'validate':
      _imagefield_widget_form_validate($node, $field, $node_field);
      return;
  }
}
function _imagefield_widget_prepare_form_values($node, $field, &$node_field) {
  $fieldname = $field['field_name'];

  // clean up the session if we weren't posted.
  if (!count($_POST)) {
    imagefield_clear_session();
  }

  // Attach new files
  if ($file = file_check_upload($fieldname . '_upload')) {
    $file = (array) $file;
    if (strpos($file['filemime'], 'image') !== FALSE) {
      $file = _imagefield_scale_image($file, $field['widget']['max_resolution']);

      // Create the filepath for the image preview
      $filepath = file_create_filename($file['filename'], file_create_path($field['widget']['image_path']));
      if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE) {
        if (strpos($filepath, file_directory_path()) !== FALSE) {
          $filepath = trim(substr($filepath, strlen(file_directory_path())), '\\/');
        }
        $filepath = 'system/files/' . $filepath;
      }
      $file['fid'] = 'upload';
      $file['preview'] = $filepath;

      // If a single field, mark any other images for deletion and delete files in session
      if (!$field['multiple']) {
        if (is_array($node_field)) {
          foreach ($node_field as $delta => $session_file) {
            $node_field[$delta]['flags']['delete'] = TRUE;
          }
        }
        imagefield_clear_field_session($fieldname);
      }

      // Add the file to the session
      $file_id = count($node_field) + count($_SESSION['imagefield'][$fieldname]);
      $_SESSION['imagefield'][$fieldname][$file_id] = $file;
    }
  }

  // Load files from preview state. before committing actions.
  if (is_array($_SESSION['imagefield'][$fieldname]) && count($_SESSION['imagefield'][$fieldname])) {
    foreach ($_SESSION['imagefield'][$fieldname] as $delta => $file) {
      $node_field[] = $file;
    }
  }
}
function _imagefield_widget_form($node, $field, &$node_field) {
  $fieldname = $field['field_name'];
  drupal_add_css(drupal_get_path('module', 'imagefield') . '/imagefield.css');
  $form = array();
  $form[$fieldname] = array(
    '#type' => 'fieldset',
    '#title' => t($field['widget']['label']),
    '#weight' => $field['widget']['weight'],
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
    '#tree' => TRUE,
  );

  // Seperate from tree becase of that silly things won't be
  // displayed if they are a child of '#type' = form issue
  $form[$fieldname][$fieldname . '_upload'] = array(
    '#type' => 'file',
    '#description' => $field['widget']['description'],
    '#tree' => FALSE,
    '#weight' => 9,
  );
  $form[$fieldname]['upload'] = array(
    '#type' => 'button',
    '#value' => t('Upload'),
    '#name' => 'cck_imagefield_' . $fieldname . '_op',
    '#attributes' => array(
      'id' => $fieldname . '-attach-button',
    ),
    '#tree' => FALSE,
    '#weight' => 10,
  );

  // Store the file data object to be carried on.
  if (is_array($node_field) && count($node_field)) {
    foreach ($node_field as $delta => $file) {
      if ($file['filepath'] && !$file['flags']['delete']) {
        $form[$fieldname][$delta] = array(
          '#theme' => 'imagefield_edit_image_row',
        );
        $form[$fieldname][$delta]['flags']['delete'] = array(
          '#type' => 'checkbox',
          '#title' => t('Delete'),
          '#default_value' => 0,
        );
        $filename = $file['fid'] == 'upload' ? file_create_filename($file['filename'], file_create_path($field['widget']['image_path'])) : $file['filepath'];
        $form[$fieldname][$delta]['preview'] = array(
          '#type' => 'markup',
          '#value' => theme('imagefield_image', $file, $file['alt'], $file['title'], array(
            'width' => '150',
          ), FALSE),
        );

        //drupal_set_message('imagefield['. $fieldname .'] '. $op .' node field: <pre>'. print_r($node_field, true) .'</pre>');
        $form[$fieldname][$delta]['description'] = array(
          '#type' => 'markup',
          '#value' => '<strong>' . t('Filename: ') . '</strong>' . $file['filename'],
        );
        $form[$fieldname][$delta]['alt'] = array(
          '#type' => 'hidden',
          '#value' => $file['filename'],
        );

        // overwrite with an input field if custom_alt is flagged;
        if ($field['widget']['custom_alt']) {
          $form[$fieldname][$delta]['alt'] = array(
            '#type' => 'textfield',
            '#title' => t('Alternate text'),
            '#default_value' => $file['alt'],
            '#description' => t('Alternate text to be displayed if the image cannot be displayed.'),
            '#maxlength' => 255,
            '#size' => 10,
          );
        }
        $form[$fieldname][$delta]['title'] = array(
          '#type' => 'hidden',
          '#value' => $file['filename'],
        );

        // overwrite with an input field if custom_title is flagged;
        if ($field['widget']['custom_title']) {
          $form[$fieldname][$delta]['title'] = array(
            '#type' => 'textfield',
            '#title' => t('Title'),
            '#default_value' => $file['title'],
            '#description' => t('Text to be displayed on mouse overs.'),
            '#maxlength' => 255,
            '#size' => 10,
          );
        }

        // Special handling for single value fields
        if (!$field['multiple']) {
          $form[$fieldname][$delta]['replace'] = array(
            '#type' => 'markup',
            '#value' => t('If a new image is chosen, the current image will be replaced upon submitting the form.'),
          );
        }
      }
      elseif ($file['filepath'] && $file['flags']['delete']) {

        // Hide all the form values if this item is marked for deletion
        $form[$fieldname][$delta]['flags']['delete'] = array(
          '#type' => 'value',
          '#value' => $file['flags']['delete'],
        );
        $form[$fieldname][$delta]['title'] = array(
          '#type' => 'value',
          '#value' => $file['title'],
        );
        $form[$fieldname][$delta]['alt'] = array(
          '#type' => 'value',
          '#value' => $file['alt'],
        );
      }
      $form[$fieldname][$delta]['filename'] = array(
        '#type' => 'value',
        '#value' => $file['filename'],
      );
      $form[$fieldname][$delta]['filepath'] = array(
        '#type' => 'value',
        '#value' => $file['filepath'],
      );
      $form[$fieldname][$delta]['filemime'] = array(
        '#type' => 'value',
        '#value' => $file['filemime'],
      );
      $form[$fieldname][$delta]['filesize'] = array(
        '#type' => 'value',
        '#value' => $file['filesize'],
      );
      $form[$fieldname][$delta]['fid'] = array(
        '#type' => 'value',
        '#value' => $file['fid'],
      );
    }
  }
  return $form;
}
function _imagefield_widget_form_validate($node, $field, $node_field) {
  $fieldname = $field['field_name'];
  if ($field['required']) {
    if (!count($node_field)) {
      form_set_error($fieldname, $field['widget']['label'] . ' is required.');
    }
  }
}

/**
 * Implementation of hook_field_formatter_info().
 */
function imagefield_field_formatter_info() {
  $formatters = array(
    'default' => array(
      'label' => 'Default',
      'field types' => array(
        'image',
      ),
    ),
  );
  return $formatters;
}

/**
 * Implementation of hook_field_formatter().
 *
 */
function imagefield_field_formatter($field, $item, $formatter) {
  if (empty($item['fid'])) {
    return '';
  }
  $file = _imagefield_file_load($item['fid']);
  return theme('imagefield_image', $file, $item['alt'], $item['title']);
}
function _imagefield_file_load($fid = NULL) {

  // Don't bother if we weren't passed and fid.
  if (!empty($fid) && is_numeric($fid)) {
    $result = db_query('SELECT * FROM {files} WHERE fid = %d', $fid);
    $file = db_fetch_array($result);
    if ($file) {
      return $file;
    }
  }

  // return an empty array if nothing was found.
  return array();
}
function theme_imagefield_view_image($file, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) {
  return theme('imagefield_image', $file, $alt, $title, $attributes, $getsize);
}
function theme_imagefield_edit_image_row($element) {
  $output = '<div class="imagefield-edit-preview">' . drupal_render($element['preview']) . '</div>';
  $output .= '<div class="imagefield-edit-image-detail">';
  $output .= '<div class="imagefield-edit-image-flags">' . drupal_render($element['flags']) . '</div>';
  $output .= '<div class="imagefield-edit-image-description">' . drupal_render($element['description']);
  $output .= '</div>';
  $output .= drupal_render($element['alt']);
  $output .= drupal_render($element['title']);
  $output .= '</div>';

  //$output .= '<div class="imagefield-edit-image-fid">'. $element['fid']['#value'] .'</div>';
  $output = '<div class="imagefield-edit-image-row clear-block">' . $output . '</div>';
  if (isset($element['replace'])) {
    $output .= '<div class="imagefield-edit-image-replace">' . drupal_render($element['replace']) . '</div>';
  }
  return $output;
}
function theme_imagefield_image($file, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) {
  $file = (array) $file;
  if (!$getsize || is_file($file['filepath']) && (list($width, $height, $type, $image_attributes) = @getimagesize($file['filepath']))) {
    $attributes = drupal_attributes($attributes);
    $path = $file['fid'] == 'upload' ? $file['preview'] : $file['filepath'];
    $alt = empty($alt) ? $file['alt'] : $alt;
    $title = empty($title) ? $file['title'] : $title;
    $url = $file['fid'] == 'upload' ? url($file['preview']) : file_create_url($path);
    return '<img src="' . check_url($url) . '" alt="' . check_plain($alt) . '" title="' . check_plain($title) . '" ' . $image_attributes . $attributes . ' />';
  }
}

/**
 * formats an array of images.
 * @param images
 *    array of individually themed images
 * @return 
 *    html string
 */
function theme_imagefield_multiple($images) {
  return implode("\n", $images);
}

/** 
 * implementation of hook_filedownload
 * replicated from upload.module.
 *
 * conditionally included since we're just replicating the 
 * work of upload.module for now.
 */
if (!function_exists('upload_file_download')) {
  function imagefield_file_download($file) {
    $file = file_create_path($file);
    $result = db_query("SELECT f.* FROM {files} f WHERE filepath = '%s'", $file);
    if ($file = db_fetch_object($result)) {
      if (user_access('view uploaded files')) {
        $node = node_load($file->nid);
        if (node_access('view', $node)) {
          $name = mime_header_encode($file->filename);
          $type = mime_header_encode($file->filemime);

          // Serve images and text inline for the browser to display rather than download.
          $disposition = ereg('^(text/|image/)', $file->filemime) ? 'inline' : 'attachment';
          return array(
            'Content-Type: ' . $type . '; name=' . $name,
            'Content-Length: ' . $file->filesize,
            'Content-Disposition: ' . $disposition . '; filename=' . $name,
            'Cache-Control: private',
          );
        }
        else {
          return -1;
        }
      }
      else {
        return -1;
      }
    }
  }
}

Functions

Namesort descending Description
imagefield_check_directory Create the image directory relative to the 'files' dir recursively for every directory in the path.
imagefield_clear_field_session
imagefield_clear_session
imagefield_default_item @file Defines an image field type. imagefield uses content.module to store the fid, and the drupal files table to store the actual file data.
imagefield_field Implementation of hook_field().
imagefield_field_formatter Implementation of hook_field_formatter().
imagefield_field_formatter_info Implementation of hook_field_formatter_info().
imagefield_field_info Implementation of hook_field_info().
imagefield_field_settings Implementation of hook_field_settings().
imagefield_file_insert insert a file into the database.
imagefield_file_update update the file record if necessary
imagefield_form_check_directory Wrapper function for imagefield_check_directory that accepts a form element to validate - if user specified one. Won't allow form submit unless the directory exists & is writable
imagefield_menu
imagefield_widget Implementation of hook_widget().
imagefield_widget_info Implementation of hook_widget_info().
imagefield_widget_settings Implementation of hook_widget_settings().
theme_imagefield_edit_image_row
theme_imagefield_image
theme_imagefield_multiple formats an array of images.
theme_imagefield_view_image
_imagefield_file_delete
_imagefield_file_load
_imagefield_preview transfer a file that is in a 'preview' state. @todo multiple support
_imagefield_scale_image
_imagefield_widget_form
_imagefield_widget_form_validate
_imagefield_widget_prepare_form_values