Edgewall Software

Ticket #7316: 7316-timeline-render-hints-r7595.diff

File 7316-timeline-render-hints-r7595.diff, 15.5 KB (added by cboos, 3 months ago)

Use rendering hints in the context for telling the ITimelineEventProviders in which way they should format their wiki texts

  • trac/attachment.py

     
    4242                            INavigationContributor 
    4343from trac.web.href import Href 
    4444from trac.wiki.api import IWikiSyntaxProvider 
    45 from trac.wiki.formatter import format_to_oneliner 
     45from trac.wiki.formatter import format_to 
    4646 
    4747 
    4848class InvalidAttachment(TracError): 
     
    474474            return tag(tag.em(os.path.basename(attachment.id)), 
    475475                       _(" attached to "), tag.em(name, title=title)) 
    476476        elif field == 'description': 
    477             return format_to_oneliner(self.env, context(attachment.parent), 
    478                                       descr) 
     477            return format_to(self.env, None, context(attachment.parent), descr) 
    479478    
    480479    def get_search_results(self, req, resource_realm, terms): 
    481480        """Return a search result generator suitable for ISearchSource. 
  • trac/mimeview/api.py

     
    124124        self.resource = resource 
    125125        self.href = href 
    126126        self.perm = resource and perm and perm(resource) or perm 
     127        self._hints = None 
    127128 
    128129    def from_request(cls, req, resource=None, id=False, version=False, 
    129130                     absurls=False): 
     
    171172            context = context.parent 
    172173        return '<%s %s>' % (type(self).__name__, ' - '.join(reversed(path))) 
    173174 
    174     def __call__(self, resource, id=False, version=False): 
     175    def __call__(self, resource=None, id=False, version=False): 
    175176        """Create a nested rendering context. 
    176177 
    177178        `self` will be the parent for the new nested context. 
    178179 
    179180        :param resource: either a `Resource` object or the realm string for a 
    180181                         resource specification to be associated to the new 
    181                          context 
     182                         context. If `None`, the resource will be the same 
     183                         as the resource of the parent context. 
    182184        :param id: the identifier part of the resource specification 
    183185        :param version: the version of the resource specification 
    184186        :return: the new context object 
     
    190192        True 
    191193        >>> context(ticket1).resource is ticket1 
    192194        True 
     195        >>> context(ticket1)().resource is ticket1 
     196        True 
    193197        """ 
    194         resource = Resource(resource, id=id, version=version) 
     198        if resource: 
     199            resource = Resource(resource, id=id, version=version) 
     200        else: 
     201            resource = self.resource 
    195202        context = Context(resource, href=self.href, perm=self.perm) 
    196203        context.parent = self 
    197204 
     
    222229                return True 
    223230            context = context.parent 
    224231 
     232    # Rendering hints  
     233    # 
     234    # A rendering hint is a key/value pairs that can influence renderers, 
     235    # wiki formatters and processors in the way they produce their output. 
     236    # The keys are strings, but the values could be anything. 
     237    # 
     238    # In nested contexts, the hints are inherited from their parent context, 
     239    # unless overriden locally. 
    225240 
     241    def set_hints(self, **keyvalues): 
     242        """Set rendering hints for this rendering context. 
     243 
     244        >>> ctx = Context('timeline') 
     245        >>> ctx.set_hints(wiki_flavor='oneliner', shorten_lines=True) 
     246        >>> t_ctx = ctx('ticket', 1) 
     247        >>> t_ctx.set_hints(wiki_flavor='html', escape_newlines=True) 
     248        >>> (t_ctx.get_hint('wiki_flavor'), t_ctx.get_hint('shorten_lines'), \ 
     249             t_ctx.get_hint('escape_newlines')) 
     250        ('html', True, True) 
     251        >>> (ctx.get_hint('wiki_flavor'), ctx.get_hint('shorten_lines'), \ 
     252             ctx.get_hint('escape_newlines')) 
     253        ('oneliner', True, None) 
     254        """ 
     255        if self._hints is None: 
     256            self._hints = {} 
     257            hints = self._parent_hints() 
     258            if hints is not None: 
     259                self._hints.update(hints) 
     260        self._hints.update(keyvalues) 
     261 
     262    def get_hint(self, hint, default=None): 
     263        """Retrieve a rendering hint from this context or an ancestor context. 
     264 
     265        >>> ctx = Context('timeline') 
     266        >>> ctx.set_hints(wiki_flavor='oneliner') 
     267        >>> t_ctx = ctx('ticket', 1) 
     268        >>> t_ctx.get_hint('wiki_flavor') 
     269        'oneliner' 
     270        >>> t_ctx.get_hint('escape_newlines', True) 
     271        True 
     272        """ 
     273        hints = self._hints 
     274        if hints is None: 
     275            hints = self._parent_hints() 
     276            if hints is None: 
     277                return default 
     278        return hints.get(hint, default) 
     279 
     280    def has_hint(self, hint): 
     281        """Test whether a rendering hint is defined in this context or in some 
     282        ancestor context. 
     283 
     284        >>> ctx = Context('timeline') 
     285        >>> ctx.set_hints(wiki_flavor='oneliner') 
     286        >>> t_ctx = ctx('ticket', 1) 
     287        >>> t_ctx.has_hint('wiki_flavor') 
     288        True 
     289        >>> t_ctx.has_hint('escape_newlines') 
     290        False 
     291        """ 
     292        hints = self._hints 
     293        if hints is None: 
     294            hints = self._parent_hints() 
     295            if hints is None: 
     296                return False 
     297        return hint in hints 
     298 
     299    def _parent_hints(self): 
     300        p = self.parent 
     301        while p and p._hints is None: 
     302            p = p.parent 
     303        return p and p._hints 
     304 
     305 
    226306# Some common MIME types and their associated keywords and/or file extensions 
    227307 
    228308KNOWN_MIME_TYPES = { 
  • trac/ticket/web_ui.py

     
    113113 
    114114    timeline_newticket_formatter = Option('timeline', 'newticket_formatter', 
    115115                                          'oneliner', 
    116         """Which formatter flavor (e.g. 'default' or 'oneliner') should be 
     116        """Which formatter flavor (e.g. 'html' or 'oneliner') should be 
    117117        used when presenting the description for new tickets. 
    118118        If 'oneliner', the [timeline] abbreviated_messages option applies. 
    119119        (''since 0.11'').""") 
     
    125125        but keeps the old behavior for upgraded environments (i.e. 'no'). 
    126126        (''since 0.11'').""") 
    127127 
     128    def _must_preserve_newlines(self): 
     129        preserve_newlines = self.preserve_newlines 
     130        if preserve_newlines == 'default': 
     131            preserve_newlines = self.env.get_version(initial=True) >= 21 # 0.11 
     132        return preserve_newlines in _TRUE_VALUES 
     133    must_preserve_newlines = property(_must_preserve_newlines) 
     134 
    128135    # IContentConverter methods 
    129136 
    130137    def get_supported_conversions(self): 
     
    339346            descr = message = '' 
    340347            if status == 'new': 
    341348                message = description 
    342                 flavor = self.timeline_newticket_formatter 
    343349            else: 
    344350                descr = info 
    345351                message = comment 
    346                 flavor = 'oneliner' 
    347             if message: 
    348                 if self.config['timeline'].getbool('abbreviated_messages'): 
    349                     message = shorten_line(message) 
    350                 descr += format_to(self.env, flavor, context(resource=ticket), 
    351                                    message) 
    352             return descr 
     352            t_context = context(resource=ticket) 
     353            t_context.set_hints(escape_newlines=self.must_preserve_newlines) 
     354            if status == 'new' and \ 
     355                    context.get_hint('wiki_flavor') == 'oneliner':  
     356                flavor = self.timeline_newticket_formatter 
     357                t_context.set_hints(wiki_flavor=flavor, 
     358                                    shorten_lines=flavor == 'oneliner') 
     359            return descr + format_to(self.env, None, t_context, message) 
    353360 
    354361    # Internal methods 
    355362 
     
    578585        return 'ticket.html', data, None 
    579586 
    580587    def _prepare_data(self, req, ticket, absurls=False): 
    581         preserve_newlines = self.preserve_newlines 
    582         if preserve_newlines == 'default': 
    583             preserve_newlines = self.env.get_version(initial=True) >= 21 # 0.11 
    584         preserve_newlines = preserve_newlines in _TRUE_VALUES 
    585588        return {'ticket': ticket, 
    586589                'context': Context.from_request(req, ticket.resource, 
    587590                                                absurls=absurls), 
    588                 'preserve_newlines': preserve_newlines} 
     591                'preserve_newlines': self.must_preserve_newlines} 
    589592 
    590593    def _toggle_cc(self, req, cc): 
    591594        """Return an (action, recipient) tuple corresponding to a change 
  • trac/ticket/roadmap.py

     
    4242from trac.web.chrome import add_link, add_stylesheet, add_warning, \ 
    4343                            INavigationContributor 
    4444from trac.wiki.api import IWikiSyntaxProvider 
    45 from trac.wiki.formatter import format_to_html 
     45from trac.wiki.formatter import format_to 
    4646 
    4747class ITicketGroupStatsProvider(Interface): 
    4848    def get_ticket_group_stats(ticket_ids): 
     
    532532        elif field == 'title': 
    533533            return tag('Milestone ', tag.em(milestone.id), ' completed') 
    534534        elif field == 'description': 
    535             return format_to_html(self.env, context(resource=milestone), 
    536                                   shorten_line(description)) 
     535            return format_to(self.env, None, context(resource=milestone), 
     536                             description) 
    537537 
    538538    # IRequestHandler methods 
    539539 
  • trac/versioncontrol/web_ui/changeset.py

     
    4747from trac.web.chrome import add_ctxtnav, add_link, add_script, add_stylesheet, \ 
    4848                            prevnext_nav, INavigationContributor, Chrome 
    4949from trac.wiki import IWikiSyntaxProvider, WikiParser 
    50 from trac.wiki.formatter import format_to_html 
     50from trac.wiki.formatter import format_to 
    5151 
    5252 
    5353class IPropertyDiffRenderer(Interface): 
     
    824824                return context.href.log(rev=rev_b, stop_rev=rev_a) 
    825825             
    826826        elif field == 'description': 
    827             if not self.timeline_long_messages: 
    828                 message = shorten_line(message) 
    829827            if self.wiki_format_messages: 
    830828                markup = '' 
     829                if self.timeline_long_messages: # override default flavor 
     830                    context = context() 
     831                    context.set_hints(wiki_flavor='html') 
    831832            else: 
    832833                markup = message 
    833834                message = None 
     
    861862                        files = files[:show_files] + [tag.li(u'\u2026')] 
    862863                    markup = tag(tag.ul(files, class_="changes"), markup) 
    863864            if message: 
    864                 markup += format_to_html(self.env, context, message) 
     865                markup += format_to(self.env, None, context, message) 
    865866            return markup 
    866867 
    867868        if rev_a == rev_b: 
  • trac/wiki/web_ui.py

     
    4141                            INavigationContributor, ITemplateProvider 
    4242from trac.web import IRequestHandler 
    4343from trac.wiki.api import IWikiPageManipulator, WikiSystem 
    44 from trac.wiki.formatter import format_to_oneliner 
     44from trac.wiki.formatter import format_to 
    4545from trac.wiki.model import WikiPage 
    4646  
    4747class InvalidWikiPage(TracError): 
     
    600600            return tag(tag.em(get_resource_name(self.env, wiki_page)), 
    601601                       wiki_page.version > 1 and ' edited' or ' created') 
    602602        elif field == 'description': 
    603             if self.config['timeline'].getbool('abbreviated_messages'): 
    604                 comment = shorten_line(comment) 
    605             markup = format_to_oneliner(self.env, context(resource=wiki_page), 
    606                                         comment) 
     603            markup = format_to(self.env, None, context(resource=wiki_page), 
     604                               comment) 
    607605            if wiki_page.version > 1: 
    608606                diff_href = context.href.wiki( 
    609607                    wiki_page.id, version=wiki_page.version, action='diff') 
  • trac/wiki/formatter.py

     
    10851085 
    10861086 
    10871087def format_to(env, flavor, context, wikidom, **options): 
     1088    if flavor is None: 
     1089        flavor = context.get_hint('wiki_flavor', 'html') 
    10881090    if flavor == 'oneliner': 
    10891091        return format_to_oneliner(env, context, wikidom, **options) 
    10901092    else: 
    10911093        return format_to_html(env, context, wikidom, **options) 
    10921094 
    1093 def format_to_html(env, context, wikidom, escape_newlines=False): 
     1095def format_to_html(env, context, wikidom, escape_newlines=None): 
    10941096    if not wikidom: 
    10951097        return Markup() 
     1098    if escape_newlines is None: 
     1099        escape_newlines = context.get_hint('escape_newlines', False) 
    10961100    return HtmlFormatter(env, context, wikidom).generate(escape_newlines) 
    10971101 
    1098 def format_to_oneliner(env, context, wikidom, shorten=False): 
     1102def format_to_oneliner(env, context, wikidom, shorten=None): 
    10991103    if not wikidom: 
    11001104        return Markup() 
     1105    if shorten is None: 
     1106        shorten = context.get_hint('shorten_lines', False) 
    11011107    return InlineHtmlFormatter(env, context, wikidom).generate(shorten) 
    11021108 
    11031109def extract_link(env, context, wikidom): 
  • trac/timeline/web_ui.py

     
    5858        Timeline. (''since 0.11'')""") 
    5959 
    6060    abbreviated_messages = BoolOption('timeline', 'abbreviated_messages', 
    61                                       'true', 
     61                                      True, 
    6262        """Whether wiki-formatted event messages should be truncated or not. 
    6363 
    6464        This only affects the default rendering, and can be overriden by 
     
    179179                    if email: 
    180180                        email_map[username] = email 
    181181            data['email_map'] = email_map 
    182             data['context'] = Context.from_request(req, absurls=True) 
     182            rss_context = Context.from_request(req, absurls=True) 
     183            rss_context.set_hints(wiki_flavor='html', shorten_lines=False) 
     184            data['context'] = rss_context 
    183185            return 'timeline.rss', data, 'application/rss+xml' 
    184186        else: 
    185187            req.session['timeline.daysback'] = daysback 
     188            html_context = Context.from_request(req, absurls=True) 
     189            html_context.set_hints(wiki_flavor='oneliner',  
     190                                   shorten_lines=self.abbreviated_messages) 
     191            data['context'] = html_context 
    186192 
    187193        add_stylesheet(req, 'common/css/timeline.css') 
    188194        rss_href = req.href.timeline([(f, 'on') for f in filters], 
     
    284290                kind, date, author, data, provider = event 
    285291            else: 
    286292                kind, date, author, data = event 
    287             render = lambda field, context: provider.render_timeline_event( 
    288                 context, field, event) 
     293            render = lambda field, context: \ 
     294                    provider.render_timeline_event(context, field, event) 
    289295        if isinstance(date, datetime): 
    290296            dateuid = to_timestamp(date) 
    291297        else: