diff -r 000000000000 -r b97547f5f1fa web/views/timeline.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/views/timeline.py Wed Nov 05 15:52:50 2008 +0100 @@ -0,0 +1,124 @@ +"""basic support for SIMILE's timline widgets + +cf. http://code.google.com/p/simile-widgets/ + +:organization: Logilab +:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +""" +__docformat__ = "restructuredtext en" + +import simplejson + +from logilab.mtconverter import html_escape + +from cubicweb.interfaces import ICalendarable +from cubicweb.common.view import EntityView, StartupView +from cubicweb.common.selectors import interface_selector + + +# +class TimelineJsonView(EntityView): + """generates a json file to feed Timeline.loadJSON() + NOTE: work in progress (image_url, bubbleUrl and so on + should be properties of entity classes or subviews) + """ + id = 'timeline-json' + binary = True + templatable = False + content_type = 'application/json' + + __selectors__ = (interface_selector,) + accepts_interfaces = (ICalendarable,) + date_fmt = '%Y/%m/%d' + + def call(self): + events = [] + for entity in self.rset.entities(): + event = self.build_event(entity) + if event is not None: + events.append(event) + timeline_data = {'dateTimeFormat': self.date_fmt, + 'events': events} + self.w(simplejson.dumps(timeline_data)) + + # FIXME: those properties should be defined by the entity class + def onclick_url(self, entity): + return entity.absolute_url() + + def onclick(self, entity): + url = self.onclick_url(entity) + if url: + return u"javascript: document.location.href='%s'" % url + return None + + def build_event(self, entity): + """converts `entity` into a JSON object + {'start': '1891', + 'end': '1915', + 'title': 'Portrait of Horace Brodsky', + 'description': 'by Henri Gaudier-Brzeska, French Sculptor, 1891-1915', + 'image': 'http://imagecache2.allposters.com/images/BRGPOD/102770_b.jpg', + 'link': 'http://www.allposters.com/-sp/Portrait-of-Horace-Brodsky-Posters_i1584413_.htm' + } + """ + start = entity.start + stop = entity.stop + start = start or stop + if start is None and stop is None: + return None + event_data = {'start': start.strftime(self.date_fmt), + 'title': html_escape(entity.dc_title()), + 'description': entity.dc_description(), + 'link': entity.absolute_url(), + } + onclick = self.onclick(entity) + if onclick: + event_data['onclick'] = onclick + if stop: + event_data['end'] = stop.strftime(self.date_fmt) + return event_data + + +class TimelineViewMixIn(object): + widget_class = 'TimelineWidget' + jsfiles = ('cubicweb.timeline-bundle.js', 'cubicweb.widgets.js', + 'cubicweb.timeline-ext.js', 'cubicweb.ajax.js') + + def render(self, loadurl, tlunit=None): + tlunit = tlunit or self.req.form.get('tlunit') + self.req.add_js(self.jsfiles) + self.req.add_css('timeline-bundle.css') + if tlunit: + additional = u' cubicweb:tlunit="%s"' % tlunit + else: + additional = u'' + self.w(u'
' % + (self.widget_class, html_escape(loadurl), + additional)) + self.w(u'
') + + +class TimelineView(TimelineViewMixIn, EntityView): + """builds a cubicweb timeline widget node""" + id = 'timeline' + __selectors__ = (interface_selector,) + accepts_interfaces = (ICalendarable,) + need_navigation = False + def call(self, tlunit=None): + self.req.html_headers.define_var('Timeline_urlPrefix', self.req.datadir_url) + rql = self.rset.printable_rql() + loadurl = self.build_url(rql=rql, vid='timeline-json') + self.render(loadurl, tlunit) + + +class StaticTimelineView(TimelineViewMixIn, StartupView): + """similar to `TimelineView` but loads data from a static + JSON file instead of one after a RQL query. + """ + id = 'static-timeline' + + def call(self, loadurl, tlunit=None, wdgclass=None): + self.widget_class = wdgclass or self.widget_clas + self.render(loadurl, tlunit)