plugins/calendar plugins/libcalendaring

Thomas Brüderli bruederli at kolabsys.com
Thu Apr 24 15:04:43 CEST 2014


 plugins/calendar/calendar.php                 |   97 ---------
 plugins/calendar/calendar_ui.js               |  149 -------------
 plugins/calendar/lib/calendar_ui.php          |  157 --------------
 plugins/calendar/localization/cs_CZ.inc       |   26 --
 plugins/calendar/localization/da_DK.inc       |   25 --
 plugins/calendar/localization/de_CH.inc       |   27 --
 plugins/calendar/localization/de_DE.inc       |   27 --
 plugins/calendar/localization/en_US.inc       |   31 --
 plugins/calendar/localization/fr_FR.inc       |   25 --
 plugins/calendar/localization/hu_HU.inc       |   27 --
 plugins/calendar/localization/it_IT.inc       |   27 --
 plugins/calendar/localization/ja_JP.inc       |   25 --
 plugins/calendar/localization/nl_NL.inc       |   27 --
 plugins/calendar/localization/pl_PL.inc       |   27 --
 plugins/calendar/localization/pt_BR.inc       |   27 --
 plugins/calendar/localization/ru_RU.inc       |   27 --
 plugins/calendar/skins/larry/calendar.css     |   33 ---
 plugins/libcalendaring/libcalendaring.js      |  188 +++++++++++++++++
 plugins/libcalendaring/libcalendaring.php     |  278 ++++++++++++++++++++++++++
 plugins/libcalendaring/localization/cs_CZ.inc |   29 ++
 plugins/libcalendaring/localization/da_DK.inc |   28 ++
 plugins/libcalendaring/localization/de_CH.inc |   30 ++
 plugins/libcalendaring/localization/de_DE.inc |   30 ++
 plugins/libcalendaring/localization/en_US.inc |   30 ++
 plugins/libcalendaring/localization/fr_FR.inc |   28 ++
 plugins/libcalendaring/localization/hu_HU.inc |   30 ++
 plugins/libcalendaring/localization/it_IT.inc |   29 ++
 plugins/libcalendaring/localization/ja_JP.inc |   28 ++
 plugins/libcalendaring/localization/nl_NL.inc |   30 ++
 plugins/libcalendaring/localization/pl_PL.inc |   29 ++
 plugins/libcalendaring/localization/pt_BR.inc |   29 ++
 plugins/libcalendaring/localization/ru_RU.inc |   29 ++
 plugins/libcalendaring/skins/larry/libcal.css |   51 ++++
 33 files changed, 909 insertions(+), 771 deletions(-)

New commits:
commit e987e51af2770972166097017ef09104e8c9a6a4
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Apr 24 15:04:18 2014 +0200

    Move recurrence form handling functions and texts from calendar to libcalendaring for shared use

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 4bfe0d9..1d40efe 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1334,18 +1334,8 @@ class calendar extends rcube_plugin
       $event['valarms'] = libcalendaring::to_client_alarms($event['valarms']);
     }
     if ($event['recurrence']) {
-      $event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
-      if ($event['recurrence']['UNTIL'])
-        $event['recurrence']['UNTIL'] = $this->lib->adjust_timezone($event['recurrence']['UNTIL'], $event['allday'])->format('c');
-      unset($event['recurrence']['EXCEPTIONS']);
-
-      // format RDATE values
-      if (is_array($event['recurrence']['RDATE'])) {
-        $libcal = $this->lib;
-        $event['recurrence']['RDATE'] = array_map(function($rdate) use ($libcal) {
-          return $libcal->adjust_timezone($rdate, true)->format('c');
-        }, $event['recurrence']['RDATE']);
-      }
+      $event['recurrence_text'] = $this->lib->recurrence_text($event['recurrence']);
+      $event['recurrence'] = $this->lib->to_client_recurrence($event['recurrence'], $event['allday']);
     }
 
     foreach ((array)$event['attachments'] as $k => $attachment) {
@@ -1390,70 +1380,6 @@ class calendar extends rcube_plugin
 
 
   /**
-   * Render localized text describing the recurrence rule of an event
-   */
-  private function _recurrence_text($rrule)
-  {
-    // derive missing FREQ and INTERVAL from RDATE list
-    if (empty($rrule['FREQ']) && !empty($rrule['RDATE'])) {
-      $first = $rrule['RDATE'][0];
-      $second = $rrule['RDATE'][1];
-      $third  = $rrule['RDATE'][2];
-      if (is_a($first, 'DateTime') && is_a($second, 'DateTime')) {
-        $diff = $first->diff($second);
-        foreach (array('y' => 'YEARLY', 'm' => 'MONTHLY', 'd' => 'DAILY') as $k => $freq) {
-          if ($diff->$k != 0) {
-            $rrule['FREQ'] = $freq;
-            $rrule['INTERVAL'] = $diff->$k;
-
-            // verify interval with next item
-            if (is_a($third, 'DateTime')) {
-              $diff2 = $second->diff($third);
-              if ($diff2->$k != $diff->$k) {
-                unset($rrule['INTERVAL']);
-              }
-            }
-            break;
-          }
-        }
-      }
-      if (!$rrule['INTERVAL'])
-        $rrule['FREQ'] = 'RDATE';
-      $rrule['UNTIL'] = end($rrule['RDATE']);
-    }
-
-    // TODO: finish this
-    $freq = sprintf('%s %d ', $this->gettext('every'), $rrule['INTERVAL']);
-    $details = '';
-    switch ($rrule['FREQ']) {
-      case 'DAILY':
-        $freq .= $this->gettext('days');
-        break;
-      case 'WEEKLY':
-        $freq .= $this->gettext('weeks');
-        break;
-      case 'MONTHLY':
-        $freq .= $this->gettext('months');
-        break;
-      case 'YEARLY':
-        $freq .= $this->gettext('years');
-        break;
-    }
-    
-    if ($rrule['INTERVAL'] <= 1)
-      $freq = $this->gettext(strtolower($rrule['FREQ']));
-      
-    if ($rrule['COUNT'])
-      $until =  $this->gettext(array('name' => 'forntimes', 'vars' => array('nr' => $rrule['COUNT'])));
-    else if ($rrule['UNTIL'])
-      $until = $this->gettext('recurrencend') . ' ' . format_date($rrule['UNTIL'], libcalendaring::to_php_date_format($this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format'])));
-    else
-      $until = $this->gettext('forever');
-    
-    return rtrim($freq . $details . ', ' . $until);
-  }
-
-  /**
    * Generate a unique identifier for an event
    */
   public function generate_uid()
@@ -1570,22 +1496,9 @@ class calendar extends rcube_plugin
       return;
     }
 
-    if (is_array($event['recurrence']) && !empty($event['recurrence']['UNTIL']))
-      $event['recurrence']['UNTIL'] = new DateTime($event['recurrence']['UNTIL'], $this->timezone);
-
-    if (is_array($event['recurrence']) && is_array($event['recurrence']['RDATE'])) {
-      $tz = $this->timezone;
-      $start = $event['start'];
-      $event['recurrence']['RDATE'] = array_map(function($rdate) use ($tz, $start) {
-        try {
-          $dt = new DateTime($rdate, $tz);
-          $dt->setTime($start->format('G'), $start->format('i'));
-          return $dt;
-        }
-        catch (Exception $e) {
-          return null;
-        }
-      }, $event['recurrence']['RDATE']);
+    // convert the submitted recurrence settings
+    if (is_array($event['recurrence'])) {
+      $event['recurrence'] = $this->lib->from_client_recurrence($event['recurrence'], $event['start']);
     }
 
     // convert the submitted alarm values
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 44681ba..a864261 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -81,6 +81,7 @@ function rcube_calendar_ui(settings)
     var date2unixtime = this.date2unixtime;
     var fromunixtime = this.fromunixtime;
     var parseISO8601 = this.parseISO8601;
+    var date2servertime = this.date2ISO8601;
 
 
     /***  private methods  ***/
@@ -142,23 +143,11 @@ function rcube_calendar_ui(settings)
         date.setHours(0);
     };
 
-    // turn the given date into an ISO 8601 date string understandable by PHPs strtotime()
-    var date2servertime = function(date)
-    {
-      return date.getFullYear()+'-'+zeropad(date.getMonth()+1)+'-'+zeropad(date.getDate())
-          + 'T'+zeropad(date.getHours())+':'+zeropad(date.getMinutes())+':'+zeropad(date.getSeconds());
-    }
-
     var date2timestring = function(date, dateonly)
     {
       return date2servertime(date).replace(/[^0-9]/g, '').substr(0, (dateonly ? 8 : 14));
     }
 
-    var zeropad = function(num)
-    {
-      return (num < 10 ? '0' : '') + num;
-    }
-
     var render_link = function(url)
     {
       var islink = false, href = url;
@@ -530,55 +519,6 @@ function rcube_calendar_ui(settings)
         $('<option>').attr('value', event.categories).text(event.categories).appendTo(categories).prop('selected', true);
       }
 
-      // set recurrence form
-      var recurrence, interval, rrtimes, rrenddate;
-      var load_recurrence_tab = function()
-      {
-        recurrence = $('#edit-recurrence-frequency').val(event.recurrence ? event.recurrence.FREQ || (event.recurrence.RDATE ? 'RDATE' : '') : '').change();
-        interval = $('#eventedit select.edit-recurrence-interval').val(event.recurrence ? event.recurrence.INTERVAL : 1);
-        rrtimes = $('#edit-recurrence-repeat-times').val(event.recurrence ? event.recurrence.COUNT : 1);
-        rrenddate = $('#edit-recurrence-enddate').val(event.recurrence && event.recurrence.UNTIL ? $.fullCalendar.formatDate(parseISO8601(event.recurrence.UNTIL), settings['date_format']) : '');
-        $('#eventedit input.edit-recurrence-until:checked').prop('checked', false);
-        $('#edit-recurrence-rdates').html('');
-      
-        var weekdays = ['SU','MO','TU','WE','TH','FR','SA'];
-        var rrepeat_id = '#edit-recurrence-repeat-forever';
-        if (event.recurrence && event.recurrence.COUNT)      rrepeat_id = '#edit-recurrence-repeat-count';
-        else if (event.recurrence && event.recurrence.UNTIL) rrepeat_id = '#edit-recurrence-repeat-until';
-        $(rrepeat_id).prop('checked', true);
-      
-        if (event.recurrence && event.recurrence.BYDAY && event.recurrence.FREQ == 'WEEKLY') {
-          var wdays = event.recurrence.BYDAY.split(',');
-          $('input.edit-recurrence-weekly-byday').val(wdays);
-        }
-        if (event.recurrence && event.recurrence.BYMONTHDAY) {
-          $('input.edit-recurrence-monthly-bymonthday').val(String(event.recurrence.BYMONTHDAY).split(','));
-          $('input.edit-recurrence-monthly-mode').val(['BYMONTHDAY']);
-        }
-        if (event.recurrence && event.recurrence.BYDAY && (event.recurrence.FREQ == 'MONTHLY' || event.recurrence.FREQ == 'YEARLY')) {
-          var byday, section = event.recurrence.FREQ.toLowerCase();
-          if ((byday = String(event.recurrence.BYDAY).match(/(-?[1-4])([A-Z]+)/))) {
-            $('#edit-recurrence-'+section+'-prefix').val(byday[1]);
-            $('#edit-recurrence-'+section+'-byday').val(byday[2]);
-          }
-          $('input.edit-recurrence-'+section+'-mode').val(['BYDAY']);
-        }
-        else if (event.start) {
-          $('#edit-recurrence-monthly-byday').val(weekdays[event.start.getDay()]);
-        }
-        if (event.recurrence && event.recurrence.BYMONTH) {
-          $('input.edit-recurrence-yearly-bymonth').val(String(event.recurrence.BYMONTH).split(','));
-        }
-        else if (event.start) {
-          $('input.edit-recurrence-yearly-bymonth').val([String(event.start.getMonth()+1)]);
-        }
-        if (event.recurrence && event.recurrence.RDATE) {
-          $.each(event.recurrence.RDATE, function(i,rdate){
-            add_rdate(parseISO8601(rdate));
-          });
-        }
-      };
-      
       // show warning if editing a recurring event
       if (event.id && event.recurrence) {
         var sel = event.thisandfuture ? 'future' : (event.isexception ? 'current' : 'all');
@@ -664,7 +604,7 @@ function rcube_calendar_ui(settings)
           priority: priority.val(),
           sensitivity: sensitivity.val(),
           status: eventstatus.val(),
-          recurrence: '',
+          recurrence: me.serialize_recurrence(),
           valarms: me.serialize_alarms('#edit-alarms'),
           attendees: event_attendees,
           deleted_attachments: rcmail.env.deleted_attachments,
@@ -694,56 +634,6 @@ function rcube_calendar_ui(settings)
           data._notify = 1;
         }
 
-        // gather recurrence settings
-        var freq;
-        if ((freq = recurrence.val()) != '') {
-          data.recurrence = {
-            FREQ: freq,
-            INTERVAL: $('#edit-recurrence-interval-'+freq.toLowerCase()).val()
-          };
-          
-          var until = $('input.edit-recurrence-until:checked').val();
-          if (until == 'count')
-            data.recurrence.COUNT = rrtimes.val();
-          else if (until == 'until')
-            data.recurrence.UNTIL = date2servertime(parse_datetime(endtime.val(), rrenddate.val()));
-          
-          if (freq == 'WEEKLY') {
-            var byday = [];
-            $('input.edit-recurrence-weekly-byday:checked').each(function(){ byday.push(this.value); });
-            if (byday.length)
-              data.recurrence.BYDAY = byday.join(',');
-          }
-          else if (freq == 'MONTHLY') {
-            var mode = $('input.edit-recurrence-monthly-mode:checked').val(), bymonday = [];
-            if (mode == 'BYMONTHDAY') {
-              $('input.edit-recurrence-monthly-bymonthday:checked').each(function(){ bymonday.push(this.value); });
-              if (bymonday.length)
-                data.recurrence.BYMONTHDAY = bymonday.join(',');
-            }
-            else
-              data.recurrence.BYDAY = $('#edit-recurrence-monthly-prefix').val() + $('#edit-recurrence-monthly-byday').val();
-          }
-          else if (freq == 'YEARLY') {
-            var byday, bymonth = [];
-            $('input.edit-recurrence-yearly-bymonth:checked').each(function(){ bymonth.push(this.value); });
-            if (bymonth.length)
-              data.recurrence.BYMONTH = bymonth.join(',');
-            if ((byday = $('#edit-recurrence-yearly-byday').val()))
-              data.recurrence.BYDAY = $('#edit-recurrence-yearly-prefix').val() + byday;
-          }
-          else if (freq == 'RDATE') {
-            data.recurrence = { RDATE:[] };
-            // take selected but not yet added date into account
-            if ($('#edit-recurrence-rdate-input').val() != '') {
-              $('#recurrence-form-rdate input.button.add').click();
-            }
-            $('#edit-recurrence-rdates li').each(function(i, li){
-              data.recurrence.RDATE.push($(li).attr('data-value'));
-            });
-          }
-        }
-
         data.calendar = calendars.val();
 
         if (event.id) {
@@ -811,7 +701,7 @@ function rcube_calendar_ui(settings)
       title.select();
 
       // init other tabs asynchronously
-      window.setTimeout(load_recurrence_tab, exec_deferred);
+      window.setTimeout(function(){ me.set_recurrence_edit(event); }, exec_deferred);
       if (calendar.attendees)
         window.setTimeout(load_attendees_tab, exec_deferred);
       if (calendar.attachments)
@@ -3223,38 +3113,11 @@ function rcube_calendar_ui(settings)
             };
         });
 
