<?php
// $Id: inline_upload.module,v 1.4 2009/05/15 21:41:40 sun Exp $

/**
 * @file
 * Inline API implementation to render uploaded attachments inline.
 */

/**
 * Implementation of hook_inline().
 *
 * @param string $op
 *   The current operation performed.
 * @param array $params
 *   An array of user supplied values.
 */
function inline_upload_inline($op, $params = array()) {
  switch ($op) {
    case 'args':
      // Return an array of available/required tag arguments.
      $args = array(
        'nid' => array(
          '#title' => t('Node id'),
          '#description' => t('A node id containing a file to render.'),
          '#type' => 'int',
          '#default_value' => 0,
        ),
        'file' => array(
          '#title' => t('File'),
          '#description' => t('An id or name of a file to render.'),
          '#required' => TRUE,
        ),
        'title' => array(
          '#title' => t('Title'),
          '#description' => t('A optional title to use for the link text.'),
          '#type' => 'string',
        ),
      );
      return $args;

    case 'validate':
      // Custom validation of user supplied values.
      return TRUE;

    case 'prepare':
      // Prepare user supplied values for rendering.
      if ($params['nid'] == 0) {
        // Insert some tricky code to insert current node id here. 02/02/2008 sun
        if (arg(0) == 'node' && is_numeric(arg(1))) {
          $params['nid'] = (int)arg(1);
        }
      }
      // Load a node object if valid nid is given.
      if ($params['nid'] > 0) {
        $node = node_load($params['nid']);
        if (node_access('view', $node)) {
          $params['#node'] = $node;
        }
      }
      // Add new uploaded files to $node->files in node preview.
      // @see upload_js(), upload.module
      // @todo Fetch cached form state.
      if (!empty($_SESSION['file_previews'])) {
        foreach ($_SESSION['file_previews'] as $file_preview) {
          $params['#node']->files[count($params['#node']->files) + 1] = drupal_clone($file_preview);
        }
      }
      // Convert a numeric file reference to a named one.
      if (is_int($params['file']) && isset($params['#node'])) {
        if (isset($params['#node']->files[$params['file']])) {
          $params['file'] = $params['#node']->files[$params['file']]->filename;
        }
      }
      return $params;

    case 'render':
      // Return a rendered representation to replace a tag.
      if (!isset($params['#node'])) {
        return;
      }
      // Find the referenced file.
      $file = '';
      foreach ($params['#node']->files as $node_file) {
        if ($node_file->filename == $params['file']) {
          $file = $node_file;
          break;
        }
      }
      // Alter the file object on node previews.
      inline_upload_prepare_file_object($file);

      if ($file) {
        if (!empty($params['title'])) {
          $file->title = $params['title'];
        }
        // Decide whether to show a link or an image tag.
        if (_inline_upload_decide_img_tag($file)) {
          // @todo Provide different rendering options for images, i.e.
          //   imagecache presets. Seems like we can't support different presets
          //   for teaser and page view anymore. :-(
          $output = theme('inline_upload_img', $file, $field);
        }
        else {
          $output = theme('inline_upload_as_link', $file);
        }
      }
      else {
        $output = '<span style="color: red; font-weight: bold;">NOT FOUND: '. $params['file'] .'</span>';
      }
      return $output;
  }
}

/**
 * Implementation of hook_theme().
 */
function inline_upload_theme() {
  return array(
    'inline_upload_as_link' => array(
      'arguments' => array('file' => NULL),
    ),
    'inline_upload_img' => array(
      'arguments' => array('file' => NULL, 'field' => NULL),
    ),
    'inline_upload_prepend_to_field' => array(
      'arguments' => array('node' => NULL, 'file' => NULL, 'field' => NULL),
    ),
  );
}

/**
 * Implementation of hook_menu().
 */
function inline_upload_menu() {
  $items['admin/settings/inline/inline_upload'] = array(
    'title' => 'Upload',
    'description' => 'Manage automatic and manual inclusion of attachments in the content of your posts.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('inline_upload_settings'),
    'access arguments' => array('administer inline settings'),
  );
  return $items;
}

/**
 * Inline settings form builder function.
 */
