You are here

ml_image.module in Media Library 6

Media Library Image module.

File

ml_image/ml_image.module
View source
<?php

/**
 * @file
 * Media Library Image module.
 */

/**
 * Drupal Hooks
 */

/**
 * Implementation of hook_theme()
 */
function ml_image_theme($existing, $type, $theme, $path) {
  return array(
    'ml_image' => array(
      'arguments' => array(
        'image' => NULL,
        'preview' => FALSE,
      ),
    ),
  );
}

/**
 * Implementation of hook_init()
 */
function ml_image_init() {
  drupal_add_css(drupal_get_path('module', 'ml_image') . '/ml_image.css');
  drupal_add_js(drupal_get_path('module', 'ml_image') . '/ml_image.js');
}

/**
 * Implementation of hook_menu()
 */
function ml_image_menu() {
  $items = array();
  return $items;
}

/**
 * Implementation of hook_nodeapi()
 */
function ml_image_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {

  // We keep a record of all imagefield submitted images to use later
  if ($op == 'presave' && module_exists('imagefield')) {
    $type = content_types($node->type);
    foreach ($type['fields'] as $field => $field_info) {
      if ($field_info['module'] == 'filefield' && $field_info['widget']['module'] == 'imagefield') {
        foreach ($node->{$field} as $item) {
          if ($item['fid'] && !ml_image_get_image($item['fid'])) {
            $metatags = new stdClass();
            $metatags->title = $item['data']['title'];
            $metatags->fid = $item['fid'];
            $metatags->source = 'imagefield';
            ml_image_save_metatags($metatags);
          }
        }
      }
    }
  }
}

/**
 * Implementation of hook_flush_caches()
 */
function ml_image_flush_caches() {

  // Wipe orphaned entries
  db_query('DELETE FROM {ml_image_metadata} WHERE fid NOT IN (SELECT fid FROM {files})');
}

/**
 * Media Library Hooks
 */

/**
 * Implementation of hook_media_types()
 */
function ml_image_media_types() {
  $types = array();
  $types['image'] = array(
    'title' => t('Image'),
    'description' => t('Inline images'),
    //TODO: Is this being used?
    'steps' => 1,
    'settings' => 'ml_image_settings',
    'icon' => drupal_get_path('module', 'ml_image') . '/drupalimage.gif',
  );
  return $types;
}

/**
 * Implementation of hook_filter_media()
 */
function ml_image_filter_media($content, $preview = FALSE) {
  if ($content['type'] == 'image') {
    return theme('ml_image', $content, $preview);
  }
}

/**
 * Implementation of hook_filter_media_alter() 
 * Just for fun. We add a dummy attribute.
 */
function ml_image_filter_media_alter(&$content, $preview = FALSE) {
}

/**
 * Implementation of hook_media_forms()
 */
function ml_image_media_forms($type) {
  switch ($type) {
    case 'image':
      $steps = array(
        'source_select' => array(
          'label' => t('Source selection'),
          'form id' => 'ml_image_source_selection_form',
        ),
        'source_image' => array(
          'label' => t('Image select from source'),
          'form id' => 'ml_image_source_options_form',
        ),
        'image_attributes' => array(
          'label' => t('Image Attributes'),
          'form id' => 'ml_image_attributes_form',
        ),
      );
      return $steps;
  }
}

/**
 * Implementation of hook_media_tag()
 */
function ml_image_media_tag($object) {
  if ($object->type == 'image') {
    $tag['filepath'] = $object->filepath;
    if (is_array($object->attributes)) {
      foreach ($object->attributes as $attr => $value) {
        $tag[$attr] = $value;
      }
    }
    if (is_array($object->metatags)) {
      foreach ($object->metatags as $key => $value) {

        // To avoid namespace conflitcts, we prefix metatag names with 'meta-'
        $tag['meta-' . $key] = $value;
      }
    }
  }
  return $tag;
}

/**
 * Forms
 */

/**
 * Form for our image source selection. Handles both steps of source
 * selection process.
 */
function ml_image_source_selection_form(&$form, &$form_state) {

  // Check for update and skip step
  if ($form_state['action'] == 'update') {
    media_library_modal_skip_step($form_state);
  }
  $sources = ml_image_get_sources();
  if (empty($sources)) {

    // No sources activated. Snap the admin
    drupal_set_message(t('No sources were configured for this media. Please contact the site administrator'), 'error');

    // Disable buttons so user won't be able to go ahead
    unset($form['buttons']['next']);
    unset($form['buttons']['return']);
    return;
  }

  // Proper source selection
  foreach ($sources as $source => $info) {
    $form[$source] = array(
      '#type' => 'fieldset',
      '#title' => $info['label'],
    );

    // Our main radio
    $form[$source]['select'] = array(
      '#type' => 'radio',
      '#return_value' => $source,
      '#attributes' => array(
        'class' => 'ml-image-source-choice',
      ),
    );

    // Get basic form from each source module
    $func_name = $info['module'] . '_' . $source . '_source_form';
    if (function_exists($func_name)) {
      $form[$source][$source] = $func_name($form_state);
    }
    $form[$source][$source]['#tree'] = TRUE;
  }

  // We set this so we can be sure that file uploads work in ctools modal.
  // See here: http://drupal.org/node/451928
  $form['#attributes']['enctype'] = 'multipart/form-data';
  $form_state['no buttons'] = TRUE;
}

/**
 * Validate Callback for ml_image_source_selection_form()
 * This only validates if a source was properly selected.
 * Cleans the other options.
 * It then calls our validate function for the selected source module.
 */
function ml_image_source_selection_form_validate(&$form, &$form_state) {

  // Check op
  if ($form_state['clicked_button']['#value'] != t('Next')) {
    return;
  }
  $sources = ml_image_get_sources();

  // Find out our source
  $option = $form_state['values']['select'];

  //$form_state['values'] = $form_state['values'][$source];
  $form_state_new = $form_state;
  $form_state_new['values'] = $form_state['values'][$source];
  if (!$option) {
    form_set_error('', t('No source selected.'));
  }
  $validate_func = $sources[$option]['module'] . '_' . $option . '_source_form_validate';

  // Execute validation callbacks
  if (function_exists($validate_func)) {
    $validate_func($form, $form_state_new);
  }
  $form_state['source'] = $option;
}

/**
 * Submit Callback for ml_image_source_selection_form()
 */
function ml_image_source_selection_form_submit(&$form, &$form_state) {

  // Check op
  if ($form_state['clicked_button']['#value'] != t('Next')) {
    return;
  }
  $sources = ml_image_get_sources();
  $source = $form_state['source'];
  $form_state_new = $form_state;
  $form_state_new['values'] = $form_state['values'][$source];
  $submit_func = $sources[$source]['module'] . '_' . $source . '_source_form_submit';
  if (function_exists($submit_func)) {
    $submit_func($form, $form_state_new);
  }
  $form_state['media_obj']->source = $source;
}

/**
 * Form for selection source options
 */
function ml_image_source_options_form(&$form, &$form_state) {
  if ($form_state['action'] == 'update') {
    media_library_modal_skip_step($form_state);
  }
  $sources = ml_image_get_sources();

  // Source options
  $source = $form_state['media_obj']->source;
  $func_name = $sources[$source]['module'] . '_' . $source . '_options_form';
  if (function_exists($func_name)) {
    $form[$source] = $func_name($form_state);
  }
  else {

    // Skip the step
    media_library_modal_skip_step($form_state);
  }
  $form[$source]['#tree'] = TRUE;
}

/**
 * Validate callback for ml_image_source_options_form()
 */
function ml_image_source_options_form_validate(&$form, &$form_state) {

  // Check op
  if ($form_state['clicked_button']['#value'] != t('Next')) {
    return;
  }
  $sources = ml_image_get_sources();

  // Find out our source
  $option = $form_state['media_obj']->source;
  $form_state_new = $form_state;
  $form_state_new['values'] = $form_state['values'][$option];
  $validate_func = $sources[$option]['module'] . '_' . $option . '_options_form_validate';

  // Execute validation callbacks
  if (function_exists($validate_func)) {
    $validate_func($form, $form_state_new);
  }
}

/**
 * Submit callback for ml_image_source_options_form()
 */
function ml_image_source_options_form_submit(&$form, &$form_state) {

  // Check op
  if ($form_state['clicked_button']['#value'] != t('Next')) {
    return;
  }
  $sources = ml_image_get_sources();
  $source = $form_state['media_obj']->source;
  $form_state_new = $form_state;
  $form_state_new['values'] = $form_state['values'][$source];
  $submit_func = $sources[$source]['module'] . '_' . $source . '_options_form_submit';
  if (function_exists($submit_func)) {
    $submit_func($form, $form_state_new);
  }
}

