Example #1
0
 def _event_data(self, req, provider, event, lastvisit):
     """Compose the timeline event date from the event tuple and prepared
     provider methods"""
     if len(event) == 5:  # with special provider
         kind, datetime, author, data, provider = event
     else:
         kind, datetime, author, data = event
     render = lambda field, context: \
              provider.render_timeline_event(context, field, event)
     localized_datetime = to_datetime(datetime, tzinfo=req.tz)
     localized_date = truncate_datetime(localized_datetime)
     datetime_uid = to_utimestamp(localized_datetime)
     return {'kind': kind, 'author': author, 'date': localized_date,
             'datetime': localized_datetime, 'datetime_uid': datetime_uid,
             'render': render,
             'unread': lastvisit and lastvisit < datetime_uid,
             'event': event, 'data': data, 'provider': provider}
Example #2
0
    def process_request(self, req):
        req.perm('timeline').require('TIMELINE_VIEW')

        format = req.args.get('format')
        maxrows = req.args.getint('max', 50 if format == 'rss' else 0)
        lastvisit = req.session.as_int('timeline.lastvisit', 0)

        # indication of new events is unchanged when form is updated by user
        revisit = any(a in req.args
                      for a in ['update', 'from', 'daysback', 'author'])
        if revisit:
            lastvisit = req.session.as_int('timeline.nextlastvisit',
                                           lastvisit)

        # Parse the from date and adjust the timestamp to the last second of
        # the day
        fromdate = datetime_now(req.tz)
        today = truncate_datetime(fromdate)
        yesterday = to_datetime(today.replace(tzinfo=None) - timedelta(days=1),
                                req.tz)
        precisedate = precision = None
        if 'from' in req.args:
            # Acquire from date only from non-blank input
            reqfromdate = req.args.get('from').strip()
            if reqfromdate:
                try:
                    precisedate = user_time(req, parse_date, reqfromdate)
                except TracError as e:
                    add_warning(req, e)
                else:
                    fromdate = precisedate.astimezone(req.tz)
            precision = req.args.get('precision', '')
            if precision.startswith('second'):
                precision = timedelta(seconds=1)
            elif precision.startswith('minute'):
                precision = timedelta(minutes=1)
            elif precision.startswith('hour'):
                precision = timedelta(hours=1)
            else:
                precision = None
        fromdate = to_datetime(datetime(fromdate.year, fromdate.month,
                                        fromdate.day, 23, 59, 59, 999999),
                               req.tz)

        pref = req.session.as_int('timeline.daysback', self.default_daysback)
        default = 90 if format == 'rss' else pref
        daysback = req.args.as_int('daysback', default,
                                   min=1, max=self.max_daysback)

        authors = req.args.get('authors')
        if authors is None and format != 'rss':
            authors = req.session.get('timeline.authors')
        authors = (authors or '').strip()

        data = {'fromdate': fromdate, 'daysback': daysback,
                'authors': authors, 'today': today, 'yesterday': yesterday,
                'precisedate': precisedate, 'precision': precision,
                'events': [], 'filters': [],
                'abbreviated_messages': self.abbreviated_messages}

        available_filters = []
        for event_provider in self.event_providers:
            with component_guard(self.env, req, event_provider):
                available_filters += (event_provider.get_timeline_filters(req)
                                      or [])

        # check the request or session for enabled filters, or use default
        filters = [f[0] for f in available_filters if f[0] in req.args]
        if not filters and format != 'rss':
            filters = [f[0] for f in available_filters
                            if req.session.as_int('timeline.filter.' + f[0])]
        if not filters:
            filters = [f[0] for f in available_filters if len(f) == 2 or f[2]]

        # save the results of submitting the timeline form to the session
        if 'update' in req.args:
            for filter_ in available_filters:
                key = 'timeline.filter.%s' % filter_[0]
                if filter_[0] in req.args:
                    req.session[key] = '1'
                elif key in req.session:
                    del req.session[key]

        stop = fromdate
        start = to_datetime(stop.replace(tzinfo=None) -
                            timedelta(days=daysback + 1), req.tz)

        # create author include and exclude sets
        include = set()
        exclude = set()
        for match in self._authors_pattern.finditer(authors):
            name = (match.group(2) or match.group(3) or match.group(4)).lower()
            if match.group(1):
                exclude.add(name)
            else:
                include.add(name)

        # gather all events for the given period of time
        events = []
        for provider in self.event_providers:
            with component_guard(self.env, req, provider):
                for event in provider.get_timeline_events(req, start, stop,
                                                          filters) or []:
                    author = (event[2] or '').lower()
                    if ((not include or author in include) and
                        author not in exclude):
                        events.append(
                            self._event_data(req, provider, event, lastvisit))

        # prepare sorted global list
        events = sorted(events, key=lambda e: e['datetime'], reverse=True)
        if maxrows:
            events = events[:maxrows]

        data['events'] = events

        if format == 'rss':
            rss_context = web_context(req, absurls=True)
            rss_context.set_hints(wiki_flavor='html', shorten_lines=False)
            data['context'] = rss_context
            return 'timeline.rss', data, {'content_type': 'application/rss+xml'}
        else:
            req.session.set('timeline.daysback', daysback,
                            self.default_daysback)
            req.session.set('timeline.authors', authors, '')
            # store lastvisit
            if events and not revisit:
                lastviewed = to_utimestamp(events[0]['datetime'])
                req.session['timeline.lastvisit'] = max(lastvisit, lastviewed)
                req.session['timeline.nextlastvisit'] = lastvisit
            html_context = web_context(req)
            html_context.set_hints(wiki_flavor='oneliner',
                                   shorten_lines=self.abbreviated_messages)
            data['context'] = html_context

        add_stylesheet(req, 'common/css/timeline.css')
        rss_href = req.href.timeline([(f, 'on') for f in filters],
                                     daysback=90, max=50, authors=authors,
                                     format='rss')
        add_link(req, 'alternate', auth_link(req, rss_href), _('RSS Feed'),
                 'application/rss+xml', 'rss')
        Chrome(self.env).add_jquery_ui(req)

        for filter_ in available_filters:
            data['filters'].append({'name': filter_[0], 'label': filter_[1],
                                    'enabled': filter_[0] in filters})

        # Navigation to the previous/next period of 'daysback' days
        previous_start = fromdate.replace(tzinfo=None) - \
                         timedelta(days=daysback + 1)
        previous_start = format_date(previous_start, format='iso8601',
                                     tzinfo=req.tz)
        add_link(req, 'prev', req.href.timeline(from_=previous_start,
                                                authors=authors,
                                                daysback=daysback),
                 _("Previous Period"))
        if today - fromdate > timedelta(days=0):
            next_start = fromdate.replace(tzinfo=None) + \
                         timedelta(days=daysback + 1)
            next_start = format_date(to_datetime(next_start, req.tz),
                                     format='iso8601', tzinfo=req.tz)
            add_link(req, 'next', req.href.timeline(from_=next_start,
                                                    authors=authors,
                                                    daysback=daysback),
                     _("Next Period"))
        prevnext_nav(req, _("Previous Period"), _("Next Period"))

        return 'timeline.html', data