Index: date_api_ical.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date_api_ical.inc,v
retrieving revision 1.35.4.32
diff -u -p -r1.35.4.32 date_api_ical.inc
--- date_api_ical.inc	13 Aug 2010 15:26:24 -0000	1.35.4.32
+++ date_api_ical.inc	13 Oct 2010 09:33:52 -0000
@@ -65,6 +65,18 @@
  *         [tz] => US/Eastern
  *       )
  *     )
+ *     [RDATE] => Array (
+ *       [0] = Array (
+ *         [datetime] => 1997-09-21 09:00:00
+ *         [all_day] => 0
+ *         [tz] => US/Eastern
+ *       )
+ *       [1] = Array (
+ *         [datetime] => 1997-10-05 09:00:00
+ *         [all_day] => 0
+ *         [tz] => US/Eastern
+ *       )
+ *     )
  *   )
  *
  * @param $filename
@@ -245,7 +257,6 @@ function date_ical_parse($icaldatafolded
         case 'DTSTART':
         case 'DTEND':
         case 'DTSTAMP':
-        case 'RDATE':
         case 'FREEBUSY':
         case 'DUE':
         case 'COMPLETED':
@@ -253,6 +264,7 @@ function date_ical_parse($icaldatafolded
           break;
 
         case 'EXDATE':
+        case 'RDATE':
           $parse_result = date_ical_parse_exceptions($field, $data);
           break;
 
@@ -454,7 +466,7 @@ function date_ical_parse_rrule($field, $
  *   an array of date value arrays.
  */
 function date_ical_parse_exceptions($field, $data) {
-  $data = str_replace('EXDATE:', '', $data);
+  $data = str_replace($field.':', '', $data);
   $items = array('DATA' => $data);
   $ex_dates = explode(',', $data);
   foreach ($ex_dates as $ex_date) {
@@ -632,6 +644,18 @@ function date_ical_escape_text($text) {
  *         [tz] => US/Eastern
  *       )
  *     )
+ *     [RDATE] => Array (
+ *       [0] = Array (
+ *         [datetime] => 1997-09-21 09:00:00
+ *         [all_day] => 0
+ *         [tz] => US/Eastern
+ *       )
+ *       [1] = Array (
+ *         [datetime] => 1997-10-05 09:00:00
+ *         [all_day] => 0
+ *         [tz] => US/Eastern
+ *       )
+ *     )
  *
  */
 function date_api_ical_build_rrule($form_values) {
@@ -694,5 +718,24 @@ function date_api_ical_build_rrule($form
   elseif (!empty($form_values['EXDATE'])) {
     $RRULE .= chr(13) . chr(10) .'EXDATE:'. $form_values['EXDATE'];
   }
+
+  // Exceptions dates go last, on their own line.
+  if (isset($form_values['RDATE']) && is_array($form_values['RDATE'])) {
+    $ex_dates = array();
+    foreach ($form_values['RDATE'] as $value) {
+      $ex_date = date_convert($value['datetime'], DATE_DATETIME, DATE_ICAL);
+      if (!empty($ex_date)) {
+        $ex_dates[] = $ex_date;
+      }
+    }
+    if (!empty($ex_dates)) {
+      sort($ex_dates);
+      $RRULE .= chr(13) . chr(10) .'RDATE:'. implode(',', $ex_dates);
+    }
+  }
+  elseif (!empty($form_values['RDATE'])) {
+    $RRULE .= chr(13) . chr(10) .'RDATE:'. $form_values['RDATE'];
+  }
+
   return $RRULE;
 }
Index: date/date_repeat.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date/date_repeat.inc,v
retrieving revision 1.22.2.1.2.23
diff -u -p -r1.22.2.1.2.23 date_repeat.inc
--- date/date_repeat.inc	9 Jun 2009 10:33:48 -0000	1.22.2.1.2.23
+++ date/date_repeat.inc	13 Oct 2010 09:33:52 -0000
@@ -156,8 +156,8 @@ function date_repeat_build_dates($rrule 
     $end = date_ical_date($values['UNTIL'], $timezone);
   }
   $end_datetime = date_format($end, DATE_FORMAT_DATETIME);
-  
-  // Split the RRULE into RRULE and EXDATE parts.
+
+  // Split the RRULE into RRULE, EXDATE, and RDATE parts.
   $parts = date_repeat_split_rrule($rrule);
   $parsed_exceptions = (array) $parts[1];
   $exceptions = array();
@@ -165,8 +165,16 @@ function date_repeat_build_dates($rrule 
     $date = date_ical_date($exception);
     $exceptions[] = date_format($date, 'Y-m-d');
   }
+
+  $parsed_rdates = (array) $parts[2];
+  $additions = array();
+  foreach ($parsed_rdates as $rdate) {
+    $additions[] = date_ical_date($rdate);
+  }
+  
+  $dates = date_repeat_calc($rrule, $start_datetime, $end_datetime, $exceptions, $timezone, $additions);
+
   
-  $dates = date_repeat_calc($rrule, $start_datetime, $end_datetime, $exceptions, $timezone);
   $value = array();
   foreach ($dates as $delta => $date) {
     // date_repeat_calc always returns DATE_DATETIME dates, which is
Index: date_repeat/date_repeat.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date_repeat/date_repeat.module,v
retrieving revision 1.30.4.13
diff -u -p -r1.30.4.13 date_repeat.module
--- date_repeat/date_repeat.module	12 Aug 2010 19:41:04 -0000	1.30.4.13
+++ date_repeat/date_repeat.module	13 Oct 2010 09:33:53 -0000
@@ -30,6 +30,7 @@ function date_repeat_theme() {
   return array(
     'date_repeat' => array('arguments' => array('element' => NULL)),
     'date_repeat_current_exceptions' => array('arguments' => array('element' => NULL)),
+    'date_repeat_current_additions' => array('arguments' => array('element' => NULL)),
   );
 }
 
@@ -161,9 +162,11 @@ function date_repeat_rrule_description($
     '!count' => '',
     '!until' => '', 
     '!except' => '',
+    '!additional' => '',
     '!week_starts_on' => '',
     );
   $parts = date_repeat_split_rrule($rrule);
+  $additions = $parts[2];
   $exceptions = $parts[1];
   $rrule = $parts[0];
   $interval = INTERVAL_options();
@@ -230,11 +233,18 @@ function date_repeat_rrule_description($
     }
     $description['!except'] = trim(t('!repeats_every_interval except !except_dates', array('!repeats_every_interval ' => '', '!except_dates' => implode(', ', $values))));
   }
+  if ($additions) {
+    $values = array();
+    foreach ($additions as $addition) {
+      $values[] = date_format_date(date_ical_date($addition), 'custom', $format);
+    }
+    $description['!additional'] = trim(t('Also includes !additional_dates.', array('!additional_dates' => implode(', ', $values))));
+  }
   if (!empty($rrule['WKST'])) {
     $day_names = date_repeat_dow_day_options();
     $description['!week_starts_on'] = trim(t('!repeats_every_interval where the week start on !day_of_week', array('!repeats_every_interval ' => '', '!day_of_week' => $day_names[trim($rrule['WKST'])])));
   }
-  return t('Repeats !interval !bymonth !byday !count !until !except.', $description);
+  return t('Repeats !interval !bymonth !byday !count !until !except. !additional', $description);
 }
 
 /**
@@ -244,6 +254,7 @@ function date_repeat_split_rrule($rrule)
   $parts = explode("\n", $rrule);
   $rrule = array();
   $exceptions = array();
+  $rdate = array();
   foreach ($parts as $part) {
     if (strstr($part, 'RRULE')) {
       $RRULE = str_replace('RRULE:', '', $part);
@@ -254,16 +265,21 @@ function date_repeat_split_rrule($rrule)
       $exceptions = (array) date_ical_parse_exceptions('EXDATE:', $EXDATE);
       unset($exceptions['DATA']);
     }
+    elseif (strstr($part, 'RDATE')) {
+      $RDATE = str_replace('RDATE:', '', $part);
+      $rdate = (array) date_ical_parse_exceptions('RDATE:', $RDATE);
+      unset($rdate['DATA']);
+    }
   }
-  return array($rrule, $exceptions);
+  return array($rrule, $exceptions, $rdate);
 }
 
 /**
  * Analyze a RRULE and return dates that match it.
  */
-function date_repeat_calc($rrule, $start, $end, $exceptions = array(), $timezone = NULL) {
+function date_repeat_calc($rrule, $start, $end, $exceptions = array(), $timezone = NULL, $additions = array()) {
   require_once('./'. drupal_get_path('module', 'date_repeat') .'/date_repeat_calc.inc');
-  return _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone);
+  return _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additions);
 }
 
 /**
Index: date_repeat/date_repeat_calc.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date_repeat/date_repeat_calc.inc,v
retrieving revision 1.8.4.19
diff -u -p -r1.8.4.19 date_repeat_calc.inc
--- date_repeat/date_repeat_calc.inc	12 Aug 2010 21:06:57 -0000	1.8.4.19
+++ date_repeat/date_repeat_calc.inc	13 Oct 2010 09:33:53 -0000
@@ -14,7 +14,7 @@
  * so the calculation code is not called more often than necessary.
  *
  * Currently implemented:
- * INTERVAL, UNTIL, COUNT, EXDATE, BYDAY, BYMONTHDAY, BYMONTH,
+ * INTERVAL, UNTIL, COUNT, EXDATE, RDATE, BYDAY, BYMONTHDAY, BYMONTH,
  * YEARLY, MONTHLY, WEEKLY, DAILY
  *
  * Currently not implemented:
@@ -31,7 +31,7 @@
  *
  * Compute dates that match the requested rule, within a specified date range.
  */
-function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone) {
+function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additions) {
   require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_ical.inc');
   
   if (empty($timezone)) {
@@ -341,6 +341,12 @@ function _date_repeat_calc($rrule, $star
       }
     }
   }
+  
+  // add additional dates
+  foreach($additions as $addition) {
+    $days[] = date_format($addition, DATE_FORMAT_DATETIME);
+  }
+  
   sort($days);
   return $days;
 }
Index: date_repeat/date_repeat_form.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date_repeat/date_repeat_form.inc,v
retrieving revision 1.15.4.16
diff -u -p -r1.15.4.16 date_repeat_form.inc
--- date_repeat/date_repeat_form.inc	12 Aug 2010 22:32:58 -0000	1.15.4.16
+++ date_repeat/date_repeat_form.inc	13 Oct 2010 09:33:54 -0000
@@ -9,7 +9,7 @@
  * so the code is not parsed unless needed.
  *
  * Currently implemented:
- * INTERVAL, UNTIL, EXDATE, BYDAY, BYMONTHDAY, BYMONTH,
+ * INTERVAL, UNTIL, EXDATE, RDATE, BYDAY, BYMONTHDAY, BYMONTH,
  * YEARLY, MONTHLY, WEEKLY, DAILY
  *
  * Currently not implemented:
@@ -54,6 +54,8 @@ function _date_repeat_rrule_process($ele
   $parts = date_repeat_split_rrule($rrule);
   $rrule = $parts[0];
   $exceptions = $parts[1];
+  $additions = $parts[2];
+  
   $timezone = !empty($element['#date_timezone']) ? $element['#date_timezone'] : date_default_timezone_name();
   $merged_values = date_repeat_merge($rrule, $element);
   
@@ -192,6 +194,39 @@ function _date_repeat_rrule_process($ele
       'granularity' => array('#type' => 'hidden', '#value' => serialize(array('year', 'month', 'day'))),
       );
   }
+  
+  // collect additions in the same way as exceptions - implements RDATE.
+  $element['additions'] = array(
+    '#type' => 'fieldset',
+    '#collapsible' => TRUE,
+    '#collapsed' => empty($additions) ? TRUE : FALSE,
+    '#title' => t('Additional'),
+    '#description' => t('Dates to add to the list of repeating dates.'),
+    '#prefix' => '<div class="date-repeat">',
+    '#suffix' => '</div>',
+    );
+  $max = !empty($additions) ? sizeof($additions) : 0;
+  for ($i = 0; $i <= $max; $i++) {
+    $RDATE = '';
+    if (!empty($additions[$i]['datetime'])) {
+      $RDATE = $additions[$i]['datetime'];
+    }
+    $element['additions']['RDATE'][$i] = array(
+      '#tree' => TRUE,
+      'datetime' => array(
+        '#type' => $element['#date_repeat_widget'],
+        '#default_value' => $RDATE,
+        '#date_timezone' => !empty($element['#date_timezone']) ? $element['#date_timezone'] : date_default_timezone_name(),
+        '#date_format' => !empty($element['#date_format']) ? $element['#date_format'] : 'Y-m-d',
+        '#date_text_parts'  => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
+        '#date_year_range'  => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
+        '#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
+        ),
+      'tz' => array('#type' => 'hidden', '#value' => $element['#date_timezone']),
+      'all_day' => array('#type' => 'hidden', '#value' => 1),
+      'granularity' => array('#type' => 'hidden', '#value' => serialize(array('year', 'month', 'day'))),
+      );
+  }
   return $element;
 }
 
@@ -202,12 +237,14 @@ function date_repeat_merge($form_values,
   if (empty($form_values) || !is_array($form_values)) {
     return $form_values;
   }
-  if (array_key_exists('advanced', $form_values) || array_key_exists('exceptions', $form_values)) {
+  if (array_key_exists('advanced', $form_values) || array_key_exists('exceptions', $form_values) || array_key_exists('additions', $form_values)) {
     if (!array_key_exists('advanced', $form_values)) $form_values['advanced'] = array();
     if (!array_key_exists('exceptions', $form_values)) $form_values['exceptions'] = array();
-    $form_values = array_merge($form_values, (array) $form_values['advanced'], (array) $form_values['exceptions']);
+    if (!array_key_exists('additions', $form_values)) $form_values['additions'] = array();
+    $form_values = array_merge($form_values, (array) $form_values['advanced'], (array) $form_values['exceptions'], (array) $form_values['additions']);
     unset($form_values['advanced']);
     unset($form_values['exceptions']);
+    unset($form_values['additions']);
   }
   if (array_key_exists('BYDAY', $form_values)) unset($form_values['BYDAY']['']);
   if (array_key_exists('BYMONTH', $form_values)) unset($form_values['BYMONTH']['']);
@@ -229,6 +266,16 @@ function date_repeat_merge($form_values,
       }
     }
   }
+  if (array_key_exists('RDATE', $form_values) && is_array($form_values['RDATE'])) {
+    $function = $element['#date_repeat_widget'] .'_input_value';
+    $rdate_element = $element;
+    foreach ($form_values['RDATE'] as $delta => $value) {
+      if (is_array($value['datetime'])) {
+        $rdate_element['#value'] = $form_values['RDATE'][$delta]['datetime'];
+        $form_values['RDATE'][$delta]['datetime'] = $function($rdate_element);
+      }
+    }
+  }
   return $form_values;
 }
 
@@ -259,6 +306,19 @@ function theme_date_repeat_current_excep
 }
 
 /**
+ * Theme the exception list as a table so the buttons line up
+ */
+function theme_date_repeat_current_additions($rows = array()) {
+  $rows_info = array();
+  foreach ($rows as $key => $value) {
+    if (substr($key, 0, 1) != '#') {
+      $rows_info[] = array(drupal_render($value['action']), drupal_render($value['display']));
+    }
+  }
+  return theme('table', array(t('Delete'), t('Current additions')), $rows_info);
+}
+
+/**
  * Themes the date repeat element.
  */
 function theme_date_repeat($element) {