function inline_upload_settings() {
  $form = array();

  // Check if Inline filter is enabled
  $inline_upload_activated = FALSE;
  foreach (filter_formats() as $format) {
    foreach (filter_list_format($format->format) as $filter) {
      if ($filter->module == 'inline') {
        $inline_upload_activated = TRUE;
        break 2;
      }
    }
  }
  if ($inline_upload_activated == FALSE) {
    drupal_set_message(t('Inline filter is not yet enabled for at least one <a href="!formats">input format</a>.', array('!formats' => url('admin/settings/filters'))), 'error');
  }

  $form['inline']['upload']['image_link'] = array(
    '#type' => 'fieldset',
    '#title' => t('Image output'),
    '#collapsible' => TRUE,
    '#description' => t('<strong>Note:</strong> Images are only processed if a tag is referencing them. However, there is a auto-inline feature to inline all uploaded images automatically. Auto-inline can be enabled for certain <a href="!content-types">content types</a>.', array('!content-types' => url('admin/content/types'))),
  );
  $form['inline']['upload']['image_link']['inline_upload_link_img'] = array(
    '#type' => 'radios',
    '#title' => t('Link to images'),
    '#default_value' => variable_get('inline_upload_link_img', 1),
    '#options' => array(
      '0' => t('Display image only'),
      '1' => t('Display image with a link to the image file')
    ),
  );

  $imagecache_path = '';
  $presets = array();
  if (module_exists('imagecache')) {
    // ImageCache v2 API.
    if (function_exists('imagecache_presets')) {
      $imagecache_path = url('admin/build/imagecache');
      $rules = imagecache_presets();
      foreach ($rules as $preset_id => $preset_info) {
        $presets[$preset_id] = $preset_info['presetname'];
      }
    }
    // ImageCache v1 API (deprecated).
    else {
      $imagecache_path = url('admin/settings/imagecache');
      $presets = _imagecache_get_presets();
    }
  }
  $form['inline']['upload']['image_scaling'] = array(
    '#type' => 'fieldset',
    '#title' => t('Image dimensions and scaling'),
    '#collapsible' => TRUE,
    '#description' => (module_exists('imagecache') ? t('Select the <a href="!presets">Imagecache presets</a> to use for inlined images.', array('!presets' => $imagecache_path)) : t('<strong>Note:</strong> If <a href="!imagecache">Imagecache</a> module is installed, Inline provides support for image scaling.', array('!imagecache' => url('http://drupal.org/project/imagecache')))),
  );

  // If Imagecache module exists and is enabled, we assume that we want to use
  // the improved image handling instead of our own.
  if ($presets) {
    $options     = array();
    $options[''] = 'No Imagecache processing';
    foreach ($presets as $id => $name) {
      $options[$name] = $name;
    }
    $form['inline']['upload']['image_scaling']['inline_upload_teaser_preset'] = array(
      '#title' => t('Teaser preset'),
      '#description' => t('Select the Imagecache preset to use for inlined images in teaser view.'),
      '#type' => 'select',
      '#options' => $options,
      '#default_value' => variable_get('inline_upload_teaser_preset', ''),
    );
    $form['inline']['upload']['image_scaling']['inline_upload_full_preset'] = array(
      '#title' => t('Full preset'),
      '#description' => t('Select the Imagecache preset to use for inlined images in full view.'),
      '#type' => 'select',
      '#options' => $options,
      '#default_value' => variable_get('inline_upload_full_preset', ''),
    );
  }
  else {
    $form['inline']['upload']['image_scaling']['inline_upload_img_dim'] = array(
      '#type' => 'textfield',
      '#title' => t('Maximum width and height for inline images (format: XXX,YYY)'),
      '#size' => 10,
      '#maxlength' => 10,
      '#required' => TRUE,
      '#default_value' => variable_get('inline_upload_img_dim', '150,150'),
      '#description' => t('This setting limits the dimensions of displayed images in pixels. They will not be resized. Images exceeding these dimensions are automatically not displayed.', array('!content-types' => url('admin/content/types'))),
    );
  }

  return system_settings_form($form);
}

/**
 * Change file path of new files for previews.
 * 
 * New files are stored in a temporary upload directory until the content
 * is saved. We alter the file object accordingly, so such files may be
 * displayed if the temporary directory is inside the root directory of
 * this Drupal site (publicly accessible).
 */
function inline_upload_prepare_file_object($file) {
  $file = (object)$file;
  // @todo Remove.
  if (!is_numeric($file->fid)) {
    $file->preview   = TRUE;
    $file->real_path = $file->filepath;
    $file->filepath  = $file->_filename;
  }
  return $file;
}

/**
 * Decide if an image tag (&lt;IMG&gt;) or a link to a file should be rendered.
 *
 * @param $file
 *   A file object.
 *
 * @return
 *   TRUE in case an image tag should be generated.
 */
function _inline_upload_decide_img_tag($file) {
  $inlined = array('jpg', 'jpeg', 'pjpeg', 'gif', 'png');
  $mime = array_pop(explode('/', $file->filemime));
  if (in_array($mime, $inlined)) {
    if (module_exists('imagecache')) {
      return TRUE;
    }
    else {
      // Read maximum dimension settings.
      list($maxwidth, $maxheight) = explode(',', variable_get('inline_upload_img_dim', '150,150'));
      
      if ($file->preview) {
        list($width, $height) = getimagesize($file->real_path);
      }
      else {
        list($width, $height) = getimagesize($file->filepath);
      }
      
      if (($width && $height) && ($width <= $maxwidth && $height <= $maxheight)) {
        return TRUE;
      }
    }
  }
  return FALSE;
}

/**
 * Return HTML for a link to a file.
 */
function theme_inline_upload_as_link($file) {
  // Prepare link text with title or filename.
  $linktext = (!empty($file->title) ? $file->title : $file->filename);
  
  return l($linktext, file_create_url($file->filepath), array('attributes' => array('title' => t('Download: @name (@size)', array('@name' => $file->filename, '@size' => format_size($file->filesize))))));
}

/**
 * Return HTML for an image.
 */
function theme_inline_upload_img($file, $field) {
  // Prepare link text with inline title, file description or filename.
  $title = (!empty($file->title) ? $file->title : (!empty($file->description) ? $file->description : $file->filename));
  $inline_upload_preset = ($field == 'teaser' ? 'inline_upload_teaser_preset' : 'inline_upload_full_preset');
  
  if (module_exists('imagecache') && variable_get($inline_upload_preset, '') != '') {
    $output = theme('imagecache',
      variable_get($inline_upload_preset, ''),
      $file->filepath,
      $title,
      $title,
      array('class' => 'inline')
    );
  }
  else {
    $output = theme('image',
      $file->filepath,
      $title,
      $title,
      array('class' => 'inline'),
      !isset($file->preview)
    );
  }
  
  if (variable_get('inline_upload_link_img', '1')) {
    $attributes = array(
      'class' => 'inline-image-link',
      'title' => t('View: @file', array('@file' => $title)),
    );
    $output = l($output, $file->filepath, array('attributes' => $attributes, 'html' => TRUE));
  }
  
  return $output;
}

/**
 * @defgroup inline_upload_auto Auto inline support
 * @{
 */

/**
 * Implementation of hook_form_alter().
 *
 * Allows to enable/disable auto-inline support for each content type.
 */
function inline_upload_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
    $form['workflow']['upload_inline_upload'] = array(
      '#type' => 'radios',
      '#title' => t('Display attachments inline automatically'),
      '#default_value' => variable_get('upload_inline_upload_'. $form['#node_type']->type, 0),
      '#options' => array(
        0 => t('Disabled'),
        1 => t('Only in teaser view'),
        2 => t('Only in body view'),
        3 => t('In teaser and body view')),
      '#description' => t('Choose whether uploaded images should be shown inline automatically. Make sure you set the dimensions at !settings_url', array('!settings_url' => l(t('Inline Upload settings'), 'admin/settings/inline/inline_upload'))),
    );
  }
}

/**
 * Implementation of hook_nodeapi().
 *
 * @todo Break processing at all if Inline filter is not enabled.
 */
function inline_upload_nodeapi(&$node, $op, $arg) {
  if (!(isset($node->files) && is_array($node->files))) {
    return;
  }
  switch ($op) {
    case 'alter':
    case 'print':
    case 'rss item':
      if (variable_get('upload_inline_upload_'. $node->type, 0)) {
        $node = _inline_upload_auto_add($node);
      }
      return;
  }
}

/**
 * Automatically add all images to configured node views.
 *
 * This feature can be configured per content-type.
 */
function _inline_upload_auto_add($node) {
  switch (variable_get('upload_inline_upload_'. $node->type, 0)) {
    case 1:
      // Display only in teaser.
      foreach ($node->files as $fid => $file) {
        $file = inline_upload_prepare_file_object($file);
        if (_inline_upload_decide_img_tag($file)) {
          $node->files[$fid]->inline = TRUE;
          $node->teaser = theme('inline_upload_prepend_to_field', $node, $file, 'teaser');
        }
        else {
          $node->files[$fid]->inline = FALSE;
        }
      }
      break;

    case 2:
      // Display only in body.
      foreach ($node->files as $fid => $file) {
        $file = inline_upload_prepare_file_object($file);
        if (_inline_upload_decide_img_tag($file)) {
          $node->files[$fid]->inline = TRUE;
          $node->body = theme('inline_upload_prepend_to_field', $node, $file, 'body');
        }
        else {
          $node->files[$fid]->inline = FALSE;
        }
      }
      break;

    case 3:
      // Display in teaser and body.
      foreach ($node->files as $fid => $file) {
        $file = inline_upload_prepare_file_object($file);
        if (_inline_upload_decide_img_tag($file)) {
          $node->files[$fid]->inline = TRUE;
          $node->teaser = theme('inline_upload_prepend_to_field', $node, $file, 'teaser');
          $node->body = theme('inline_upload_prepend_to_field', $node, $file, 'body');
        }
        else {
          $node->files[$fid]->inline = FALSE;
        }
      }
      break;
  }
  return $node;
}

/**
 * Insert an image in front of node field.
 *
 * @param object $node
 *   The node object to process.
 * @param object $file
 *   A file object of an image to insert.
 * @param string $field
 *   The field name to prepend with the image.
 */
function theme_inline_upload_prepend_to_field($node, $file, $field) {
  return theme('inline_upload_img', $file, $field) . $node->$field;
}

/**
 * @} End of "defgroup inline_upload_auto".
 */

