# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""html calendar views"""
__docformat__ = "restructuredtext en"
_ = unicode
import copy
from datetime import timedelta
from logilab.mtconverter import xml_escape
from logilab.common.date import todatetime
from cubicweb.utils import json_dumps, make_uid
from cubicweb.interfaces import ICalendarable
from cubicweb.selectors import implements, adaptable
from cubicweb.view import EntityView, EntityAdapter, implements_adapter_compat
# useful constants & functions ################################################
ONEDAY = timedelta(1)
WEEKDAYS = (_("monday"), _("tuesday"), _("wednesday"), _("thursday"),
_("friday"), _("saturday"), _("sunday"))
MONTHNAMES = ( _('january'), _('february'), _('march'), _('april'), _('may'),
_('june'), _('july'), _('august'), _('september'), _('october'),
_('november'), _('december')
)
class ICalendarableAdapter(EntityAdapter):
__needs_bw_compat__ = True
__regid__ = 'ICalendarable'
__select__ = implements(ICalendarable, warn=False) # XXX for bw compat, should be abstract
@property
@implements_adapter_compat('ICalendarable')
def start(self):
"""return start date"""
raise NotImplementedError
@property
@implements_adapter_compat('ICalendarable')
def stop(self):
"""return stop date"""
raise NotImplementedError
# Calendar views ##############################################################
try:
from vobject import iCalendar
class iCalView(EntityView):
"""A calendar view that generates a iCalendar file (RFC 2445)
Does apply to ICalendarable compatible entities
"""
__select__ = adaptable('ICalendarable')
paginable = False
content_type = 'text/calendar'
title = _('iCalendar')
templatable = False
__regid__ = 'ical'
def call(self):
ical = iCalendar()
for i in range(len(self.cw_rset.rows)):
task = self.cw_rset.complete_entity(i, 0)
event = ical.add('vevent')
event.add('summary').value = task.dc_title()
event.add('description').value = task.dc_description()
icalendarable = task.cw_adapt_to('ICalendarable')
if icalendarable.start:
event.add('dtstart').value = icalendarable.start
if icalendarable.stop:
event.add('dtend').value = icalendarable.stop
buff = ical.serialize()
if not isinstance(buff, unicode):
buff = unicode(buff, self._cw.encoding)
self.w(buff)
except ImportError:
pass
class hCalView(EntityView):
"""A calendar view that generates a hCalendar file
Does apply to ICalendarable compatible entities
"""
__regid__ = 'hcal'
__select__ = adaptable('ICalendarable')
paginable = False
title = _('hCalendar')
#templatable = False
def call(self):
self.w(u'<div class="hcalendar">')
for i in range(len(self.cw_rset.rows)):
task = self.cw_rset.complete_entity(i, 0)
self.w(u'<div class="vevent">')
self.w(u'<h3 class="summary">%s</h3>' % xml_escape(task.dc_title()))
self.w(u'<div class="description">%s</div>'
% task.dc_description(format='text/html'))
icalendarable = task.cw_adapt_to('ICalendarable')
if icalendarable.start:
self.w(u'<abbr class="dtstart" title="%s">%s</abbr>'
% (icalendarable.start.isoformat(),
self._cw.format_date(icalendarable.start)))
if icalendarable.stop:
self.w(u'<abbr class="dtstop" title="%s">%s</abbr>'
% (icalendarable.stop.isoformat(),
self._cw.format_date(icalendarable.stop)))
self.w(u'</div>')
self.w(u'</div>')
class CalendarItemView(EntityView):
__regid__ = 'calendaritem'
def cell_call(self, row, col, dates=False):
task = self.cw_rset.complete_entity(row, 0)
task.view('oneline', w=self.w)
if dates:
icalendarable = task.cw_adapt_to('ICalendarable')
if icalendarable.start and icalendarable.stop:
self.w('<br/> %s' % self._cw._('from %(date)s')
% {'date': self._cw.format_date(icalendarable.start)})
self.w('<br/> %s' % self._cw._('to %(date)s')
% {'date': self._cw.format_date(icalendarable.stop)})
else:
self.w('<br/>%s'%self._cw.format_date(icalendarable.start
or icalendarable.stop))
class _TaskEntry(object):
def __init__(self, task, color, index=0):
self.task = task
self.color = color
self.index = index
self.length = 1
icalendarable = task.cw_adapt_to('ICalendarable')
self.start = icalendarable.start
self.stop = icalendarable.stop
def in_working_hours(self):
"""predicate returning True is the task is in working hours"""
if todatetime(self.start).hour > 7 and todatetime(self.stop).hour < 20:
return True
return False
def is_one_day_task(self):
return self.start and self.stop and self.start.isocalendar() == self.stop.isocalendar()
class CalendarView(EntityView):
__regid__ = 'calendar'
__select__ = adaptable('ICalendarable')
paginable = False
title = _('calendar')
fullcalendar_options = {
'firstDay': 1,
'firstHour': 8,
'defaultView': 'month',
'editable': True,
'header': {'left': 'prev,next today',
'center': 'title',
'right': 'month,agendaWeek,agendaDay',
},
}
def call(self):
self._cw.demote_to_html()
self._cw.add_css(('fullcalendar.css', 'cubicweb.calendar.css'))
self._cw.add_js(('jquery.ui.js', 'fullcalendar.min.js', 'jquery.qtip.min.js', 'fullcalendar.locale.js'))
self.calendar_id = 'cal' + make_uid('uid')
self.add_onload()
# write calendar div to load jquery fullcalendar object
self.w(u'<div id="%s"></div>' % self.calendar_id)
def add_onload(self):
fullcalendar_options = self.fullcalendar_options.copy()
fullcalendar_options['events'] = self.get_events()
# i18n
# js callback to add a tooltip and to put html in event's title
js = """
var options = $.fullCalendar.regional('%s', %s);
options.eventRender = function(event, $element) {
// add a tooltip for each event
var div = '<div class="tooltip">'+ event.description+ '</div>';
$element.append(div);
// allow to have html tags in event's title
$element.find('span.fc-event-title').html($element.find('span.fc-event-title').text());
};
$("#%s").fullCalendar(options);
""" #"
self._cw.add_onload(js % (self._cw.lang, json_dumps(fullcalendar_options), self.calendar_id))
def get_events(self):
events = []
for entity in self.cw_rset.entities():
icalendarable = entity.cw_adapt_to('ICalendarable')
if not (icalendarable.start and icalendarable.stop):
continue
start_date = icalendarable.start or icalendarable.stop
event = {'eid': entity.eid,
'title': entity.view('calendaritem'),
'url': xml_escape(entity.absolute_url()),
'className': 'calevent',
'description': entity.view('tooltip'),
}
event['start'] = start_date.strftime('%Y-%m-%dT%H:%M')
event['allDay'] = True
if icalendarable.stop:
event['end'] = icalendarable.stop.strftime('%Y-%m-%dT%H:%M')
event['allDay'] = False
events.append(event)
return events
class OneMonthCal(CalendarView):
__regid__ = 'onemonthcal'
title = _('one month')
class OneWeekCal(CalendarView):
__regid__ = 'oneweekcal'
title = _('one week')
fullcalendar_options = CalendarView.fullcalendar_options.copy()
fullcalendar_options['defaultView'] = 'agendaWeek'