/**
 * Form for modal dialog
 * For updating media, use the contents of $form_state['update']
 * You can form_alter this safely to add any attributes you like
 * They will show in the tag and will be available at the theme
 * function automatically
 */
function ml_image_attributes_form(&$form, &$form_state) {

  // Check for default value from content being updated
  if (isset($form_state['update'])) {
    $image = $form_state['update'];
  }
  else {
    $image = '';
  }

  // Put elements inside attributes array so we can separate
  // from form API elements such as form_id
  $form['attributes'] = array(
    '#tree' => TRUE,
  );
  $options = array_filter(variable_get('ml_image_imagecache_presets', array()));
  $options['none'] = t('Original Size');

  // Generates preview for image
  $option = isset($image['preset']) ? $image['preset'] : key($options);
  if ($option == 'none') {
    $imgtag = theme('image', $form_state['media_obj']->filepath);
  }
  else {
    $imgtag = theme('imagecache', $option, $form_state['media_obj']->filepath);
  }
  $form['preview'] = array(
    '#value' => '<div class="ml-image-edit preview">' . $imgtag . '</div>',
    '#weight' => -1,
  );

  // Size. Imagecache presets enabled
  $form['attributes']['preset'] = array(
    '#type' => 'select',
    '#title' => t('Image size'),
    '#description' => t('Predefined image size choice'),
    '#options' => $options,
    '#default_value' => isset($image['preset']) ? array(
      $image['preset'],
    ) : array(),
  );

  // Image alignment. This can be easily overriden by theme
  $form['attributes']['align'] = array(
    '#type' => 'select',
    '#title' => t('Image Alignment'),
    '#options' => array(
      'center' => t('Center'),
      'left' => t('Left'),
      'right' => t('Right'),
    ),
    '#default_value' => isset($image['align']) ? $image['align'] : 'center',
  );

  // Title of the block, primarily, but can be used for img title or alt
  $form['attributes']['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => isset($image['title']) ? $image['title'] : '',
  );

  // Legend. Small text to appear inside block
  $form['attributes']['label'] = array(
    '#type' => 'textfield',
    '#title' => t('Label'),
    '#default_value' => isset($image['label']) ? $image['label'] : '',
  );
  $form_state['no buttons'] = TRUE;
}

/**
 * Validate Callback for ml_image_attributes_form()
 */
function ml_image_attributes_form_validate(&$form, &$form_state) {
}

/**
 * Submit Callback for ml_image_attributes_form()
 */
function ml_image_attributes_form_submit(&$form, &$form_state) {
  foreach ($form_state['values']['attributes'] as $attr => $value) {
    $form_state['media_obj']->attributes[$attr] = $value;
  }
}

/**
 * Settings form for Media Library settings page
 */
function ml_image_settings($form_state) {
  $form = array();

  // General settings
  $form['main'] = array(
    '#type' => 'fieldset',
    '#title' => t('General Settings'),
    '#collapsible' => TRUE,
  );

  // Get imagecache data
  $presets = imagecache_presets();
  $options = array();
  foreach ($presets as $preset) {
    $options[$preset['presetname']] = $preset['presetname'];
  }
  $options['none'] = t('Original Size');
  $form['main']['ml_image_imagecache_presets'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Imagecache presets'),
    '#description' => t('Select wich imagecache presets should be available for the user to insert an image'),
    '#options' => $options,
    '#default_value' => variable_get('ml_image_imagecache_presets', array(
      0,
    )),
    '#required' => TRUE,
  );

  // Term relationship
  $vocabularies = taxonomy_get_vocabularies();
  $options = array();
  foreach ($vocabularies as $vid => $data) {
    $options[$vid] = $data->name;
  }
  array_unshift($options, t('<< None >>'));
  $form['main']['ml_image_vocabulary'] = array(
    '#type' => 'select',
    '#title' => t('Vocabulary for term relation'),
    '#description' => t('Use this to be able to select tags to images'),
    '#options' => $options,
    '#default_value' => array(
      variable_get('ml_image_vocabulary', 0),
    ),
  );

  // Sources settings
  $sources = ml_image_get_sources();
  foreach ($sources as $source => $info) {
    if (isset($info['settings'])) {
      $form_func = $info['settings'];
      if (function_exists($form_func)) {
        $form['sources'][$type] = $form_func($form_state);
        $form['sources'][$type] += array(
          '#type' => 'fieldset',
          '#title' => 'Source: ' . $info['label'],
          '#collapsible' => TRUE,
        );
      }
    }
  }
  return system_settings_form($form);
}

