web/views/calendar.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
     1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """html calendar views"""
       
    19 
       
    20 __docformat__ = "restructuredtext en"
       
    21 from cubicweb import _
       
    22 
       
    23 import copy
       
    24 from datetime import timedelta
       
    25 
       
    26 from logilab.mtconverter import xml_escape
       
    27 from logilab.common.date import todatetime
       
    28 
       
    29 from cubicweb.utils import json_dumps, make_uid
       
    30 from cubicweb.predicates import adaptable
       
    31 from cubicweb.view import EntityView, EntityAdapter
       
    32 
       
    33 # useful constants & functions ################################################
       
    34 
       
    35 ONEDAY = timedelta(1)
       
    36 
       
    37 WEEKDAYS = (_("monday"), _("tuesday"), _("wednesday"), _("thursday"),
       
    38             _("friday"), _("saturday"), _("sunday"))
       
    39 MONTHNAMES = ( _('january'), _('february'), _('march'), _('april'), _('may'),
       
    40                _('june'), _('july'), _('august'), _('september'), _('october'),
       
    41                _('november'), _('december')
       
    42                )
       
    43 
       
    44 
       
    45 class ICalendarableAdapter(EntityAdapter):
       
    46     __needs_bw_compat__ = True
       
    47     __regid__ = 'ICalendarable'
       
    48     __abstract__ = True
       
    49 
       
    50     @property
       
    51     def start(self):
       
    52         """return start date"""
       
    53         raise NotImplementedError
       
    54 
       
    55     @property
       
    56     def stop(self):
       
    57         """return stop date"""
       
    58         raise NotImplementedError
       
    59 
       
    60 
       
    61 # Calendar views ##############################################################
       
    62 
       
    63 try:
       
    64     from vobject import iCalendar
       
    65 
       
    66     class iCalView(EntityView):
       
    67         """A calendar view that generates a iCalendar file (RFC 2445)
       
    68 
       
    69         Does apply to ICalendarable compatible entities
       
    70         """
       
    71         __select__ = adaptable('ICalendarable')
       
    72         paginable = False
       
    73         content_type = 'text/calendar'
       
    74         title = _('iCalendar')
       
    75         templatable = False
       
    76         __regid__ = 'ical'
       
    77 
       
    78         def call(self):
       
    79             ical = iCalendar()
       
    80             for i in range(len(self.cw_rset.rows)):
       
    81                 task = self.cw_rset.complete_entity(i, 0)
       
    82                 event = ical.add('vevent')
       
    83                 event.add('summary').value = task.dc_title()
       
    84                 event.add('description').value = task.dc_description()
       
    85                 icalendarable = task.cw_adapt_to('ICalendarable')
       
    86                 if icalendarable.start:
       
    87                     event.add('dtstart').value = icalendarable.start
       
    88                 if icalendarable.stop:
       
    89                     event.add('dtend').value = icalendarable.stop
       
    90 
       
    91             buff = ical.serialize()
       
    92             if not isinstance(buff, unicode):
       
    93                 buff = unicode(buff, self._cw.encoding)
       
    94             self.w(buff)
       
    95 
       
    96 except ImportError:
       
    97     pass
       
    98 
       
    99 class hCalView(EntityView):
       
   100     """A calendar view that generates a hCalendar file
       
   101 
       
   102     Does apply to ICalendarable compatible entities
       
   103     """
       
   104     __regid__ = 'hcal'
       
   105     __select__ = adaptable('ICalendarable')
       
   106     paginable = False
       
   107     title = _('hCalendar')
       
   108     #templatable = False
       
   109 
       
   110     def call(self):
       
   111         self.w(u'<div class="hcalendar">')
       
   112         for i in range(len(self.cw_rset.rows)):
       
   113             task = self.cw_rset.complete_entity(i, 0)
       
   114             self.w(u'<div class="vevent">')
       
   115             self.w(u'<h3 class="summary">%s</h3>' % xml_escape(task.dc_title()))
       
   116             self.w(u'<div class="description">%s</div>'
       
   117                    % task.dc_description(format='text/html'))
       
   118             icalendarable = task.cw_adapt_to('ICalendarable')
       
   119             if icalendarable.start:
       
   120                 self.w(u'<abbr class="dtstart" title="%s">%s</abbr>'
       
   121                        % (icalendarable.start.isoformat(),
       
   122                           self._cw.format_date(icalendarable.start)))
       
   123             if icalendarable.stop:
       
   124                 self.w(u'<abbr class="dtstop" title="%s">%s</abbr>'
       
   125                        % (icalendarable.stop.isoformat(),
       
   126                           self._cw.format_date(icalendarable.stop)))
       
   127             self.w(u'</div>')
       
   128         self.w(u'</div>')
       
   129 
       
   130 
       
   131 class CalendarItemView(EntityView):
       
   132     __regid__ = 'calendaritem'
       
   133 
       
   134     def cell_call(self, row, col, dates=False):
       
   135         task = self.cw_rset.complete_entity(row, 0)
       
   136         task.view('oneline', w=self.w)
       
   137         if dates:
       
   138             icalendarable = task.cw_adapt_to('ICalendarable')
       
   139             if icalendarable.start and icalendarable.stop:
       
   140                 self.w('<br/> %s' % self._cw._('from %(date)s')
       
   141                        % {'date': self._cw.format_date(icalendarable.start)})
       
   142                 self.w('<br/> %s' % self._cw._('to %(date)s')
       
   143                        % {'date': self._cw.format_date(icalendarable.stop)})
       
   144             else:
       
   145                 self.w('<br/>%s'%self._cw.format_date(icalendarable.start
       
   146                                                       or icalendarable.stop))
       
   147 
       
   148 
       
   149 class _TaskEntry(object):
       
   150     def __init__(self, task, color, index=0):
       
   151         self.task = task
       
   152         self.color = color
       
   153         self.index = index
       
   154         self.length = 1
       
   155         icalendarable = task.cw_adapt_to('ICalendarable')
       
   156         self.start = icalendarable.start
       
   157         self.stop = icalendarable.stop
       
   158 
       
   159     def in_working_hours(self):
       
   160         """predicate returning True is the task is in working hours"""
       
   161         if todatetime(self.start).hour > 7 and todatetime(self.stop).hour < 20:
       
   162             return True
       
   163         return False
       
   164 
       
   165     def is_one_day_task(self):
       
   166         return self.start and self.stop and self.start.isocalendar() == self.stop.isocalendar()
       
   167 
       
   168 
       
   169 class CalendarView(EntityView):
       
   170     __regid__ = 'calendar'
       
   171     __select__ = adaptable('ICalendarable')
       
   172 
       
   173     paginable = False
       
   174     title = _('calendar')
       
   175 
       
   176     fullcalendar_options = {
       
   177         'firstDay': 1,
       
   178         'firstHour': 8,
       
   179         'defaultView': 'month',
       
   180         'editable': True,
       
   181         'header': {'left': 'prev,next today',
       
   182                    'center': 'title',
       
   183                    'right': 'month,agendaWeek,agendaDay',
       
   184                    },
       
   185         }
       
   186 
       
   187     def call(self):
       
   188         self._cw.add_css(('fullcalendar.css', 'cubicweb.calendar.css'))
       
   189         self._cw.add_js(('jquery.ui.js', 'fullcalendar.min.js', 'jquery.qtip.min.js', 'fullcalendar.locale.js'))
       
   190         self.calendar_id = 'cal' + make_uid('uid')
       
   191         self.add_onload()
       
   192         # write calendar div to load jquery fullcalendar object
       
   193         self.w(u'<div id="%s"></div>' % self.calendar_id)
       
   194 
       
   195     def add_onload(self):
       
   196         fullcalendar_options = self.fullcalendar_options.copy()
       
   197         fullcalendar_options['events'] = self.get_events()
       
   198         # i18n
       
   199         # js callback to add a tooltip and to put html in event's title
       
   200         js = """
       
   201         var options = $.fullCalendar.regional('%s', %s);
       
   202         options.eventRender = function(event, $element) {
       
   203           // add a tooltip for each event
       
   204           var div = '<div class="tooltip">'+ event.description+ '</div>';
       
   205           $element.append(div);
       
   206           // allow to have html tags in event's title
       
   207           $element.find('span.fc-event-title').html($element.find('span.fc-event-title').text());
       
   208         };
       
   209         $("#%s").fullCalendar(options);
       
   210         """ #"
       
   211         self._cw.add_onload(js % (self._cw.lang, json_dumps(fullcalendar_options), self.calendar_id))
       
   212 
       
   213     def get_events(self):
       
   214         events = []
       
   215         for entity in self.cw_rset.entities():
       
   216             icalendarable = entity.cw_adapt_to('ICalendarable')
       
   217             if not (icalendarable.start and icalendarable.stop):
       
   218                 continue
       
   219             start_date = icalendarable.start or  icalendarable.stop
       
   220             event = {'eid': entity.eid,
       
   221                      'title': entity.view('calendaritem'),
       
   222                      'url': xml_escape(entity.absolute_url()),
       
   223                      'className': 'calevent',
       
   224                      'description': entity.view('tooltip'),
       
   225                      }
       
   226             event['start'] = start_date.strftime('%Y-%m-%dT%H:%M')
       
   227             event['allDay'] = True
       
   228             if icalendarable.stop:
       
   229                 event['end'] = icalendarable.stop.strftime('%Y-%m-%dT%H:%M')
       
   230                 event['allDay'] = False
       
   231             events.append(event)
       
   232         return events
       
   233 
       
   234 class OneMonthCal(CalendarView):
       
   235     __regid__ = 'onemonthcal'
       
   236 
       
   237     title = _('one month')
       
   238 
       
   239 class OneWeekCal(CalendarView):
       
   240     __regid__ = 'oneweekcal'
       
   241 
       
   242     title = _('one week')
       
   243     fullcalendar_options = CalendarView.fullcalendar_options.copy()
       
   244     fullcalendar_options['defaultView'] = 'agendaWeek'