*** exif.module	2007-05-29 00:21:06.000000000 +1000
--- exif.module.new	2007-09-27 20:00:30.000000000 +1000
***************
*** 1,5 ****
--- 1,6 ----
  <?php
  // $Id: exif.module,v 1.4.2.2 2007/05/28 14:21:06 davidlesieur Exp $
+ // vim:set ft=php:
  
  include_once drupal_get_path('module', 'exif') .'/pel/PelJpeg.php';
      
***************
*** 22,27 ****
--- 23,96 ----
    return $items;
  }
  
+ function _exif_node_file($node) {
+   // Don't bother if no locations in array
+   if (!array_key_exists('location', $node)) return FALSE;
+ 
+   // Access the file's EXIF data. Simply break when no EXIF data is found.
+   $file = file_create_path($node->images['_original']);
+   if (!file_exists($file)) {
+     watchdog('exif', t('Image %file not found.', array('%file' => $file)), WATCHDOG_WARNING);
+   }
+   if (!is_file($file)) return FALSE;
+   if (exif_imagetype($file) != IMAGETYPE_JPEG) return FALSE;
+ 
+   return $file;
+ }
+ 
+ function _exif_DMS2D($entry) {
+   $value = $entry->getValue();
+   $degrees = $value[0][0] / $value[0][1];
+   $minutes = $value[1][0] / $value[1][1];
+   $seconds = $value[2][0] / $value[2][1];
+ 
+   return $degrees + $minutes/60 + $seconds/3600;
+ }
+ 
+ function _exif_fill_node_fields(&$node) {
+ /*
+ ** As we only care about location right now, don't bother if
+ ** that module is installed or the location seems filled.
+ */
+ 
+   if (!module_exists('location')) return FALSE;
+   if ($node->location['lat'] != 0) return FALSE;
+ 
+   $file = _exif_node_file($node);
+   if (!$file) return;
+ 
+   $jpeg = new PelJpeg($file);
+   $exif = $jpeg->getExif();
+   if (!$exif) return FALSE;
+   $tiff = $exif->getTiff();
+   if (!$tiff) return FALSE;
+   $ifd0 = $tiff->getIfd();
+   if (!$ifd0) return FALSE;
+   $gps = $ifd0->getSubIfd(PelIfd::GPS);
+   if (!$gps) return FALSE;
+ 
+   $entry = $gps->getEntry(PelTag::GPS_LATITUDE);
+   if (!$entry) return FALSE;
+   $GPS_LATITUDE = _exif_DMS2D($entry);
+   $entry = $gps->getEntry(PelTag::GPS_LATITUDE_REF);
+   if (!$entry) return FALSE;
+   if ($entry->getValue() == 'S') $GPS_LATITUDE = -$GPS_LATITUDE;
+ 
+   $entry = $gps->getEntry(PelTag::GPS_LONGITUDE);
+   if (!$entry) return FALSE;
+   $GPS_LONGITUDE = _exif_DMS2D($entry);
+   $entry = $gps->getEntry(PelTag::GPS_LONGITUDE_REF);
+   if (!$entry) return FALSE;
+   if ($entry->getValue() == 'W') $GPS_LONGITUDE = -$GPS_LONGITUDE;
+ 
+   $node->locations[0]['latitude'] = $GPS_LATITUDE;
+   $node->locations[0]['longitude'] = $GPS_LONGITUDE;
+   $node->locations['lat'] = $GPS_LATITUDE;
+   $node->locations['lon'] = $GPS_LONGITUDE;
+ 
+   return TRUE;
+ }
+ 
  /**
   * Implementation of hook_nodeapi().
   */
***************
*** 36,46 ****
    switch ($op) {
      case 'view':
        // Access the file's EXIF data. Simply break when no EXIF data is found.
!       $file = file_create_path($node->images['_original']);
!       if (!file_exists($file)) {
!         watchdog('exif', t('Image %file not found.', array('%file' => $file)), WATCHDOG_WARNING);
!       }
!       if (!is_file($file)) break;
        $jpeg = new PelJpeg($file);
        $exif = $jpeg->getExif();
        if (!$exif) break;
--- 105,113 ----
    switch ($op) {
      case 'view':
        // Access the file's EXIF data. Simply break when no EXIF data is found.
!       $file = _exif_node_file($node);
!       if (!$file) break;
! 
        $jpeg = new PelJpeg($file);
        $exif = $jpeg->getExif();
        if (!$exif) break;
***************
*** 57,63 ****
        }
  
        // Retrieve the desired tag values from the file
!       $tags = exif_get_enabled_tags();
        $rows = array();
        foreach ($tags as $tag) {
          if (isset($ifds[$tag->ifd])) {
--- 124,130 ----
        }
  
        // Retrieve the desired tag values from the file
!       $tags = _exif_get_enabled_tags();
        $rows = array();
        foreach ($tags as $tag) {
          if (isset($ifds[$tag->ifd])) {
***************
*** 88,101 ****
          );
        }
        break;
!   }
  }
  
  /**
   * Administration page callback.
   */
  function exif_admin_settings() {
!   $tags = exif_load_settings();
    return drupal_get_form('exif_admin_settings_form', $tags);
  }
  
--- 155,174 ----
          );
        }
        break;
! 
! /* For updates, extract any info from EXIF and save if none already given */
! 
!     case 'update':
!       _exif_fill_node_fields($node);
!       break;
!     }
  }
  
  /**
   * Administration page callback.
   */
  function exif_admin_settings() {
!   $tags = _exif_load_settings();
    return drupal_get_form('exif_admin_settings_form', $tags);
  }
  
***************
*** 126,131 ****
--- 199,251 ----
      $form['tags']["{$tag->ifd}_{$tag->tag}"]['#tree'] = TRUE;
      $form['tags']["{$tag->ifd}_{$tag->tag}"]['#weight'] = $tag->weight;
    }
+ 
+   $form['harvest'] = array(
+     '#type' => 'fieldset',
+     '#title' => t('Node Completion'),
+     '#collapsible' => TRUE,
+     '#collapsed' => TRUE,
+     '#description' => t('When creating image nodes, what fields can be automatically harvested from the EXIF tags if not entered by the user.'),
+   );
+ 
+   /* The idea is that more than just location can be used in the future */
+   $form['harvest']['location'] = array(
+     '#type' => 'checkbox', 
+     '#title' => t('Location'),
+     '#default_value' => variable_get('exif_harvest_location', 0), 
+     '#description' => sprintf(t('From GPS co-ordinates. Requires Location module (%s).'),
+                       module_exists('location') ? t('<span class="admin-enabled">enabled</span>')
+                                                 : t('<span class="admin-disabled">disabled/missing</span>')),
+     '#disabled' => !module_exists('location')
+   );
+ 
+ 
+   /* Figure out the scan status */
+   $scan_to = variable_get('exif_scan_to', 0);
+   $scan_last = variable_get('exif_scan_last', 0);
+ 
+   if ($scan_last == $scan_to) {
+     $scan_status = sprintf(t("Finished (scanned to %d)"), $scan_to);
+   } else if ($scan_last < 1 && $scan_to > 0) {
+     $scan_status = sprintf(t("Not started (%d of %d)"), $scan_last, $scan_to);
+   } else if ($scan_last < $scan_to) {
+     $scan_status = sprintf(t("In progress (%d of %d)"), $scan_last, $scan_to);
+   } else {
+     $scan_status = t('Not started');
+   }
+ 
+   $form['harvest']['rescan'] = array(
+     '#type' => 'markup',
+     '#value' => t('<p>The Exif module can re-scan images (done as part of cron processing) and perform node completion functions based on the above rules. This is useful when the module is first installed.</p>')
+   );
+ 
+   $form['harvest']['start'] = array(
+     '#type' => 'checkbox', 
+     '#title' => t('Start Scan'),
+     '#default_value' => FALSE, 
+     '#description' => t('Check this box and submit to start scan. Status is currently: ') . $scan_status
+   );
+ 
    $form['buttons']['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Save configuration')
***************
*** 142,152 ****
    $op = isset($_POST['op']) ? $_POST['op'] : '';
  
    if ($op == t('Reset to defaults')) {
!     exif_reset_settings();
      drupal_set_message(t('The configuration options have been reset to their default values.'));
    }
    elseif ($op == t('Save configuration')) {
!     exif_save_settings($values);
      drupal_set_message(t('The configuration options have been saved.'));
    }
  }
--- 262,272 ----
    $op = isset($_POST['op']) ? $_POST['op'] : '';
  
    if ($op == t('Reset to defaults')) {
!     _exif_reset_settings();
      drupal_set_message(t('The configuration options have been reset to their default values.'));
    }
    elseif ($op == t('Save configuration')) {
!     _exif_save_settings($values);
      drupal_set_message(t('The configuration options have been saved.'));
    }
  }
***************
*** 154,160 ****
  /**
   * Return an array containing only the tags that were enabled.
   */