/**
 * Helpers and callbacks
 */

/**
 * Loads all sources implemented on other modules
 */
function ml_image_get_sources() {
  static $sources;
  if (!is_array($sources)) {
    $sources = module_invoke_all('ml_image_source');
  }
  return $sources;
}

/**
 * Get all metadata items to relate do an image.
 * TODO: add flexibility to metadata items to allow modules to add their own
 */
function ml_image_get_metadata() {
  $metadata = array(
    'title' => array(
      'type' => 'textfield',
      'label' => t('Title'),
      'description' => t('Descriptive short title'),
    ),
    'copyright' => array(
      'type' => 'textfield',
      'label' => t('Copyright'),
      'description' => t('Copyright information.'),
    ),
    'author' => array(
      'type' => 'textfield',
      'label' => t('Photographer'),
      'description' => t('Picture author name.'),
    ),
  );
  if ($vid = variable_get('ml_image_vocabulary', 0)) {
    $vocab = taxonomy_vocabulary_load($vid);
    $metadata['tags'] = array(
      'type' => 'term',
      'label' => $vocab->name,
      'description' => $vocab->description,
      'vocabulary' => $vocab,
    );
  }
  return $metadata;
}

/**
 * Saves metatags according to fields returned from ml_image_get_metadata()
 * @param
 *  metatags - object with properties. Must have 'fid' property
 */
function ml_image_save_metatags($metatags) {
  $metadata = ml_image_get_metadata();
  foreach ($metadata as $field => $data) {
    switch ($data['type']) {
      case 'textfield':
      case 'textarea':
        break;
      case 'term':
        if (isset($metatags->{$field})) {
          foreach (array_keys($metatags->{$field}) as $tid) {

            // Save each term
            $relation = new stdClass();
            $relation->tid = $tid;
            $relation->fid = $metatags->fid;
            drupal_write_record('ml_image_term', $relation);
          }
          unset($metatags->{$field});
        }
    }
  }

  // TODO: implement hook here
  // Saves all remaining fields (base)
  drupal_write_record('ml_image_metadata', $metatags);
}

/**
 * Load existing images according to filters
 * @param
 *  terms - array of tids for filtering
 *  source - image source for filtering
 * @return
 *  array of images
 */
function ml_image_get_images($terms = array(), $source = 0, $limit = 10, $page = 0) {

  // Base query
  $query = '
    SELECT
      metadata.*,
      files.filepath,
      files.uid,
      files.timestamp,
      GROUP_CONCAT(tags.tid) as tags
    FROM {ml_image_metadata} metadata
    INNER JOIN {files} files ON metadata.fid = files.fid
    LEFT JOIN {ml_image_term} tags ON metadata.fid = tags.fid';
  $count_query = '
    SELECT
      COUNT(metadata.fid)
    FROM {ml_image_metadata} metadata
    INNER JOIN {files} files ON metadata.fid = files.fid';

  // Terms filtering
  if (!empty($terms)) {
    $term_filter = '';
    foreach ($terms as $tid) {
      $term_filter .= ' INNER JOIN ml_image_term tags_filter' . $tid . ' ON metadata.fid = tags_filter' . $tid . '.fid AND tags_filter' . $tid . '.tid = ' . $tid;
    }
    $query .= $term_filter;
    $count_query .= $term_filter;
  }

  // Source filtering
  if ($source) {
    $query .= ' WHERE source = "%s" ';
    $count_query .= ' WHERE source = "%s" ';
  }

  // Group multiple terms
  $query .= ' GROUP BY metadata.fid';

  // Prepare range

  //$from = $page * $limit;

  //$result = db_query_range($query, $from, $limit);
  $result = pager_query($query, $limit, 0, $count_query, $source);
  $images = array();
  while ($item = db_fetch_object($result)) {
    $item->tags = explode(',', $item->tags);
    $images[] = $item;
  }
  return $images;
}

/**
 * Load single image object by fid
 */
