Ticket #153: privacy-r4476.diff
| File privacy-r4476.diff, 28.4 KB (added by cboos, 2 years ago) |
|---|
-
trac/ticket/api.py
102 102 field = {'name': 'owner', 'label': 'Owner'} 103 103 if self.restrict_owner: 104 104 field['type'] = 'select' 105 users = [''] # for clearing assignment106 105 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)] 111 111 field['optional'] = True 112 112 else: 113 113 field['type'] = 'text' -
trac/ticket/report.py
276 276 header_groups.append([]) 277 277 header_group.append(header) 278 278 279 # Get the email addresses of all known users280 email_map = {}281 for username, name, email in self.env.get_known_users():282 if email:283 email_map[username] = email284 285 279 # Structure the rows and cells: 286 280 # - group rows according to __group__ value, if defined 287 281 # - group cells the same way headers are grouped … … 313 307 # Special casing based on column name 314 308 col = col.strip('_') 315 309 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 320 311 elif col == 'resource': 321 312 resource = value 322 313 cell_group.append(cell) … … 329 320 row_groups = [(None, row_group)] 330 321 row_group.append(row) 331 322 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 332 330 data.update({'header_groups': header_groups, 333 331 'row_groups': row_groups, 334 332 'numrows': len(results), 335 'sorting_enabled': len(row_groups)==1}) 333 'sorting_enabled': len(row_groups)==1, 334 'email_map': email_map}) 336 335 337 336 if id: 338 337 self.add_alternate_links(req, args) -
trac/ticket/query.py
684 684 query.verbose = True 685 685 db = self.env.get_db_cnx() 686 686 results = query.execute(req, db) 687 for result in results:688 if result['reporter'].find('@') == -1:689 result['reporter'] = ''690 687 query_href = req.abs_href.query(group=query.group, 691 688 groupdesc=query.groupdesc and 1 or None, 692 689 verbose=query.verbose and 1 or None, -
trac/versioncontrol/web_ui/log.py
179 179 changes = get_changes(repos, revs) 180 180 extra_changes = {} 181 181 email_map = {} 182 show_email_addresses = self.config.getbool('trac', 183 'show_email_addresses') 182 184 if format == 'rss': 183 185 # 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] = email186 if show_email_addresses: 187 for username,name,email in self.env.get_known_users(): 188 if email: 189 email_map[username] = email 188 190 elif format == 'changelog': 189 191 for rev in revs: 190 192 changeset = changes[rev] -
trac/perm.py
246 246 # IPermissionRequestor methods 247 247 248 248 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'] 251 253 for requestor in [r for r in self.requestors if r is not self]: 252 254 for action in requestor.get_permission_actions(): 253 255 if isinstance(action, tuple): 254 256 actions.append(action[0]) 255 257 else: 256 258 actions.append(action) 257 return [('TRAC_ADMIN', actions) ]259 return [('TRAC_ADMIN', actions), 'EMAIL_VIEW'] 258 260 259 261 260 262 class PermissionCache(object): -
trac/timeline/web_ui.py
139 139 if format == 'rss': 140 140 # Get the email addresses of all known users 141 141 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 145 146 data['email_map'] = email_map 146 147 return 'timeline.rss', data, 'application/rss+xml' 147 148 -
trac/web/chrome.py
34 34 get_module_path 35 35 from trac.util.compat import partial, set 36 36 from trac.util.html import plaintext 37 from trac.util.text import pretty_size, shorten_line, unicode_quote_plus, \38 to_unicode37 from trac.util.text import pretty_size, obfuscate_email_address, \ 38 shorten_line, unicode_quote_plus, to_unicode 39 39 from trac.util.datefmt import pretty_timedelta, format_datetime, format_date, \ 40 40 format_time, http_date 41 41 from trac.web.api import IRequestHandler, HTTPNotFound … … 194 194 logo_height = IntOption('header_logo', 'height', -1, 195 195 """Height of the header logo image in pixels.""") 196 196 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 197 201 templates = None 198 202 199 203 # A dictionary of default context data for templates … … 464 468 'logo': self.get_logo_data(self.env.abs_href), 465 469 }) 466 470 471 show_email_addresses = (self.show_email_addresses or not req or \ 472 'EMAIL_VIEW' in req.perm) 467 473 tzinfo = None 468 474 if req: 469 475 tzinfo = req.tz … … 474 480 'href': req and req.href, 475 481 'perm': req and req.perm, 476 482 'authname': req and req.authname or '<trac>', 483 'show_email_addresses': show_email_addresses, 484 'format_author': partial(self._format_author, req), 477 485 478 486 # Date/time formatting 479 487 'format_datetime': partial(format_datetime, tzinfo=tzinfo), … … 543 551 544 552 return stream.render(method, doctype=doctype) 545 553 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 546 562 # Template filters 547 563 548 564 def _add_form_token(self, token): -
trac/util/text.py
170 170 except ImportError: 171 171 return t 172 172 173 def 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 173 179 174 180 # -- Conversion 175 181 -
templates/ticket_view.html
54 54 py:with="fields = [f for f in fields if not f.skip]"> 55 55 <tr> 56 56 <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> 58 58 <th id="h_owner">Assigned to:</th> 59 <td headers="h_owner">${ ticket.owner}59 <td headers="h_owner">${authorinfo(ticket.owner)} 60 60 <py:if test="ticket.status == 'assigned'">(accepted)</py:if> 61 61 </td> 62 62 </tr> … … 85 85 Description 86 86 <span py:if="description_change" class="lastmod" 87 87 title="$description_change.date"> 88 (last modified by ${ description_change.author})88 (last modified by ${authorinfo(description_change.author)}) 89 89 (<a href="${href.ticket(ticket.id, action='diff', version=description_change.cnum)}">diff</a>) 90 90 </span> 91 91 </h3> … … 127 127 </py:if> 128 128 129 129 </span> 130 ${change.date} changed by ${ change.author}130 ${change.date} changed by ${authorinfo(change.author)} 131 131 </h3> 132 132 <ul py:if="change.fields" class="changes"> 133 133 <li py:for="field_name, field in change.fields.items()" -
templates/report.rss
1 1 <?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" /> 3 6 <channel> 4 7 <title>$project.name: $report.title</title> 5 8 <link>${abs_href.report(report.id)}</link> … … 17 20 <py:with vars="col = cell.header.col.strip('_')"> 18 21 <py:choose> 19 22 <py:when test="col == 'reporter'"> 20 <author py:if="cell.author">$cell.author</author>23 ${author_or_creator(cell.author, email_map)} 21 24 </py:when> 22 25 <py:when test="col in ('time', 'changetime', 'created', 'modified')"> 23 26 <!-- FIXME: we end up with multiple pubDate --> -
templates/browser.html
98 98 </td> 99 99 <td class="age">${dateinfo(change.date)}</td> 100 100 <td class="change"> 101 <span class="author">$ change.author:</span>101 <span class="author">${authorinfo(change.author)}:</span> 102 102 <span class="change" py:choose=""> 103 103 <py:when test="wiki_format_messages"> 104 104 ${context('changeset', change.rev).wiki_to_oneliner(change.message, shorten=True)} … … 117 117 <tr py:if="file"> 118 118 <th scope="col"> 119 119 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) 121 121 </th> 122 122 </tr> 123 123 <tr py:if="file"> -
templates/wiki_history.html
47 47 <a href="${href.wiki(page.name, version=item.version)}" title="View this version">$item.version</a> 48 48 </td> 49 49 <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> 51 51 <td class="comment">${context.wiki_to_oneliner(item.comment, shorten=True)}</td> 52 52 </tr> 53 53 </tbody> -
templates/macros.html
25 25 pretty_size(size) 26 26 }</span></py:def> 27 27 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 28 37 <!--! Display how many time elapsed since the given date. 29 38 - 30 39 - Typically used in sentences like "changed ${dateinfo(date)} ago" … … 151 160 <py:def function="show_one_attachment(attachment)"> 152 161 <a href="${context.href('attachment', context.resource, context.id, attachment.filename)}" 153 162 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> 155 164 ${dateinfo(attachment.date)} ago. 156 165 </py:def> 157 166 <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
131 131 [$item.rev]</a> 132 132 </td> 133 133 <td class="date" py:content="format_datetime(change.date)"/> 134 <td class="author">$ change.author</td>134 <td class="author">${authorinfo(change.author)}</td> 135 135 <td class="summary" py:choose=""> 136 136 <py:when test="verbose"></py:when> 137 137 <py:when test="wiki_format_messages"> -
templates/revisionlog.rss
1 1 <?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" /> 3 6 <channel py:with="log_href = abs_href.log(path, rev=rev)"> 4 7 <title>Revisions of $path</title> 5 8 <link>$log_href</link> … … 13 16 </image> 14 17 15 18 <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)} 17 20 <pubDate>${http_date(change.date)}</pubDate> 18 21 <title>Revision $item.rev: ${shorten_line(change.message)}</title> 19 22 <link>${abs_href.changeset(rev, path)}</link> -
templates/search.html
5 5 xmlns:py="http://genshi.edgewall.org/" 6 6 xmlns:xi="http://www.w3.org/2001/XInclude"> 7 7 <xi:include href="layout.html" /> 8 <xi:include href="macros.html" /> 8 9 <head> 9 10 <title>Search<py:if test="query"> Results</py:if></title> 10 11 <py:if test="results"> … … 74 75 <dt><a href="${result.href}" class="searchable">${result.title}</a></dt> 75 76 <dd class="searchable">${result.excerpt}</dd> 76 77 <dd> 77 <span class="author">By ${ result.author}</span> —78 <span class="author">By ${authorinfo(result.author)}</span> — 78 79 <span class="date">${result.date}</span> 79 80 <py:if test="result.keywords"> 80 81 — <span class="keywords">Keywords: <em class="searchable">${result.keywords}</em></span> -
templates/timeline.html
5 5 xmlns:py="http://genshi.edgewall.org/" 6 6 xmlns:xi="http://www.w3.org/2001/XInclude"> 7 7 <xi:include href="layout.html" /> 8 <xi:include href="macros.html" /> 8 9 <head> 9 10 <title>Timeline</title> 10 11 </head> … … 38 39 <py:for each="event in events"> 39 40 <dt class="${event.kind}"><a href="${event.href}"> 40 41 <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> 42 43 </a></dt> 43 44 <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"> 44 45 ${event.markup} -
templates/timeline.rss
1 1 <?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" /> 3 6 <channel> 4 7 <title>${project.name}</title> 5 8 <link>${abs_href.timeline()}</link> … … 14 17 15 18 <item py:for="event in events"> 16 19 <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)} 20 21 <pubDate>${http_date(event.date)}</pubDate> 21 22 <link>${event.abs_href}</link> 22 23 <guid isPermaLink="false">${event.abs_href}/${event.dateuid()}</guid> -
templates/ticket.rss
1 1 <?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" /> 3 6 <channel py:with="abs_context = context(abs_urls=True)"> 4 7 <title>${project.name}: Ticket $title</title> 5 8 <link>${abs_href.ticket(ticket.id)}</link> … … 13 16 <generator>Trac $trac.version</generator> 14 17 15 18 <item py:for="change in changes"> 16 <author py:if="change.author">$change.author</author>19 ${author_or_creator(change.author)} 17 20 <pubDate>${http_date(change.date)}</pubDate> 18 21 <title>$change.title</title> 19 22 <link>${abs_href.ticket(ticket.id)}<py:if test="change.cnum">#comment:$change.cnum</py:if></link> … … 38 41 </py:for> 39 42 </ul> 40 43 </py:if> 41 ${unicode(abs_context.wiki_to_html(change.comment , absurls=True))}44 ${unicode(abs_context.wiki_to_html(change.comment))} 42 45 </description> 43 46 <category>Ticket</category> 44 47 </item> -
templates/attachment.html
78 78 <tr> 79 79 <th scope="col"> 80 80 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) 82 82 </th> 83 83 </tr> 84 84 <tr> -
templates/wiki_diff.html
46 46 <dt class="property author">Author:</dt> 47 47 <dd class="author" py:choose=""> 48 48 <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> 50 50 </dd> 51 51 <dt class="property message">Comment:</dt> 52 52 <dd class="message" py:choose=""> -
templates/wiki_view.html
41 41 <table id="info" summary="Revision info"> 42 42 <tbody> 43 43 <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) 45 45 </th></tr> 46 46 <tr><td class="message"> 47 47 ${context.wiki_to_html(page.comment or '--')} -
templates/changeset.html
103 103 <dt class="property time">Timestamp:</dt> 104 104 <dd class="time">${format_datetime(changeset.date)} (${pretty_timedelta(changeset.date, None, 3600) or 'less than one hour'} ago)</dd> 105 105 <dt class="property author">Author:</dt> 106 <dd class="author">${ changeset.author or 'anonymous'}</dd>106 <dd class="author">${authorinfo(changeset.author)}</dd> 107 107 <py:for each="prop in changeset_properties"> 108 108 <dt class="property $prop.htmlclass">$prop.name:</dt> 109 109 <dd class="$prop.htmlclass" py:choose=""> -
templates/query_div.html
50 50 <td py:otherwise="" class="$name" py:choose=""> 51 51 <a py:when="name == 'summary'" href="$result.href" title="View ticket">$value</a> 52 52 <span py:when="isinstance(value, datetime)">${format_datetime(value)}</span> 53 <span py:when="name in ('owner', 'reporter')">${authorinfo(value)}</span> 53 54 <span py:otherwise="">$value</span> 54 55 </td> 55 56 </py:with> -
templates/query.rss
1 1 <?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" /> 3 6 <channel> 4 7 <title>$project.name: Ticket Query</title> 5 8 <link>$query_href</link> … … 16 19 <guid isPermaLink="false">$href</guid> 17 20 <title>#$result.id: $result.summary</title> 18 21 <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)} 20 23 <description>${unicode(context('ticket', result.id, abs_urls=True).wiki_to_html(result.description))}</description> 21 24 <category>Results</category> 22 25 <comments>$href#changelog</comments> -
templates/revisionlog.txt
6 6 7 7 #for item in items 8 8 #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] 10 10 #for idx, file in enumerate(extra.files) 11 11 * $file (${dict(edit='modified', add='added', delete='deleted', 12 12 copy='copied', move='moved')[extra.actions[idx]]}) 13 13 #end 14 14 15 ${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 17 19 #end 18 20 #end -
templates/ticket_diff.html
50 50 <dt class="property author">Author:</dt> 51 51 <dd class="author" py:choose=""> 52 52 <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> 54 54 </dd> 55 55 <dt class="property message">Comment:</dt> 56 56 <dd class="message" py:choose="">
