Edgewall Software

Ticket #153: privacy-r4476.diff

File privacy-r4476.diff, 28.4 KB (added by cboos, 2 years ago)

Updated wkornewald's patch to use macros in templates

  • trac/ticket/api.py

     
    102102        field = {'name': 'owner', 'label': 'Owner'} 
    103103        if self.restrict_owner: 
    104104            field['type'] = 'select' 
    105             users = [''] # for clearing assignment 
    106105            perm = PermissionSystem(self.env) 
    107             for username, name, email in self.env.get_known_users(db): 
    108                 if perm.get_user_permissions(username).get('TICKET_MODIFY'): 
    109                     users.append(username) 
    110             field['options'] = users 
     106            def valid_owner(username): 
     107                return perm.get_user_permissions(username).get('TICKET_MODIFY') 
     108            field['options'] = [username for username, name, email 
     109                                in self.env.get_known_users() 
     110                                if valid_owner(username)] 
    111111            field['optional'] = True 
    112112        else: 
    113113            field['type'] = 'text' 
  • trac/ticket/report.py

     
    276276                header_groups.append([]) 
    277277            header_group.append(header) 
    278278 
    279         # Get the email addresses of all known users 
    280         email_map = {} 
    281         for username, name, email in self.env.get_known_users(): 
    282             if email: 
    283                 email_map[username] = email 
    284  
    285279        # Structure the rows and cells: 
    286280        #  - group rows according to __group__ value, if defined 
    287281        #  - group cells the same way headers are grouped 
     
    313307                    # Special casing based on column name 
    314308                    col = col.strip('_') 
    315309                    if col == 'reporter': 
    316                         if '@' in value: 
    317                             cell['author'] = value 
    318                         elif value in email_map: 
    319                             cell['author'] = email_map[value] 
     310                        cell['author'] = value 
    320311                    elif col == 'resource': 
    321312                        resource = value 
    322313                    cell_group.append(cell) 
     
    329320                row_groups = [(None, row_group)] 
    330321            row_group.append(row) 
    331322 
     323        # Get the email addresses of all known users 
     324        email_map = {} 
     325        if self.config.getbool('trac', 'show_email_addresses'): 
     326            for username, name, email in self.env.get_known_users(): 
     327                if email: 
     328                    email_map[username] = email 
     329 
    332330        data.update({'header_groups': header_groups, 
    333331                     'row_groups': row_groups, 
    334332                     'numrows': len(results), 
    335                      'sorting_enabled': len(row_groups)==1}) 
     333                     'sorting_enabled': len(row_groups)==1, 
     334                     'email_map': email_map}) 
    336335 
    337336        if id: 
    338337            self.add_alternate_links(req, args) 
  • trac/ticket/query.py

     
    684684        query.verbose = True 
    685685        db = self.env.get_db_cnx() 
    686686        results = query.execute(req, db) 
    687         for result in results: 
    688             if result['reporter'].find('@') == -1: 
    689                 result['reporter'] = '' 
    690687        query_href = req.abs_href.query(group=query.group, 
    691688                                        groupdesc=query.groupdesc and 1 or None, 
    692689                                        verbose=query.verbose and 1 or None, 
  • trac/versioncontrol/web_ui/log.py

     
    179179        changes = get_changes(repos, revs) 
    180180        extra_changes = {} 
    181181        email_map = {} 
     182        show_email_addresses = self.config.getbool('trac', 
     183                                                   'show_email_addresses') 
    182184        if format == 'rss': 
    183185            # Get the email addresses of all known users 
    184             email_map = {} 
    185             for username,name,email in self.env.get_known_users(): 
    186                 if email: 
    187                     email_map[username] = email 
     186            if show_email_addresses: 
     187                for username,name,email in self.env.get_known_users(): 
     188                    if email: 
     189                        email_map[username] = email 
    188190        elif format == 'changelog': 
    189191            for rev in revs: 
    190192                changeset = changes[rev] 
  • trac/perm.py

     
    246246    # IPermissionRequestor methods 
    247247 
    248248    def get_permission_actions(self): 
    249         """Implement the global `TRAC_ADMIN` meta permission.""" 
    250         actions = [] 
     249        """Implement the global `TRAC_ADMIN` meta permission and the 
     250        `EMAIL_VIEW` permission which allows for showing email addresses 
     251        when "show_email_addresses" is "false".""" 
     252        actions = ['EMAIL_VIEW'] 
    251253        for requestor in [r for r in self.requestors if r is not self]: 
    252254            for action in requestor.get_permission_actions(): 
    253255                if isinstance(action, tuple): 
    254256                    actions.append(action[0]) 
    255257                else: 
    256258                    actions.append(action) 
    257         return [('TRAC_ADMIN', actions)] 
     259        return [('TRAC_ADMIN', actions), 'EMAIL_VIEW'] 
    258260 
    259261 
    260262class PermissionCache(object): 
  • trac/timeline/web_ui.py

     
    139139        if format == 'rss': 
    140140            # Get the email addresses of all known users 
    141141            email_map = {} 
    142             for username, name, email in self.env.get_known_users(): 
    143                 if email: 
    144                     email_map[username] = email 
     142            if self.config.getbool('trac', 'show_email_addresses'): 
     143                for username, name, email in self.env.get_known_users(): 
     144                    if email: 
     145                        email_map[username] = email 
    145146            data['email_map'] = email_map 
    146147            return 'timeline.rss', data, 'application/rss+xml' 
    147148 
  • trac/web/chrome.py

     
    3434                      get_module_path 
    3535from trac.util.compat import partial, set 
    3636from trac.util.html import plaintext 
    37 from trac.util.text import pretty_size, shorten_line, unicode_quote_plus, \ 
    38                            to_unicode 
     37from trac.util.text import pretty_size, obfuscate_email_address, \ 
     38                           shorten_line, unicode_quote_plus, to_unicode 
    3939from trac.util.datefmt import pretty_timedelta, format_datetime, format_date, \ 
    4040                              format_time, http_date 
    4141from trac.web.api import IRequestHandler, HTTPNotFound 
     
    194194    logo_height = IntOption('header_logo', 'height', -1, 
    195195        """Height of the header logo image in pixels.""") 
    196196 
     197    show_email_addresses = BoolOption('trac', 'show_email_addresses', 'false', 
     198        """Show email addresses instead of usernames. If false, we obfuscate 
     199        email addresses (''since 0.11'').""") 
     200 
    197201    templates = None 
    198202 
    199203    # A dictionary of default context data for templates 
     
    464468                'logo': self.get_logo_data(self.env.abs_href), 
    465469            }) 
    466470 
     471        show_email_addresses = (self.show_email_addresses or not req or \ 
     472                                'EMAIL_VIEW' in req.perm) 
    467473        tzinfo = None 
    468474        if req: 
    469475            tzinfo = req.tz 
     
    474480            'href': req and req.href, 
    475481            'perm': req and req.perm, 
    476482            'authname': req and req.authname or '<trac>', 
     483            'show_email_addresses': show_email_addresses, 
     484            'format_author': partial(self._format_author, req), 
    477485 
    478486            # Date/time formatting 
    479487            'format_datetime': partial(format_datetime, tzinfo=tzinfo), 
     
    543551 
    544552        return stream.render(method, doctype=doctype) 
    545553 
     554    # Helpers 
     555 
     556    def _format_author(self, req, author): 
     557        if self.show_email_addresses or not req or 'EMAIL_VIEW' in req.perm: 
     558            return author 
     559        else: 
     560            return obfuscate_email_address(author) 
     561 
    546562    # Template filters 
    547563 
    548564    def _add_form_token(self, token): 
  • trac/util/text.py

     
    170170    except ImportError: 
    171171        return t 
    172172 
     173def obfuscate_email_address(address): 
     174    if address: 
     175        at = address.find('@') 
     176        if at != -1: 
     177            return address[:at] + "@..." + ((address[-1] == '>' and '>') or '') 
     178    return address 
    173179 
    174180# -- Conversion 
    175181 
  • templates/ticket_view.html

     
    5454               py:with="fields = [f for f in fields if not f.skip]"> 
    5555          <tr> 
    5656            <th id="h_reporter">Reported by:</th> 
    57             <td headers="h_reporter" class="searchable">${ticket.reporter}</td> 
     57            <td headers="h_reporter" class="searchable">${authorinfo(ticket.reporter)}</td> 
    5858            <th id="h_owner">Assigned to:</th> 
    59             <td headers="h_owner">${ticket.owner} 
     59            <td headers="h_owner">${authorinfo(ticket.owner)} 
    6060              <py:if test="ticket.status == 'assigned'">(accepted)</py:if> 
    6161            </td> 
    6262          </tr> 
     
    8585              Description 
    8686              <span py:if="description_change" class="lastmod" 
    8787                title="$description_change.date"> 
    88                 (last modified by ${description_change.author}) 
     88                (last modified by ${authorinfo(description_change.author)}) 
    8989                (<a href="${href.ticket(ticket.id, action='diff', version=description_change.cnum)}">diff</a>) 
    9090              </span> 
    9191            </h3> 
     
    127127                  </py:if> 
    128128                  &nbsp; 
    129129                </span> 
    130                 ${change.date} changed by ${change.author} 
     130                ${change.date} changed by ${authorinfo(change.author)} 
    131131              </h3> 
    132132              <ul py:if="change.fields" class="changes"> 
    133133                <li py:for="field_name, field in change.fields.items()" 
  • templates/report.rss

     
    11<?xml version="1.0"?> 
    2 <rss version="2.0" xmlns:py="http://genshi.edgewall.org/"> 
     2<rss version="2.0" xmlns:py="http://genshi.edgewall.org/" 
     3                   xmlns:dc="http://purl.org/dc/elements/1.1/" 
     4                   xmlns:xi="http://www.w3.org/2001/XInclude"> 
     5  <xi:include href="macros.rss" /> 
    36  <channel> 
    47    <title>$project.name: $report.title</title> 
    58    <link>${abs_href.report(report.id)}</link> 
     
    1720        <py:with vars="col = cell.header.col.strip('_')"> 
    1821          <py:choose> 
    1922            <py:when test="col == 'reporter'"> 
    20               <author py:if="cell.author">$cell.author</author> 
     23              ${author_or_creator(cell.author, email_map)} 
    2124            </py:when> 
    2225            <py:when test="col in ('time', 'changetime', 'created', 'modified')"> 
    2326              <!-- FIXME: we end up with multiple pubDate --> 
  • templates/browser.html

     
    9898                  </td> 
    9999                  <td class="age">${dateinfo(change.date)}</td> 
    100100                  <td class="change"> 
    101                     <span class="author">$change.author:</span> 
     101                    <span class="author">${authorinfo(change.author)}:</span> 
    102102                    <span class="change" py:choose=""> 
    103103                      <py:when test="wiki_format_messages"> 
    104104                        ${context('changeset', change.rev).wiki_to_oneliner(change.message, shorten=True)} 
     
    117117        <tr py:if="file"> 
    118118          <th scope="col"> 
    119119            Revision <a href="${href.changeset(rev)}">$rev</a>, ${sizeinfo(file.size)} 
    120             (checked in by $file.changeset.author, ${dateinfo(file.changeset.date)} ago) 
     120            (checked in by ${authorinfo(file.changeset.author)}, ${dateinfo(file.changeset.date)} ago) 
    121121          </th> 
    122122        </tr> 
    123123        <tr py:if="file"> 
  • templates/wiki_history.html

     
    4747                <a href="${href.wiki(page.name, version=item.version)}" title="View this version">$item.version</a> 
    4848              </td> 
    4949              <td class="date">${format_datetime(item.date)}</td> 
    50               <td class="author" title="${item.ipnr and 'IP-Address: ' + item.ipnr or None">$item.author</td> 
     50              <td class="author" title="${item.ipnr and 'IP-Address: ' + item.ipnr or None">${authorinfo(item.author)}</td> 
    5151              <td class="comment">${context.wiki_to_oneliner(item.comment, shorten=True)}</td> 
    5252            </tr> 
    5353          </tbody> 
  • templates/macros.html

     
    2525      pretty_size(size) 
    2626  }</span></py:def> 
    2727 
     28  <!--!  Display author information, eventually obfuscating the e-mail address 
     29  - 
     30  -      We take care to not insert any extra space. 
     31  --> 
     32  <py:def function="authorinfo(author, email_map=None)"><py:choose><py:when test="author"><py:with 
     33    vars="author = show_email_addresses and email_map and '@' not in author and email_map[author] or author">${ 
     34      author and format_author(author) or 'anonymous' 
     35  }</py:with></py:when><py:otherwise>anonymous</py:otherwise></py:choose></py:def> 
     36 
    2837  <!--!  Display how many time elapsed since the given date. 
    2938  - 
    3039  -      Typically used in sentences like "changed ${dateinfo(date)} ago" 
     
    151160    <py:def function="show_one_attachment(attachment)"> 
    152161      <a href="${context.href('attachment', context.resource, context.id, attachment.filename)}" 
    153162         title="View attachment">$attachment.filename</a> 
    154         (${sizeinfo(attachment.size)}) - added by <em>$attachment.author</em> 
     163       (${sizeinfo(attachment.size)}) - added by <em>${authorinfo(attachment.author)}</em> 
    155164        ${dateinfo(attachment.date)} ago. 
    156165    </py:def> 
    157166    <py:choose test=""> 
  • templates/macros.rss

     
     1<?xml version="1.0"?> 
     2<rss version="2.0" xmlns:py="http://genshi.edgewall.org/" 
     3                   xmlns:dc="http://purl.org/dc/elements/1.1/" py:strip=""> 
     4 
     5  <!--! Generate an <author> or a <dc:creator> tag, based on the presence 
     6  -     of an email or not in the author's information. 
     7  - 
     8  -     Assume 'show_email_addresses' to be available in the global data. 
     9  --> 
     10  <py:def function="author_or_creator(author, email_map=None)"> 
     11    <py:if test="author"> 
     12      <!--! Try our best to retrieve an email address if wanted and possible --> 
     13      <py:with vars="author = show_email_addresses and email_map and '@' not in author and email_map[author] or author"> 
     14        <py:choose> 
     15          <author py:when="show_email_addresses and '@' in author">${format_author(author)}</author> 
     16          <dc:creator py:otherwise="">${format_author(author)}</dc:creator> 
     17        </py:choose> 
     18      </py:with> 
     19    </py:if> 
     20  </py:def> 
     21</rss> 
  • templates/revisionlog.html

    Property changes on: templates\macros.rss
    ___________________________________________________________________
    Name: svn:eol-style
       + native
    
     
    131131                      [$item.rev]</a> 
    132132                  </td> 
    133133                  <td class="date" py:content="format_datetime(change.date)"/> 
    134                   <td class="author">$change.author</td> 
     134                  <td class="author">${authorinfo(change.author)}</td> 
    135135                  <td class="summary" py:choose=""> 
    136136                    <py:when test="verbose"></py:when> 
    137137                    <py:when test="wiki_format_messages"> 
  • templates/revisionlog.rss

     
    11<?xml version="1.0"?> 
    2 <rss version="2.0" xmlns:py="http://genshi.edgewall.org/"> 
     2<rss version="2.0" xmlns:py="http://genshi.edgewall.org/" 
     3                   xmlns:dc="http://purl.org/dc/elements/1.1/" 
     4                   xmlns:xi="http://www.w3.org/2001/XInclude"> 
     5  <xi:include href="macros.rss" /> 
    36  <channel py:with="log_href = abs_href.log(path, rev=rev)"> 
    47    <title>Revisions of $path</title> 
    58    <link>$log_href</link> 
     
    1316    </image> 
    1417 
    1518    <item py:for="item in items" py:with="change = changes[item.rev]; item_context = context('changeset', change.rev, abs_urls=True)"> 
    16       <author py:if="change.author" py:with="a = change.author">${a and '@' in a and a or email_map.get(a)}</author> 
     19      ${author_or_creator(change.author, email_map)} 
    1720      <pubDate>${http_date(change.date)}</pubDate> 
    1821      <title>Revision $item.rev: ${shorten_line(change.message)}</title> 
    1922      <link>${abs_href.changeset(rev, path)}</link> 
  • templates/search.html

     
    55      xmlns:py="http://genshi.edgewall.org/" 
    66      xmlns:xi="http://www.w3.org/2001/XInclude"> 
    77  <xi:include href="layout.html" /> 
     8  <xi:include href="macros.html" /> 
    89  <head> 
    910    <title>Search<py:if test="query"> Results</py:if></title> 
    1011    <py:if test="results"> 
     
    7475              <dt><a href="${result.href}" class="searchable">${result.title}</a></dt> 
    7576              <dd class="searchable">${result.excerpt}</dd> 
    7677              <dd> 
    77                 <span class="author">By ${result.author}</span> &mdash; 
     78                <span class="author">By ${authorinfo(result.author)}</span> &mdash; 
    7879                <span class="date">${result.date}</span> 
    7980                <py:if test="result.keywords"> 
    8081                  &mdash; <span class="keywords">Keywords: <em class="searchable">${result.keywords}</em></span> 
  • templates/timeline.html

     
    55      xmlns:py="http://genshi.edgewall.org/" 
    66      xmlns:xi="http://www.w3.org/2001/XInclude"> 
    77  <xi:include href="layout.html" /> 
     8  <xi:include href="macros.html" /> 
    89  <head> 
    910    <title>Timeline</title> 
    1011  </head> 
     
    3839          <py:for each="event in events"> 
    3940            <dt class="${event.kind}"><a href="${event.href}"> 
    4041              <span class="time">${format_time(event.date, str('%H:%M'))}</span> ${event.title} 
    41                 <py:if test="event.author">by ${event.author}</py:if> 
     42                <py:if test="event.author">by ${authorinfo(event.author)}</py:if> 
    4243            </a></dt> 
    4344            <dd class="${event.kind}" py:with="wikify = event.use_oneliner and partial(event.context.wiki_to_oneliner, shorten=event.shorten_oneliner) or event.context.wiki_to_html"> 
    4445              ${event.markup} 
  • templates/timeline.rss

     
    11<?xml version="1.0"?> 
    2 <rss version="2.0" xmlns:py="http://genshi.edgewall.org/"> 
     2<rss version="2.0" xmlns:py="http://genshi.edgewall.org/" 
     3                   xmlns:dc="http://purl.org/dc/elements/1.1/" 
     4                   xmlns:xi="http://www.w3.org/2001/XInclude"> 
     5  <xi:include href="macros.rss" /> 
    36  <channel> 
    47    <title>${project.name}</title> 
    58    <link>${abs_href.timeline()}</link> 
     
    1417 
    1518    <item py:for="event in events"> 
    1619      <title>${plaintext(event.title, keeplinebreaks=False)}</title> 
    17       <py:with vars="author=event.author; author = author and '@' in author and author or email_map.get(author)"> 
    18         <author py:if="author">$author</author> 
    19       </py:with> 
     20      ${author_or_creator(event.author, email_map)} 
    2021      <pubDate>${http_date(event.date)}</pubDate> 
    2122      <link>${event.abs_href}</link> 
    2223      <guid isPermaLink="false">${event.abs_href}/${event.dateuid()}</guid> 
  • templates/ticket.rss

     
    11<?xml version="1.0"?> 
    2 <rss version="2.0" xmlns:py="http://genshi.edgewall.org/"> 
     2<rss version="2.0" xmlns:py="http://genshi.edgewall.org/" 
     3                   xmlns:dc="http://purl.org/dc/elements/1.1/" 
     4                   xmlns:xi="http://www.w3.org/2001/XInclude"> 
     5  <xi:include href="macros.rss" /> 
    36  <channel py:with="abs_context = context(abs_urls=True)"> 
    47    <title>${project.name}: Ticket $title</title> 
    58    <link>${abs_href.ticket(ticket.id)}</link> 
     
    1316    <generator>Trac $trac.version</generator> 
    1417 
    1518    <item py:for="change in changes"> 
    16       <author py:if="change.author">$change.author</author> 
     19      ${author_or_creator(change.author)} 
    1720      <pubDate>${http_date(change.date)}</pubDate> 
    1821      <title>$change.title</title> 
    1922      <link>${abs_href.ticket(ticket.id)}<py:if test="change.cnum">#comment:$change.cnum</py:if></link> 
     
    3841          </py:for> 
    3942          &lt;/ul&gt; 
    4043        </py:if> 
    41         ${unicode(abs_context.wiki_to_html(change.comment, absurls=True))} 
     44        ${unicode(abs_context.wiki_to_html(change.comment))} 
    4245      </description> 
    4346      <category>Ticket</category> 
    4447    </item> 
  • templates/attachment.html

     
    7878            <tr> 
    7979              <th scope="col"> 
    8080                File $attachment.filename, ${sizeinfo(attachment.size)} 
    81                 (added by $attachment.author,  ${dateinfo(attachment.date)} ago) 
     81                (added by ${authorinfo(attachment.author)},  ${dateinfo(attachment.date)} ago) 
    8282              </th> 
    8383            </tr> 
    8484            <tr> 
  • templates/wiki_diff.html

     
    4646        <dt class="property author">Author:</dt> 
    4747        <dd class="author" py:choose=""> 
    4848          <em py:when="multi" class="multi">(multiple changes)</em> 
    49           <py:otherwise>$change.author <span py:if="change.ipnr" class="ipnr">(IP: $change.ipnr)</span></py:otherwise> 
     49          <py:otherwise>${authorinfo(change.author)} <span py:if="change.ipnr" class="ipnr">(IP: $change.ipnr)</span></py:otherwise> 
    5050        </dd> 
    5151        <dt class="property message">Comment:</dt> 
    5252        <dd class="message" py:choose=""> 
  • templates/wiki_view.html

     
    4141        <table id="info" summary="Revision info"> 
    4242          <tbody> 
    4343            <tr><th scope="row"> 
    44               Version $page.version (modified by $page.author, ${dateinfo(page.time)} ago) 
     44                Version $page.version (modified by ${authorinfo(page.author)}, ${dateinfo(page.time)} ago) 
    4545            </th></tr> 
    4646            <tr><td class="message"> 
    4747              ${context.wiki_to_html(page.comment or '--')} 
  • templates/changeset.html

     
    103103          <dt class="property time">Timestamp:</dt> 
    104104          <dd class="time">${format_datetime(changeset.date)} (${pretty_timedelta(changeset.date, None, 3600) or 'less than one hour'} ago)</dd> 
    105105          <dt class="property author">Author:</dt> 
    106           <dd class="author">${changeset.author or 'anonymous'}</dd> 
     106          <dd class="author">${authorinfo(changeset.author)}</dd> 
    107107          <py:for each="prop in changeset_properties"> 
    108108            <dt class="property $prop.htmlclass">$prop.name:</dt> 
    109109            <dd class="$prop.htmlclass" py:choose=""> 
  • templates/query_div.html

     
    5050                <td py:otherwise="" class="$name" py:choose=""> 
    5151                  <a py:when="name == 'summary'" href="$result.href" title="View ticket">$value</a> 
    5252                  <span py:when="isinstance(value, datetime)">${format_datetime(value)}</span> 
     53                  <span py:when="name in ('owner', 'reporter')">${authorinfo(value)}</span> 
    5354                  <span py:otherwise="">$value</span> 
    5455                </td> 
    5556              </py:with> 
  • templates/query.rss

     
    11<?xml version="1.0"?> 
    2 <rss version="2.0" xmlns:py="http://genshi.edgewall.org/"> 
     2<rss version="2.0" xmlns:py="http://genshi.edgewall.org/" 
     3                   xmlns:dc="http://purl.org/dc/elements/1.1/" 
     4                   xmlns:xi="http://www.w3.org/2001/XInclude"> 
     5  <xi:include href="macros.rss" /> 
    36  <channel> 
    47    <title>$project.name: Ticket Query</title> 
    58    <link>$query_href</link> 
     
    1619      <guid isPermaLink="false">$href</guid> 
    1720      <title>#$result.id: $result.summary</title> 
    1821      <pubDate py:if="result.time">${http_date(result.time)}</pubDate> 
    19       <author py:if="result.reporter">$result.reporter</author> 
     22      ${author_or_creator(result.reporter)} 
    2023      <description>${unicode(context('ticket', result.id, abs_urls=True).wiki_to_html(result.description))}</description> 
    2124      <category>Results</category> 
    2225      <comments>$href#changelog</comments> 
  • templates/revisionlog.txt

     
    66  
    77#for item in items 
    88  #with change = changes[item.rev]; extra = extra_changes[item.rev] 
    9 ${http_date(change.date)} $change.author [$item.rev] 
     9${http_date(change.date)} ${format_author(change.author)} [$item.rev] 
    1010    #for idx, file in enumerate(extra.files) 
    1111        * $file (${dict(edit='modified', add='added', delete='deleted', 
    1212                        copy='copied', move='moved')[extra.actions[idx]]}) 
    1313    #end 
     14 
    1415${verbose and change.message or shorten_line(change.message)} 
    15 --  
    16 ## TODO: blank lines in text templates are no working yet... 
     16## TODO: add some wrapping to the above message 
     17 
     18 
    1719  #end 
    1820#end 
  • templates/ticket_diff.html

     
    5050        <dt class="property author">Author:</dt> 
    5151        <dd class="author" py:choose=""> 
    5252          <em py:when="multi" class="multi">(multiple changes)</em> 
    53           <py:otherwise>$change.author <span py:if="change.ipnr" class="ipnr">(IP: $change.ipnr)</span></py:otherwise> 
     53          <py:otherwise>${authorinfo(change.author)} <span py:if="change.ipnr" class="ipnr">(IP: $change.ipnr)</span></py:otherwise> 
    5454        </dd> 
    5555        <dt class="property message">Comment:</dt> 
    5656        <dd class="message" py:choose="">