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

Thomas Brüderli bruederli at kolabsys.com
Mon Mar 17 17:29:20 CET 2014


 plugins/calendar/calendar.php                           |   19 +++++
 plugins/calendar/calendar_ui.js                         |   52 ++++++++++++++
 plugins/calendar/drivers/ldap/resources_driver_ldap.php |    3 
 plugins/calendar/drivers/resources_driver.php           |   58 +++++++++++++++-
 plugins/calendar/lib/calendar_ui.php                    |   13 +++
 plugins/calendar/skins/classic/calendar.css             |   39 ++++++++++
 plugins/calendar/skins/classic/templates/calendar.html  |    2 
 plugins/calendar/skins/larry/calendar.css               |   38 +++++++++-
 plugins/calendar/skins/larry/templates/calendar.html    |    6 +
 9 files changed, 221 insertions(+), 9 deletions(-)

New commits:
commit 0946cc37a420c3698228e0157aaad7e2fa343048
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Mon Mar 17 17:29:12 2014 +0100

    Display resource's availability in a small calendar widget. Data is derived from the resource free/busy data

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 365ce9e..c549840 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -144,6 +144,7 @@ class calendar extends rcube_plugin
       $this->register_action('check-recent', array($this, 'check_recent'));
       $this->register_action('resources-list', array($this, 'resources_list'));
       $this->register_action('resources-owner', array($this, 'resources_owner'));
+      $this->register_action('resources-calendar', array($this, 'resources_calendar'));
       $this->register_action('resources-autocomplete', array($this, 'resources_autocomplete'));
       $this->add_hook('refresh', array($this, 'refresh'));
 
@@ -2007,6 +2008,24 @@ class calendar extends rcube_plugin
     $this->rc->output->send();
   }
 
+  /**
+   * Deliver event data for a resource's calendar
+   */
+  function resources_calendar()
+  {
+    $events = array();
+
+    if ($directory = $this->resources_directory()) {
+      $events = $directory->get_resource_calendar(
+        rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC),
+        rcube_utils::get_input_value('start', RCUBE_INPUT_GET),
+        rcube_utils::get_input_value('end', RCUBE_INPUT_GET));
+    }
+
+    echo $this->encode($events);
+    exit;
+  }
+
 
   /****  Event invitation plugin hooks ****/
   
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index f0440d9..9d03987 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -52,6 +52,7 @@ function rcube_calendar_ui(settings)
     var resources_data = {};
     var resources_index = [];
     var resource_owners = {};
+    var resources_events_source = { url:null, editable:false };
     var freebusy_ui = { workinhoursonly:false, needsupdate:false };
     var freebusy_data = {};
     var current_view = null;
@@ -1624,6 +1625,10 @@ function rcube_calendar_ui(settings)
         close: function() {
           $dialog.dialog('destroy').hide();
         },
+        resize: function(e) {
+          var container = $(rcmail.gui_objects.resourceinfocalendar)
+          container.fullCalendar('option', 'height', container.height() + 4);
+        },
         buttons: buttons,
         width: 900,
         height: 500
@@ -1654,6 +1659,7 @@ function rcube_calendar_ui(settings)
             rcmail.enable_command('add-resource', false);
             $(rcmail.gui_objects.resourceinfo).hide();
             $(rcmail.gui_objects.resourceownerinfo).hide();
+            $(rcmail.gui_objects.resourceinfocalendar).fullCalendar('removeEventSource', resources_events_source);
           }
         });
 
@@ -1663,6 +1669,43 @@ function rcube_calendar_ui(settings)
 
         // register button
         rcmail.register_button('add-resource', 'rcmbtncalresadd', 'uibutton');