-      // register events on alarm fields
+      // register events on alarms and recurrence fields
       me.init_alarms_edit('#edit-alarms');
+      me.init_recurrence_edit('#eventedit');
 
-      // toggle recurrence frequency forms
-      $('#edit-recurrence-frequency').change(function(e){
-        var freq = $(this).val().toLowerCase();
-        $('.recurrence-form').hide();
-        if (freq) {
-          $('#recurrence-form-'+freq).show();
-          if (freq != 'rdate')
-            $('#recurrence-form-until').show();
-        }
-      });
-      $('#recurrence-form-rdate input.button.add').click(function(e){
-        var dt, dv = $('#edit-recurrence-rdate-input').val();
-        if (dv && (dt = parse_datetime('12:00', dv))) {
-          add_rdate(dt);
-          sort_rdates();
-          $('#edit-recurrence-rdate-input').val('')
-        }
-        else {
-          $('#edit-recurrence-rdate-input').select();
-        }
-      });
-      $('#edit-recurrence-rdates').on('click', 'a.delete', function(e){
-        $(this).closest('li').remove();
-        return false;
-      });
-      $('#edit-recurrence-enddate').datepicker(datepicker_settings).click(function(){ $("#edit-recurrence-repeat-until").prop('checked', true) });
-      $('#edit-recurrence-repeat-times').change(function(e){ $('#edit-recurrence-repeat-count').prop('checked', true); });
-
-      $('#edit-recurrence-rdate-input, #event-export-startdate').datepicker(datepicker_settings);
+      $('#event-export-startdate').datepicker(datepicker_settings);
 
       // init attendees autocompletion
       var ac_props;
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 2f54499..984ce03 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -79,7 +79,7 @@ class calendar_ui
     $this->cal->register_handler('plugin.priority_select', array($this, 'priority_select'));
     $this->cal->register_handler('plugin.sensitivity_select', array($this, 'sensitivity_select'));
     $this->cal->register_handler('plugin.alarm_select', array($this, 'alarm_select'));
-    $this->cal->register_handler('plugin.recurrence_form', array($this, 'recurrence_form'));
+    $this->cal->register_handler('plugin.recurrence_form', array($this->cal->lib, 'recurrence_form'));
     $this->cal->register_handler('plugin.attachments_form', array($this, 'attachments_form'));
     $this->cal->register_handler('plugin.attachments_list', array($this, 'attachments_list'));
     $this->cal->register_handler('plugin.filedroparea', array($this, 'file_drop_area'));
@@ -397,161 +397,6 @@ class calendar_ui
        
     return html::div($attrib, html::div('message', html::span('ui-icon ui-icon-alert', '') . $this->cal->gettext('changerecurringeventwarning')) . html::div('savemode', $form));
   }
