Edgewall Software

ChristianBoos: timeline-refactoring.patch

File timeline-refactoring.patch, 39.4 kB (added by cboos, 10 months ago)

clean-up the API changes that were introduced during 0.11dev.

  • trac/attachment.py

    changeset:   31:f08a0d1b8960
    tag:         qtip
    tag:         overhaul
    tag:         tip
    tag:         qbase
    user:        Christian Boos <cboos@neuf.fr>
    date:        Mon Oct 22 12:34:10 2007 +0200
    files:       trac/attachment.py trac/ticket/roadmap.py trac/ticket/web_ui.py trac/timeline/api.py trac/timeline/templates/timeline.html trac/timeline/templates/timeline.rss trac/timeline/web_ui.py trac/versioncontrol/web_ui/changeset.py trac/wiki/formatter.py trac/wiki/web_ui.py
    description:
    Timeline refactoring: clean-up the API changes that were introduced during 0.11dev.
    
    The main idea of the original refactoring has been kept: don't render the events directly when generating them, but defer this last step when the event is actually processed within the template.
    
    But instead of requiring a intermediate `TimelineEvent` object, we now rely on a `render_timeline_event` method of the `ITimelineEventProvider` to do the job. The event itself is still a tuple, but of different arity than the one of 0.10, so that we can easily make the difference and keep backward compatibility. The new tuple enable the components to add an arbitrary amount of "private" data to the tuple, for the rendering needs
    
    
    diff -r 00135419c47f -r f08a0d1b8960 trac/attachment.py
    a b  
    3232from trac.env import IEnvironmentSetupParticipant 
    3333from trac.perm import PermissionError, PermissionSystem, IPermissionPolicy 
    3434from trac.mimeview import * 
    35 from trac.timeline.api import TimelineEvent 
    3635from trac.util import get_reporter_id, create_unique_file, content_disposition 
    3736from trac.util.datefmt import to_timestamp, utc 
    3837from trac.util.text import unicode_quote, unicode_unquote, pretty_size 
     
    4140from trac.web.chrome import add_link, add_stylesheet, INavigationContributor 
    4241from trac.web.href import Href 
    4342from trac.wiki.api import IWikiSyntaxProvider 
     43from trac.wiki.formatter import format_to_oneliner 
    4444 
    4545 
    4646class InvalidAttachment(TracError): 
     
    431431        """ 
    432432        for change, realm, id, filename, time, descr, author in \ 
    433433                self.get_history(start, stop, resource_realm.realm): 
    434             parent = resource_realm(id=id) 
    435             attachment = parent.child('attachment', filename) 
     434            attachment = resource_realm(id=id).child('attachment', filename) 
    436435            if 'ATTACHMENT_VIEW' in req.perm(attachment): 
    437                 title = tag(tag.em(os.path.basename(filename)), 
    438                             _(" attached to "), 
    439                             tag.em(get_name(self.env, parent), 
    440                                    title=get_summary(self.env, parent))) 
    441                 ### FIXME: the link is no longer to the attachment... 
    442                 event = TimelineEvent(self, 'attachment') 
    443                 event.set_changeinfo(time, author) 
    444                 event.add_markup(title=title) 
    445                 event.add_wiki(parent, body=descr) 
    446                 yield event 
    447  
    448     def event_formatter(self, event, key): 
    449         return None 
     436                yield ('attachment', time, author, (attachment, descr), self) 
     437 
     438    def render_timeline_event(self, context, field, event): 
     439        attachment, descr = event[3] 
     440        if field == 'url': 
     441            return self.get_resource_url(attachment, context.href) 
     442        elif field == 'title': 
     443            return tag(tag.em(os.path.basename(attachment.id)), 
     444                       _(" attached to "), 
     445                       tag.em(get_name(self.env, attachment.parent), 
     446                              title=get_summary(self.env, attachment.parent))) 
     447        elif field == 'description': 
     448            return format_to_oneliner(context(attachment.parent), descr) 
    450449     
    451450    # IResourceManager methods 
    452451     
  • trac/ticket/roadmap.py

    diff -r 00135419c47f -r f08a0d1b8960 trac/ticket/roadmap.py
    a b  
    2323 
    2424from trac import __version__ 
    2525from trac.attachment import AttachmentModule 
     26from trac.config import ExtensionOption 
    2627from trac.context import * 
    2728from trac.core import * 
    2829from trac.perm import IPermissionRequestor 
     
    3435from trac.util.translation import _ 
    3536from trac.ticket import Milestone, Ticket, TicketSystem 
    3637from trac.ticket.query import Query 
    37 from trac.timeline.api import ITimelineEventProvider, TimelineEvent 
     38from trac.timeline.api import ITimelineEventProvider 
    3839from trac.web import IRequestHandler 
    3940from trac.web.chrome import add_link, add_stylesheet, INavigationContributor 
    4041from trac.wiki.api import IWikiSyntaxProvider 
    41 from trac.config import ExtensionOption 
     42from trac.wiki.formatter import format_to_html 
    4243 
    4344class ITicketGroupStatsProvider(Interface): 
    4445    def get_ticket_group_stats(self, ticket_ids): 
     
    506507            cursor.execute("SELECT completed,name,description FROM milestone " 
    507508                           "WHERE completed>=%s AND completed<=%s", 
    508509                           (to_timestamp(start), to_timestamp(stop))) 
    509             for ts, name, description in cursor: 
     510            for completed, name, description in cursor: 
    510511                milestone = milestone_realm(id=name) 
    511512                if 'MILESTONE_VIEW' in req.perm(milestone): 
    512                     completed = datetime.fromtimestamp(ts, utc) 
    513                     title = tag('Milestone ', tag.em(name), ' completed') 
    514                     event = TimelineEvent('milestone', title, 
    515                                           req.href.milestone(name)) 
    516                     event.set_changeinfo(completed, '') # FIXME: store the author 
    517                     event.add_wiki(milestone, description) # FIXME xxxo 
    518                     yield event 
     513                    yield('milestone', datetime.fromtimestamp(completed, utc), 
     514                          '', (milestone, description)) # FIXME: author? 
    519515 
    520516            # Attachments 
    521517            for event in AttachmentModule(self.env).get_timeline_events( 
    522518                req, milestone_realm, start, stop): 
    523519                yield event 
    524520                 
    525  
    526     def event_formatter(self, event, key): 
    527         return None 
     521    def render_timeline_event(self, context, field, event): 
     522        milestone, description = event[3] 
     523        if field == 'url': 
     524            return context.href.milestone(milestone.id) 
     525        elif field == 'title': 
     526            return tag('Milestone ', tag.em(milestone.id), ' completed') 
     527        elif field == 'description': 
     528            return format_to_html(context(resource=milestone), 
     529                                  shorten_line(description)) 
    528530 
    529531    # IRequestHandler methods 
    530532 
  • trac/ticket/web_ui.py

    diff -r 00135419c47f -r f08a0d1b8960 trac/ticket/web_ui.py
    a b  
    3333from trac.ticket import Milestone, Ticket, TicketSystem, ITicketManipulator 
    3434from trac.ticket import ITicketActionController 
    3535from trac.ticket.notification import TicketNotifyEmail 
    36 from trac.timeline.api import ITimelineEventProvider, TimelineEvent 
     36from trac.timeline.api import ITimelineEventProvider 
    3737from trac.util import get_reporter_id 
    3838from trac.util.compat import any 
    3939from trac.util.datefmt import to_timestamp, utc 
     
    4444from trac.web import IRequestHandler 
    4545from trac.web.chrome import add_link, add_script, add_stylesheet, Chrome, \ 
    4646                            INavigationContributor, ITemplateProvider 
    47  
     47from trac.wiki.formatter import format_to 
    4848 
    4949class InvalidTicket(TracError): 
    5050    """Exception raised when a ticket fails validation.""" 
     
    204204                      'reopened': ('reopenedticket', 'reopened'), 
    205205                      'closed': ('closedticket', 'closed'), 
    206206                      'edit': ('editedticket', 'updated')} 
     207 
    207208        ticket_realm = Resource('ticket') 
    208         ticketsystem = TicketSystem(self.env) 
    209         description = {} 
    210  
    211         def produce((id, ts, author, type, summary, description), 
    212                     status, fields, comment, cid): 
    213             resource = ticket_realm(id=id) 
    214             if 'TICKET_VIEW' not in req.perm(resource): 
     209 
     210        def produce_event((id, ts, author, type, summary, description), 
     211                          status, fields, comment, cid): 
     212            ticket = ticket_realm(id=id) 
     213            if 'TICKET_VIEW' not in req.perm(ticket): 
    215214                return None 
     215            resolution = fields.get('resolution') 
    216216            info = '' 
    217             resolution = fields.get('resolution') 
    218217            if status == 'edit': 
    219218                if 'ticket_details' in filters: 
    220219                    if len(fields) > 0: 
     
    231230            else: 
    232231                return None 
    233232            kind, verb = status_map[status] 
    234             title = ticketsystem.format_summary(summary, status, 
    235                                                 resolution, type) 
    236             title = tag('Ticket ', tag.em(get_shortname(self.env, resource), 
    237                                           title=title), 
    238                         ' (', shorten_line(summary), ') ', verb) 
    239             markup = message = None 
    240             if status == 'new': 
    241                 message = description 
    242             else: 
    243                 markup = info 
    244                 message = comment 
    245             t = datetime.fromtimestamp(ts, utc) 
    246             event = TimelineEvent(self, kind) 
    247             event.set_changeinfo(t, author) 
    248             event.add_markup(title=title, header=markup) # FIXME 
    249             event.add_wiki(resource, body=message) # FIXME 
    250             if cid: 
    251                 event.href_fragment = '#comment:' + cid 
    252             return event 
     233            return (kind, datetime.fromtimestamp(ts, utc), author, 
     234                    (ticket, verb, info, summary, status, resolution, type, 
     235                     description, comment, cid)) 
    253236 
    254237        # Ticket changes 
    255238        db = self.env.get_db_cnx() 
     
    267250            for id,t,author,type,summary,field,oldvalue,newvalue in cursor: 
    268251                if not previous_update or (id,t,author) != previous_update[:3]: 
    269252                    if previous_update: 
    270                         ev = produce(previous_update, status, fields, 
    271                                      comment, cid) 
     253                        ev = produce_event(previous_update, status, fields, 
     254                                           comment, cid) 
    272255                        if ev: 
    273256                            yield ev 
    274257                    status, fields, comment, cid = 'edit', {}, '', None 
     
    281264                else: 
    282265                    fields[field] = newvalue 
    283266            if previous_update: 
    284                 ev = produce(previous_update, status, fields, comment, cid) 
     267                ev = produce_event(previous_update, status, fields, 
     268                                   comment, cid) 
    285269                if ev: 
    286270                    yield ev 
    287271 
     
    292276                               "  FROM ticket WHERE time>=%s AND time<=%s", 
    293277                               (ts_start, ts_stop)) 
    294278                for row in cursor: 
    295                     ev = produce(row, 'new', {}, None, None) 
     279                    ev = produce_event(row, 'new', {}, None, None) 
    296280                    if ev: 
    297281                        yield ev 
    298282 
     
    302286                    req, ticket_realm, start, stop): 
    303287                    yield event 
    304288 
    305     def event_formatter(self, event, key): 
    306         flavor = 'oneliner' 
    307         if event.kind == 'newticket': 
    308             flavor = self.timeline_newticket_formatter 
    309         return (flavor, {}) 
     289    def render_timeline_event(self, context, field, event): 
     290        ticket, verb, info, summary, status, resolution, type, \ 
     291                description, comment, cid = event[3] 
     292        if field == 'url': 
     293            href = context.href.ticket(ticket.id) 
     294            if cid: 
     295                href += '#comment:' + cid 
     296            return href 
     297        elif field == 'title': 
     298            title = TicketSystem(self.env).format_summary(summary, status, 
     299                                                          resolution, type) 
     300            return tag('Ticket ', tag.em('#', ticket.id, title=title), 
     301                       ' (', shorten_line(summary), ') ', verb) 
     302        elif field == 'description': 
     303            descr = message = '' 
     304            if status == 'new': 
     305                message = description 
     306                flavor = self.timeline_newticket_formatter 
     307            else: 
     308                descr = info 
     309                message = comment 
     310                flavor = 'oneliner' 
     311            if message: 
     312                if self.config['timeline'].getbool('abbreviated_messages'): 
     313                    message = shorten_line(message) 
     314                descr += format_to(flavor, context(resource=ticket), message) 
     315            return descr 
    310316 
    311317    # Internal methods 
    312318 
  • trac/timeline/api.py

    diff -r 00135419c47f -r f08a0d1b8960 trac/timeline/api.py
    a b  
    2525from trac.web.href import Href 
    2626 
    2727 
    28 class TimelineEvent(object): 
    29     """Group event related information. 
    30  
    31     WARNING: this interface is going to be overhauled 
    32  
    33     The first two properties are set in the constructor: 
    34  
    35     provider: reference to the event provider 
    36  
    37     kind: category of the event, will also be used as the CSS class for 
    38           the event's entry in the timeline 
    39  
    40     The following is set using the `add_markup` method. 
    41      
    42     markup: dictionary of litteral informations regarding the events. 
    43             Standard keys include: 
    44              - 'title': short summary for the event 
    45              - 'header': markup that comes before the main body 
    46              - 'footer': markup that comes after the main body 
    47  
    48     The next two are set using the `add_wiki` method. 
    49      
    50     resource: resource context 
    51     wikitext: dictionary of contextual information 
    52               Standard keys include: 
    53               `body` will be interpreted as the main text 
    54               `summary` 
    55  
    56     The next four are set using the `set_changeinfo` method. 
    57      
    58     date, author, authenticated, ipnr: 
    59              date and authorship info for the event; 
    60              `date` is a datetime instance 
    61  
    62     Other properties: 
    63  
    64     href_fragment: optional fragment that will position to some place 
    65                    within the resource page 
    66  
    67     direct_href: direct link to the event,, if there's no resource associated 
    68                  to it 
    69     """ 
    70  
    71     def __init__(self, *args, **kwargs): 
    72         """`TimelineEvent(provider, kind)` creates an event. 
    73  
    74         `provider` is the Component which provided the event and 
    75         `kind` is the specific sub-type of this event. 
    76  
    77         Note that 0.11dev API introduced originally another signature: 
    78         `(self, kind, title='', href=None, markup=None)` 
    79         We'll also stay compatible with the above until 0.12. 
    80         """ 
    81         self.markup = {} 
    82         self.wikitext = {} 
    83         self.author = 'unknown' 
    84         self.date = self.authenticated = self.ipnr = None 
    85         self.resource = None 
    86         self.href_fragment = '' 
    87         if isinstance(args[0], Component): 
    88             self.provider = args[0] 
    89             self.kind = args[1] 
    90             self.env = self.provider.env 
    91         else: 
    92             self.kind = args[0] 
    93             class DummyProvider(object): 
    94                 def event_formatter(self, event, key): 
    95                     return ('oneliner', {'shorten': True}) 
    96             self.provider = DummyProvider() 
    97             title = len(args) > 1 and args[1] or kwargs.get('title') 
    98             href = len(args) > 2 and args[2] or kwargs.get('href') 
    99             markup = len(args) > 3 and args[3] or kwargs.get('markup') 
    100             self.direct_href = href 
    101             if title: 
    102                 self.markup['title'] = title 
    103             if markup: 
    104                 self.markup['header'] = markup 
    105  
    106     def get_href(self, href=None): 
    107         if self.resource: 
    108             return get_url(self.env, self.resource, href) + self.href_fragment 
    109         else: 
    110             return self.direct_href 
    111  
    112     def __repr__(self): 
    113         return '<TimelineEvent %s - %r>' % (self.date, 
    114                                             self.resource or self.direct_href) 
    115  
    116     def set_changeinfo(self, date, author='anonymous', authenticated=None, 
    117                        ipnr=None): 
    118         self.date = date 
    119         self.author = author 
    120         self.authenticated = authenticated 
    121         self.ipnr = ipnr 
    122  
    123     def add_markup(self, **kwargs): 
    124         """Populate the markup dictionary.""" 
    125         for k, v in kwargs.iteritems(): 
    126             if v: 
    127                 self.markup[k] = v 
    128  
    129     def add_wiki(self, resource, **kwargs): 
    130         """Populate the wikitext dictionary.""" 
    131         self.resource = resource 
    132         for k, v in kwargs.iteritems(): 
    133             if v: 
    134                 self.wikitext[k] = v 
    135  
    136     def dateuid(self): 
    137         return to_timestamp(self.date) 
    138  
    139  
    140  
    14128class ITimelineEventProvider(Interface): 
    14229    """Extension point interface for adding sources for timed events to the 
    14330    timeline. 
     
    16148        The `filters` parameters is a list of the enabled filters, each item 
    16249        being the name of the tuples returned by `get_timeline_filters`. 
    16350 
    164         Since 0.11, the events are TimelineEvent instances. 
     51        Since 0.11, the events are `(kind, date, author, data)` tuples, 
     52        where `kind` is a string used for categorizing the event, `date` 
     53        is a `datetime` object, `author` is a string and `data` is some 
     54        private data that the component will reuse when rendering the event. 
    16555 
    166         Note: 
    167         The events returned by this function used to be tuples of the form 
    168         (kind, href, title, date, author, markup). This is now deprecated. 
     56        When the event has been created indirectly by another module, 
     57        like this happens when calling `AttachmentModule.get_timeline_events()` 
     58        the tuple can also specify explicitly the provider by returning tuples 
     59        of the following form: `(kind, date, author, data, provider)`. 
     60 
     61        Before version 0.11,  the events returned by this function used to 
     62        be tuples of the form `(kind, href, title, date, author, markup)`. 
     63        This is still supported but less flexible, as `href`, `title` and 
     64        `markup` are not context dependent. 
    16965        """ 
    17066 
    171     def event_formatter(event, wikitext_key): 
    172         """For a given key (as found in the TimelineEvent.wikitext dictionary), 
    173         specify which formatter flavor and options should be used. 
     67    def render_timeline_event(context, field, event): 
     68        """Display the title of the event in the given context. 
    17469 
    175         Returning `('oneliner', {})` is a safe choice and returning `None` 
    176         will let the template decide. 
     70        :param context: the rendering `Context` object that can be used for 
     71                        rendering 
     72        :param field: what specific part information from the event should 
     73                      be rendered: can be the 'title', the 'description' or 
     74                      the 'url' 
     75        :param event: the event tuple, as returned by `get_timeline_events` 
    17776        """ 
     77 
     78 
  • trac/timeline/templates/timeline.html

    diff -r 00135419c47f -r f08a0d1b8960 trac/timeline/templates/timeline.html
    a b  
    4242          <py:for each="event in events" 
    4343            py:with="highlight = precision and precisedate and timedelta(0) &lt;= (event.date - precisedate) &lt; precision"> 
    4444            <dt class="${classes(event.kind, highlight=highlight)}"> 
    45               <a href="${event.get_href(href)}"> 
    46                 <span class="time">${format_time(event.date, str('%H:%M'))}</span> ${event.markup.get('title')} 
     45              <a href="${event.render('url', context)}"> 
     46                <span class="time">${format_time(event.date, str('%H:%M'))}</span> ${event.render('title', context)} 
    4747                <py:if test="event.author">by <span class="author">${format_author(event.author)}</span></py:if> 
    4848              </a> 
    4949            </dt> 
    50             <!--! TODO: move the generation of the description back into the components --> 
    5150            <dd class="${classes(event.kind, highlight=highlight)}"> 
    52               ${event.markup.get('header')} 
    53               <py:for each="key in event.wikitext"> 
    54                 <py:with vars="flavor, options = event.provider.event_formatter(event, key) or ('oneliner', {})"><?python 
    55                   if flavor == 'oneliner' and 'shorten' not in options: 
    56                       options['shorten'] = abbreviated_messages 
    57                   ?> 
    58                   ${wiki_to(flavor, context(event.resource), event.wikitext[key], **options)} 
    59                 </py:with> 
    60               </py:for> 
    61               ${event.markup.get('footer')} 
     51              ${event.render('description', context)} 
    6252            </dd> 
    6353          </py:for> 
    6454        </dl> 
  • trac/timeline/templates/timeline.rss

    diff -r 00135419c47f -r f08a0d1b8960 trac/timeline/templates/timeline.rss
    a b  
    1111    <generator>Trac ${trac.version}</generator> 
    1212    <image py:if="chrome.logo.src"> 
    1313      <title>${project.name}</title> 
    14       <url>${chrome.logo.src_abs and chrome.logo.src \ 
     14      <url>${chrome.logo.src_abs and chrome.logo.src 
    1515                                 or abs_href(chrome.logo.src)}</url> 
    1616      <link>${abs_href.timeline()}</link> 
    1717    </image> 
    1818 
    1919    <item py:for="event in events"> 
    20       <title>${plaintext(event.markup.get('summary') or event.markup.get('title'), keeplinebreaks=False)}</title> 
     20      <title>${plaintext(event.render('summary', context) or  
     21                         event.render('title', context), keeplinebreaks=False)}</title> 
    2122      ${author_or_creator(event.author, email_map)} 
    22       <pubDate>${http_date(event.date)}</pubDate> 
    23       <link>${event.get_href(abs_href)}</link> 
    24       <guid isPermaLink="false">${event.get_href(abs_href)}/${event.dateuid()}</guid> 
    25       <!--! TODO: move the generation of the description back to the components --> 
     23      <py:with vars="abs_url = event.render('url', abs_context)"> 
     24        <pubDate>${http_date(event.date)}</pubDate> 
     25        <link>${abs_url}</link> 
     26      </py:with> 
     27      <guid isPermaLink="false">${abs_url}/${event.dateuid()}</guid> 
    2628      <description>${ 
    27         unicode(event.markup.get('header', '')) 
    28       }<py:if test="'body' in event.wikitext">${unicode(wiki_to_html(context(event.resource, href=abs_href), event.wikitext.get('body')))}</py:if>${ 
    29         unicode(event.markup.get('footer', '')) 
     29        unicode(event.render('description', abs_context) 
    3030      }</description> 
    31        <category>$event.kind</category> 
     31      <category>$event.kind</category> 
    3232    </item> 
    3333 
    3434   </channel> 
  • trac/timeline/web_ui.py

    diff -r 00135419c47f -r f08a0d1b8960 trac/timeline/web_ui.py
    a b  
    2828from trac.config import IntOption, BoolOption 
    2929from trac.core import * 
    3030from trac.perm import IPermissionRequestor 
    31 from trac.timeline.api import ITimelineEventProvider, TimelineEvent 
     31from trac.timeline.api import ITimelineEventProvider 
    3232from trac.util.compat import sorted 
    3333from trac.util.datefmt import format_date, format_datetime, parse_date, \ 
    3434                              to_timestamp, utc, pretty_timedelta 
     
    122122        filters = [] 
    123123        # check the request or session for enabled filters, or use default 
    124124        for test in (lambda f: f[0] in req.args, 
    125                      lambda f: req.session.get('timeline.filter.%s' % f[0], '')\ 
    126                                == '1', 
     125                     lambda f: req.session.get('timeline.filter.%s' % f[0], 
     126                                               '') == '1', 
    127127                     lambda f: len(f) == 2 or f[2]): 
    128128            if filters: 
    129129                break 
     
    147147            try: 
    148148                for event in provider.get_timeline_events(req, start, stop, 
    149149                                                          filters): 
    150                     # compatibility with 0.10 providers 
    151                     if isinstance(event, tuple): 
    152                         event = self._event_from_tuple(req, event) 
    153                     events.append(event) 
     150                    events.append(self._event_data(provider, event)) 
    154151            except Exception, e: # cope with a failure of that provider 
    155152                self._provider_failure(e, req, provider, filters, 
    156153                                       [f[0] for f in available_filters]) 
    157154 
    158         events = sorted(events, key=lambda e: e.date, reverse=True) 
    159  
    160155        # prepare sorted global list 
     156        events = sorted(events, key=lambda e: e['date'], reverse=True) 
    161157        if maxrows: 
    162             data['events'] = events[:maxrows] 
    163         else: 
    164             data['events'] = events 
     158            events = events[:maxrows] 
     159 
     160        data['events'] = events 
     161         
    165162 
    166163        if format == 'rss': 
    167164            # Get the email addresses of all known users 
     
    260257 
    261258    # Internal methods 
    262259 
    263     def _event_from_tuple(self, req, event): 
    264         """Build a TimelineEvent from a pre-0.11 ITimelineEventProvider tuple 
    265         """ 
    266         kind, href, title, date, author, markup = event 
    267         if not isinstance(date, datetime): 
     260    def _event_data(self, provider, event): 
     261        """Compose the timeline event date from the event tuple and prepared 
     262        provider methods""" 
     263        if len(event) == 6: # 0.10 events 
     264            kind, href, title, date, author, markup = event 
     265            fields = {'href': href, 'title': title, 'description': markup} 
     266            render = lambda field, context: fields.get(field) 
     267        else: # 0.11 events 
     268            if len(event) == 5: # with special provider 
     269                kind, date, author, data, provider = event 
     270            else: 
     271                kind, date, author, data = event 
     272            render = lambda field, context: provider.render_timeline_event( 
     273                context, field, event) 
     274        if isinstance(date, datetime): 
     275            dateuid = to_timestamp(date) 
     276        else: 
     277            dateuid = date 
    268278            date = datetime.fromtimestamp(date, utc) 
    269         if href and href.startswith(req.abs_href.base): 
    270             href = urlparse(href)[2] 
    271         event = TimelineEvent(kind, title, href, markup) 
    272         event.set_changeinfo(date, author) 
    273         return event 
     279        return {'kind': kind, 'author': author, 'date': date, 
     280                'dateuid': dateuid, 'render': render} 
    274281 
    275282    def _provider_failure(self, exc, req, ep, current_filters, all_filters): 
    276283        """Raise a TracError exception explaining the failure of a provider. 
  • trac/versioncontrol/web_ui/changeset.py

    diff -r 00135419c47f -r f08a0d1b8960 trac/versioncontrol/web_ui/changeset.py
    a b  
    3333from trac.mimeview import Mimeview, is_binary 
    3434from trac.perm import IPermissionRequestor 
    3535from trac.search import ISearchSource, search_to_sql, shorten_result 
    36 from trac.timeline.api import ITimelineEventProvider, TimelineEvent 
     36from trac.timeline.api import ITimelineEventProvider 
    3737from trac.util import embedded_numbers, content_disposition 
    3838from trac.util.compat import any, sorted, groupby 
    3939from trac.util.datefmt import pretty_timedelta, utc 
     
    4747from trac.web.chrome import add_link, add_script, add_stylesheet, \ 
    4848                            INavigationContributor, Chrome 
    4949from trac.wiki import IWikiSyntaxProvider, WikiParser 
     50from trac.wiki.formatter import format_to_html 
    5051 
    5152 
    5253class IPropertyDiffRenderer(Interface): 
     
    763764                show_files = int(show_files) 
    764765            else: 
    765766                show_files = 0 # disabled 
    766             wiki_format = self.wiki_format_messages 
    767             long_messages = self.timeline_long_messages 
    768767             
    769768            repos = self.env.get_repository(req.authname) 
    770769 
     
    775774                 
    776775            for _, changesets in groupby(repos.get_changesets(start, stop), 
    777776                                         key=collapse_changesets): 
    778                 changesets = list(changesets) 
    779                 chgset = changesets[-1] 
    780                 if not 'CHANGESET_VIEW' in req.perm('changeset', chgset.rev): 
    781