+
+        // initialize resource calendar display
+        var resource_cal = $(rcmail.gui_objects.resourceinfocalendar);
+        resource_cal.fullCalendar({
+          header: { left: '', center: '', right: '' },
+          height: resource_cal.height() + 4,
+          defaultView: 'agendaWeek',
+          ignoreTimezone: true,
+          eventSources: [],
+          monthNames: settings['months'],
+          monthNamesShort: settings['months_short'],
+          dayNames: settings['days'],
+          dayNamesShort : settings['days_short'],
+          firstDay: settings['first_day'],
+          firstHour: settings['first_hour'],
+          slotMinutes: 60,
+          allDaySlot: false,
+          timeFormat: { '': settings['time_format'] },
+          axisFormat: settings['time_format'],
+          columnFormat: { day: 'dddd ' + settings['date_short'] },
+          titleFormat: { day: 'dddd ' + settings['date_long'] },
+          currentTimeIndicator: settings.time_indicator,
+          eventRender: function(event, element, view) {
+            element.addClass('status-' + event.status);
+            element.find('.fc-event-head').hide();
+            element.find('.fc-event-title').text(rcmail.get_label(event.status, 'calendar'));
+          }
+        });
+
+        $('#resource-calendar-prev').click(function(){
+          resource_cal.fullCalendar('prev');
+          return false;
+        });
+        $('#resource-calendar-next').click(function(){
+          resource_cal.fullCalendar('next');
+          return false;
+        });
       }
       else if (search) {
         resource_search();
@@ -1670,6 +1713,10 @@ function rcube_calendar_ui(settings)
       else {
         resource_render_list(resources_index);
       }
+
+      if (me.selected_event && me.selected_event.start) {
+        $(rcmail.gui_objects.resourceinfocalendar).fullCalendar('gotoDate', me.selected_event.start);
+      }
     };
 
     // render the resource details UI box
@@ -1699,6 +1746,7 @@ function rcube_calendar_ui(settings)
         }
 
         $(rcmail.gui_objects.resourceownerinfo).hide();
+        $(rcmail.gui_objects.resourceinfocalendar).fullCalendar('removeEventSource', resources_events_source);
 
         if (resource.owner) {
           // display cached data
@@ -1711,6 +1759,10 @@ function rcube_calendar_ui(settings)
             rcmail.http_request('resources-owner', { _id: resource.owner }, me.loading_lock);
           }
         }
+
+        // load resource calendar
+        resources_events_source.url = "./?_task=calendar&_action=resources-calendar&_id="+escape(resource.ID);
+        $(rcmail.gui_objects.resourceinfocalendar).fullCalendar('addEventSource', resources_events_source);
       }
     };
 