-  
-  /**
-   * Generate the form for recurrence settings
-   */
-  function recurrence_form($attrib = array())
-  {
-    switch ($attrib['part']) {
-      // frequency selector
-      case 'frequency':
-        $select = new html_select(array('name' => 'frequency', 'id' => 'edit-recurrence-frequency'));
-        $select->add($this->cal->gettext('never'), '');
-        $select->add($this->cal->gettext('daily'), 'DAILY');
-        $select->add($this->cal->gettext('weekly'), 'WEEKLY');
-        $select->add($this->cal->gettext('monthly'), 'MONTHLY');
-        $select->add($this->cal->gettext('yearly'), 'YEARLY');
-        $select->add($this->cal->gettext('rdate'), 'RDATE');
-        $html = html::label('edit-frequency', $this->cal->gettext('frequency')) . $select->show('');
-        break;
-
-      // daily recurrence
-      case 'daily':
-        $select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-daily'));
-        $html = html::div($attrib, html::label(null, $this->cal->gettext('every')) . $select->show(1) . html::span('label-after', $this->cal->gettext('days')));
-        break;
-
-      // weekly recurrence form
-      case 'weekly':
-        $select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-weekly'));
-        $html = html::div($attrib, html::label(null, $this->cal->gettext('every')) . $select->show(1) . html::span('label-after', $this->cal->gettext('weeks')));
-        // weekday selection
-        $daymap = array('sun','mon','tue','wed','thu','fri','sat');
-        $checkbox = new html_checkbox(array('name' => 'byday', 'class' => 'edit-recurrence-weekly-byday'));
-        $first = $this->rc->config->get('calendar_first_day', 1);
-        for ($weekdays = '', $j = $first; $j <= $first+6; $j++) {
-            $d = $j % 7;
-            $weekdays .= html::label(array('class' => 'weekday'), $checkbox->show('', array('value' => strtoupper(substr($daymap[$d], 0, 2)))) . $this->cal->gettext($daymap[$d])) . ' ';
-        }
-        $html .= html::div($attrib, html::label(null, $this->cal->gettext('bydays')) . $weekdays);
-        break;
-
-      // monthly recurrence form
-      case 'monthly':
-        $select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-monthly'));
-        $html = html::div($attrib, html::label(null, $this->cal->gettext('every')) . $select->show(1) . html::span('label-after', $this->cal->gettext('months')));
-
-        $checkbox = new html_checkbox(array('name' => 'bymonthday', 'class' => 'edit-recurrence-monthly-bymonthday'));
-        for ($monthdays = '', $d = 1; $d <= 31; $d++) {
-            $monthdays .= html::label(array('class' => 'monthday'), $checkbox->show('', array('value' => $d)) . $d);
-            $monthdays .= $d % 7 ? ' ' : html::br();
-        }
-
-        // rule selectors
-        $radio = new html_radiobutton(array('name' => 'repeatmode', 'class' => 'edit-recurrence-monthly-mode'));
-        $table = new html_table(array('cols' => 2, 'border' => 0, 'cellpadding' => 0, 'class' => 'formtable'));
-        $table->add('label', html::label(null, $radio->show('BYMONTHDAY', array('value' => 'BYMONTHDAY')) . ' ' . $this->cal->gettext('each')));
-        $table->add(null, $monthdays);
-        $table->add('label', html::label(null, $radio->show('', array('value' => 'BYDAY')) . ' ' . $this->cal->gettext('onevery')));
-        $table->add(null, $this->rrule_selectors($attrib['part']));
-        
-        $html .= html::div($attrib, $table->show());
-
-        break;
-
-      // annually recurrence form
-      case 'yearly':
-        $select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-yearly'));
-        $html = html::div($attrib, html::label(null, $this->cal->gettext('every')) . $select->show(1) . html::span('label-after', $this->cal->gettext('years')));
-        // month selector
-        $monthmap = array('','jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec');
-        $checkbox = new html_checkbox(array('name' => 'bymonth', 'class' => 'edit-recurrence-yearly-bymonth'));
-        for ($months = '', $m = 1; $m <= 12; $m++) {
-            $months .= html::label(array('class' => 'month'), $checkbox->show(null, array('value' => $m)) . $this->cal->gettext($monthmap[$m]));
-            $months .= $m % 4 ? ' ' : html::br();
-        }
-        $html .= html::div($attrib + array('id' => 'edit-recurrence-yearly-bymonthblock'), $months);
-        
-        // day rule selection
-        $html .= html::div($attrib, html::label(null, $this->cal->gettext('onevery')) . $this->rrule_selectors($attrib['part'], '---'));
-        break;
-
-      // end of recurrence form
-      case 'until':
-        $radio = new html_radiobutton(array('name' => 'repeat', 'class' => 'edit-recurrence-until'));
-        $select = $this->interval_selector(array('name' => 'times', 'id' => 'edit-recurrence-repeat-times'));
-        $input = new html_inputfield(array('name' => 'untildate', 'id' => 'edit-recurrence-enddate', 'size' => "10"));
-
-        $table = new html_table(array('cols' => 2, 'border' => 0, 'cellpadding' => 0, 'class' => 'formtable'));
-
-        $table->add('label', ucfirst($this->cal->gettext('recurrencend')));
-        $table->add(null, html::label(null, $radio->show('', array('value' => '', 'id' => 'edit-recurrence-repeat-forever')) . ' ' .
-          $this->cal->gettext('forever')));
-
-        $table->add('label', '');
-        $table->add(null, $radio->show('', array('value' => 'count', 'id' => 'edit-recurrence-repeat-count')) . ' ' .
-          $this->cal->gettext(array(
-            'name' => 'forntimes',
-            'vars' => array('nr' => $select->show(1)))
-          ));
-
-        $table->add('label', '');
-        $table->add(null, $radio->show('', array('value' => 'until', 'id' => 'edit-recurrence-repeat-until')) . ' ' .
-          $this->cal->gettext('untildate') . ' ' . $input->show(''));
-        $html = $table->show();
-        break;
-
-      case 'rdate':
-        $ul = html::tag('ul', array('id' => 'edit-recurrence-rdates'), '');
-        $input = new html_inputfield(array('name' => 'rdate', 'id' => 'edit-recurrence-rdate-input', 'size' => "10"));
-        $button = new html_inputfield(array('type' => 'button', 'class' => 'button add', 'value' => $this->cal->gettext('addrdate')));
-        $html .= html::div($attrib, $ul . html::div('inputform', $input->show() . $button->show()));
-        break;
-    }
-
-    return $html;
-  }
-
-  /**
-   * Input field for interval selection
-   */
-  private function interval_selector($attrib)
-  {
-    $select = new html_select($attrib);
-    $select->add(range(1,30), range(1,30));
-    return $select;
-  }
-  
-  /**
-   * Drop-down menus for recurrence rules like "each last sunday of"
-   */
-  private function rrule_selectors($part, $noselect = null)
-  {
-    // rule selectors
-    $select_prefix = new html_select(array('name' => 'bydayprefix', 'id' => "edit-recurrence-$part-prefix"));
-    if ($noselect) $select_prefix->add($noselect, '');
-    $select_prefix->add(array(
-        $this->cal->gettext('first'),
-        $this->cal->gettext('second'),
-        $this->cal->gettext('third'),
-        $this->cal->gettext('fourth'),
-        $this->cal->gettext('last')
-      ),
-      array(1, 2, 3, 4, -1));
-    
-    $select_wday = new html_select(array('name' => 'byday', 'id' => "edit-recurrence-$part-byday"));
-    if ($noselect) $select_wday->add($noselect, '');
-    
-    $daymap = array('sunday','monday','tuesday','wednesday','thursday','friday','saturday');
-    $first = $this->rc->config->get('calendar_first_day', 1);
-    for ($j = $first; $j <= $first+6; $j++) {
-      $d = $j % 7;
-      $select_wday->add($this->cal->gettext($daymap[$d]), strtoupper(substr($daymap[$d], 0, 2)));
-    }
-    
-    return $select_prefix->show() . ' ' . $select_wday->show();
-  }
 
   /**
    * Form for uploading and importing events
diff --git a/plugins/calendar/localization/cs_CZ.inc b/plugins/calendar/localization/cs_CZ.inc
index ea52eb3..4a2a4c7 100644
--- a/plugins/calendar/localization/cs_CZ.inc
+++ b/plugins/calendar/localization/cs_CZ.inc
@@ -169,32 +169,6 @@ $labels['importerror'] = 'Při importu došlo k chybě';
 $labels['aclnorights'] = 'Nemáte administrátorská práva k tomuto kalendáři.';
 // recurrence form
 $labels['repeat'] = 'Opakování';
-$labels['frequency'] = 'Opakovat';
-$labels['never'] = 'nikdy';
-$labels['daily'] = 'dennÄ›';
-$labels['weekly'] = 'týdně';
-$labels['monthly'] = 'měsíčně';
-$labels['yearly'] = 'ročně';
-$labels['rdate'] = 'on dates';
-$labels['every'] = 'Každý';
-$labels['days'] = 'den (dny)';
-$labels['weeks'] = 'týden (týdny)';
-$labels['months'] = 'měsíc(e/ů)';
-$labels['years'] = 'rok(y/ů) v:';
-$labels['bydays'] = 'On';
-$labels['untildate'] = 'do';
-$labels['each'] = 'Každý';
-$labels['onevery'] = 'Vždy v';
-$labels['onsamedate'] = 'Ve stejné datum';
-$labels['forever'] = 'trvale';
-$labels['recurrencend'] = 'do';
-$labels['forntimes'] = 'jen $nrkrát';
-$labels['first'] = 'první';
-$labels['second'] = 'druhý';
-$labels['third'] = 'třetí';
-$labels['fourth'] = 'čtvrtý';
-$labels['last'] = 'poslední';
-$labels['dayofmonth'] = 'Den v měsíci';
 $labels['changeeventconfirm'] = 'Změnit událost';
 $labels['removeeventconfirm'] = 'Odstranit událost';
 $labels['changerecurringeventwarning'] = 'Toto je opakovaná událost. Chcete upravit jen toto konání, toto a všechna následující konání, úplně všechna konání nebo uložit událost jako novou?';
diff --git a/plugins/calendar/localization/da_DK.inc b/plugins/calendar/localization/da_DK.inc
index f6863f8..863e3a7 100644
--- a/plugins/calendar/localization/da_DK.inc
+++ b/plugins/calendar/localization/da_DK.inc
@@ -181,31 +181,6 @@ $labels['importerror'] = 'Der opstod en fejl under import';
 $labels['aclnorights'] = 'Du har ikke administratorrettigheder for denne kalender.';
 // recurrence form
 $labels['repeat'] = 'Gentag';
-$labels['frequency'] = 'Gentag';
-$labels['never'] = 'aldrig';
-$labels['daily'] = 'dagligt';
-$labels['weekly'] = 'ugentligt';
-$labels['monthly'] = 'månedligt';
-$labels['yearly'] = 'Ã¥rligt';
-$labels['every'] = 'Hver';
-$labels['days'] = 'dag(e)';
-$labels['weeks'] = 'uge(r)';
-$labels['months'] = 'måned(er)';
-$labels['years'] = 'Ã¥r i:';
-$labels['bydays'] = 'Per';
-$labels['untildate'] = 'den';
-$labels['each'] = 'Hver';
-$labels['onevery'] = 'PÃ¥ hver';
-$labels['onsamedate'] = 'PÃ¥ samme dato';
-$labels['forever'] = 'for altid';
-$labels['recurrencend'] = 'indtil';
-$labels['forntimes'] = 'for $nr gang(e)';
-$labels['first'] = 'første';
-$labels['second'] = 'anden';
-$labels['third'] = 'tredje';
-$labels['fourth'] = 'fjerde';
-$labels['last'] = 'sidste';
-$labels['dayofmonth'] = 'Dag på måneden';
 $labels['changeeventconfirm'] = 'Tilpas arrangement';
 $labels['removeeventconfirm'] = 'Fjern arrangement';
 $labels['changerecurringeventwarning'] = 'Dette er et tilbagevendende arrangement. Ønsker du kun at redige det aktuelle arrangement, dette og alle fremtidige forekomster, alle forekomster eller gemme det som et nyt arrangement?';
diff --git a/plugins/calendar/localization/de_CH.inc b/plugins/calendar/localization/de_CH.inc
index 007d713..c9f45a3 100644
--- a/plugins/calendar/localization/de_CH.inc
+++ b/plugins/calendar/localization/de_CH.inc
@@ -181,33 +181,6 @@ $labels['importerror'] = 'Fehler beim Importieren';
 $labels['aclnorights'] = 'Sie haben keine Administrator-Rechte für diesen Kalender.';
 // recurrence form
 $labels['repeat'] = 'Wiederholung';
-$labels['frequency'] = 'Wiederholung';
-$labels['never'] = 'nie';
-$labels['daily'] = 'täglich';
-$labels['weekly'] = 'wöchentlich';
-$labels['monthly'] = 'monatlich';
-$labels['yearly'] = 'jährlich';
-$labels['rdate'] = 'on dates';
-$labels['every'] = 'Alle';
-$labels['days'] = 'Tag(e)';
-$labels['weeks'] = 'Woche(n)';
-$labels['months'] = 'Monat(e)';
-$labels['years'] = 'Jahre(e) im:';
-$labels['bydays'] = 'Am';
-$labels['untildate'] = 'am';
-$labels['each'] = 'Jeden';
-$labels['onevery'] = 'An jedem';
-$labels['onsamedate'] = 'Am gleichen Tag';
-$labels['forever'] = 'unendlich';
-$labels['recurrencend'] = 'bis';
-$labels['forntimes'] = '$nr Wiederholungen';
-$labels['first'] = 'erster';
-$labels['second'] = 'zweiter';
-$labels['third'] = 'dritter';
-$labels['fourth'] = 'vierter';
-$labels['last'] = 'letzter';
-$labels['dayofmonth'] = 'Tag des Montats';
-$labels['addrdate'] = 'Add repeat date';
 $labels['changeeventconfirm'] = 'Termin ändern';
 $labels['removeeventconfirm'] = 'Termin löschen';
 $labels['changerecurringeventwarning'] = 'Dies ist eine Terminreihe. Möchten Sie nur den aktuellen, diesen und alle zukünftigen oder alle Termine bearbeiten oder die Änderungen als neuen Termin speichern?';
diff --git a/plugins/calendar/localization/de_DE.inc b/plugins/calendar/localization/de_DE.inc
index 2b54134..101021a 100644
--- a/plugins/calendar/localization/de_DE.inc
+++ b/plugins/calendar/localization/de_DE.inc
@@ -185,33 +185,6 @@ $labels['importerror'] = 'Fehler beim Importieren';
 $labels['aclnorights'] = 'Der Zugriff auf diesen Kalender erfordert Administrator-Rechte.';
 // recurrence form
 $labels['repeat'] = 'Wiederholung';
-$labels['frequency'] = 'Wiederholung';
-$labels['never'] = 'nie';
-$labels['daily'] = 'täglich';
-$labels['weekly'] = 'wöchentlich';
-$labels['monthly'] = 'monatlich';
-$labels['yearly'] = 'jährlich';
-$labels['rdate'] = 'on dates';
-$labels['every'] = 'Alle';
-$labels['days'] = 'Tag(e)';
-$labels['weeks'] = 'Woche(n)';
-$labels['months'] = 'Monat(e)';
-$labels['years'] = 'Jahre(e) im:';
-$labels['bydays'] = 'Am';
-$labels['untildate'] = 'am';
-$labels['each'] = 'Jeden';
-$labels['onevery'] = 'An jedem';
-$labels['onsamedate'] = 'Am gleichen Tag';
-$labels['forever'] = 'unendlich';
-$labels['recurrencend'] = 'bis';
-$labels['forntimes'] = '$nr Wiederholungen';
-$labels['first'] = 'erster';
-$labels['second'] = 'zweiter';
-$labels['third'] = 'dritter';
-$labels['fourth'] = 'vierter';
-$labels['last'] = 'letzter';
-$labels['dayofmonth'] = 'Tag des Montats';
-$labels['addrdate'] = 'Add repeat date';
 $labels['changeeventconfirm'] = 'Termin ändern';
 $labels['removeeventconfirm'] = 'Termin löschen';
 $labels['changerecurringeventwarning'] = 'Dies ist eine Terminreihe. Möchten Sie nur den aktuellen, diesen und alle zukünftigen oder alle Termine bearbeiten oder die Änderungen als neuen Termin speichern?';
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
index 9be8fc0..4621feb 100644
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -54,6 +54,7 @@ $labels['url'] = 'URL';
 $labels['date'] = 'Date';
 $labels['start'] = 'Start';
 $labels['end'] = 'End';
+$labels['repeat'] = 'Repeat';
 $labels['selectdate'] = 'Choose date';
 $labels['freebusy'] = 'Show me as';
 $labels['free'] = 'Free';
@@ -211,36 +212,6 @@ $labels['importnone'] = 'No events found to be imported';
 $labels['importerror'] = 'An error occured while importing';
 $labels['aclnorights'] = 'You do not have administrator rights on this calendar.';
 
-// recurrence form
-$labels['repeat'] = 'Repeat';
-$labels['frequency'] = 'Repeat';
-$labels['never'] = 'never';
-$labels['daily'] = 'daily';
-$labels['weekly'] = 'weekly';
-$labels['monthly'] = 'monthly';
-$labels['yearly'] = 'annually';
-$labels['rdate'] = 'on dates';
-$labels['every'] = 'Every';
-$labels['days'] = 'day(s)';
-$labels['weeks'] = 'week(s)';
-$labels['months'] = 'month(s)';
-$labels['years'] = 'year(s) in:';
-$labels['bydays'] = 'On';
-$labels['untildate'] = 'the';
-$labels['each'] = 'Each';
-$labels['onevery'] = 'On every';
-$labels['onsamedate'] = 'On the same date';
-$labels['forever'] = 'forever';
-$labels['recurrencend'] = 'until';
-$labels['forntimes'] = 'for $nr time(s)';
-$labels['first'] = 'first';
-$labels['second'] = 'second';
-$labels['third'] = 'third';
-$labels['fourth'] = 'fourth';
-$labels['last'] = 'last';
-$labels['dayofmonth'] = 'Day of month';
-$labels['addrdate'] = 'Add repeat date';
-
 $labels['changeeventconfirm'] = 'Change event';
 $labels['removeeventconfirm'] = 'Remove event';
 $labels['changerecurringeventwarning'] = 'This is a recurring event. Would you like to edit the current event only, this and all future occurences, all occurences or save it as a new event?';
diff --git a/plugins/calendar/localization/fr_FR.inc b/plugins/calendar/localization/fr_FR.inc
index b409375..c3f334a 100644
--- a/plugins/calendar/localization/fr_FR.inc
+++ b/plugins/calendar/localization/fr_FR.inc
@@ -179,31 +179,6 @@ $labels['importerror'] = 'Une erreur est arrivée lors de l\'import';
 $labels['aclnorights'] = 'Vous n\'avez pas les droits d\'administration sur cet agenda.';
 // recurrence form
 $labels['repeat'] = 'Répéter';
-$labels['frequency'] = 'Répéter';
-$labels['never'] = 'Jamais';
-$labels['daily'] = 'Quotidienne';
-$labels['weekly'] = 'Hebdomadaire';
-$labels['monthly'] = 'Mensuelle';
-$labels['yearly'] = 'Annuelle';
-$labels['every'] = 'Tous les';
-$labels['days'] = 'jour(s)';
-$labels['weeks'] = 'semaine(s)';
-$labels['months'] = 'mois';
-$labels['years'] = 'année(s) en :';
-$labels['bydays'] = 'Le';
-$labels['untildate'] = 'le';
-$labels['each'] = 'Chaque';
-$labels['onevery'] = 'Tous les';
-$labels['onsamedate'] = 'à la même date';
-$labels['forever'] = 'Pour toujours';
-$labels['recurrencend'] = 'Jusqu\'à';
-$labels['forntimes'] = '$nr fois';
-$labels['first'] = 'premier';
-$labels['second'] = 'deuxième';
-$labels['third'] = 'troisième';
-$labels['fourth'] = 'quatrième';
-$labels['last'] = 'dernier';
-$labels['dayofmonth'] = 'Jour du mois';
 $labels['changeeventconfirm'] = 'Modifier l\'évènement';
 $labels['removeeventconfirm'] = 'Supprimer l\'évènement';
 $labels['changerecurringeventwarning'] = 'Ceci est un évènement récurant. Voulez vous éditer seulement cette occurrence, celle-ci et toutes les suivantes, toutes les occurrences ou l\'enregistrer comme un nouvel évènement? ';
diff --git a/plugins/calendar/localization/hu_HU.inc b/plugins/calendar/localization/hu_HU.inc
index 3b4db02..e5de112 100644
--- a/plugins/calendar/localization/hu_HU.inc
+++ b/plugins/calendar/localization/hu_HU.inc
@@ -195,33 +195,6 @@ $labels['importerror'] = 'Hiba importálás közben';
 $labels['aclnorights'] = 'Nincs adminisztrátori joga ehhez a naptárhoz';
 // recurrence form
 $labels['repeat'] = 'Ismétlődés';
-$labels['frequency'] = 'Ismétlődik';
-$labels['never'] = 'soha';
-$labels['daily'] = 'naponta';
-$labels['weekly'] = 'hetente';
-$labels['monthly'] = 'havonta';
-$labels['yearly'] = 'évente';
-$labels['rdate'] = 'adott időpontokban';
-$labels['every'] = 'Minden';
-$labels['days'] = 'napon';
-$labels['weeks'] = 'héten';
-$labels['months'] = 'hónapban';
-$labels['years'] = 'évben ekkor:';
-$labels['bydays'] = 'napon';
-$labels['untildate'] = 'dátumig';
-$labels['each'] = 'napon';
-$labels['onevery'] = 'minden';
-$labels['onsamedate'] = 'Ugyanazon a dátumon';
-$labels['forever'] = 'örökké';
-$labels['recurrencend'] = 'Eddig';
-$labels['forntimes'] = '$nr alkalommal';
-$labels['first'] = 'első';
-$labels['second'] = 'második';
-$labels['third'] = 'harmadik';
-$labels['fourth'] = 'negyedik';
-$labels['last'] = 'utolsó';
-$labels['dayofmonth'] = 'hónap napja';
-$labels['addrdate'] = 'Ismétlési dátum hozzáadása';
 $labels['changeeventconfirm'] = 'Bejegyzés módosítása';
 $labels['removeeventconfirm'] = 'Bejegyzés törlése';
 $labels['changerecurringeventwarning'] = 'Ez egy ismétlődő esemény. Csak ezt az előfordulást szeretné módosítani, esetleg ezt az előfordulást az összes következővel, netán a teljes sorozatot, vagy új eseményként legyen mentve?';
diff --git a/plugins/calendar/localization/it_IT.inc b/plugins/calendar/localization/it_IT.inc
index 1b9946f..a8990e3 100644
--- a/plugins/calendar/localization/it_IT.inc
+++ b/plugins/calendar/localization/it_IT.inc
@@ -180,33 +180,6 @@ $labels['importerror'] = 'Si è verificato un errore durante l\'importazione';
 $labels['aclnorights'] = 'Non hai i diritti di amministratore per questo calendario.';
 // recurrence form
 $labels['repeat'] = 'Ricorrenza';
-$labels['frequency'] = 'Frequenza';
-$labels['never'] = 'una volta';
-$labels['daily'] = 'quotidiana';
-$labels['weekly'] = 'settimanale';
-$labels['monthly'] = 'mensile';
-$labels['yearly'] = 'annuale';
-$labels['rdate'] = 'on dates';
-$labels['every'] = 'Ogni';
-$labels['days'] = 'giorno/i';
-$labels['weeks'] = 'settimana/e';
-$labels['months'] = 'mese/i';
-$labels['years'] = 'anno/i in:';
-$labels['bydays'] = 'Di';
-$labels['untildate'] = 'il';
-$labels['each'] = 'Nei giorni';
-$labels['onevery'] = 'Ogni';
-$labels['onsamedate'] = 'Alla stessa data';
-$labels['forever'] = 'per sempre';
-$labels['recurrencend'] = 'fino al';
-$labels['forntimes'] = 'per $nr volte';
-$labels['first'] = 'primo';
-$labels['second'] = 'secondo';
-$labels['third'] = 'terzo';
-$labels['fourth'] = 'quarto';
-$labels['last'] = 'ultimo';
-$labels['dayofmonth'] = 'Giorno del mese';
-$labels['addrdate'] = 'Add repeat date';
 $labels['changeeventconfirm'] = 'Cambia evento';
 $labels['removeeventconfirm'] = 'Rimuovi evento';
 $labels['changerecurringeventwarning'] = 'This is a recurring event. Would you like to edit the current event only, this and all future occurences, all occurences or save it as a new event?';
diff --git a/plugins/calendar/localization/ja_JP.inc b/plugins/calendar/localization/ja_JP.inc
index 708c439..1e76090 100644
--- a/plugins/calendar/localization/ja_JP.inc
+++ b/plugins/calendar/localization/ja_JP.inc
@@ -169,31 +169,6 @@ $labels['importerror'] = 'インポート中にエラーが発生しました。
 $labels['aclnorights'] = 'このカレンダーの管理権限がありません。';
 // recurrence form
 $labels['repeat'] = '繰返し';
-$labels['frequency'] = '繰返し';
-$labels['never'] = '繰返さない';
-$labels['daily'] = '毎日';
-$labels['weekly'] = '毎週';
-$labels['monthly'] = '毎月';
-$labels['yearly'] = '毎年';
-$labels['every'] = 'いつでも';
-$labels['days'] = 'æ—¥(s)';
-$labels['weeks'] = '週(s)';
-$labels['months'] = '月(s)';
-$labels['years'] = 'å¹´(s):';
-$labels['bydays'] = '上';
-$labels['untildate'] = 'その';
-$labels['each'] = 'いずれも';
-$labels['onevery'] = '各';
-$labels['onsamedate'] = 'いつか';
-$labels['forever'] = 'ずっと';
-$labels['recurrencend'] = 'まで';
-$labels['forntimes'] = '$nr まで(s)';
-$labels['first'] = '第1週';
-$labels['second'] = '第2週';
-$labels['third'] = '第3週';
-$labels['fourth'] = '第4週';
-$labels['last'] = '最終週';
-$labels['dayofmonth'] = 'æ—¥';
 $labels['changeeventconfirm'] = 'イベント変更';
 $labels['removeeventconfirm'] = 'イベント削除';
 $labels['changerecurringeventwarning'] = 'これは繰返しイベントです。現在のイベントのみ、このイベントと今後の全イベント、全イベント、編集したい、もしくは新しいイベントとして保存したい?';
diff --git a/plugins/calendar/localization/nl_NL.inc b/plugins/calendar/localization/nl_NL.inc
index 9fc0d3f..9fcb8c1 100644
--- a/plugins/calendar/localization/nl_NL.inc
+++ b/plugins/calendar/localization/nl_NL.inc
@@ -195,33 +195,6 @@ $labels['importerror'] = 'Er is een fout tijdens het importeren opgetreden';
 $labels['aclnorights'] = 'U heeft geen administratieve rechten op deze kalender.';
 // recurrence form
 $labels['repeat'] = 'Herhaal';
-$labels['frequency'] = 'Herhaal';
-$labels['never'] = 'nooit';
-$labels['daily'] = 'dagelijks';
-$labels['weekly'] = 'wekelijks';
-$labels['monthly'] = 'maandelijks';
-$labels['yearly'] = 'jaarlijks';
-$labels['rdate'] = 'op datums';
-$labels['every'] = 'Elke';
-$labels['days'] = 'dag(en)';
-$labels['weeks'] = 'week / weken';
-$labels['months'] = 'maand(en)';
-$labels['years'] = 'ja(a)r(en) in:';
-$labels['bydays'] = 'Op';
-$labels['untildate'] = 'de';
-$labels['each'] = 'Elke';
-$labels['onevery'] = 'Op elke';
-$labels['onsamedate'] = 'Op dezelfde datum';
-$labels['forever'] = 'oneindig';
-$labels['recurrencend'] = 'tot';
-$labels['forntimes'] = 'voor $nr keer';
-$labels['first'] = 'eerste';
-$labels['second'] = 'tweede';
-$labels['third'] = 'derde';
-$labels['fourth'] = 'vierde';
-$labels['last'] = 'laatste';
-$labels['dayofmonth'] = 'Dag van de maand';
-$labels['addrdate'] = 'Voeg herhaal datum toe';
 $labels['changeeventconfirm'] = 'Wijzig afspraak';
 $labels['removeeventconfirm'] = 'Verwijder afspraak';
 $labels['changerecurringeventwarning'] = 'Dit is een terugkerende afspraak. Wilt u alleen de huidige afspraak wijzigen, deze en alle toekomstige afspraken, alle afspraken of opslaan als een nieuwe afspraak?';
diff --git a/plugins/calendar/localization/pl_PL.inc b/plugins/calendar/localization/pl_PL.inc
index 3b6b160..96b804a 100644
--- a/plugins/calendar/localization/pl_PL.inc
+++ b/plugins/calendar/localization/pl_PL.inc
@@ -182,33 +182,6 @@ $labels['importerror'] = 'Wystąpił błąd podczas importu.';
 $labels['aclnorights'] = 'Nie masz uprawnień administracyjnych dla tego kalendarza.';
 // recurrence form
 $labels['repeat'] = 'Powtórz';
-$labels['frequency'] = 'Powtórz';
-$labels['never'] = 'nigdy';
-$labels['daily'] = 'codziennie';
-$labels['weekly'] = 'cotygodniowo';
-$labels['monthly'] = 'miesięcznie';
-$labels['yearly'] = 'corocznie';
-$labels['rdate'] = 'on dates';
-$labels['every'] = 'Każdy';
-$labels['days'] = 'dnia';
-$labels['weeks'] = 'tygodnia';
-$labels['months'] = 'miesiÄ…ca';
-$labels['years'] = 'roku w:';
-$labels['bydays'] = 'W';
-$labels['untildate'] = 'do';
-$labels['each'] = 'Każdy';
-$labels['onevery'] = 'W każdy';
-$labels['onsamedate'] = 'W tej samej dacie';
-$labels['forever'] = 'zawsze';
-$labels['recurrencend'] = 'dopóki';
-$labels['forntimes'] = '$nr raz(y)';
-$labels['first'] = 'pierwszy';
-$labels['second'] = 'drugi';
-$labels['third'] = 'trzeci';
-$labels['fourth'] = 'czwarty';
-$labels['last'] = 'ostatni';
-$labels['dayofmonth'] = 'Dzień miesiąca';
-$labels['addrdate'] = 'Add repeat date';
 $labels['changeeventconfirm'] = 'Zmień zdarzenie';
 $labels['removeeventconfirm'] = 'Usuń zdarzenie';
 $labels['changerecurringeventwarning'] = 'To jest zdarzenie powtarzalne. Czy chcesz zmienić bieżące zdarzenie, bieżące i przyszłe, wszystkie, a może zapisać je jako nowe zdarzenie?';
diff --git a/plugins/calendar/localization/pt_BR.inc b/plugins/calendar/localization/pt_BR.inc
index 07a8c9d..35fab94 100644
--- a/plugins/calendar/localization/pt_BR.inc
+++ b/plugins/calendar/localization/pt_BR.inc
@@ -185,33 +185,6 @@ $labels['importerror'] = 'Ocorreu um erro na importação';
 $labels['aclnorights'] = 'Você não tem permissão de administrador neste calendário.';
 // recurrence form
 $labels['repeat'] = 'Repetir';
-$labels['frequency'] = 'Repetir';
-$labels['never'] = 'nunca';
-$labels['daily'] = 'diariamente';
-$labels['weekly'] = 'semanalmente';
-$labels['monthly'] = 'mensalmente';
-$labels['yearly'] = 'anualmente';
-$labels['rdate'] = 'on dates';
-$labels['every'] = 'À cada';
-$labels['days'] = 'dia(s)';
-$labels['weeks'] = 'semana(s)';
-$labels['months'] = 'mês(es)';
-$labels['years'] = 'ano(s) em:';
-$labels['bydays'] = 'No';
-$labels['untildate'] = 'em';
-$labels['each'] = 'Cada';
-$labels['onevery'] = 'Em cada';
-$labels['onsamedate'] = 'Na mesma data';
-$labels['forever'] = 'nunca termina';
-$labels['recurrencend'] = 'até';
-$labels['forntimes'] = 'por $nr vez(es)';
-$labels['first'] = 'primeira';
-$labels['second'] = 'segunda';
-$labels['third'] = 'terceira';
-$labels['fourth'] = 'quarta';
-$labels['last'] = 'última';
-$labels['dayofmonth'] = 'Dia do mês';
-$labels['addrdate'] = 'Add repeat date';
 $labels['changeeventconfirm'] = 'Trocar evento';
 $labels['removeeventconfirm'] = 'Remover evento';
 $labels['changerecurringeventwarning'] = 'Este é um evento com repetição. Você gostaria de editar o evento atual somente, estas e todas as futuras ocorrências ou salvar este como um novo evento?';
diff --git a/plugins/calendar/localization/ru_RU.inc b/plugins/calendar/localization/ru_RU.inc
index b039c68..7472a93 100644
--- a/plugins/calendar/localization/ru_RU.inc
+++ b/plugins/calendar/localization/ru_RU.inc
@@ -182,33 +182,6 @@ $labels['importerror'] = 'Ошибка при импорте';
 $labels['aclnorights'] = 'Вы не имеете прав администратора для этого календаря.';
 // recurrence form
 $labels['repeat'] = 'Повторить';
-$labels['frequency'] = 'Повторить';
-$labels['never'] = 'никогда';
-$labels['daily'] = 'ежедневно';
-$labels['weekly'] = 'еженедельно';
-$labels['monthly'] = 'ежемесячно';
-$labels['yearly'] = 'ежегодно';
-$labels['rdate'] = 'on dates';
-$labels['every'] = 'Каждый(ую)';
-$labels['days'] = 'день';
-$labels['weeks'] = 'неделю';
-$labels['months'] = 'месяц';
-$labels['years'] = 'год в:';
-$labels['bydays'] = 'Ð’';
-$labels['untildate'] = 'до';
-$labels['each'] = 'Каждого';
-$labels['onevery'] = 'Ð’';
-$labels['onsamedate'] = 'В ту же самую дату';
-$labels['forever'] = 'всегда';
-$labels['recurrencend'] = 'Повторять';
-$labels['forntimes'] = '$nr раз(а)';
-$labels['first'] = 'первый(ую)';
-$labels['second'] = 'вторый(ую)';
-$labels['third'] = 'третий(ую)';
-$labels['fourth'] = 'четвертый(ую)';
-$labels['last'] = 'последний(ую)';
-$labels['dayofmonth'] = 'День месяца';
-$labels['addrdate'] = 'Add repeat date';
 $labels['changeeventconfirm'] = 'Изменить событие';
 $labels['removeeventconfirm'] = 'Удалить событие';
 $labels['changerecurringeventwarning'] = 'Это - повторяющееся событие. Хотели бы Вы редактировать только текущее событие, это и все будущие повторения, все события или сохранять его как новое событие?';
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index 69a68ac..f47032f 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -618,39 +618,6 @@ td.topalign {
 	min-width: 5em;
 }
 
-#edit-recurrence-yearly-bymonthblock {
-	margin-left: 7.5em;
-}
-
-#eventedit .recurrence-form {
-	display: none;
-}
-
-#edit-recurrence-rdates {
-	display: block;
-	list-style: none;
-	margin: 0 0 0.8em 0;
-	padding: 0;
-	max-height: 300px;
-	overflow: auto;
-}
-
-#edit-recurrence-rdates li {
-	display: block;
-	position: relative;
-	width: 12em;
-	padding: 4px 0 4px 0;
-}
-
-#edit-recurrence-rdates li a.delete {
-	position: absolute;
-	top: 2px;
-	right: 0;
-	width: 20px;
-	height: 18px;
-	background-position: -7px -337px;
-}
-
 #eventedit .formtable td {
 	padding: 0.2em 0;
 }
diff --git a/plugins/libcalendaring/libcalendaring.js b/plugins/libcalendaring/libcalendaring.js
index aa9d227..c338684 100644
--- a/plugins/libcalendaring/libcalendaring.js
+++ b/plugins/libcalendaring/libcalendaring.js
@@ -159,6 +159,17 @@ function rcube_libcalendaring(settings)
     }
 
     /**
+     * Turn the given date into an ISO 8601 date string understandable by PHPs strtotime()
+     */
+    this.date2ISO8601 = function(date)
+    {
+        var zeropad = function(num) { return (num < 10 ? '0' : '') + num; };
+
+        return date.getFullYear() + '-' + zeropad(date.getMonth()+1) + '-' + zeropad(date.getDate())
+            + 'T' + zeropad(date.getHours()) + ':' + zeropad(date.getMinutes()) + ':' + zeropad(date.getSeconds());
+    };
+
+    /**
      * Format the given date object according to user's prefs
      */
     this.format_datetime = function(date, mode)