! function exif_get_enabled_tags() {
    static $tags = array();
  
    if (!count($tags)) {
--- 274,280 ----
  /**
   * Return an array containing only the tags that were enabled.
   */
! function _exif_get_enabled_tags() {
    static $tags = array();
  
    if (!count($tags)) {
***************
*** 164,170 ****
      }
      if (!count($tags)) {
        // Table is empty, get some defaults
!       $tags = exif_get_default_settings();
        foreach ($tags as $key => $tag) {
          if (!$tag->status) {
            unset($tags[$key]);
--- 284,290 ----
      }
      if (!count($tags)) {
        // Table is empty, get some defaults
!       $tags = _exif_get_default_settings();
        foreach ($tags as $key => $tag) {
          if (!$tag->status) {
            unset($tags[$key]);
***************
*** 180,187 ****
  /**
   * Return an array with all the valid tags and their settings.
   */
! function exif_load_settings() {
!   $tags = exif_get_default_settings();
    
    $result = db_query('SELECT * FROM {exif}');
    while ($tag = db_fetch_object($result)) {
--- 300,307 ----
  /**
   * Return an array with all the valid tags and their settings.
   */
! function _exif_load_settings() {
!   $tags = _exif_get_default_settings();
    
    $result = db_query('SELECT * FROM {exif}');
    while ($tag = db_fetch_object($result)) {
***************
*** 192,201 ****
    return $tags;
  }
  
! function exif_save_settings($values) {
    db_lock_table('exif');
!   foreach ($values as $tag) {
      if (!is_array($tag) || !isset($tag['ifd'])) {
        continue; // Save only appropriate form values
      }
      db_query('DELETE FROM {exif} WHERE ifd = %d AND tag = %d', $tag['ifd'], $tag['tag']);
--- 312,330 ----
    return $tags;
  }
  
! function _exif_save_settings($values) {
    db_lock_table('exif');
!   foreach ($values as $name => $tag) {
      if (!is_array($tag) || !isset($tag['ifd'])) {
+       /* Handle 'standard' options */
+       if ($name == 'location') {
+         variable_set('exif_harvest_location', $tag);        
+       } else if ($name = 'start' && $tag) {
+         /* Reset vars to restart scan */
+         variable_set('exif_scan_last', 0);        
+         variable_set('exif_scan_from', 1);        
+         variable_set('exif_scan_to', 2000);        
+       }
        continue; // Save only appropriate form values
      }
      db_query('DELETE FROM {exif} WHERE ifd = %d AND tag = %d', $tag['ifd'], $tag['tag']);
***************
*** 204,218 ****
    db_unlock_tables();
  }
  
! function exif_reset_settings() {
    db_query('DELETE FROM {exif}');
  }
  
  /**
   * Return an array with all the valid tags, with some useful default settings.
   */
! function exif_get_default_settings() {
!   $tags = exif_get_valid_tags();
    $tags[PelIfd::EXIF .'_'. PelTag::DATE_TIME_ORIGINAL]->status = 1;
    $tags[PelIfd::EXIF .'_'. PelTag::DATE_TIME_ORIGINAL]->weight = -10;
    $tags[PelIfd::IFD0 .'_'. PelTag::MODEL]->status = 1;
--- 333,347 ----
    db_unlock_tables();
  }
  
! function _exif_reset_settings() {
    db_query('DELETE FROM {exif}');
  }
  
  /**
   * Return an array with all the valid tags, with some useful default settings.
   */
! function _exif_get_default_settings() {
!   $tags = _exif_get_valid_tags();
    $tags[PelIfd::EXIF .'_'. PelTag::DATE_TIME_ORIGINAL]->status = 1;
    $tags[PelIfd::EXIF .'_'. PelTag::DATE_TIME_ORIGINAL]->weight = -10;
    $tags[PelIfd::IFD0 .'_'. PelTag::MODEL]->status = 1;
***************
*** 234,240 ****
   * For convenience, each tag has a key in the form: "ifd_tag", where ifd and tag
   * are standard Exif ids. Those ids can be found in PelIfd.php and PelTag.php.
   */
! function exif_get_valid_tags() {
    $valid_tags = array();
    $valid_tags = array_merge($valid_tags, _exif_get_valid_ifd_tags(PelIfd::IFD0, new PelIfd(PelIfd::IFD0)));
    $valid_tags = array_merge($valid_tags, _exif_get_valid_ifd_tags(PelIfd::EXIF, new PelIfd(PelIfd::EXIF)));
--- 363,369 ----
   * For convenience, each tag has a key in the form: "ifd_tag", where ifd and tag
   * are standard Exif ids. Those ids can be found in PelIfd.php and PelTag.php.
   */
! function _exif_get_valid_tags() {
    $valid_tags = array();
    $valid_tags = array_merge($valid_tags, _exif_get_valid_ifd_tags(PelIfd::IFD0, new PelIfd(PelIfd::IFD0)));
    $valid_tags = array_merge($valid_tags, _exif_get_valid_ifd_tags(PelIfd::EXIF, new PelIfd(PelIfd::EXIF)));
***************
*** 293,295 ****
--- 422,453 ----
    return $output;
  }
  
+ /**
+  * Cron - rescan images for location info if we've been directed to.
+  */
+ 
+ function exif_cron() {
+   /* Don't bother if we're done or not started */
+   $scan_to = variable_get('exif_scan_to', 0);
+   $scan_last = variable_get('exif_scan_last', 0);
+ 
+   if ($scan_to == 0 or $scan_last >= $scan_to) return;
+ 
+   /* Continue where we left off - or start 'from' if not done before */
+   if ($scan_last == 0) $scan_last = variable_get('exif_scan_from', 0);
+ 
+   /* Don't be too greedy with the execution time - take one quarter */
+   $time_limit = time() + ini_get("max_execution_time") / 4;
+ 
+   while ($scan_last++ < $scan_to && time() < $time_limit) {
+     $node = node_load($scan_last);
+     /* If no node loaded, or not an image we don't care */
+     if ($node->nid && $node->type == 'image') {
+       /* Save if we filled anything */
+       if (_exif_fill_node_fields($node)) {
+         location_nodeapi($node, "update");
+       }
+     }
+     variable_set('exif_scan_last', $scan_last);
+   }
+ }