function ml_image_get_image($fid) {
  static $images;
  if (!is_array($images)) {
    $images = array();
  }
  if (!isset($images[$fid])) {

    // Base query
    $query = '
      SELECT
        metadata.*,
        files.filepath,
        files.uid,
        files.timestamp,
        GROUP_CONCAT(tags.tid) as tags
      FROM {ml_image_metadata} metadata
      INNER JOIN {files} files ON metadata.fid = files.fid
      LEFT JOIN {ml_image_term} tags ON metadata.fid = tags.fid
      WHERE metadata.fid = %d
      GROUP BY metadata.fid';
    $image = db_fetch_object(db_query($query, $fid));
    if ($image) {
      $images[$fid] = $image;
    }
  }
  return $images[$fid];
}

/**
 * Theme functions
 */

/**
 * Renders the image tag
 * @param
 *  image - The image array with its properties
 *  preview - wether this is being generated on wysiwyg preview.
 */
function theme_ml_image($image, $preview = FALSE) {
  $classes = 'ml-image';
  $output = '';

  // Preview class
  if ($preview) {
    $classes .= ' ml-image-preview';

    // We may receive the object with array structures;
    if (is_array($image['attributes'])) {
      foreach ($image['attributes'] as $attr => $value) {
        $image[$attr] = $value;
      }
    }
    if (is_array($object->metatags)) {
      foreach ($object->metatags as $key => $value) {

        // To avoid namespace conflitcts, we prefix metatag names with 'meta-'
        $tag['meta-' . $key] = $value;
      }
    }
  }
  if (!empty($image)) {

    // As an example, we use title for everything, but you could use metadata
    // like copyright and author info
    $alt = $title = $image['title'];
    $filepath = $image['filepath'];

    // Render the img tag
    if ($image['preset'] == 'none') {
      $imgtag = theme('image', $filepath, $alt, $title);
    }
    else {
      $imgtag = theme('imagecache', $image['preset'], $filepath, $alt, $title);
    }

    // Alignment
    if (isset($image['align'])) {
      $classes .= ' align-' . $image['align'];
    }
    if (!empty($image['title']) || !empty($image['label'])) {

      // Render a box
      $classes .= ' box';
      $label = !empty($image['label']) ? '<div class="label">' . $image['label'] . '</div>' : '';
      $content = '<h2>' . $image['title'] . '</h2>' . $imgtag . $label;
    }
    else {
      $content = $imgtag;
    }
  }
  $output = "<div class=\"{$classes}\">{$content}</div>";
  return $output;
}

Functions

Namesort descending Description
ml_image_attributes_form Form for modal dialog For updating media, use the contents of $form_state['update'] You can form_alter this safely to add any attributes you like They will show in the tag and will be available at the theme function automatically
ml_image_attributes_form_submit Submit Callback for ml_image_attributes_form()
ml_image_attributes_form_validate Validate Callback for ml_image_attributes_form()
ml_image_filter_media Implementation of hook_filter_media()
ml_image_filter_media_alter Implementation of hook_filter_media_alter() Just for fun. We add a dummy attribute.
ml_image_flush_caches Implementation of hook_flush_caches()
ml_image_get_image Load single image object by fid
ml_image_get_images Load existing images according to filters
ml_image_get_metadata Get all metadata items to relate do an image. TODO: add flexibility to metadata items to allow modules to add their own
ml_image_get_sources Loads all sources implemented on other modules
ml_image_init Implementation of hook_init()
ml_image_media_forms Implementation of hook_media_forms()
ml_image_media_tag Implementation of hook_media_tag()
ml_image_media_types Implementation of hook_media_types()
ml_image_menu Implementation of hook_menu()
ml_image_nodeapi Implementation of hook_nodeapi()
ml_image_save_metatags Saves metatags according to fields returned from ml_image_get_metadata()
ml_image_settings Settings form for Media Library settings page
ml_image_source_options_form Form for selection source options
ml_image_source_options_form_submit Submit callback for ml_image_source_options_form()
ml_image_source_options_form_validate Validate callback for ml_image_source_options_form()
ml_image_source_selection_form Form for our image source selection. Handles both steps of source selection process.
ml_image_source_selection_form_submit Submit Callback for ml_image_source_selection_form()
ml_image_source_selection_form_validate Validate Callback for ml_image_source_selection_form() This only validates if a source was properly selected. Cleans the other options. It then calls our validate function for the selected source module.
ml_image_theme Implementation of hook_theme()
theme_ml_image Renders the image tag