@@ -511,6 +522,183 @@ function rcube_libcalendaring(settings)
 
         this.dismiss_link = null;
     };
+
+
+    /*****  Recurrence form handling  *****/
+
+    /**
+     * Install event handlers on recurrence form elements
+     */
+    this.init_recurrence_edit = function(prefix)
+    {
+        // toggle recurrence frequency forms
+        $('#edit-recurrence-frequency').change(function(e){
+            var freq = $(this).val().toLowerCase();
+            $('.recurrence-form').hide();
+            if (freq) {
+              $('#recurrence-form-'+freq).show();
+              if (freq != 'rdate')
+                $('#recurrence-form-until').show();
+            }
+        });
+        $('#recurrence-form-rdate input.button.add').click(function(e){
+            var dt, dv = $('#edit-recurrence-rdate-input').val();
+            if (dv && (dt = me.parse_datetime('12:00', dv))) {
+                me.add_rdate(dt);
+                me.sort_rdates();
+                $('#edit-recurrence-rdate-input').val('')
+            }
+            else {
+                $('#edit-recurrence-rdate-input').select();
+            }
+        });
+        $('#edit-recurrence-rdates').on('click', 'a.delete', function(e){
+            $(this).closest('li').remove();
+            return false;
+        });
+
+        $('#edit-recurrence-enddate').datepicker(datepicker_settings).click(function(){ $("#edit-recurrence-repeat-until").prop('checked', true) });
+        $('#edit-recurrence-repeat-times').change(function(e){ $('#edit-recurrence-repeat-count').prop('checked', true); });
+        $('#edit-recurrence-rdate-input').datepicker(datepicker_settings);
+    };
+
+    /**
+     * Set recurrence form according to the given event/task record
+     */
+    this.set_recurrence_edit = function(rec)
+    {
+        var recurrence = $('#edit-recurrence-frequency').val(rec.recurrence ? rec.recurrence.FREQ || (rec.recurrence.RDATE ? 'RDATE' : '') : '').change(),
+            interval = $('.recurrence-form select.edit-recurrence-interval').val(rec.recurrence ? rec.recurrence.INTERVAL || 1 : 1),
+            rrtimes = $('#edit-recurrence-repeat-times').val(rec.recurrence ? rec.recurrence.COUNT || 1 : 1),
+            rrenddate = $('#edit-recurrence-enddate').val(rec.recurrence && rec.recurrence.UNTIL ? this.format_datetime(this.parseISO8601(rec.recurrence.UNTIL), 1) : '');
+        $('.recurrence-form input.edit-recurrence-until:checked').prop('checked', false);
+        $('#edit-recurrence-rdates').html('');
+
+        var weekdays = ['SU','MO','TU','WE','TH','FR','SA'],
+            rrepeat_id = '#edit-recurrence-repeat-forever';
+        if      (rec.recurrence && rec.recurrence.COUNT) rrepeat_id = '#edit-recurrence-repeat-count';
+        else if (rec.recurrence && rec.recurrence.UNTIL) rrepeat_id = '#edit-recurrence-repeat-until';
+        $(rrepeat_id).prop('checked', true);
+
+        if (rec.recurrence && rec.recurrence.BYDAY && rec.recurrence.FREQ == 'WEEKLY') {
+            var wdays = rec.recurrence.BYDAY.split(',');
+            $('input.edit-recurrence-weekly-byday').val(wdays);
+        }
+        if (rec.recurrence && rec.recurrence.BYMONTHDAY) {
+            $('input.edit-recurrence-monthly-bymonthday').val(String(rec.recurrence.BYMONTHDAY).split(','));
+            $('input.edit-recurrence-monthly-mode').val(['BYMONTHDAY']);
+        }
+        if (rec.recurrence && rec.recurrence.BYDAY && (rec.recurrence.FREQ == 'MONTHLY' || rec.recurrence.FREQ == 'YEARLY')) {
+            var byday, section = rec.recurrence.FREQ.toLowerCase();
+            if ((byday = String(rec.recurrence.BYDAY).match(/(-?[1-4])([A-Z]+)/))) {
+                $('#edit-recurrence-'+section+'-prefix').val(byday[1]);
+                $('#edit-recurrence-'+section+'-byday').val(byday[2]);
+            }
+            $('input.edit-recurrence-'+section+'-mode').val(['BYDAY']);
+        }
+        else if (rec.start) {
+            $('#edit-recurrence-monthly-byday').val(weekdays[rec.start.getDay()]);
+        }
+        if (rec.recurrence && rec.recurrence.BYMONTH) {
+            $('input.edit-recurrence-yearly-bymonth').val(String(rec.recurrence.BYMONTH).split(','));
+        }
+        else if (rec.start) {
+            $('input.edit-recurrence-yearly-bymonth').val([String(rec.start.getMonth()+1)]);
+        }
+        if (rec.recurrence && rec.recurrence.RDATE) {
+            $.each(rec.recurrence.RDATE, function(i,rdate){
+                me.add_rdate(me.parseISO8601(rdate));
+            });
+        }
+    };
+
+    /**
+     * Gather recurrence settings from form
+     */
+    this.serialize_recurrence = function()
+    {
+        var recurrence = '',
+            freq = $('#edit-recurrence-frequency').val();
+
+        if (freq != '') {
+            recurrence = {
+                FREQ: freq,
+                INTERVAL: $('#edit-recurrence-interval-'+freq.toLowerCase()).val()
+            };
+
+            var until = $('input.edit-recurrence-until:checked').val();
+            if (until == 'count')
+                recurrence.COUNT = $('#edit-recurrence-repeat-times').val();
+            else if (until == 'until')
+                recurrence.UNTIL = me.date2ISO8601(me.parse_datetime(endtime.val(), $('#edit-recurrence-enddate').val()));
+
+            if (freq == 'WEEKLY') {
+                var byday = [];
+                $('input.edit-recurrence-weekly-byday:checked').each(function(){ byday.push(this.value); });
+                if (byday.length)
+                    recurrence.BYDAY = byday.join(',');
+            }
+            else if (freq == 'MONTHLY') {
+                var mode = $('input.edit-recurrence-monthly-mode:checked').val(), bymonday = [];
+                if (mode == 'BYMONTHDAY') {
+                    $('input.edit-recurrence-monthly-bymonthday:checked').each(function(){ bymonday.push(this.value); });
+                    if (bymonday.length)
+                        recurrence.BYMONTHDAY = bymonday.join(',');
+                }
+                else
+                    recurrence.BYDAY = $('#edit-recurrence-monthly-prefix').val() + $('#edit-recurrence-monthly-byday').val();
+            }
+            else if (freq == 'YEARLY') {
+                var byday, bymonth = [];
+                $('input.edit-recurrence-yearly-bymonth:checked').each(function(){ bymonth.push(this.value); });
+                if (bymonth.length)
+                    recurrence.BYMONTH = bymonth.join(',');
+                if ((byday = $('#edit-recurrence-yearly-byday').val()))
+                    recurrence.BYDAY = $('#edit-recurrence-yearly-prefix').val() + byday;
+            }
+            else if (freq == 'RDATE') {
+                recurrence = { RDATE:[] };
+                // take selected but not yet added date into account
+                if ($('#edit-recurrence-rdate-input').val() != '') {
+                    $('#recurrence-form-rdate input.button.add').click();
+                }
+                $('#edit-recurrence-rdates li').each(function(i, li){
+                    recurrence.RDATE.push($(li).attr('data-value'));
+                });
+            }
+        }
+
+        return recurrence;
+    };
+
+    // add the given date to the RDATE list
+    this.add_rdate = function(date)
+    {
+        var li = $('<li>')
+            .attr('data-value', this.date2ISO8601(date))
+            .html('<span>' + Q(this.format_datetime(date, 1)) + '</span>')
+            .appendTo('#edit-recurrence-rdates');
+
+        $('<a>').attr('href', '#del')
+            .addClass('iconbutton delete')
+            .html(rcmail.get_label('delete', 'libcalendaring'))
+            .attr('title', rcmail.get_label('delete', 'libcalendaring'))
+            .appendTo(li);
+    };
+
+    // re-sort the list items by their 'data-value' attribute
+    this.sort_rdates = function()
+    {
+        var mylist = $('#edit-recurrence-rdates'),
+            listitems = mylist.children('li').get();
+        listitems.sort(function(a, b) {
+            var compA = $(a).attr('data-value');
+            var compB = $(b).attr('data-value');
+            return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
+        })
+        $.each(listitems, function(idx, item) { mylist.append(item); });
+    };
+
 }
 
 //////  static methods
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index e71509c..627fbef 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -637,6 +637,284 @@ class libcalendaring extends rcube_plugin
     }
 
 