diff --git a/plugins/calendar/drivers/ldap/resources_driver_ldap.php b/plugins/calendar/drivers/ldap/resources_driver_ldap.php
index 23439b6..2f10a1e 100644
--- a/plugins/calendar/drivers/ldap/resources_driver_ldap.php
+++ b/plugins/calendar/drivers/ldap/resources_driver_ldap.php
@@ -27,7 +27,6 @@
 class resources_driver_ldap extends resources_driver
 {
     private $rc;
-    private $cal;
     private $ldap;
 
     /**
@@ -81,7 +80,7 @@ class resources_driver_ldap extends resources_driver
       $rec = null;
 
       if ($ldap = $this->connect()) {
-        $rec = $ldap->get_record(rcube_ldap::dn_encode($dn));
+        $rec = $ldap->get_record(rcube_ldap::dn_encode($dn), true);
 
         if (!empty($rec)) {
           $rec = $this->decode_resource($rec);
diff --git a/plugins/calendar/drivers/resources_driver.php b/plugins/calendar/drivers/resources_driver.php
index b1fed9c..c51e922 100644
--- a/plugins/calendar/drivers/resources_driver.php
+++ b/plugins/calendar/drivers/resources_driver.php
@@ -27,6 +27,15 @@
  */
 abstract class resources_driver
 {
+  protected$cal;
+
+  /**
+   * Default constructor
+   */
+  function __construct($cal)
+  {
+      $this->cal = $cal;
+  }
 
   /**
    * Fetch resource objects to be displayed for booking
@@ -52,7 +61,54 @@ abstract class resources_driver
    */
   public function get_resource_owner($id)
   {
-    return null;
+      return null;
+  }
+
+  /**
+   * Get event data to display a resource's calendar
+   *
+   * The default implementation extracts the resource's email address
+   * and fetches free-busy data using the calendar backend driver.
+   *
+   * @param  integer Event's new start (unix timestamp)
+   * @param  integer Event's new end (unix timestamp)
+   * @return array A list of event objects (see calendar_driver specification)
+   */
+  public function get_resource_calendar($id, $start, $end)
+  {
+      $events = array();
+      $rec = $this->get_resource($id);
+      if ($rec && !empty($rec['email']) && $this->cal->driver) {
+          $fbtypemap = array(
+              calendar::FREEBUSY_BUSY => 'busy',
+              calendar::FREEBUSY_TENTATIVE => 'tentative',
+              calendar::FREEBUSY_OOF => 'outofoffice',
+          );
+
+          // if the backend has free-busy information
+          $fblist = $this->cal->driver->get_freebusy_list($rec['email'], $start, $end);
+          if (is_array($fblist)) {
+              foreach ($fblist as $slot) {
+                  list($from, $to, $type) = $slot;
+                  if ($type == calendar::FREEBUSY_FREE || $type == calendar::FREEBUSY_UNKNOWN) {
+                      continue;
+                  }
+                  if ($from < $end && $to > $start) {
+                      $event = array(
+                          'id'     => sha1($id . $from . $to),
+                          'title'  => $rec['name'],
+                          'start'  => new DateTime('@' . $from),
+                          'end'    => new DateTime('@' . $to),
+                          'status' => $fbtypemap[$type],
+                          'calendar' => '_resource',
+                      );
+                      $events[] = $event;
+                  }
+              }
+          }
+      }
+
+      return $events;
   }
 
 }
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 127456f..5f04575 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -88,6 +88,7 @@ class calendar_ui
     $this->cal->register_handler('plugin.resources_list', array($this, 'resources_list'));
     $this->cal->register_handler('plugin.resources_searchform', array($this, 'resources_search_form'));
     $this->cal->register_handler('plugin.resource_info', array($this, 'resource_info'));
+    $this->cal->register_handler('plugin.resource_calendar', array($this, 'resource_calendar'));
     $this->cal->register_handler('plugin.attendees_freebusy_table', array($this, 'attendees_freebusy_table'));
     $this->cal->register_handler('plugin.edit_attendees_notify', array($this, 'edit_attendees_notify'));
     $this->cal->register_handler('plugin.edit_recurring_warning', array($this, 'recurring_event_warning'));
@@ -816,6 +817,18 @@ class calendar_ui
   }
 
   /**
+   *
+   */
+  public function resource_calendar($attrib = array())
+  {
+    $attrib += array('id' => 'calendar-resources-calendar');
+
+    $this->rc->output->add_gui_object('resourceinfocalendar', $attrib['id']);
+
+    return html::div($attrib, '');
+  }
+
+  /**
    * GUI object 'searchform' for the resource finder dialog
    *
    * @param array Named parameters
diff --git a/plugins/calendar/skins/classic/calendar.css b/plugins/calendar/skins/classic/calendar.css
index 07917c3..f8361d8 100644
--- a/plugins/calendar/skins/classic/calendar.css
+++ b/plugins/calendar/skins/classic/calendar.css
@@ -1086,7 +1086,7 @@ span.spacer {
 	top: 0;
 	left: 0;
 	right: 0;
-	height: 56%;
+	height: 48%;
 	border: 1px solid #999;
 	background-color: #F9F9F9;
 	overflow: auto;
@@ -1095,7 +1095,8 @@ span.spacer {
 #resource-availability {
 	top: auto;
 	bottom: 0;
-	height: 41%;
+	height: 49%;
+	overflow: hidden;
 }
 
 #resource-info .boxtitle,
@@ -1103,6 +1104,40 @@ span.spacer {
 	margin-top: 0;
 }
 
+#resource-freebusy-calendar {
+	position: absolute;
+	top: 20px;
+	left: -1px;
+	right: -1px;
+	bottom: -1px;
+}
+
+#resource-freebusy-calendar .fc-content {
+	top: 0;
+}
+
+#resource-freebusy-calendar .fc-content .fc-event-bg {
+	background: 0;
+}
+
+#resource-freebusy-calendar .fc-event.status-busy,
+#resource-freebusy-calendar .status-busy .fc-event-skin {
+	border-color: #e26569;
+	background-color: #e26569;
+}
+
+#resource-freebusy-calendar .fc-event.status-tentative,
+#resource-freebusy-calendar .status-tentative .fc-event-skin {
+	border-color: #8383fc;
+	background: #8383fc;
+}
+
+#resource-freebusy-calendar .fc-event.status-outofoffice,
+#resource-freebusy-calendar .status-outofoffice .fc-event-skin {
+	border-color: #fbaa68;
+	background: #fbaa68;
+}
+
 #resources-list div.treetoggle {
 	left: 3px !important;
 	top: -2px;
diff --git a/plugins/calendar/skins/classic/templates/calendar.html b/plugins/calendar/skins/classic/templates/calendar.html
index b6012e7..fa93afc 100644
--- a/plugins/calendar/skins/classic/templates/calendar.html
+++ b/plugins/calendar/skins/classic/templates/calendar.html
@@ -125,7 +125,7 @@
 
     <div id="resource-availability">
       <h2 class="boxtitle"><roundcube:label name="calendar.resourceavailability" /></h2>
-      <div id="resource-freebusy-calendar"></div>
+      <roundcube:object name="plugin.resource_calendar" id="resource-freebusy-calendar" />
     </div>
   </div>
 </div>
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index 1806467..9d219fe 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -1081,7 +1081,7 @@ a.dropdown-link:after {
 	top: 0;
 	left: 0;
 	right: 0;
-	height: 56%;
+	height: 48%;
 }
 
 #resource-info table {
@@ -1100,7 +1100,41 @@ a.dropdown-link:after {
 	bottom: 0;
 	left: 0;
 	right: 0;
-	height: 40%;
+	height: 49%;
+}
+
+#resource-freebusy-calendar {
+	position: absolute;
+	top: 33px;
+	left: -1px;
+	right: -1px;
+	bottom: -1px;
+}
+
+#resource-freebusy-calendar .fc-content {
+	top: 0;
+}
+
+#resource-freebusy-calendar .fc-content .fc-event-bg {
+	background: 0;
+}
+
+#resource-freebusy-calendar .fc-event.status-busy,
+#resource-freebusy-calendar .status-busy .fc-event-skin {
+	border-color: #e26569;
+	background-color: #e26569;
+}
+
+#resource-freebusy-calendar .fc-event.status-tentative,
+#resource-freebusy-calendar .status-tentative .fc-event-skin {
+	border-color: #8383fc;
+	background: #8383fc;
+}
+
+#resource-freebusy-calendar .fc-event.status-outofoffice,
+#resource-freebusy-calendar .status-outofoffice .fc-event-skin {
+	border-color: #fbaa68;
+	background: #fbaa68;
 }
 
 #resourcequicksearch {
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
index c189491..036a4fc 100644
--- a/plugins/calendar/skins/larry/templates/calendar.html
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -139,7 +139,11 @@
 
 		<div id="resource-availability" class="uibox contentbox">
 			<h2 class="boxtitle"><roundcube:label name="calendar.resourceavailability" /></h2>
-			<div id="resource-freebusy-calendar"></div>
+			<roundcube:object name="plugin.resource_calendar" id="resource-freebusy-calendar" />
+			<div class="boxpagenav">
+				<roundcube:button name="resource-cal-prev" id="resource-calendar-prev" type="link" class="icon prevpage" title="calendar.prevslot" content="&lt;" />
+				<roundcube:button name="resource-cal-next" id="resource-calendar-next" type="link" class="icon nextpage" title="calendar.nextslot" content="&gt;" />
+			</div>
 		</div>
 	</div>
 </div>




More information about the commits mailing list