? 623448-36_datemapper.patch
? 623448_35_datemapper.patch
? 623448_36-datemapper.patch
? libraries/simplepie.inc
Index: mappers/date.inc
===================================================================
RCS file: mappers/date.inc
diff -N mappers/date.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ mappers/date.inc	13 Jan 2010 15:23:32 -0000
@@ -0,0 +1,75 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * On behalf implementation of Feeds mapping API for date
+ */
+
+/**
+ * Implementation of hook_feeds_node_processor_targets_alter().
+ *
+ * @see FeedsNodeProcessor::getMappingTargets().
+ *
+ * @todo Only provides "end date" target if field allow it.
+ */
+function date_feeds_node_processor_targets_alter(&$targets, $content_type) {
+  $info = content_types($content_type);
+  if (isset($info['fields']) && count($info['fields'])) {
+    foreach ($info['fields'] as $field_name => $field) {
+      if (in_array($field['type'], array('date', 'datestamp', 'datetime'))) {
+        $name = isset($field['widget']['label']) ? $field['widget']['label'] : $field_name;
+        $targets[$field_name.'#start'] = array(
+          'name' => $name .': Start',
+          'callback' => 'date_feeds_set_target',
+          'description' => t('The start date for the !name field. Also use if mapping both start and end.', array('!name' => $name)),
+        );
+        $targets[$field_name.'#end'] = array(
+          'name' => $name .': End',
+          'callback' => 'date_feeds_set_target',
+          'description' => t('The end date for the @name field.', array('@name' => $name)),
+        );
+      }
+    }
+  }
+}
+
+/**
+ * Implementation of hook_feeds_set_target().
+ *
+ * @param $node
+ *   The target node.
+ * @param $field_name
+ *   The name of field on the target node to map to.
+ * @param $feed_element
+ *   The value to be mapped. Should be either a (flexible) date string
+ *   or a FeedsDateTimeField object.
+ *
+ * @todo Support array of values for dates.
+ */
+function date_feeds_set_target($node, $target, $feed_element) {
+  list($field_name, $sub_field) = split('#', $target);
+  if ($feed_element instanceof FeedsDateTimeField) {
+    // The wrapper object should only be used if you know both end and start.
+    $feed_element->buildField($node, $field_name);
+    return;
+  }
+
+  $field = content_fields($field_name);
+  $timezone = date_default_timezone_name();
+  // The timezone to assume this is entered in, unless it has a timezone as part of the date itself.
+  $to_tz = date_get_timezone($field['tz_handling'], $timezone);
+
+  if (is_array($feed_element)) {
+    $feed_element = $feed_element[0];
+  }
+  // Parse the value. If it doesn't contain a timezone, it will be set to the field timezone by default.
+  $value = new FeedsDateTime($feed_element, timezone_open($to_tz));
+  if ($sub_field == 'end') {
+    $se_dt = new FeedsDateTimeField(NULL, $value);
+  }
+  else {
+    $se_dt = new FeedsDateTimeField($value, NULL);
+  }
+  $se_dt->buildField($node, $field_name);
+}
\ No newline at end of file
Index: plugins/FeedsParser.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsParser.inc,v
retrieving revision 1.5
diff -u -p -r1.5 FeedsParser.inc
--- plugins/FeedsParser.inc	12 Jan 2010 16:28:02 -0000	1.5
+++ plugins/FeedsParser.inc	13 Jan 2010 15:23:32 -0000
@@ -95,4 +95,255 @@ abstract class FeedsParser extends Feeds
   public function getSourceElement($item, $element_key) {
     return isset($item[$element_key]) ? $item[$element_key] : '';
   }
+}
+
+class FeedsDateTime extends DateTime {
+  public $granularity = array();
+  protected static $allgranularity = array('year', 'month', 'day', 'hour', 'minute', 'second', 'tz_id');
+
+  public function __construct($time, $tz) {
+    if(is_numeric($time)) {
+      //assume timestamp
+      $time = "@".$time;
+    }
+    // PHP <5.3 doesn't like the GMT- notation for parsing timezones
+    $time = str_replace("GMT-", "-", $time);
+    $time = str_replace("GMT+", "+", $time);
+
+    parent::__construct($time, $tz);
+    $this->setGranularityFromTime($time, $tz);
+  }
+
+  public function hasTime() {
+    return in_array('hour', $this->granularity);
+  }
+
+  /**
+   * This function will keep this object's values by default.
+   * Only one timezone is ever respected; ie one tz was WRONG or IRRELEVANT.
+   * Examples:
+   *   One has date info and the other time
+   *    the one with time will set TZ, and date will stay the same in new TZ
+   *   One has tz_id granularity
+   *    that tz will override all others, all elements will stay same in new TZ
+   *
+   */
+  public function merge(FeedsDateTime $dt) {
+    $old_dttz = $dt->getTimezone();
+    $old_tz = $this->getTimezone();
+    $gran_tz = FALSE;
+    // Figure out which timezone to use, with preferential order.
+    if (in_array('tz_id', $this->granularity)) {
+      // Use our timezone preferentially
+      $use_tz = $old_tz;
+      $gran_tz = TRUE;
+    } else if (in_array('tz_id', $dt->granularity)) {
+      // They specifically set tz, use it
+      $use_tz = $old_dttz;
+      $gran_tz = TRUE;
+    } else if($this->hasTime()) {
+      // We have time set, use ours
+      $use_tz = $old_tz;
+    } else if($dt->hasTime()) {
+      // They have time set, use it
+      $use_tz = $old_dttz;
+    } else {
+      // no relevant timezone; doesn't matter.
+      $use_tz = $old_tz;
+    }
+    // Convert both to the working tz, without doing conversion,
+    // The printed date will stay the same, timestamp changes.
+    $this->setTimezone($use_tz, TRUE);
+    $dt->setTimezone($use_tz, TRUE);
+    $val = $this->toArray();
+    $otherval = $dt->toArray();
+    $gran = $this->granularity;
+    foreach (self::$allgranularity as $g) {
+      if (in_array($g, $dt->granularity) && !in_array($g, $this->granularity)) {
+        // The other class has a property we don't; steal it.
+        $gran[] = $g;
+        $val[$g] = $otherval[$g];
+      }
+    }
+    // Convert back, once again not actually changing literal, printed values.
+    $dt->setTimezone($old_dttz, TRUE);
+    $this->setTimezone($old_tz, TRUE);
+    return FeedsDateTime::createFromArray($val, $gran, $use_tz);
+  }
+
+  /**
+   * Output in the format date module looks for for storage.
+   *
+   * @param boolean $iso
+   *   Whether it should be output in pseudo-iso format, used for 'date', supports granularity.
+   */
+  public function toField($iso = FALSE) {
+    $format = "Y-m-d H:i:s";
+    if ($iso) {
+      $format = "Y-m-d\TH:i:s";
+      $format = date_limit_format($format, $this->granularity);
+    }
+    return $this->format($format);
+  }
+
+  /**
+   * Overrides default DateTime function. Only changes output values if actually had
+   * time granularity.
+   */
+  public function setTimezone(DateTimeZone $dt, $force = FALSE) {
+    if(!$this->hasTime() || $force) {
+      // this has no time granularity, so timezone doesn't mean much
+      // We set the timezone using the method, which will change the day/hour, but then we switch back
+      $arr = $this->toArray();
+      parent::setTimezone($dt);
+      $this->setTime($arr['hour'], $arr['minute'], $arr['second']);
+      $this->setDate($arr['year'], $arr['month'], $arr['day']);
+      return;
+    }
+    parent::setTimezone($dt);
+  }
+
+  public function addGranularity($g) {
+    $this->granularity[] = $g;
+    $this->granularity = array_unique($this->granularity);
+  }
+
+  public function removeGranularity($g) {
+    if($key = array_search($g, $this->granularity)) {
+      unset($this->granularity[$key]);
+    }
+  }
+
+  /**
+   * Create FeedsDateTime from arrays of time and granularity.
+   *
+   * @return
+   *   A FeedsDateTime object.
+   */
+  public static function createFromArray($time, $gran, $tz) {
+    $ret = new FeedsDateTime($time['year'].'-'.$time['month'].'-'.$time['day'].' '.$time['hour'].':'.$time['minute'].':'.$time['second'] .' '. $time['tz_id'], $tz);
+    $ret->granularity = $gran;
+    return $ret;
+  }
+
+  protected function setGranularityFromTime($time, $tz) {
+    $this->granularity = array();
+    $temp = date_parse($time);
+    // This PHP method currently doesn't have resolution down to seconds, so if there is some time, all will be set.
+    foreach(self::$allgranularity AS $g) {
+      if(is_numeric($temp[$g]) || ($g == 'tz_id' && !empty($temp[$g]))) {
+        $this->granularity[] = $g;
+      }
+    }
+  }
+
+  protected function toArray() {
+    return array('year' => $this->format('Y'), 'month' => $this->format('m'), 'day' => $this->format('d'), 'hour' => $this->format('H'), 'minute' => $this->format('i'), 'second' => $this->format('s'), 'tz_id' => $this->format('e'));
+  }
+}
+
+/**
+ * Defines a parsed date (including ranges, repeat) wrapper, to be passed to mappers.
+ */
+class FeedsDateTimeField extends FeedsElement {
+  protected $start;
+  protected $end;
+
+  /**
+   * Constructor.
+   */
+  public function __construct($start = NULL, $end = NULL, DateTimeZone $tz = NULL) {
+    $this->start = new FeedsDateTime($start, $tz);
+    $this->end = new FeedsDateTime($end, $tz);
+  }
+
+  /**
+   * Override FeedsElement::getValue().
+   */
+  public function getValue() {
+    return $this->start;
+  }
+
+  /**
+   * Merge this field with another. Most stuff goes down when
+   * merging the two sub-dates.
+   *
+   * @see FeedsDateTime
+   */
+  public function merge(FeedsDateTimeField $fsedt, $byparts = TRUE) {
+    $rs = $this->start ? $this->start : $fsedt->start;
+    $re = $this->end ? $this->end : $fsedt->end;
+    if ($byparts) {
+      $rs = $this->start && $fsedt->start ? $this->start->merge($fsedt->start) : clone $rs;
+      $re = $this->end && $fsedt->end ? $this->end->merge($fsedt->end) : clone $re;
+    }
+    return new FeedsDateTimeField($rs, $re);
+  }
+
+  /**
+   * Build a node's date CCK field from our object.
+   */
+  public function buildField($node, $field_name) {
+    $field = content_fields($field_name);
+    // Put together the whole object we're dealing with, including if if already exists in some fashion on the field.
+    if ($node->{$field_name}[0]['date']) {
+      // already has some date data
+      if ($this->start) {
+        $use_start = $this->start->merge($node->{$field_name}[0]['date']);
+      }
+      else {
+        $use_start = $node->{$field_name}[0]['date'];
+      }
+    }
+    elseif ($this->start) {
+      $use_start = clone $this->start;
+    }
+    else {
+      $use_start = NULL;
+    }
+    if ($node->{$field_name}[0]['date2']) {
+      // already has some date data
+      if ($this->start) {
+        $use_end = $this->end->merge($node->{$field_name}[0]['date2']);
+      }
+      else {
+        $use_end = $node->{$field_name}[0]['date2'];
+      }
+    }
+    elseif ($this->end) {
+      $use_end = clone $this->end;
+    }
+    else {
+      $use_end = NULL;
+    }
+
+    if ($use_start) {
+      $db_tz = date_get_timezone_db($field['tz_handling'], $use_start->getTimezone()->getName());
+    }
+    elseif ($use_end) {
+      $db_tz = date_get_timezone_db($field['tz_handling'], $use_end->getTimezone()->getName());
+    }
+    else {
+      return;
+    }
+    $db_tz = new DateTimeZone($db_tz);
+
+    if (!isset($node->{$field_name})) {
+      $node->{$field_name} = array();
+    }
+    if ($use_start) {
+      $node->{$field_name}[0]['timezone'] = $use_start->getTimezone()->getName();
+      $node->{$field_name}[0]['offset'] = $use_start->getOffset();
+      $use_start->setTimezone($db_tz);
+      $node->{$field_name}[0]['date'] = $use_start;
+      $node->{$field_name}[0]['value'] = $use_start->toField($field['type'] == 'date');
+    }
+    if ($use_end) {
+      // Don't ever use end to set timezone (for now)
+      $node->{$field_name}[0]['offset2'] = $use_end->getOffset();
+      $use_end->setTimezone($db_tz);
+      $node->{$field_name}[0]['date2'] = $use_end;
+      $node->{$field_name}[0]['value2'] = $use_end->toField($field['type'] == 'date');
+    }
+  }
 }
\ No newline at end of file
