Display resource's availability in a small calendar widget. Data is derived from the resource free/busy data
This commit is contained in:
parent
17013f732f
commit
0946cc37a4
9 changed files with 221 additions and 9 deletions
|
@ -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 ****/
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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'));
|
||||
|
@ -815,6 +816,18 @@ class calendar_ui
|
|||
$table_attrib);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue