Edgewall Software

Ticket #7562: t7562-autoquery_tweaks-r7557.diff

File t7562-autoquery_tweaks-r7557.diff, 7.0 KB (added by osimons, 3 months ago)

Cumulative patch using new parse_query_string.

  • trac/ticket/templates/ticket.html

    diff --git a/trac/ticket/templates/ticket.html b/trac/ticket/templates/ticket.html
    a b  
    137137                                    not in ('type', 'owner')]"> 
    138138            <tr> 
    139139              <th id="h_reporter">Reported by:</th> 
    140               <td headers="h_reporter" class="searchable">${reporter_link}</td> 
     140              <td headers="h_reporter" class="searchable"> 
     141                ${defined('reporter_link') and reporter_link or authorinfo(ticket.reporter)} 
     142              </td> 
    141143              <th id="h_owner">Owned by:</th> 
    142               <td headers="h_owner">${owner_link} 
     144              <td headers="h_owner"> 
     145                ${defined('owner_link') and owner_link or authorinfo(ticket.owner)} 
    143146              </td> 
    144147            </tr> 
    145148            <tr py:for="row in group(fields, 2, lambda f: f.type != 'textarea')" 
  • trac/ticket/web_ui.py

    diff --git a/trac/ticket/web_ui.py b/trac/ticket/web_ui.py
    a b  
    4444from trac.util.presentation import separated 
    4545from trac.util.translation import _, tag_, N_, gettext 
    4646from trac.versioncontrol.diff import get_diff_options, diff_blocks 
    47 from trac.web import IRequestHandler 
     47from trac.web import parse_query_string, IRequestHandler 
    4848from trac.web.chrome import add_link, add_script, add_stylesheet, \ 
    4949                            add_warning, add_ctxtnav, prevnext_nav, Chrome, \ 
    5050                            INavigationContributor, ITemplateProvider 
     
    124124        If set to 'default', this is equivalent to 'yes' for new environments 
    125125        but keeps the old behavior for upgraded environments (i.e. 'no'). 
    126126        (''since 0.11'').""") 
    127      
    128     unlinked_fields = ListOption('ticket', 'unlinked_fields',  
    129                                  default=['estimatedhours', 'hours', 'totalhours'], 
    130                                  doc="fields to exclude from AutoQuery markup") 
     127 
     128    ticketlink_query = Option('query', 'ticketlink_query', 
     129        default='?status=!closed',  
     130        doc="""The base query to be used when linkifying values of ticket 
     131            fields. The query is a URL query 
     132            string starting with `?` as used in `query:` 
     133            [TracQuery#UsingTracLinks Trac links]. 
     134            (''since 0.12'')""") 
    131135 
    132136    # IContentConverter methods 
    133137 
     
    10641068        for key in field_changes: 
    10651069            ticket[key] = field_changes[key]['new'] 
    10661070 
    1067     def _query_link(self, req, name, value): 
    1068         """return a link to /query with the appropriate name and value""" 
    1069         query = req.href('query', **{name:value}) 
    1070         args = self.env.config.get('query', 'default_anonymous_query') 
    1071         if args: 
    1072             query = '%s&%s' % (query, args) 
    1073         return tag.a(value, href=query) 
     1071    def _query_link(self, req, name, value, text=None): 
     1072        """Return a link to /query with the appropriate name and value""" 
     1073        args = parse_query_string(self.ticketlink_query) 
     1074        args[name] = value 
     1075        return tag.a(text or value, href=req.href.query(**args)) 
     1076 
     1077    def _query_link_words(self, req, name, value): 
     1078        """Splits a list of words and makes a query link to each separately""" 
     1079        if not isinstance(value, basestring): # None or other non-splitable 
     1080            return value 
     1081        args = parse_query_string(self.ticketlink_query) 
     1082        items = [] 
     1083        for (i, word) in enumerate(re.split(r'(\s*(?:\s|[,;])\s*)', value)): 
     1084            if i % 2: 
     1085                items.append(word) 
     1086            elif word: 
     1087                word_args = args.copy() 
     1088                word_args[name] = '~' + word 
     1089                items.append(tag.a(word, href=req.href.query(**word_args))) 
     1090        return tag(items) 
    10741091 
    10751092    def _prepare_fields(self, req, ticket): 
    10761093        context = Context.from_request(req, ticket.resource) 
     
    10791096            name = field['name'] 
    10801097            type_ = field['type'] 
    10811098  
    1082             # enable a link to custom query for the field 
    1083             if name not in self.unlinked_fields: 
     1099            # enable a link to custom query for all choice fields 
     1100            if type_ not in ['text', 'textarea']: 
    10841101                field['rendered'] = self._query_link(req, name, ticket[name]) 
    10851102 
    10861103            # per field settings 
     
    11171134                milestone = Resource('milestone', ticket[name]) 
    11181135                field['rendered'] = render_resource_link(self.env, context, 
    11191136                                                         milestone, 'compact') 
     1137            elif name == 'keywords': 
     1138                field['rendered'] = self._query_link_words( 
     1139                                                req, name, ticket[name]) 
    11201140            elif name == 'cc': 
    11211141                emails = Chrome(self.env).format_emails(context, ticket[name]) 
    1122                 field['rendered'] = emails 
     1142                field['rendered'] = emails == ticket[name] and \ 
     1143                        self._query_link_words(req, name, emails) or emails 
    11231144                if ticket.exists and \ 
    11241145                        'TICKET_EDIT_CC' not in req.perm(ticket.resource): 
    11251146                    cc = ticket._old.get('cc', ticket['cc']) 
     
    11481169            elif type_ == 'checkbox': 
    11491170                value = ticket.values.get(name) 
    11501171                if value in ('1', '0'): 
    1151                     field['rendered'] = value == '1' and _('yes') or _('no') 
     1172                    field['rendered'] = self._query_link(req, name, value, 
     1173                                value == '1' and _('yes') or _('no')) 
    11521174                   
    11531175            # ensure sane defaults 
    11541176            field.setdefault('optional', False) 
     
    12701292            ticket.values.update(values) 
    12711293 
    12721294        context = Context.from_request(req, ticket.resource) 
     1295 
     1296        # Display the owner and reporter links when not obfuscated 
     1297        chrome = Chrome(self.env) 
     1298        for user in 'reporter', 'owner': 
     1299            if chrome.format_author(req, ticket[user]) == ticket[user]: 
     1300                data['%s_link' % user] = self._query_link(req, user, 
     1301                                                            ticket[user]) 
     1302 
    12731303        data.update({ 
    12741304            'context': context, 
    12751305            'fields': fields, 'changes': changes, 
     
    12781308            'action_controls': action_controls, 
    12791309            'action': selected_action, 
    12801310            'change_preview': change_preview, 
    1281             'reporter_link': self._query_link(req, 'reporter', ticket['reporter']), 
    1282             'owner_link': self._query_link(req, 'owner', ticket['owner']) 
    12831311        }) 
    12841312 
    12851313    def rendered_changelog_entries(self, req, ticket, when=None): 
  • trac/web/api.py

    diff --git a/trac/web/api.py b/trac/web/api.py
    a b  
    103103def parse_query_string(query_string): 
    104104    """Parse a query string into a _RequestArgs.""" 
    105105    args = _RequestArgs() 
     106    if query_string.startswith('?'): 
     107        query_string = query_string.replace('?', '', 1) 
     108    if not query_string: 
     109        return args 
    106110    for arg in query_string.split('&'): 
    107111        nv = arg.split('=', 1) 
    108112        if len(nv) == 2: