Branch 'dev/calendar-resources' - 3 commits - plugins/calendar

Thomas Brüderli bruederli at kolabsys.com
Tue Mar 11 10:15:01 CET 2014


 dev/null                                                |binary
 plugins/calendar/calendar.php                           |   67 +++++--
 plugins/calendar/calendar_ui.js                         |   84 ++++++---
 plugins/calendar/config.inc.php.dist                    |    7 
 plugins/calendar/drivers/calendar_driver.php            |   51 -----
 plugins/calendar/drivers/kolab/kolab_driver.php         |  107 -----------
 plugins/calendar/drivers/ldap/resources_driver_ldap.php |  146 ++++++++++++++++
 plugins/calendar/drivers/resources_driver.php           |   58 ++++++
 plugins/calendar/lib/calendar_ui.php                    |    4 
 plugins/calendar/skins/larry/calendar.css               |   23 +-
 plugins/calendar/skins/larry/images/attendee-status.png |binary
 plugins/calendar/skins/larry/templates/calendar.html    |    9 
 12 files changed, 337 insertions(+), 219 deletions(-)

New commits:
commit 7fe882108a4d2b4be0c7d7b62bc09f777aaf8e80
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Tue Mar 11 10:14:49 2014 +0100

    Display resource information on click to resource-type attendee; Fix css class update

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 986ea8c..86cd6b0 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -286,7 +286,7 @@ function rcube_calendar_ui(settings)
     // event details dialog (show only)
     var event_show_dialog = function(event)
     {
-      var $dialog = $("#eventshow").removeClass().addClass('uidialog');
+      var $dialog = $("#eventshow").attr('class', 'uidialog');
       var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { editable:false };
       me.selected_event = event;
 
@@ -314,9 +314,9 @@ function rcube_calendar_ui(settings)
         $('#event-alarm').show().children('.event-text').html(Q(event.alarms_text));
       
       if (calendar.name)
-        $('#event-calendar').show().children('.event-text').html(Q(calendar.name)).removeClass().addClass('event-text').addClass('cal-'+calendar.id);
+        $('#event-calendar').show().children('.event-text').html(Q(calendar.name)).attr('class', 'event-text').addClass('cal-'+calendar.id);
       if (event.categories)
-        $('#event-category').show().children('.event-text').html(Q(event.categories)).removeClass().addClass('event-text cat-'+String(event.categories).toLowerCase().replace(rcmail.identifier_expr, ''));
+        $('#event-category').show().children('.event-text').html(Q(event.categories)).attr('class', 'event-text cat-'+String(event.categories).toLowerCase().replace(rcmail.identifier_expr, ''));
       if (event.free_busy)
         $('#event-free-busy').show().children('.event-text').html(Q(rcmail.gettext(event.free_busy, 'calendar')));
       if (event.priority > 0) {
@@ -354,7 +354,7 @@ function rcube_calendar_ui(settings)
           data = event.attendees[j];
           dispname = Q(data.name || data.email);
           if (data.email) {
-            dispname = '<a href="mailto:' + data.email + '" title="' + Q(data.email) + '" class="mailtolink">' + dispname + '</a>';
+            dispname = '<a href="mailto:' + data.email + '" title="' + Q(data.email) + '" class="mailtolink" data-cutype="' + data.cutype + '">' + dispname + '</a>';
             if (data.role == 'ORGANIZER')
               organizer = true;
             else if ((data.status == 'NEEDS-ACTION' || data.status == 'TENTATIVE' || data.rsvp) && settings.identity.emails.indexOf(';'+data.email) >= 0)
@@ -377,7 +377,7 @@ function rcube_calendar_ui(settings)
           $('#event-attendees').show()
             .children('.event-text')
             .html(html)
-            .find('a.mailtolink').click(function(e) { rcmail.redirect(rcmail.url('mail/compose', { _to:this.href.substr(7) })); return false; });
+            .find('a.mailtolink').click(event_attendee_click);
 
           // display all attendees in a popup when clicking the "more" link
           if (morelink) {
@@ -388,7 +388,7 @@ function rcube_calendar_ui(settings)
                 rcmail.gettext('tabattendees','calendar'),
                 null,
                 { width:450, modal:false });
-              $('#all-event-attendees a.mailtolink').click(function(e) { rcmail.redirect(rcmail.url('mail/compose', { _to:this.href.substr(7) })); return false; });
+              $('#all-event-attendees a.mailtolink').click(event_attendee_click);
               return false;
             })
           }
@@ -444,6 +444,20 @@ function rcube_calendar_ui(settings)
 */
     };
 
+    // event handler for clicks on an attendee link
+    var event_attendee_click = function(e)
+    {
+      var cutype = $(this).attr('data-cutype'),
+        mailto = this.href.substr(7);
+      if (rcmail.env.calendar_resources && cutype == 'RESOURCE') {
+        event_resources_dialog(mailto);
+      }
+      else {
+        rcmail.redirect(rcmail.url('mail/compose', { _to:mailto }));
+      }
+      return false;
+    };
+
     // bring up the event dialog (jquery-ui popup)
     var event_edit_dialog = function(action, event)
     {
@@ -885,7 +899,7 @@ function rcube_calendar_ui(settings)
               var j = $.inArray(attendee.role, roles);
               j = (j+1) % roles.length;
               attendee.role = roles[j];
-              $(e.target).parent().removeClass().addClass('attendee '+String(attendee.role).toLowerCase());
+              $(e.target).parent().attr('class', 'attendee '+String(attendee.role).toLowerCase());
               
               // update total display if required-status changed
               if (req != (roles[j] != 'OPT-PARTICIPANT' && roles[j] != 'NON-PARTICIPANT')) {
@@ -1481,7 +1495,7 @@ function rcube_calendar_ui(settings)
 
       var dispname = Q(data.name || data.email);
       if (data.email)
-        dispname = '<a href="mailto:' + data.email + '" title="' + Q(data.email) + '" class="mailtolink">' + dispname + '</a>';
+        dispname = '<a href="mailto:' + data.email + '" title="' + Q(data.email) + '" class="mailtolink" data-cutype="' + data.cutype + '">' + dispname + '</a>';
       
       // role selection
       var organizer = data.role == 'ORGANIZER';
@@ -1523,7 +1537,7 @@ function rcube_calendar_ui(settings)
         .appendTo(table);
 
       tr.find('a.deletelink').click({ id:(data.email || data.name) }, function(e) { remove_attendee(this, e.data.id); return false; });
-      tr.find('a.mailtolink').click(function(e) { rcmail.redirect(rcmail.url('mail/compose', { _to:this.href.substr(7) })); return false; });
+      tr.find('a.mailtolink').click(event_attendee_click);
 
       // select organizer identity
       if (data.identity_id)
@@ -1555,11 +1569,11 @@ function rcube_calendar_ui(settings)
     {
       var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { freebusy:false };
       if (!calendar.freebusy) {
-        $(icon).removeClass().addClass('availabilityicon unknown');
+        $(icon).attr('class', 'availabilityicon unknown');
         return;
       }
       
-      icon = $(icon).removeClass().addClass('availabilityicon loading');
+      icon = $(icon).attr('class', 'availabilityicon loading');
       
       $.ajax({
         type: 'GET',
@@ -1583,10 +1597,9 @@ function rcube_calendar_ui(settings)
     };
 
     // open a dialog to display detailed free-busy information and to find free slots
-    var event_resources_dialog = function()
+    var event_resources_dialog = function(search)
     {
-      var $dialog = $('#eventresourcesdialog'),
-        event = me.selected_event;
+      var $dialog = $('#eventresourcesdialog');
 
       if ($dialog.is(':ui-dialog'))
         $dialog.dialog('close');
@@ -1621,6 +1634,9 @@ function rcube_calendar_ui(settings)
 
       me.dialog_resize($dialog.get(0), 540, Math.min(1000, $(window).width() - 50));
 
+      // set search query
+      $('#resourcesearchbox').val(search || '');
+
       // initialize the treelist widget
       if (!resources_treelist) {
         resources_treelist = new rcube_treelist_widget(rcmail.gui_objects.resourceslist, {
@@ -1632,7 +1648,7 @@ function rcube_calendar_ui(settings)
         resources_treelist.addEventListener('select', function(node) {
           if (resources_data[node.id]) {
             resource_showinfo(resources_data[node.id]);
-            rcmail.enable_command('add-resource', me.selected_event ? true : false);
+            rcmail.enable_command('add-resource', me.selected_event && $("#eventedit").is(':visible') ? true : false);
           }
           else {
             rcmail.enable_command('add-resource', false);
@@ -1650,6 +1666,8 @@ function rcube_calendar_ui(settings)
       }
       else {
         resources_treelist.select('__none__');
+        if (search)
+          resource_search();
       }
     };
 
@@ -1717,7 +1735,13 @@ function rcube_calendar_ui(settings)
       });
 
       resources_index.reverse();
-      resource_render_list(resources_index);
+
+      // apply search filter...
+      if ($('#resourcesearchbox').val() != '')
+        resource_search();
+      else  // ...or render full list
+        resource_render_list(resources_index);
+
       rcmail.set_busy(false, null, me.loading_lock);
     };
 
@@ -1752,7 +1776,7 @@ function rcube_calendar_ui(settings)
             continue;
 
           table.append($('<tr>').addClass(k)
-            .append('<td class="title">' + Q(ucfirst(rcmail.get_label('owner'+k, 'calendar'))) + '</td>')
+            .append('<td class="title">' + Q(ucfirst(rcmail.get_label(k, 'calendar'))) + '</td>')
             .append('<td class="value">' + text2html(data[k]) + '</td>')
           );
         }
@@ -1771,7 +1795,7 @@ function rcube_calendar_ui(settings)
         // search by iterating over all resource records
         for (var dn in resources_data) {
           rec = resources_data[dn];
-          if (String(rec.name).toLowerCase().indexOf(q) >= 0) {
+          if (String(rec.name).toLowerCase().indexOf(q) >= 0 || String(rec.email).toLowerCase() == q) {
             dataset.push(rec.ID);
           }
         }
@@ -2634,6 +2658,11 @@ function rcube_calendar_ui(settings)
 
     /***  startup code  ***/
 
+    // destroy wrongly configured treelist widget for the calendars list
+    if (rcmail.gui_objects.folderlist && rcmail.treelist) {
+      rcmail.treelist = null;
+    }
+
     // create list of event sources AKA calendars
     this.calendars = {};
     var id, li, cal, active, color, brightness, event_sources = [];
@@ -2705,6 +2734,9 @@ function rcube_calendar_ui(settings)
     if (settings.default_calendar && this.calendars[settings.default_calendar] && !this.calendars[settings.default_calendar].readonly)
       this.selected_calendar = settings.default_calendar;
     
+    if (this.selected_calendar)
+      rcmail.select_folder(this.selected_calendar, 'rcmlical');
+    
     var viewdate = new Date();
     if (rcmail.env.date)
       viewdate.setTime(fromunixtime(rcmail.env.date));
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 14bc422..127456f 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -797,6 +797,9 @@ class calendar_ui
     $this->rc->output->add_gui_object('resourceinfo', $attrib['id']);
     $this->rc->output->add_gui_object('resourceownerinfo', $attrib['id'] . '-owner');
 
+    // copy address book labels for owner details to client
+    $this->rc->output->add_label('name','firstname','surname','department','jobtitle','email','phone','address');
+
     $table_attrib = array('id','class','style','width','summary','cellpadding','cellspacing','border');
 
     return html::tag('table', $attrib,


commit 0b07e01b151ce3c1dbafa07d073fce9f36127ce0
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Tue Mar 11 10:13:14 2014 +0100

    Add icon for role NON-PARTICIPANT

diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index 7efe868..1806467 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -441,7 +441,7 @@ a.miniColors-trigger {
 .event-attendees span.attendee {
 	padding-right: 18px;
 	margin-right: 0.5em;
-	background: url(images/attendee-status.gif) right 0 no-repeat;
+	background: url(images/attendee-status.png) right 0 no-repeat;
 }
 
 .event-attendees span.attendee a.mailtolink {
@@ -467,7 +467,7 @@ a.miniColors-trigger {
 }
 
 .event-attendees span.delegated {
-	background-position: right -160px;
+	background-position: right -180px;
 }
 
 .event-attendees span.organizer {
@@ -789,7 +789,7 @@ td.topalign {
 .edit-attendees-table td.confirmstate span {
 	display: block;
 	width: 20px;
-	background: url(images/attendee-status.gif) 5px 0 no-repeat;
+	background: url(images/attendee-status.png) 5px 0 no-repeat;
 }
 
 .edit-attendees-table td.confirmstate span.needs-action {
@@ -808,7 +808,7 @@ td.topalign {
 }
 
 .edit-attendees-table td.confirmstate span.delegated {
-	background-position: 5px -160px;
+	background-position: 5px -180px;
 }
 
 #attendees-freebusy-table {
@@ -840,7 +840,7 @@ td.topalign {
 
 .attendees-list .attendee {
 	padding: 4px 4px 4px 1px;
-	background: url(images/attendee-status.gif) 2px -97px no-repeat;
+	background: url(images/attendee-status.png) 2px -97px no-repeat;
 	white-space: nowrap;
 }
 
@@ -868,10 +868,14 @@ td.topalign {
 	background-position: 2px -117px;
 }
 
-.attendees-list .chair {
+.attendees-list .non-participant {
 	background-position: 2px -137px;
 }
 
+.attendees-list .chair {
+	background-position: 2px -157px;
+}
+
 .attendees-list .loading {
 	background: url(images/loading_blue.gif) 1px 50% no-repeat;
 }
@@ -1519,9 +1523,10 @@ div.calendar-invitebox .rsvp-status.loading {
 
 div.calendar-invitebox .rsvp-status.declined,
 div.calendar-invitebox .rsvp-status.tentative,
+div.calendar-invitebox .rsvp-status.delegated,
 div.calendar-invitebox .rsvp-status.accepted {
 	padding: 0 0 1px 22px;
-	background: url(images/attendee-status.gif) 2px -20px no-repeat;
+	background: url(images/attendee-status.png) 2px -20px no-repeat;
 }
 
 div.calendar-invitebox .rsvp-status.declined {
@@ -1532,6 +1537,10 @@ div.calendar-invitebox .rsvp-status.tentative {
 	background-position: 2px -60px;
 }
 
+div.calendar-invitebox .rsvp-status.delegated {
+	background-position: 2px -180px;
+}
+
 /* iTIP attend reply page */
 
 .calendaritipattend .centerbox {
diff --git a/plugins/calendar/skins/larry/images/attendee-status.gif b/plugins/calendar/skins/larry/images/attendee-status.gif
deleted file mode 100644
index 60c5d95..0000000
Binary files a/plugins/calendar/skins/larry/images/attendee-status.gif and /dev/null differ
diff --git a/plugins/calendar/skins/larry/images/attendee-status.png b/plugins/calendar/skins/larry/images/attendee-status.png
new file mode 100644
index 0000000..59b4493
Binary files /dev/null and b/plugins/calendar/skins/larry/images/attendee-status.png differ
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
index 96be36e..c189491 100644
--- a/plugins/calendar/skins/larry/templates/calendar.html
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -182,6 +182,7 @@
 		<span class="attendee organizer"><roundcube:label name="calendar.roleorganizer" /></span>
 		<span class="attendee req-participant"><roundcube:label name="calendar.rolerequired" /></span>
 		<span class="attendee opt-participant"><roundcube:label name="calendar.roleoptional" /></span>
+		<span class="attendee non-participant"><roundcube:label name="calendar.rolenonparticipant" /></span>
 		<span class="attendee chair"><roundcube:label name="calendar.rolechair" /></span>
 	</div>
 </div>


commit 51fe7c26fb93ed201551ca44ffd590ad93424f8d
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Mon Mar 10 14:45:24 2014 +0100

    Refactored resources directory to be an individual part and not dependant on a specific calendar backend driver

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 152cc53..365ce9e 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -38,6 +38,7 @@ class calendar extends rcube_plugin
   public $rc;
   public $lib;
   public $driver;
+  public $resources_dir;
   public $home;  // declare public to be used in other classes
   public $urlbase;
   public $timezone;
@@ -211,16 +212,10 @@ class calendar extends rcube_plugin
     require_once($this->home . '/drivers/calendar_driver.php');
     require_once($this->home . '/drivers/' . $driver_name . '/' . $driver_class . '.php');
 
-    switch ($driver_name) {
-      case "kolab":
-        $this->require_plugin('libkolab');
-      default:
-        $this->driver = new $driver_class($this);
-        break;
-     }
+    $this->driver = new $driver_class($this);
 
-     if ($this->driver->undelete)
-        $this->driver->undelete = $this->rc->config->get('undo_timeout', 0) > 0;
+    if ($this->driver->undelete)
+      $this->driver->undelete = $this->rc->config->get('undo_timeout', 0) > 0;
   }
 
   /**
@@ -297,7 +292,7 @@ class calendar extends rcube_plugin
 
     $this->rc->output->set_env('timezone', $this->timezone->getName());
     $this->rc->output->set_env('calendar_driver', $this->rc->config->get('calendar_driver'), false);
-    $this->rc->output->set_env('resources', (bool)$this->driver->resources);
+    $this->rc->output->set_env('calendar_resources', (bool)$this->rc->config->get('calendar_resources_driver'));
     $this->rc->output->set_env('mscolors', $this->driver->get_color_values());
     $this->rc->output->set_env('identities-selector', $this->ui->identity_select(array('id' => 'edit-identities-list')));
 
@@ -1936,6 +1931,30 @@ class calendar extends rcube_plugin
 
   /****  Resource management functions  ****/
 
+  /**
+   * Getter for the configured implementation of the resource directory interface
+   */
+  private function resources_directory()
+  {
+    if (is_object($this->resources_dir)) {
+      return $this->resources_dir;
+    }
+
+    if ($driver_name = $this->rc->config->get('calendar_resources_driver')) {
+      $driver_class = 'resources_driver_' . $driver_name;
+
+      require_once($this->home . '/drivers/resources_driver.php');
+      require_once($this->home . '/drivers/' . $driver_name . '/' . $driver_class . '.php');
+
+      $this->resources_dir = new $driver_class($this);
+    }
+
+    return $this->resources_dir;
+  }
+
+  /**
+   * Handler for resoruce autocompletion requests
+   */
   public function resources_autocomplete()
   {
     $search = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GPC, true);
@@ -1943,12 +1962,14 @@ class calendar extends rcube_plugin
     $maxnum = (int)$this->rc->config->get('autocomplete_max', 15);
     $results = array();
 
-    foreach ($this->driver->load_resources($search, $maxnum) as $rec) {
-      $results[]  = array(
-          'name'  => $rec['name'],
-          'email' => $rec['email'],
-          'type'  => $rec['_type'],
-      );
+    if ($directory = $this->resources_directory()) {
+      foreach ($directory->load_resources($search, $maxnum) as $rec) {
+        $results[]  = array(
+            'name'  => $rec['name'],
+            'email' => $rec['email'],
+            'type'  => $rec['_type'],
+        );
+      }
     }
 
     $this->rc->output->command('ksearch_query_results', $results, $search, $sid);
@@ -1961,9 +1982,11 @@ class calendar extends rcube_plugin
   function resources_list()
   {
     $data = array();
-    foreach ($this->driver->load_resources() as $rec) {
-      $rec['dn'] = rcube_ldap::dn_decode($rec['ID']);
-      $data[] = $rec;
+
+    if ($directory = $this->resources_directory()) {
+      foreach ($directory->load_resources() as $rec) {
+        $data[] = $rec;
+      }
     }
 
     $this->rc->output->command('plugin.resource_data', $data);
@@ -1975,8 +1998,10 @@ class calendar extends rcube_plugin
    */
   function resources_owner()
   {
-    $id = rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC);
-    $data = $this->driver->get_resource_owner($id);
+    if ($directory = $this->resources_directory()) {
+      $id = rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC);
+      $data = $directory->get_resource_owner($id);
+    }
 
     $this->rc->output->command('plugin.resource_owner', $data);
     $this->rc->output->send();
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index f39c4c0..986ea8c 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -762,7 +762,7 @@ function rcube_calendar_ui(settings)
 
       // show/hide tabs according to calendar's feature support
       $('#edit-tab-attendees')[(calendar.attendees?'show':'hide')]();
-      $('#edit-tab-resources')[(calendar.resources?'show':'hide')]();
+      $('#edit-tab-resources')[(rcmail.env.calendar_resources?'show':'hide')]();
       $('#edit-tab-attachments')[(calendar.attachments?'show':'hide')]();
 
       // activate the first tab
@@ -1516,7 +1516,7 @@ function rcube_calendar_ui(settings)
         '<td class="confirmstate"><span class="' + String(data.status).toLowerCase() + '" title="' + Q(data.status || '') + '">' + Q(data.status || '') + '</span></td>' +
         '<td class="options">' + (organizer || readonly ? '' : dellink) + '</td>';
 
-      var table = calendar.resources && data.cutype == 'RESOURCE' ? resources_list : attendees_list;
+      var table = rcmail.env.calendar_resources && data.cutype == 'RESOURCE' ? resources_list : attendees_list;
       var tr = $('<tr>')
         .addClass(String(data.role).toLowerCase())
         .html(html)
@@ -1706,12 +1706,12 @@ function rcube_calendar_ui(settings)
 
       // assign parent-relations
       $.each(data, function(i, rec) {
-        resources_data[rec.dn] = rec;
-        resources_index.push(rec.dn);
+        resources_data[rec.ID] = rec;
+        resources_index.push(rec.ID);
 
         if (rec.members) {
           $.each(rec.members, function(j, m){
-            resources_data[m].parent_id = rec.dn;
+            resources_data[m].parent_id = rec.ID;
           });
         }
       });
@@ -1730,10 +1730,10 @@ function rcube_calendar_ui(settings)
       $.each(index, function(i, dn) {
         if (rec = resources_data[dn]) {
           link = $('<a>').attr('href', '#')
-            .attr('rel', rec.dn)
+            .attr('rel', rec.ID)
             .html(Q(rec.name));
 
-          resources_treelist.insert({ id:rec.dn, html:link, classes:[rec._type], collapsed:true }, rec.parent_id, false);
+          resources_treelist.insert({ id:rec.ID, html:link, classes:[rec._type], collapsed:true }, rec.parent_id, false);
         }
       });
     };
@@ -1772,7 +1772,7 @@ function rcube_calendar_ui(settings)
         for (var dn in resources_data) {
           rec = resources_data[dn];
           if (String(rec.name).toLowerCase().indexOf(q) >= 0) {
-            dataset.push(rec.dn);
+            dataset.push(rec.ID);
           }
         }
 
diff --git a/plugins/calendar/config.inc.php.dist b/plugins/calendar/config.inc.php.dist
index 9a472a7..f09d30f 100644
--- a/plugins/calendar/config.inc.php.dist
+++ b/plugins/calendar/config.inc.php.dist
@@ -135,4 +135,11 @@ $rcmail_config['calendar_itip_smtp_pass'] = '123456';
 // %i - Calendar UUID
 // $rcmail_config['calendar_caldav_url'] = 'http://%h/iRony/calendars/%u/%i';
 
+// Driver to provide a resource directory ('ldap' is the only implementation yet).
+// Leave empty or commented to disable resources support.
+// $rcmail_config['calendar_resources_driver'] = 'ldap';
+
+// LDAP directory configuration to find avilable resources for events
+// $rcmail_config['calendar_resources_directory'] = array(/* ldap_public-like address book configuration */)
+
 ?>
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index 3d8d083..20f9f16 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -86,7 +86,6 @@ abstract class calendar_driver
   // features supported by backend
   public $alarms = false;
   public $attendees = false;
-  public $resources = false;
   public $freebusy = false;
   public $attachments = false;
   public $undelete = false; // event undelete action
@@ -530,54 +529,4 @@ abstract class calendar_driver
     return $events;
   }
 
-  /**
-   * Store alarm dismissal for birtual birthay events
-   *
-   * @param  string  Event identifier
-   * @param  integer Suspend the alarm for this number of seconds
-   */
-  public function dismiss_birthday_alarm($event_id, $snooze = 0)
-  {
-    $rcmail = rcmail::get_instance();
-    $cache  = $rcmail->get_cache('calendar.birthdayalarms', 'db', 86400 * 30);
-    $cache->remove($event_id);
-
-    // compute new notification time or disable if not snoozed
-    $notifyat = $snooze > 0 ? time() + $snooze : null;
-    $cache->set($event_id, array('snooze' => $snooze, 'notifyat' => $notifyat));
-
-    return true;
-  }
-
-
-  /**
-   * Fetch resource objects to be displayed for booking
-   *
-   * @param  string  Search query (optional)
-   * @return array  List of resource records available for booking
-   */
-  public function load_resources($query = null)
-  {
-    return array();
-  }
-
-  /**
-   * Return properties of a single resource
-   *
-   * @param mixed  UID string
-   * @return array Resource object as hash array
-   */
-  public function get_resource($uid)
-  {
-    return null;
-  }
-
-  /**
-   *
-   */
-  public function get_resource_owner($id)
-  {
-    return null;
-  }
-
 }
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 1bf4e17..0bcab59 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -47,6 +47,8 @@ class kolab_driver extends calendar_driver
    */
   public function __construct($cal)
   {
+    $cal->require_plugin('libkolab');
+
     $this->cal = $cal;
     $this->rc = $cal->rc;
     $this->_read_calendars();
@@ -60,10 +62,6 @@ class kolab_driver extends calendar_driver
         $this->alarm_types = array('DISPLAY');
         $this->alarm_absolute = false;
     }
-
-    if ($this->rc->config->get('calendar_resources_directory')) {
-      $this->resources = true;
-    }
   }
 
 
@@ -1287,105 +1285,4 @@ class kolab_driver extends calendar_driver
         'FFDEAD');
   }
 
-
-  private function resurces_ldap()
-  {
-    if (!isset($this->resources_dir)) {
-      $this->resources_dir = new rcube_ldap($this->rc->config->get('calendar_resources_directory'), true);
-    }
-
-    return $this->resources_dir->ready ? $this->resources_dir : null;
-  }
-
-
-  /**
-   * Fetch resource objects to be displayed for booking
-   *
-   * @param  string  Search query (optional)
-   * @return array  List of resource records available for booking
-   */
-  public function load_resources($query = null, $num = 5000)
-  {
-    if (!($ldap = $this->resurces_ldap())) {
-      return array();
-    }
-
-    // TODO: apply paging
-    $ldap->set_pagesize($num);
-
-    if (isset($query)) {
-      $results = $ldap->search('*', $query, 0, true, true);
-    }
-    else {
-      $results = $ldap->list_records();
-    }
-
-    if ($results instanceof ArrayAccess) {
-      foreach ($results as $i => $rec) {
-        $results[$i] = $this->decode_resource($rec);
-      }
-    }
-
-    return $results;
-  }
-
-  /**
-   * Return properties of a single resource
-   *
-   * @param mixed  UID string
-   * @return array Resource object as hash array
-   */
-  public function get_resource($uid)
-  {
-    $rec = null;
-
-    if ($ldap = $this->resurces_ldap()) {
-      $rec = $ldap->get_record($uid);
-
-      if (!empty($rec)) {
-        $rec = $this->decode_resource($rec);
-      }
-    }
-
-    return $rec;
-  }
-
-  /**
-   *
-   */
-  public function get_resource_owner($dn)
-  {
-    $owner = null;
-
-    if ($ldap = $this->resurces_ldap()) {
-      $owner = $ldap->get_record(rcube_ldap::dn_encode($dn), true);
-      $owner['ID'] = rcube_ldap::dn_decode($owner['ID']);
-      unset($owner['_raw_attrib'], $owner['_type']);
-    }
-
-    return $owner;
-  }
-
-  /**
-   * Extract JSON-serialized attributes
-   */
-  private function decode_resource($rec)
-  {
-    if (is_array($rec['attributes']) && $rec['attributes'][0]) {
-      $attributes = array();
-
-      foreach ($rec['attributes'] as $sattr) {
-        $attr = @json_decode($sattr, true);
-        $attributes += $attr;
-      }
-
-      $rec['attributes'] = $attributes;
-    }
-
-    // remove unused cruft
-    unset($rec['_raw_attrib']);
-
-    return $rec;
-  }
-
 }
diff --git a/plugins/calendar/drivers/ldap/resources_driver_ldap.php b/plugins/calendar/drivers/ldap/resources_driver_ldap.php
new file mode 100644
index 0000000..23439b6
--- /dev/null
+++ b/plugins/calendar/drivers/ldap/resources_driver_ldap.php
@@ -0,0 +1,146 @@
+<?php
+
+/**
+ * LDAP-based resource directory class using rcube_ldap functionality
+ *
+ * @author Thomas Bruederli <bruederli at kolabsys.com>
+ *
+ * Copyright (C) 2014, Kolab Systems AG <contact at kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * LDAP-based resource directory implementation
+ */
+class resources_driver_ldap extends resources_driver
+{
+    private $rc;
+    private $cal;
+    private $ldap;
+
+    /**
+     * Default constructor
+     */
+    function __construct($cal)
+    {
+        $this->cal = $cal;
+        $this->rc = $cal->rc;
+    }
+
+    /**
+     * Fetch resource objects to be displayed for booking
+     *
+     * @param  string  Search query (optional)
+     * @return array  List of resource records available for booking
+     */
+    public function load_resources($query = null, $num = 5000)
+    {
+      if (!($ldap = $this->connect())) {
+        return array();
+      }
+
+      // TODO: apply paging
+      $ldap->set_pagesize($num);
+
+      if (isset($query)) {
+        $results = $ldap->search('*', $query, 0, true, true);
+      }
+      else {
+        $results = $ldap->list_records();
+      }
+
+      if ($results instanceof ArrayAccess) {
+        foreach ($results as $i => $rec) {
+          $results[$i] = $this->decode_resource($rec);
+        }
+      }
+
+      return $results;
+    }
+
+    /**
+     * Return properties of a single resource
+     *
+     * @param string  Unique resource identifier
+     * @return array Resource object as hash array
+     */
+    public function get_resource($dn)
+    {
+      $rec = null;
+
+      if ($ldap = $this->connect()) {
+        $rec = $ldap->get_record(rcube_ldap::dn_encode($dn));
+
+        if (!empty($rec)) {
+          $rec = $this->decode_resource($rec);
+        }
+      }
+
+      return $rec;
+    }
+
+    /**
+     * Return properties of a resource owner
+     *
+     * @param string  Owner identifier
+     * @return array  Resource object as hash array
+     */
+    public function get_resource_owner($dn)
+    {
+      $owner = null;
+
+      if ($ldap = $this->connect()) {
+        $owner = $ldap->get_record(rcube_ldap::dn_encode($dn), true);
+        $owner['ID'] = rcube_ldap::dn_decode($owner['ID']);
+        unset($owner['_raw_attrib'], $owner['_type']);
+      }
+
+      return $owner;
+    }
+
+    /**
+     * Extract JSON-serialized attributes
+     */
+    private function decode_resource($rec)
+    {
+      $rec['ID'] = rcube_ldap::dn_decode($rec['ID']);
+
+      if (is_array($rec['attributes']) && $rec['attributes'][0]) {
+        $attributes = array();
+
+        foreach ($rec['attributes'] as $sattr) {
+          $attr = @json_decode($sattr, true);
+          $attributes += $attr;
+        }
+
+        $rec['attributes'] = $attributes;
+      }
+
+      // remove unused cruft
+      unset($rec['_raw_attrib']);
+
+      return $rec;
+    }
+
+    private function connect()
+    {
+      if (!isset($this->ldap)) {
+        $this->ldap = new rcube_ldap($this->rc->config->get('calendar_resources_directory'), true);
+      }
+
+      return $this->ldap->ready ? $this->ldap : null;
+    }
+
+}
\ No newline at end of file
diff --git a/plugins/calendar/drivers/resources_driver.php b/plugins/calendar/drivers/resources_driver.php
new file mode 100644
index 0000000..b1fed9c
--- /dev/null
+++ b/plugins/calendar/drivers/resources_driver.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * Resources directory interface definition
+ *
+ * @author Thomas Bruederli <bruederli at kolabsys.com>
+ *
+ * Copyright (C) 2014, Kolab Systems AG <contact at kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/**
+ * Interface definition for a resources directory driver classe
+ */
+abstract class resources_driver
+{
+
+  /**
+   * Fetch resource objects to be displayed for booking
+   *
+   * @param  string  Search query (optional)
+   * @return array  List of resource records available for booking
+   */
+  abstract public function load_resources($query = null);
+
+  /**
+   * Return properties of a single resource
+   *
+   * @param string  Unique resource identifier
+   * @return array  Resource object as hash array
+   */
+  abstract public function get_resource($id);
+
+  /**
+   * Return properties of a resource owner
+   *
+   * @param string  Owner identifier
+   * @return array  Resource object as hash array
+   */
+  public function get_resource_owner($id)
+  {
+    return null;
+  }
+
+}
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 710898f..14bc422 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -196,7 +196,6 @@ class calendar_ui
       unset($prop['user_id']);
       $prop['alarms'] = $this->cal->driver->alarms;
       $prop['attendees'] = $this->cal->driver->attendees;
-      $prop['resources'] = $this->cal->driver->resources;
       $prop['freebusy'] = $this->cal->driver->freebusy;
       $prop['attachments'] = $this->cal->driver->attachments;
       $prop['undelete'] = $this->cal->driver->undelete;
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
index 574cded..96be36e 100644
--- a/plugins/calendar/skins/larry/templates/calendar.html
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -126,14 +126,6 @@
 			<div class="scroller">
 				<roundcube:object name="plugin.resources_list" id="resources-list" class="listing treelist" />
 			</div>
-		<!--
-			<div class="boxpagenav">
-				<roundcube:button command="firstpage" type="link" class="icon firstpage disabled" classAct="icon firstpage" title="firstpage" content="|&lt;" />
-				<roundcube:button command="previouspage" type="link" class="icon prevpage disabled" classAct="icon prevpage" title="previouspage" content="&lt;" />
-				<roundcube:button command="nextpage" type="link" class="icon nextpage disabled" classAct="icon nextpage" title="nextpage" content="&gt;" />
-				<roundcube:button command="lastpage" type="link" class="icon lastpage disabled" classAct="icon lastpage" title="lastpage" content="&gt;|" />
-			</div>
-		-->
 		</div>
 	</div>
 




More information about the commits mailing list