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="<" />
+ <roundcube:button name="resource-cal-next" id="resource-calendar-next" type="link" class="icon nextpage" title="calendar.nextslot" content=">" />
+ </div>
</div>
</div>
</div>
More information about the commits
mailing list