<?php
// $Id: inline.module,v 1.19.2.7 2007/04/18 11:22:27 sun Exp $


/**
 * Implementation of hook_menu().
 */
function inline_menu($may_cache) {
  $items = array();
  
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/inline',
      'title' => t('Inline'),
      'description' => t('Manage automatic and manual inclusion of attachments in the content of your posts.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array('inline_settings'),
      'access' => user_access('administer inline settings'),
    );
  }
  return $items;
}

function inline_perm() {
  return array('administer inline settings');
}

function inline_help($section = 'admin/help#inline') {
  $output = '';
  switch ($section) {
    case 'admin/help#inline':
      return t('<p>Sometimes a user may want to add an image or a file inside the body of a node. This can be done with special tags that are replaced by links to the corresponding uploaded file. If the file is an image, it will be displayed inline, otherwise a link to the file will be inserted. To enable this feature and learn the proper syntax, visit the <a href="!filters">filters configuration screen</a>.</p>', array('!filters' => url('admin/filters')));
    
    case 'filter#short-tip':
      return t('You may add links to files uploaded with this node <a href="!explanation-url">using special tags</a>', array('!explanation-url' => url('filter/tips', NULL, 'image')));
    
    case 'filter#long-tip':
      return t('<p>You may link to files uploaded with the current node using special tags. The tags will be replaced by the corresponding files. Syntax: <code>[inline:file_id]</code>. Parameter: file_id represents the file uploaded with the node in which to link, assuming that the first uploaded file is labeled as 1 and so on.</p>
    <p>If the file is an image, it will be displayed inline, otherwise a link to the file will be inserted.</p> ');
  }
}

function inline_settings() {
  $form = array();
  
  // Check if Inline filter is enabled
  $inline_activated = false;
  foreach (filter_formats() as $format) {
    foreach (filter_list_format($format->format) as $filter) {
      if ($filter->module == 'inline') {
        $inline_activated = true;
        break 2;
      }
    }
  }
  if ($inline_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_link_img'] = array(
    '#type' => 'radios',
    '#title' => t('Link to images'),
    '#default_value' => variable_get('inline_link_img', 1),
    '#options' => array(
      '0' => t('Display image only'),
      '1' => t('Display image with a link to the image file')
    ),
  );
  
  $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' => url('admin/settings/imagecache'))) : 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 (module_exists('imagecache')) {
    $options     = array();
    $options[''] = 'No Imagecache processing';
    $presets     = _imagecache_get_presets();
    foreach ($presets as $id => $name) {
      $options[$name] = $name;
    }
    $form['inline']['upload']['image_scaling']['inline_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_teaser_preset', ''),
    );
    $form['inline']['upload']['image_scaling']['inline_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_full_preset', ''),
    );
  }
  else {
    $form['inline']['upload']['image_scaling']['inline_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_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);
}

function inline_form_alter($form_id, &$form) {
  if ($form_id == 'node_type_form') {
    $node_type = $form['orig_type']['#value'];
    $form['workflow']['upload_inline'] = array(
      '#type' => 'radios',
      '#title' => t('Display attachments inline automatically'),
      '#default_value' => variable_get('upload_inline_'. $node_type, 0),
      '#options' => array(t('Disabled'), t('Only in teaser'), t('Only in body'), t('In teaser and body')),
      '#description' => t('Whether or not uploaded images should be shown inline. Make sure you set the dimensions at !settings_url', array('!settings_url' => l(t('inline settings'), 'admin/settings/inline'))),
    );
  }
}

function inline_filter($op, $delta = 0, $format = -1, $text = '') {
  if ($op == 'list') {
    return array(0 => t('Inline file filter'));
  }
  
  switch ($op) {
    case 'description':
      return t('Substitutes [inline:xx] tags with the xxth file uploaded with the node.');
    
    case 'prepare':
      return $text;
    
    case 'process':
      return $text;
  }
}

function inline_filter_tips($delta, $format, $long = false) {
  if ($long) {
    return t('
      <a id="filter-inline" name="filter-inline"></a>
      <p>You may link to files uploaded with the current node using special tags. The tags will be replaced by the corresponding files. For example:

      Suppose you uploaded three files (in this order):
      <ul>
      <li>imag1.png (referred as file #1)
      <li>file1.pdf (referred as file #2)
      <li>imag2.png (referred as file #3)
      </ul>

      <pre>[inline:1=test]  or  [inline:imag1.png=test]</pre>
      will be replaced by <em><code>&lt;img src=imag1.png alt=test&gt;</code></em>

      <pre>[file:1=test]  or  [file:imag1.png=test]</pre>
      will be replaced by <em><code>&lt;a href=imag1.png&gt;test&lt;/a&gt;</code></em>

      <pre>[attachment:2=test]  or  [attachment:file1.pdf=test]</pre>
      will be replaced by <em><code>&lt;a href=file1.pdf.png&gt;test&lt;/a&gt;</code></em>');
  }
  else {
    return t('You may use <a href="!inline_help">[inline:xx] tags</a> to display uploaded files or images inline.', array("!inline_help" => url("filter/tips/$format", NULL, 'filter-inline')));
  }
}

function inline_nodeapi(&$node, $op, $arg) {
  if (!is_array($node->files)) {
    return;
  }
  switch ($op) {
    case 'alter':
    case 'print':
      // only nodes with our inline filter in the format may be altered
      foreach (filter_list_format($node->format) as $filter) {
        if ($filter->module == 'inline') {
          _inline_substitute_tags($node, 'teaser');
          _inline_substitute_tags($node, 'body');
          break;
        }
      }
      if (variable_get('upload_inline_'. $node->type, 0)) {
        $node = _inline_auto_add($node);
      }
      return;
    
    case 'prepare':
    case 'submit':
      _inline_replace_numbers($node, 'teaser');
      _inline_replace_numbers($node, 'body');
      return;
  }
}

function _inline_fileobj($node, $id) {
  if (is_numeric($id)) {
    $n = 1;
    foreach ($node->files as $file) {
      if ($n == $id) {
        return (object)$file;
      }
      ++$n;
    }
    return NULL;
  }
  else {
    foreach ($node->files as $file) {
      $file = (object)$file;
      if ($file->filename == $id) {
        return $file;
      }
    }
    return NULL;
  }
}

/**
 * Prepares the file object
 */
function inline_prepare_file_object($file) {
  $file = (object)$file;
  $tmp = file_directory_temp();
  if (strpos($file->filepath, $tmp) === 0) {
    $file->real_path = $file->filepath;
    $file->filepath  = $file->filename;
    $file->preview   = TRUE;
  }
  return $file;
}

function theme_inline_as_link($file) {
  // prepare link text with title or filename
  $linktext = ($file->title ? $file->title : $file->filename);
  
  return l($linktext, file_create_url($file->filepath), array('title' => t('Download: @name (@size)', array('@name' => $file->filename, '@size' => format_size($file->filesize)))));
}

function theme_inline_img($file, $field) {
  $title = (!empty($file->title) ? $file->title : $file->filename);
  $inline_preset = $field == 'teaser' ? 'inline_teaser_preset' : 'inline_full_preset';
  
  if (module_exists('imagecache') && variable_get($inline_preset, '') != '') {
    $image = theme('imagecache',
      variable_get($inline_preset, ''),
      $file->filepath,
      $title,
      $title,
      array('class' => 'inline')
    );
  }
  else {
    $image = theme('image',
      $file->filepath,
      $title,
      $title,
      array('class' => 'inline')
    );
  }
  
  if (variable_get('inline_link_img', '1')) {
    $attributes = array(
      'class' => 'inline-image-link',
      'title' => t("View") .': '. $title,
    );
    $html = l($image, $file->filepath, $attributes, NULL, NULL, FALSE, TRUE);
  }
  else {
    $html = $image;
  }
  
  return $html;
}

function theme_inline_add_to_teaser($node, $file, $field) {
  return theme('inline_img', $file, $field) . $node->teaser;
}

function theme_inline_add_to_body($node, $file, $field) {
  return theme('inline_img', $file, $field) . $node->body;
}

function _inline_auto_add($node) {
  //0 Disabled
  //1 Only in teaser
  //2 Only in body
  //3 In teaser and body
  switch (variable_get('upload_inline_'. $node->type, 0)) {
    case 1:
      foreach ($node->files as $fid => $file) {
        $file = inline_prepare_file_object($file);
        if (_inline_decide_img_tag($file)) {
          $node->files[$fid]->inline = TRUE;
          $node->teaser = theme('inline_add_to_teaser', $node, $file, 'teaser');
        }
        else {
          $node->files[$fid]->inline = FALSE;
        }
      }
      break;
    
    case 2:
      foreach ($node->files as $fid => $file) {
        $file = inline_prepare_file_object($file);
        if (_inline_decide_img_tag($file)) {
          $node->files[$fid]->inline = TRUE;
          $node->body = theme('inline_add_to_body', $node, $file, 'body');
        }
        else {
          $node->files[$fid]->inline = FALSE;
        }
      }
      break;
    
    case 3:
      foreach ($node->files as $fid => $file) {
        $file = inline_prepare_file_object($file);
        if (_inline_decide_img_tag($file)) {
          $node->files[$fid]->inline = TRUE;
          $node->teaser = theme('inline_add_to_teaser', $node, $file, 'teaser');
          $node->body = theme('inline_add_to_body', $node, $file, 'body');
        }
        else {
          $node->files[$fid]->inline = FALSE;
        }
      }
      break;
  }
  return $node;
}

function _inline_substitute_tags(&$node, $field) {
  if (preg_match_all("/\[(inline|file|attachment):([^=\\]]+)=?([^\\]]*)?\]/i", $node->$field, $match)) {
    foreach ($match[2] as $key => $value) {
      // fetch file object
      $file = inline_prepare_file_object(_inline_fileobj($node, $value));
      // deal file title
      $title = $match[3][$key];
      if (!empty($title)) {
        $file->title = $title;
      }
      $replace = "";
      if ($file->fid != NULL) {
        //decide if we should show a link or an img tag
        if (_inline_decide_img_tag($file)) {
          $replace = theme('inline_img', $file, $field);
        }
        else {
          $replace = theme('inline_as_link', $file);
        }
      }
      else {
        $replace = "<span style=\"color:red; font-weight:bold\">NOT FOUND: $value</span>";
      }
      $mtch[] = $match[0][$key];
      $repl[] = $replace;
    }
    $node->$field = str_replace($s, $r, $node->$field);
  }
  
}

/**
 * Replaces numeric file references with their respective file names.
 *
 * @param &$node The node object to process.
 * @param $field Field of node to process.
 *
 * @return Processed $field of $node.
 */
function _inline_replace_numbers(&$node, $field) {
  $tag = '/\[(inline|file|attachment):(\d+?)(=.+?)?\]/i';
  // Look if there are any numeric inline tags.
  preg_match_all($tag, $node->$field, $matches, PREG_SET_ORDER);
  if (!empty($matches)) {
    foreach ($matches as $match) {
      // Attachment array key is the file ID.
      $filekeys = array_keys($node->files);
      $key = $filekeys[$match[2] - 1];
      // If a corresponding file does exist, perform the replacement.
      // Because a user starts counting files from 1, we substract 1 here.
      if (!isset($node->files[$key])) {
        // If user entered a non-existent number, continue with next tag.
        continue;
      }
      if (is_array($node->files[$key])) {
        // Node form submit
        $filename = $node->files[$key]['filename'];
      }
      else {
        // Node form prepare
        $filename = $node->files[$key]->filename;
      }
      $node->$field = str_replace($match[0], '['. $match[1] .':'. $filename . $match[3] .']', $node->$field);
    }
  }

}

/**
 * Decides if a tag (&lt;img&gt;) or a link to a file should be rendered
 *
 * @param $file a file object
 *
 * @return TRUE in case an img tag should be generated
 */
function _inline_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 settings
      list($maxwidth, $maxheight) = explode(',', variable_get('inline_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;
}