+    /*********  Recurrence rules handling ********/
+
+    /**
+     * Render localized text describing the recurrence rule of an event
+     */
+    public function recurrence_text($rrule)
+    {
+        // derive missing FREQ and INTERVAL from RDATE list
+        if (empty($rrule['FREQ']) && !empty($rrule['RDATE'])) {
+            $first = $rrule['RDATE'][0];
+            $second = $rrule['RDATE'][1];
+            $third  = $rrule['RDATE'][2];
+            if (is_a($first, 'DateTime') && is_a($second, 'DateTime')) {
+                $diff = $first->diff($second);
+                foreach (array('y' => 'YEARLY', 'm' => 'MONTHLY', 'd' => 'DAILY') as $k => $freq) {
+                    if ($diff->$k != 0) {
+                        $rrule['FREQ'] = $freq;
+                        $rrule['INTERVAL'] = $diff->$k;
+
+                        // verify interval with next item
+                        if (is_a($third, 'DateTime')) {
+                            $diff2 = $second->diff($third);
+                            if ($diff2->$k != $diff->$k) {
+                                unset($rrule['INTERVAL']);
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+            if (!$rrule['INTERVAL']) {
+                $rrule['FREQ'] = 'RDATE';
+            }
+            $rrule['UNTIL'] = end($rrule['RDATE']);
+        }
+
+        $freq = sprintf('%s %d ', $this->gettext('every'), $rrule['INTERVAL']);
+        $details = '';
+        switch ($rrule['FREQ']) {
+            case 'DAILY':
+                $freq .= $this->gettext('days');
+                break;
+            case 'WEEKLY':
+                $freq .= $this->gettext('weeks');
+                break;
+            case 'MONTHLY':
+                $freq .= $this->gettext('months');
+                break;
+            case 'YEARLY':
+                $freq .= $this->gettext('years');
+                break;
+        }
+
+        if ($rrule['INTERVAL'] <= 1) {
+            $freq = $this->gettext(strtolower($rrule['FREQ']));
+        }
+
+        if ($rrule['COUNT']) {
+            $until =  $this->gettext(array('name' => 'forntimes', 'vars' => array('nr' => $rrule['COUNT'])));
+        }
+        else if ($rrule['UNTIL']) {
+            $until = $this->gettext('recurrencend') . ' ' . format_date($rrule['UNTIL'], self::to_php_date_format($this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format'])));
+        }
+        else {
+            $until = $this->gettext('forever');
+        }
+
+        return rtrim($freq . $details . ', ' . $until);
+    }
+
+    /**
+     * Generate the form for recurrence settings
+     */
+    public function recurrence_form($attrib = array())
+    {
+        switch ($attrib['part']) {
+            // frequency selector
+            case 'frequency':
+                $select = new html_select(array('name' => 'frequency', 'id' => 'edit-recurrence-frequency'));
+                $select->add($this->gettext('never'),   '');
+                $select->add($this->gettext('daily'),   'DAILY');
+                $select->add($this->gettext('weekly'),  'WEEKLY');
+                $select->add($this->gettext('monthly'), 'MONTHLY');
+                $select->add($this->gettext('yearly'),  'YEARLY');
+                $select->add($this->gettext('rdate'),   'RDATE');
+                $html = html::label('edit-frequency', $this->gettext('frequency')) . $select->show('');
+                break;
+
+            // daily recurrence
+            case 'daily':
+                $select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-daily'));
+                $html = html::div($attrib, html::label(null, $this->gettext('every')) . $select->show(1) . html::span('label-after', $this->gettext('days')));
+                break;
+
+            // weekly recurrence form
+            case 'weekly':
+                $select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-weekly'));
+                $html = html::div($attrib, html::label(null, $this->gettext('every')) . $select->show(1) . html::span('label-after', $this->gettext('weeks')));
+                // weekday selection
+                $daymap = array('sun','mon','tue','wed','thu','fri','sat');
+                $checkbox = new html_checkbox(array('name' => 'byday', 'class' => 'edit-recurrence-weekly-byday'));
+                $first = $this->rc->config->get('calendar_first_day', 1);
+                for ($weekdays = '', $j = $first; $j <= $first+6; $j++) {
+                    $d = $j % 7;
+                    $weekdays .= html::label(array('class' => 'weekday'),
+                        $checkbox->show('', array('value' => strtoupper(substr($daymap[$d], 0, 2)))) .
+                        $this->gettext($daymap[$d])
+                    ) . ' ';
+                }
+                $html .= html::div($attrib, html::label(null, $this->gettext('bydays')) . $weekdays);
+                break;
+
+            // monthly recurrence form
+            case 'monthly':
+                $select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-monthly'));
+                $html = html::div($attrib, html::label(null, $this->gettext('every')) . $select->show(1) . html::span('label-after', $this->gettext('months')));
+
+                $checkbox = new html_checkbox(array('name' => 'bymonthday', 'class' => 'edit-recurrence-monthly-bymonthday'));
+                for ($monthdays = '', $d = 1; $d <= 31; $d++) {
+                    $monthdays .= html::label(array('class' => 'monthday'), $checkbox->show('', array('value' => $d)) . $d);
+                    $monthdays .= $d % 7 ? ' ' : html::br();
+                }
+
+                // rule selectors
+                $radio = new html_radiobutton(array('name' => 'repeatmode', 'class' => 'edit-recurrence-monthly-mode'));
+                $table = new html_table(array('cols' => 2, 'border' => 0, 'cellpadding' => 0, 'class' => 'formtable'));
+                $table->add('label', html::label(null, $radio->show('BYMONTHDAY', array('value' => 'BYMONTHDAY')) . ' ' . $this->gettext('each')));
+                $table->add(null, $monthdays);
+                $table->add('label', html::label(null, $radio->show('', array('value' => 'BYDAY')) . ' ' . $this->gettext('onevery')));
+                $table->add(null, $this->rrule_selectors($attrib['part']));
+
+                $html .= html::div($attrib, $table->show());
+                break;
+
+            // annually recurrence form
+            case 'yearly':
+                $select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-yearly'));
+                $html = html::div($attrib, html::label(null, $this->gettext('every')) . $select->show(1) . html::span('label-after', $this->gettext('years')));
+                // month selector
+                $monthmap = array('','jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec');
+                $checkbox = new html_checkbox(array('name' => 'bymonth', 'class' => 'edit-recurrence-yearly-bymonth'));
+                for ($months = '', $m = 1; $m <= 12; $m++) {
+                    $months .= html::label(array('class' => 'month'), $checkbox->show(null, array('value' => $m)) . $this->gettext($monthmap[$m]));
+                    $months .= $m % 4 ? ' ' : html::br();
+                }
+                $html .= html::div($attrib + array('id' => 'edit-recurrence-yearly-bymonthblock'), $months);
+
+                // day rule selection
+                $html .= html::div($attrib, html::label(null, $this->gettext('onevery')) . $this->rrule_selectors($attrib['part'], '---'));
+                break;
+
+            // end of recurrence form
+            case 'until':
+                $radio = new html_radiobutton(array('name' => 'repeat', 'class' => 'edit-recurrence-until'));
+                $select = $this->interval_selector(array('name' => 'times', 'id' => 'edit-recurrence-repeat-times'));
+                $input = new html_inputfield(array('name' => 'untildate', 'id' => 'edit-recurrence-enddate', 'size' => "10"));
+
+                $html = html::div('line first',
+                    html::label(null, $radio->show('', array('value' => '', 'id' => 'edit-recurrence-repeat-forever')) . ' ' .
+                        $this->gettext('forever'))
+                );
+
+                $html .= html::div('line',
+                    $radio->show('', array('value' => 'count', 'id' => 'edit-recurrence-repeat-count')) . ' ' .
+                        $this->gettext(array(
+                            'name' => 'forntimes',
+                            'vars' => array('nr' => $select->show(1)))
+                        )
+                );
+
+                $html .= html::div('line',
+                    $radio->show('', array('value' => 'until', 'id' => 'edit-recurrence-repeat-until')) . ' ' .
+                        $this->gettext('untildate') . ' ' . $input->show('')
+                );
+
+                $html = html::div($attrib, html::label(null, ucfirst($this->gettext('recurrencend'))) . $html);
+                break;
+
+            case 'rdate':
+                $ul = html::tag('ul', array('id' => 'edit-recurrence-rdates'), '');
+                $input = new html_inputfield(array('name' => 'rdate', 'id' => 'edit-recurrence-rdate-input', 'size' => "10"));
+                $button = new html_inputfield(array('type' => 'button', 'class' => 'button add', 'value' => $this->gettext('addrdate')));
+                $html .= html::div($attrib, $ul . html::div('inputform', $input->show() . $button->show()));
+                break;
+        }
+
+        return $html;
+    }
+
+    /**
+     * Input field for interval selection
+     */
+    private function interval_selector($attrib)
+    {
+        $select = new html_select($attrib);
+        $select->add(range(1,30), range(1,30));
+        return $select;
+    }
+
+    /**
+     * Drop-down menus for recurrence rules like "each last sunday of"
+     */
+    private function rrule_selectors($part, $noselect = null)
+    {
+        // rule selectors
+        $select_prefix = new html_select(array('name' => 'bydayprefix', 'id' => "edit-recurrence-$part-prefix"));
+        if ($noselect) $select_prefix->add($noselect, '');
+        $select_prefix->add(array(
+                $this->gettext('first'),
+                $this->gettext('second'),
+                $this->gettext('third'),
+                $this->gettext('fourth'),
+                $this->gettext('last')
+            ),
+            array(1, 2, 3, 4, -1));
+
+        $select_wday = new html_select(array('name' => 'byday', 'id' => "edit-recurrence-$part-byday"));
+        if ($noselect) $select_wday->add($noselect, '');
+
+        $daymap = array('sunday','monday','tuesday','wednesday','thursday','friday','saturday');
+        $first = $this->rc->config->get('calendar_first_day', 1);
+        for ($j = $first; $j <= $first+6; $j++) {
+            $d = $j % 7;
+            $select_wday->add($this->gettext($daymap[$d]), strtoupper(substr($daymap[$d], 0, 2)));
+        }
+
+        return $select_prefix->show() . ' ' . $select_wday->show();
+    }
+
+    /**
+     * Convert the recurrence settings to be processed on the client
+     */
+    public function to_client_recurrence($recurrence, $allday = false)
+    {
+        if ($recurrence['UNTIL'])
+            $recurrence['UNTIL'] = $this->adjust_timezone($recurrence['UNTIL'], $allday)->format('c');
+
+        // format RDATE values
+        if (is_array($recurrence['RDATE'])) {
+            $libcal = $this;
+            $recurrence['RDATE'] = array_map(function($rdate) use ($libcal) {
+                return $libcal->adjust_timezone($rdate, true)->format('c');
+            }, $recurrence['RDATE']);
+        }
+
+        unset($recurrence['EXCEPTIONS']);
+
+        return $recurrence;
+    }
+
+    /**
+     * Process the alarms values submitted by the client
+     */
+    public function from_client_recurrence($recurrence, $start = null)
+    {
+        if (is_array($recurrence) && !empty($recurrence['UNTIL'])) {
+            $recurrence['UNTIL'] = new DateTime($recurrence['UNTIL'], $this->timezone);
+        }
+
+        if (is_array($recurrence) && is_array($recurrence['RDATE'])) {
+            $tz = $this->timezone;
+            $recurrence['RDATE'] = array_map(function($rdate) use ($tz, $start) {
+                try {
+                    $dt = new DateTime($rdate, $tz);
+                    if (is_a($start, 'DateTime'))
+                        $dt->setTime($start->format('G'), $start->format('i'));
+                    return $dt;
+                }
+                catch (Exception $e) {
+                    return null;
+                }
+            }, $recurrence['RDATE']);
+        }
+
+        return $recurrence;
+    }
+
+
     /*********  Attachments handling  *********/
 
     /**
diff --git a/plugins/libcalendaring/localization/cs_CZ.inc b/plugins/libcalendaring/localization/cs_CZ.inc
index 8317e8c..be4a07b 100644
--- a/plugins/libcalendaring/localization/cs_CZ.inc
+++ b/plugins/libcalendaring/localization/cs_CZ.inc
@@ -22,6 +22,35 @@ $labels['repeatinhrs'] = 'Zopakovat za $hrs hodin';
 $labels['repeattomorrow'] = 'Zopakovat zítra';
 $labels['repeatinweek'] = 'Zopakovat za týden';
 $labels['showmore'] = 'Ukázat víc...';
+
+// recurrence related labels
+$labels['frequency'] = 'Opakovat';
+$labels['never'] = 'nikdy';
+$labels['daily'] = 'dennÄ›';
+$labels['weekly'] = 'týdně';
+$labels['monthly'] = 'měsíčně';
+$labels['yearly'] = 'ročně';
+$labels['rdate'] = 'on dates';
+$labels['every'] = 'Každý';
+$labels['days'] = 'den (dny)';
+$labels['weeks'] = 'týden (týdny)';
+$labels['months'] = 'měsíc(e/ů)';
+$labels['years'] = 'rok(y/ů) v:';
+$labels['bydays'] = 'On';
+$labels['untildate'] = 'do';
+$labels['each'] = 'Každý';
+$labels['onevery'] = 'Vždy v';
+$labels['onsamedate'] = 'Ve stejné datum';
+$labels['forever'] = 'trvale';
+$labels['recurrencend'] = 'do';
+$labels['forntimes'] = 'jen $nrkrát';
+$labels['first'] = 'první';
+$labels['second'] = 'druhý';
+$labels['third'] = 'třetí';
+$labels['fourth'] = 'čtvrtý';
+$labels['last'] = 'poslední';
+$labels['dayofmonth'] = 'Den v měsíci';
+
 // itip related labels
 $labels['itipinvitation'] = 'Pozvání na událost';
 $labels['itipupdate'] = 'Aktualizace události';
diff --git a/plugins/libcalendaring/localization/da_DK.inc b/plugins/libcalendaring/localization/da_DK.inc
index 3b4f053..5cf29f6 100644
--- a/plugins/libcalendaring/localization/da_DK.inc
+++ b/plugins/libcalendaring/localization/da_DK.inc
@@ -22,6 +22,34 @@ $labels['repeatinhrs'] = 'Gentag om $hrs timer';
 $labels['repeattomorrow'] = 'Gentag i morgen';
 $labels['repeatinweek'] = 'Gentag om en uge';
 $labels['showmore'] = 'Vis mere ...';
+
+// recurrence related labels
+$labels['frequency'] = 'Gentag';
+$labels['never'] = 'aldrig';
+$labels['daily'] = 'dagligt';
+$labels['weekly'] = 'ugentligt';
+$labels['monthly'] = 'månedligt';
+$labels['yearly'] = 'Ã¥rligt';
+$labels['every'] = 'Hver';
+$labels['days'] = 'dag(e)';
+$labels['weeks'] = 'uge(r)';
+$labels['months'] = 'måned(er)';
+$labels['years'] = 'Ã¥r i:';
+$labels['bydays'] = 'Per';
+$labels['untildate'] = 'den';
+$labels['each'] = 'Hver';
+$labels['onevery'] = 'PÃ¥ hver';
+$labels['onsamedate'] = 'PÃ¥ samme dato';
+$labels['forever'] = 'for altid';
+$labels['recurrencend'] = 'indtil';
+$labels['forntimes'] = 'for $nr gang(e)';
+$labels['first'] = 'første';
+$labels['second'] = 'anden';
+$labels['third'] = 'tredje';
+$labels['fourth'] = 'fjerde';
+$labels['last'] = 'sidste';
+$labels['dayofmonth'] = 'Dag på måneden';
+
 // itip related labels
 $labels['itipinvitation'] = 'Invitation til';
 $labels['itipupdate'] = 'Opdatering per';
diff --git a/plugins/libcalendaring/localization/de_CH.inc b/plugins/libcalendaring/localization/de_CH.inc
index e77d1a0..cf3f50c 100644
--- a/plugins/libcalendaring/localization/de_CH.inc
+++ b/plugins/libcalendaring/localization/de_CH.inc
@@ -22,6 +22,36 @@ $labels['repeatinhrs'] = 'Wiederholung in $hrs Stunden';
 $labels['repeattomorrow'] = 'Wiederholung morgen';
 $labels['repeatinweek'] = 'Wiederholung in einer Woche';
 $labels['showmore'] = 'Mehr anzeigen...';
+
+// recurrence related labels
+$labels['frequency'] = 'Wiederholung';
+$labels['never'] = 'nie';
+$labels['daily'] = 'täglich';
+$labels['weekly'] = 'wöchentlich';
+$labels['monthly'] = 'monatlich';
+$labels['yearly'] = 'jährlich';
+$labels['rdate'] = 'on dates';
+$labels['every'] = 'Alle';
+$labels['days'] = 'Tag(e)';
+$labels['weeks'] = 'Woche(n)';
+$labels['months'] = 'Monat(e)';
+$labels['years'] = 'Jahre(e) im:';
+$labels['bydays'] = 'Am';
+$labels['untildate'] = 'am';
+$labels['each'] = 'Jeden';
+$labels['onevery'] = 'An jedem';
+$labels['onsamedate'] = 'Am gleichen Tag';
+$labels['forever'] = 'unendlich';
+$labels['recurrencend'] = 'bis';
+$labels['forntimes'] = '$nr Wiederholungen';
+$labels['first'] = 'erster';
+$labels['second'] = 'zweiter';
+$labels['third'] = 'dritter';
+$labels['fourth'] = 'vierter';
+$labels['last'] = 'letzter';
+$labels['dayofmonth'] = 'Tag des Montats';
+$labels['addrdate'] = 'Datum hinzufügen';
+
 // itip related labels
 $labels['itipinvitation'] = 'Einladung zu';
 $labels['itipupdate'] = 'Aktialisiert:';
diff --git a/plugins/libcalendaring/localization/de_DE.inc b/plugins/libcalendaring/localization/de_DE.inc
index e77d1a0..cf3f50c 100644
--- a/plugins/libcalendaring/localization/de_DE.inc
+++ b/plugins/libcalendaring/localization/de_DE.inc
@@ -22,6 +22,36 @@ $labels['repeatinhrs'] = 'Wiederholung in $hrs Stunden';
 $labels['repeattomorrow'] = 'Wiederholung morgen';
 $labels['repeatinweek'] = 'Wiederholung in einer Woche';
 $labels['showmore'] = 'Mehr anzeigen...';
+
+// recurrence related labels
+$labels['frequency'] = 'Wiederholung';
+$labels['never'] = 'nie';
+$labels['daily'] = 'täglich';
+$labels['weekly'] = 'wöchentlich';
+$labels['monthly'] = 'monatlich';
+$labels['yearly'] = 'jährlich';
+$labels['rdate'] = 'on dates';
+$labels['every'] = 'Alle';
+$labels['days'] = 'Tag(e)';
+$labels['weeks'] = 'Woche(n)';
+$labels['months'] = 'Monat(e)';
+$labels['years'] = 'Jahre(e) im:';
+$labels['bydays'] = 'Am';
+$labels['untildate'] = 'am';
+$labels['each'] = 'Jeden';
+$labels['onevery'] = 'An jedem';
+$labels['onsamedate'] = 'Am gleichen Tag';
+$labels['forever'] = 'unendlich';
+$labels['recurrencend'] = 'bis';
+$labels['forntimes'] = '$nr Wiederholungen';
+$labels['first'] = 'erster';
+$labels['second'] = 'zweiter';
+$labels['third'] = 'dritter';
+$labels['fourth'] = 'vierter';
+$labels['last'] = 'letzter';
+$labels['dayofmonth'] = 'Tag des Montats';
+$labels['addrdate'] = 'Datum hinzufügen';
+
 // itip related labels
 $labels['itipinvitation'] = 'Einladung zu';
 $labels['itipupdate'] = 'Aktialisiert:';
diff --git a/plugins/libcalendaring/localization/en_US.inc b/plugins/libcalendaring/localization/en_US.inc
index c3159ff..f768fbf 100644
--- a/plugins/libcalendaring/localization/en_US.inc
+++ b/plugins/libcalendaring/localization/en_US.inc
@@ -2,6 +2,7 @@
 
 $labels = array();
 
+// alarms related labels
 $labels['alarmemail'] = 'Send Email';
 $labels['alarmdisplay'] = 'Show message';
 $labels['alarmaudio'] = 'Play sound';
@@ -31,6 +32,35 @@ $labels['repeatinweek'] = 'Repeat in a week';
 
 $labels['showmore'] = 'Show more...';
 
+// recurrence related labels
+$labels['frequency'] = 'Repeat';
+$labels['never'] = 'never';
+$labels['daily'] = 'daily';
+$labels['weekly'] = 'weekly';
+$labels['monthly'] = 'monthly';
+$labels['yearly'] = 'annually';
+$labels['rdate'] = 'on dates';
+$labels['every'] = 'Every';
+$labels['days'] = 'day(s)';
+$labels['weeks'] = 'week(s)';
+$labels['months'] = 'month(s)';
+$labels['years'] = 'year(s)';
+$labels['bydays'] = 'On';
+$labels['untildate'] = 'the';
+$labels['each'] = 'Each';
+$labels['onevery'] = 'On every';
+$labels['onsamedate'] = 'On the same date';
+$labels['forever'] = 'forever';
+$labels['recurrencend'] = 'until';
+$labels['forntimes'] = 'for $nr time(s)';
+$labels['first'] = 'first';
+$labels['second'] = 'second';
+$labels['third'] = 'third';
+$labels['fourth'] = 'fourth';
+$labels['last'] = 'last';
+$labels['dayofmonth'] = 'Day of month';
+$labels['addrdate'] = 'Add repeat date';
+
 // itip related labels
 $labels['itipinvitation'] = 'Invitation to';
 $labels['itipupdate'] = 'Update of';
diff --git a/plugins/libcalendaring/localization/fr_FR.inc b/plugins/libcalendaring/localization/fr_FR.inc
index f8c18a3..0c34d79 100644
--- a/plugins/libcalendaring/localization/fr_FR.inc
+++ b/plugins/libcalendaring/localization/fr_FR.inc
@@ -22,6 +22,34 @@ $labels['repeatinhrs'] = 'Répéter dans $hrs heures';
 $labels['repeattomorrow'] = 'Répéter demain';
 $labels['repeatinweek'] = 'Répéter dans une semaine';
 $labels['showmore'] = 'Afficher plus...';
+
+// recurrence related labels
+$labels['frequency'] = 'Répéter';
+$labels['never'] = 'Jamais';
+$labels['daily'] = 'Quotidienne';
+$labels['weekly'] = 'Hebdomadaire';
+$labels['monthly'] = 'Mensuelle';
+$labels['yearly'] = 'Annuelle';
+$labels['every'] = 'Tous les';
+$labels['days'] = 'jour(s)';
+$labels['weeks'] = 'semaine(s)';
+$labels['months'] = 'mois';
+$labels['years'] = 'année(s) en :';
+$labels['bydays'] = 'Le';
+$labels['untildate'] = 'le';
+$labels['each'] = 'Chaque';
+$labels['onevery'] = 'Tous les';
+$labels['onsamedate'] = 'à la même date';
+$labels['forever'] = 'Pour toujours';
+$labels['recurrencend'] = 'Jusqu\'à';
+$labels['forntimes'] = '$nr fois';
+$labels['first'] = 'premier';
+$labels['second'] = 'deuxième';
+$labels['third'] = 'troisième';
+$labels['fourth'] = 'quatrième';
+$labels['last'] = 'dernier';
+$labels['dayofmonth'] = 'Jour du mois';
+
 // itip related labels
 $labels['itipinvitation'] = 'Invitation à';
 $labels['itipupdate'] = 'Mise à jour de';
diff --git a/plugins/libcalendaring/localization/hu_HU.inc b/plugins/libcalendaring/localization/hu_HU.inc
index 6fe0996..775c848 100644
--- a/plugins/libcalendaring/localization/hu_HU.inc
+++ b/plugins/libcalendaring/localization/hu_HU.inc
@@ -22,6 +22,36 @@ $labels['repeatinhrs'] = 'Emlékeztessen újra $hrs óra múlva';
 $labels['repeattomorrow'] = 'Emlékeztessen újra holnap';
 $labels['repeatinweek'] = 'Emlékeztessen újra egy hét múlva';
 $labels['showmore'] = 'Tovább...';
+
+// recurrence related labels
+$labels['frequency'] = 'Ismétlődik';
+$labels['never'] = 'soha';
+$labels['daily'] = 'naponta';
+$labels['weekly'] = 'hetente';
+$labels['monthly'] = 'havonta';
+$labels['yearly'] = 'évente';
+$labels['rdate'] = 'adott időpontokban';
+$labels['every'] = 'Minden';
+$labels['days'] = 'napon';
+$labels['weeks'] = 'héten';
+$labels['months'] = 'hónapban';
+$labels['years'] = 'évben ekkor:';
+$labels['bydays'] = 'napon';
+$labels['untildate'] = 'dátumig';
+$labels['each'] = 'napon';
+$labels['onevery'] = 'minden';
+$labels['onsamedate'] = 'Ugyanazon a dátumon';
+$labels['forever'] = 'örökké';
+$labels['recurrencend'] = 'Eddig';
+$labels['forntimes'] = '$nr alkalommal';
+$labels['first'] = 'első';
+$labels['second'] = 'második';
+$labels['third'] = 'harmadik';
+$labels['fourth'] = 'negyedik';
+$labels['last'] = 'utolsó';
+$labels['dayofmonth'] = 'hónap napja';
+$labels['addrdate'] = 'Ismétlési dátum hozzáadása';
+
 // itip related labels
 $labels['itipinvitation'] = 'Új esemény:';
 $labels['itipupdate'] = 'Módosítva:';
diff --git a/plugins/libcalendaring/localization/it_IT.inc b/plugins/libcalendaring/localization/it_IT.inc
index bc2fa46..a612aa0 100644
--- a/plugins/libcalendaring/localization/it_IT.inc
+++ b/plugins/libcalendaring/localization/it_IT.inc
@@ -21,6 +21,35 @@ $labels['repeatinhrs'] = 'Ripeti tra $hrs ore';
 $labels['repeattomorrow'] = 'Ripeti domani';
 $labels['repeatinweek'] = 'Ripeti tra una settimana';
 $labels['showmore'] = 'Mostra altro...';
+
+// recurrence related labels
+$labels['frequency'] = 'Frequenza';
+$labels['never'] = 'una volta';
+$labels['daily'] = 'quotidiana';
+$labels['weekly'] = 'settimanale';
+$labels['monthly'] = 'mensile';
+$labels['yearly'] = 'annuale';
+$labels['rdate'] = 'on dates';
+$labels['every'] = 'Ogni';
+$labels['days'] = 'giorno/i';
+$labels['weeks'] = 'settimana/e';
+$labels['months'] = 'mese/i';
+$labels['years'] = 'anno/i in:';
+$labels['bydays'] = 'Di';
+$labels['untildate'] = 'il';
+$labels['each'] = 'Nei giorni';
+$labels['onevery'] = 'Ogni';
+$labels['onsamedate'] = 'Alla stessa data';
+$labels['forever'] = 'per sempre';
+$labels['recurrencend'] = 'fino al';
+$labels['forntimes'] = 'per $nr volte';
+$labels['first'] = 'primo';
+$labels['second'] = 'secondo';
+$labels['third'] = 'terzo';
+$labels['fourth'] = 'quarto';
+$labels['last'] = 'ultimo';
+$labels['dayofmonth'] = 'Giorno del mese';
+
 // itip related labels
 $labels['itipinvitation'] = 'Invito a';
 $labels['itipupdate'] = 'Aggiornamento di';
diff --git a/plugins/libcalendaring/localization/ja_JP.inc b/plugins/libcalendaring/localization/ja_JP.inc
index 2582f2e..0d346c3 100644
--- a/plugins/libcalendaring/localization/ja_JP.inc
+++ b/plugins/libcalendaring/localization/ja_JP.inc
@@ -22,6 +22,34 @@ $labels['repeatinhrs'] = '$hrs で繰返し';
 $labels['repeattomorrow'] = '明日繰返し';
 $labels['repeatinweek'] = '1週間で繰返し';
 $labels['showmore'] = 'さらに表示…';
+
+// recurrence related labels
+$labels['frequency'] = '繰返し';
+$labels['never'] = '繰返さない';
+$labels['daily'] = '毎日';
+$labels['weekly'] = '毎週';
+$labels['monthly'] = '毎月';
+$labels['yearly'] = '毎年';
+$labels['every'] = 'いつでも';
+$labels['days'] = 'æ—¥(s)';
+$labels['weeks'] = '週(s)';
+$labels['months'] = '月(s)';
+$labels['years'] = 'å¹´(s):';
+$labels['bydays'] = '上';
+$labels['untildate'] = 'その';
+$labels['each'] = 'いずれも';
+$labels['onevery'] = '各';
+$labels['onsamedate'] = 'いつか';
+$labels['forever'] = 'ずっと';
+$labels['recurrencend'] = 'まで';
+$labels['forntimes'] = '$nr まで(s)';
+$labels['first'] = '第1週';
+$labels['second'] = '第2週';
+$labels['third'] = '第3週';
+$labels['fourth'] = '第4週';
+$labels['last'] = '最終週';
+$labels['dayofmonth'] = 'æ—¥';
+
 // itip related labels
 $labels['itipinvitation'] = '招待する';
 $labels['itipupdate'] = 'æ›´æ–°';
diff --git a/plugins/libcalendaring/localization/nl_NL.inc b/plugins/libcalendaring/localization/nl_NL.inc
index b820fb5..8ff3a8b 100644
--- a/plugins/libcalendaring/localization/nl_NL.inc
+++ b/plugins/libcalendaring/localization/nl_NL.inc
@@ -22,6 +22,36 @@ $labels['repeatinhrs'] = 'Herhaal over $hrs uur';
 $labels['repeattomorrow'] = 'Herhaal morgen';
 $labels['repeatinweek'] = 'Herhaal over een week';
 $labels['showmore'] = 'Toon meer...';
+
+// recurrence related labels
+$labels['frequency'] = 'Herhaal';
+$labels['never'] = 'nooit';
+$labels['daily'] = 'dagelijks';
+$labels['weekly'] = 'wekelijks';
+$labels['monthly'] = 'maandelijks';
+$labels['yearly'] = 'jaarlijks';
+$labels['rdate'] = 'op datums';
+$labels['every'] = 'Elke';
+$labels['days'] = 'dag(en)';
+$labels['weeks'] = 'week / weken';
+$labels['months'] = 'maand(en)';
+$labels['years'] = 'ja(a)r(en) in:';
+$labels['bydays'] = 'Op';
+$labels['untildate'] = 'de';
+$labels['each'] = 'Elke';
+$labels['onevery'] = 'Op elke';
+$labels['onsamedate'] = 'Op dezelfde datum';
+$labels['forever'] = 'oneindig';
+$labels['recurrencend'] = 'tot';
+$labels['forntimes'] = 'voor $nr keer';
+$labels['first'] = 'eerste';
+$labels['second'] = 'tweede';
+$labels['third'] = 'derde';
+$labels['fourth'] = 'vierde';
+$labels['last'] = 'laatste';
+$labels['dayofmonth'] = 'Dag van de maand';
+$labels['addrdate'] = 'Voeg herhaal datum toe';
+
 // itip related labels
 $labels['itipinvitation'] = 'Uitnodiging voor';
 $labels['itipupdate'] = 'Update van';
diff --git a/plugins/libcalendaring/localization/pl_PL.inc b/plugins/libcalendaring/localization/pl_PL.inc
index fdcfbb7..cdd7ccb 100644
--- a/plugins/libcalendaring/localization/pl_PL.inc
+++ b/plugins/libcalendaring/localization/pl_PL.inc
@@ -22,6 +22,35 @@ $labels['repeatinhrs'] = 'Powtórz po $hrs godzinach';
 $labels['repeattomorrow'] = 'Powtórz jutro';
 $labels['repeatinweek'] = 'Powtórz za tydzień';
 $labels['showmore'] = 'Pokaż więcej...';
+
+// recurrence related labels
+$labels['frequency'] = 'Powtórz';
+$labels['never'] = 'nigdy';
+$labels['daily'] = 'codziennie';
+$labels['weekly'] = 'cotygodniowo';
+$labels['monthly'] = 'miesięcznie';
+$labels['yearly'] = 'corocznie';
+$labels['rdate'] = 'on dates';
+$labels['every'] = 'Każdy';
+$labels['days'] = 'dnia';
+$labels['weeks'] = 'tygodnia';
+$labels['months'] = 'miesiÄ…ca';
+$labels['years'] = 'roku w:';
+$labels['bydays'] = 'W';
+$labels['untildate'] = 'do';
+$labels['each'] = 'Każdy';
+$labels['onevery'] = 'W każdy';
+$labels['onsamedate'] = 'W tej samej dacie';
+$labels['forever'] = 'zawsze';
+$labels['recurrencend'] = 'dopóki';
+$labels['forntimes'] = '$nr raz(y)';
+$labels['first'] = 'pierwszy';
+$labels['second'] = 'drugi';
+$labels['third'] = 'trzeci';
+$labels['fourth'] = 'czwarty';
+$labels['last'] = 'ostatni';
+$labels['dayofmonth'] = 'Dzień miesiąca';
+
 // itip related labels
 $labels['itipinvitation'] = 'Zaproszenie do';
 $labels['itipupdate'] = 'Aktualizacja';
diff --git a/plugins/libcalendaring/localization/pt_BR.inc b/plugins/libcalendaring/localization/pt_BR.inc
index 19ba4fa..45d108d 100644
--- a/plugins/libcalendaring/localization/pt_BR.inc
+++ b/plugins/libcalendaring/localization/pt_BR.inc
@@ -22,6 +22,35 @@ $labels['repeatinhrs'] = 'Repetir em $hrs horas';
 $labels['repeattomorrow'] = 'Repetir amanhã';
 $labels['repeatinweek'] = 'Repetir em uma semana';
 $labels['showmore'] = 'Mostrar mais...';
+
+// recurrence related labels
+$labels['frequency'] = 'Repetir';
+$labels['never'] = 'nunca';
+$labels['daily'] = 'diariamente';
+$labels['weekly'] = 'semanalmente';
+$labels['monthly'] = 'mensalmente';
+$labels['yearly'] = 'anualmente';
+$labels['rdate'] = 'on dates';
+$labels['every'] = 'À cada';
+$labels['days'] = 'dia(s)';
+$labels['weeks'] = 'semana(s)';
+$labels['months'] = 'mês(es)';
+$labels['years'] = 'ano(s) em:';
+$labels['bydays'] = 'No';
+$labels['untildate'] = 'em';
+$labels['each'] = 'Cada';
+$labels['onevery'] = 'Em cada';
+$labels['onsamedate'] = 'Na mesma data';
+$labels['forever'] = 'nunca termina';
+$labels['recurrencend'] = 'até';
+$labels['forntimes'] = 'por $nr vez(es)';
+$labels['first'] = 'primeira';
+$labels['second'] = 'segunda';
+$labels['third'] = 'terceira';
+$labels['fourth'] = 'quarta';
+$labels['last'] = 'última';
+$labels['dayofmonth'] = 'Dia do mês';
+
 // itip related labels
 $labels['itipinvitation'] = 'Convite para';
 $labels['itipupdate'] = 'Atualização de';
diff --git a/plugins/libcalendaring/localization/ru_RU.inc b/plugins/libcalendaring/localization/ru_RU.inc
index d939699..e78df64 100644
--- a/plugins/libcalendaring/localization/ru_RU.inc
+++ b/plugins/libcalendaring/localization/ru_RU.inc
@@ -22,6 +22,35 @@ $labels['repeatinhrs'] = 'Повторить через $hrs часов';
 $labels['repeattomorrow'] = 'Повторить завтра';
 $labels['repeatinweek'] = 'Повторить через неделю';
 $labels['showmore'] = 'Показать больше...';
+
+// recurrence related labels
+$labels['frequency'] = 'Повторить';
+$labels['never'] = 'никогда';
+$labels['daily'] = 'ежедневно';
+$labels['weekly'] = 'еженедельно';
+$labels['monthly'] = 'ежемесячно';
+$labels['yearly'] = 'ежегодно';
+$labels['rdate'] = 'on dates';
+$labels['every'] = 'Каждый(ую)';
+$labels['days'] = 'день';
+$labels['weeks'] = 'неделю';
+$labels['months'] = 'месяц';
+$labels['years'] = 'год в:';
+$labels['bydays'] = 'Ð’';
+$labels['untildate'] = 'до';
+$labels['each'] = 'Каждого';
+$labels['onevery'] = 'Ð’';
+$labels['onsamedate'] = 'В ту же самую дату';
+$labels['forever'] = 'всегда';
+$labels['recurrencend'] = 'Повторять';
+$labels['forntimes'] = '$nr раз(а)';
+$labels['first'] = 'первый(ую)';
+$labels['second'] = 'вторый(ую)';
+$labels['third'] = 'третий(ую)';
+$labels['fourth'] = 'четвертый(ую)';
+$labels['last'] = 'последний(ую)';
+$labels['dayofmonth'] = 'День месяца';
+
 // itip related labels
 $labels['itipinvitation'] = 'Приглашение на';
 $labels['itipupdate'] = 'Обновление';
diff --git a/plugins/libcalendaring/skins/larry/libcal.css b/plugins/libcalendaring/skins/larry/libcal.css
index 04fb2f1..856e2f1 100644
--- a/plugins/libcalendaring/skins/larry/libcal.css
+++ b/plugins/libcalendaring/skins/larry/libcal.css
@@ -111,3 +111,54 @@ a.reply-comment-toggle {
 	display: none;
 }
 
+.recurrence-form {
+	display: none;
+}
+
+.recurrence-form label.weekday,
+.recurrence-form label.monthday {
+	min-width: 3em;
+}
+
+.recurrence-form label.month {
+	min-width: 5em;
+}
+
+#edit-recurrence-yearly-bymonthblock {
+	margin-left: 7.5em;
+}
+
+#edit-recurrence-rdates {
+	display: block;
+	list-style: none;
+	margin: 0 0 0.8em 0;
+	padding: 0;
+	max-height: 300px;
+	overflow: auto;
+}
+
+#edit-recurrence-rdates li {
+	display: block;
+	position: relative;
+	width: 12em;
+	padding: 4px 0 4px 0;
+}
+
+#edit-recurrence-rdates li a.delete {
+	position: absolute;
+	top: 2px;
+	right: 0;
+	width: 20px;
+	height: 18px;
+	background-position: -7px -337px;
+}
+
+#recurrence-form-until div.line {
+	margin-left: 7.5em;
+	margin-bottom: 0.3em;
+}
+
+#recurrence-form-until div.line.first {
+	margin-top: -1.4em;
+}
+






More information about the commits mailing list