Ticket #7228: 7228-download-source-r9975.patch
| File 7228-download-source-r9975.patch, 32.8 KB (added by rblank, 22 months ago) |
|---|
-
trac/attachment.py
diff --git a/trac/attachment.py b/trac/attachment.py
a b 809 809 title=get_resource_name(self.env, attachment)) 810 810 href = get_resource_url(self.env, attachment, formatter.href) 811 811 title = get_resource_name(self.env, attachment) 812 img = tag.img(src=formatter.href.chrome('common/download.png'),813 alt=_("Download"))814 812 return tag(tag.a(label, class_='attachment', title=title, 815 813 href=href + params), 816 tag.span(" ", 817 tag.a(img, class_='trac-rawlink', 818 href=raw_href + params, 819 title=_("Download")), 820 class_="noprint")) 814 tag.a(u'\u200b', class_='trac-rawlink', 815 href=raw_href + params, title=_("Download"))) 821 816 except ResourceNotFound: 822 817 pass 823 818 # FIXME: should be either: -
trac/htdocs/css/browser.css
diff --git a/trac/htdocs/css/browser.css b/trac/htdocs/css/browser.css
a b 63 63 vertical-align: middle; 64 64 font-size: 70%; 65 65 } 66 table.dirlist td.size a.trac-rawlink { 67 margin-left: .3em; 68 } 66 69 table.dirlist td.age { 67 70 border-width: 0 2px 0 0; 68 71 border-style: solid; 69 72 font-size: 85%; 70 73 } 71 table.dirlist td.name { width: 100% } 72 table.dirlist td.name a, table.dirlist td.name span { 73 background-position: 0% 50%; 74 background-repeat: no-repeat; 75 padding-left: 20px; 74 table.dirlist td.name { width: 100%; white-space: nowrap } 75 table.dirlist td.name a.parent { 76 background: url(../parent.png) 0 50% no-repeat; 77 padding-left: 20px; 76 78 } 77 table.dirlist td.name a.parent { background-image: url(../parent.png) }78 table.dirlist td.name div { white-space: pre }79 79 table.dirlist tr span.expander { 80 background -image: url(../expander_normal.png);80 background: url(../expander_normal.png) 0 50% no-repeat; 81 81 cursor: pointer; 82 82 padding-left: 8px; 83 83 margin-left: 4px; 84 84 } 85 85 table.dirlist tr span.expander:hover { 86 background -image: url(../expander_normal_hover.png);86 background: url(../expander_normal_hover.png) 0 50% no-repeat; 87 87 } 88 88 table.dirlist tr.expanded span.expander { 89 background -image: url(../expander_open.png);89 background: url(../expander_open.png) 0 50% no-repeat; 90 90 padding-left: 12px; 91 91 margin-left: 0; 92 92 } 93 93 table.dirlist tr.expanded span.expander:hover { 94 background -image: url(../expander_open_hover.png);94 background: url(../expander_open_hover.png) 0 50% no-repeat; 95 95 } 96 table.dirlist td.name a.dir { background-image: url(../folder.png) } 97 table.dirlist td.name a.file { background-image: url(../file.png); display: block } 96 table.dirlist td.name a.dir { 97 background: url(../folder.png) 0 50% no-repeat; 98 padding-left: 20px; 99 } 100 table.dirlist td.name a.file { 101 background: url(../file.png) 0 50% no-repeat; 102 padding-left: 20px; 103 } 98 104 table.dirlist td.name a, table.dirlist td.rev a { border-bottom: none } 99 105 table.dirlist td.author, table.dirlist td.change { font-size: 85% } 100 106 table.dirlist td.rev a.chgset { 101 background-repeat: no-repeat; 102 background-image: url(../changeset.png); 103 background-position: 100% 50%; 107 background: url(../changeset.png) 100% 50% no-repeat; 104 108 padding: 0 0 0 5px; 105 109 margin: 0 5px 0 0; 106 110 } 107 111 table.dirlist td.description { padding-left: 2em } 108 112 table.dirlist td.description > :first-child { margin-top: 0 } 109 113 table.dirlist td.description > :last-child { margin-bottom: 0 } 110 111 114 table.dirlist td span.loading { 112 background-image: url(../loading.gif); 115 background: url(../loading.gif) 0 50% no-repeat; 116 padding-left: 20px; 113 117 font-style: italic 114 118 } 115 119 -
trac/htdocs/css/trac.css
diff --git a/trac/htdocs/css/trac.css b/trac/htdocs/css/trac.css
a b 60 60 background: url(../envelope.png) center center no-repeat; 61 61 padding-left: 14px; 62 62 } 63 a.trac-rawlink { 64 background: url(../download.png) center center no-repeat; 65 padding-left: 21px; 66 } 63 67 } 64 68 65 69 /* Forms */ … … 616 620 @media print { 617 621 #header, #altlinks, #footer, #help { display: none } 618 622 .nav, form, .buttons form, form .buttons, form .inlinebuttons, 619 .noprint, .trac- rawlink, .trac-nav, .trac-topnav {623 .noprint, .trac-nav, .trac-topnav { 620 624 display: none; 621 625 } 622 626 form.printableform { display: block } -
trac/htdocs/js/expand_dir.js
diff --git a/trac/htdocs/js/expand_dir.js b/trac/htdocs/js/expand_dir.js
a b 41 41 var expander = $('<span class="expander"> </span>') 42 42 .attr("title", _("Expand sub-directory in place")) 43 43 .click(function() { toggleDir($(this), qargs); }) 44 a. wrap('<div></div>').before(expander);44 a.before(expander); 45 45 if (autoexpand && a.text() == autoexpand[0]) 46 46 autoexpand_expander = expander; 47 47 } -
trac/templates/list_of_attachments.html
diff --git a/trac/templates/list_of_attachments.html b/trac/templates/list_of_attachments.html
a b 16 16 foldable = value_of('foldable', False)" py:strip=""> 17 17 <py:def function="show_one_attachment(attachment)"> 18 18 <i18n:msg params="file, size, author, date"> 19 <a href="${url_of(attachment.resource)}" title="View attachment">$attachment.filename</a> 20 <a href="${url_of(attachment.resource, format='raw')}" class="trac-rawlink" 21 title="Download"><img src="${chrome.htdocs_location}download.png" alt="Download"/></a> 19 <a href="${url_of(attachment.resource)}" title="View attachment">${attachment.filename 20 }</a><a href="${url_of(attachment.resource, format='raw')}" class="trac-rawlink" title="Download">​</a> 22 21 (<span title="${_('%(size)s bytes', size=attachment.size)}">${pretty_size(attachment.size)}</span>) - 23 22 added by <em>${authorinfo(attachment.author)}</em> ${dateinfo(attachment.date)} ago. 24 23 </i18n:msg> -
trac/tests/wikisyntax.py
diff --git a/trac/tests/wikisyntax.py b/trac/tests/wikisyntax.py
a b 40 40 ------------------------------ 41 41 """ 42 42 43 ATTACHMENT_TEST_CASES = """43 ATTACHMENT_TEST_CASES = u""" 44 44 ============================== attachment: link resolver (deprecated) 45 45 attachment:wiki:WikiStart:file.txt (deprecated) 46 46 attachment:ticket:123:file.txt (deprecated) … … 48 48 [attachment:ticket:123:file.txt] (deprecated) 49 49 ------------------------------ 50 50 <p> 51 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:wiki:WikiStart:file.txt</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span> (deprecated)52 <a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">attachment:ticket:123:file.txt</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span> (deprecated)53 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">file.txt</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span> (deprecated)54 <a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">ticket:123:file.txt</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span> (deprecated)51 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:wiki:WikiStart:file.txt</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a> (deprecated) 52 <a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">attachment:ticket:123:file.txt</a><a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download">\u200b</a> (deprecated) 53 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">file.txt</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a> (deprecated) 54 <a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">ticket:123:file.txt</a><a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download">\u200b</a> (deprecated) 55 55 </p> 56 56 ------------------------------ 57 57 ============================== attachment: "foreign" links … … 62 62 attachment:foo.txt:wiki:SomePage/SubPage 63 63 ------------------------------ 64 64 <p> 65 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:file.txt:wiki:WikiStart</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>66 <a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">attachment:file.txt:ticket:123</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>67 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">file.txt</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>68 <a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">file.txt:ticket:123</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>69 <a class="attachment" href="/attachment/wiki/SomePage/SubPage/foo.txt" title="Attachment 'foo.txt' in SomePage/SubPage">attachment:foo.txt:wiki:SomePage/SubPage</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/SomePage/SubPage/foo.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>65 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:file.txt:wiki:WikiStart</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a> 66 <a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">attachment:file.txt:ticket:123</a><a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download">\u200b</a> 67 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">file.txt</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a> 68 <a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">file.txt:ticket:123</a><a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download">\u200b</a> 69 <a class="attachment" href="/attachment/wiki/SomePage/SubPage/foo.txt" title="Attachment 'foo.txt' in SomePage/SubPage">attachment:foo.txt:wiki:SomePage/SubPage</a><a class="trac-rawlink" href="/raw-attachment/wiki/SomePage/SubPage/foo.txt" title="Download">\u200b</a> 70 70 </p> 71 71 ------------------------------ 72 72 ============================== attachment: "local" links … … 74 74 [attachment:file.txt that file] 75 75 ------------------------------ 76 76 <p> 77 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:file.txt</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>78 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">that file</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>77 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:file.txt</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a> 78 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">that file</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a> 79 79 </p> 80 80 ------------------------------ 81 81 ============================== attachment: "missing" links … … 101 101 [attachment:file.txt?format=raw that file] 102 102 ------------------------------ 103 103 <p> 104 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt?format=raw" title="Attachment 'file.txt' in WikiStart">attachment:file.txt?format=raw</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt?format=raw" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>105 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt?format=raw" title="Attachment 'file.txt' in WikiStart">that file</a>< span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt?format=raw" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>104 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt?format=raw" title="Attachment 'file.txt' in WikiStart">attachment:file.txt?format=raw</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt?format=raw" title="Download">\u200b</a> 105 <a class="attachment" href="/attachment/wiki/WikiStart/file.txt?format=raw" title="Attachment 'file.txt' in WikiStart">that file</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt?format=raw" title="Download">\u200b</a> 106 106 </p> 107 107 ------------------------------ 108 108 """ # " -
trac/ticket/templates/ticket_change.html
diff --git a/trac/ticket/templates/ticket_change.html b/trac/ticket/templates/ticket_change.html
a b 17 17 <strong>${field.label}</strong> 18 18 <py:choose> 19 19 <py:when test="field_name == 'attachment'"><i18n:msg params="name"> 20 <a href="${href.attachment('ticket', ticket.id, field.new)}"><em>${field.new }</em></a>21 <a href="${href('raw-attachment', 'ticket', ticket.id, field.new)}"22 title="Download" class="trac-rawlink"><img src="${chrome.htdocs_location}download.png" alt="Download"/></a>20 <a href="${href.attachment('ticket', ticket.id, field.new)}"><em>${field.new 21 }</em></a><a href="${href('raw-attachment', 'ticket', ticket.id, field.new)}" 22 title="Download" class="trac-rawlink">​</a> 23 23 added 24 24 </i18n:msg></py:when> 25 25 <py:when test="'rendered' in field">${field.rendered}</py:when> -
trac/versioncontrol/templates/dir_entries.html
diff --git a/trac/versioncontrol/templates/dir_entries.html b/trac/versioncontrol/templates/dir_entries.html
a b 13 13 order=(order != 'name' and order or None), desc=desc)}">$entry.name</a> 14 14 </td> 15 15 <td class="size"> 16 <span title="${_('%(size)s bytes', size=entry.content_length)}">${pretty_size(entry.content_length)}</span> 16 <span title="${_('%(size)s bytes', size=entry.content_length)}">${pretty_size(entry.content_length) 17 }</span><a py:if="entry.raw_href" class="trac-rawlink" href="$entry.raw_href" 18 title="${entry.kind == 'dir' and _('Download as Zip archive') or _('Download')}">​</a> 17 19 </td> 18 20 <td class="rev"> 19 21 <a title="View Revision Log" href="${href.log(reponame, entry.path, rev=rev)}">${display_rev(entry.rev)}</a> -
trac/versioncontrol/templates/repository_index.html
diff --git a/trac/versioncontrol/templates/repository_index.html b/trac/versioncontrol/templates/repository_index.html
a b 5 5 <table class="listing dirlist" id="${repoindex or None}"> 6 6 <xi:include href="dirlist_thead.html" /> 7 7 <tbody> 8 <py:for each="idx, (reponame, repoinfo, repos, change, err ) in enumerate(repo.repositories)"8 <py:for each="idx, (reponame, repoinfo, repos, change, err, raw_href) in enumerate(repo.repositories)" 9 9 py:with="chgset_context = change and context('changeset', change.rev, parent=repos.resource); 10 10 chgset_view = change and change.can_view(perm)"> 11 11 <tr class="${idx % 2 and 'even' or 'odd'}"> … … 13 13 <em py:strip="not err"> 14 14 <b py:strip="repoinfo.alias != ''"> 15 15 <a class="dir" title="View Root Directory" 16 href="${href.browser(repos.reponame, order=(order != 'name' and order or None), desc=desc)}">$reponame</a>16 href="${href.browser(repos.reponame, order=(order != 'name' and order or None), desc=desc)}">$reponame</a> 17 17 </b> 18 18 </em> 19 19 </td> 20 <td class="size" /> 20 <td class="size"> 21 <a py:if="raw_href" class="trac-rawlink" href="$raw_href" title="Download as Zip archive">​</a> 22 </td> 21 23 <td class="rev"> 22 24 <py:if test="not err"> 23 25 <a title="View Revision Log" href="${href.log(repos.reponame)}">${repos.display_rev(change.rev)}</a> -
trac/versioncontrol/web_ui/browser.py
diff --git a/trac/versioncontrol/web_ui/browser.py b/trac/versioncontrol/web_ui/browser.py
a b 330 330 331 331 path = req.args.get('path', '/') 332 332 rev = req.args.get('rev', '') 333 if rev in ('', 'HEAD'):333 if rev.lower() in ('', 'head'): 334 334 rev = None 335 335 order = req.args.get('order', 'name').lower() 336 336 desc = req.args.has_key('desc') … … 499 499 timerange = TimeRange(youngest.date) 500 500 else: 501 501 timerange.insert(youngest.date) 502 entry = (reponame, repoinfo, repos, youngest, None) 502 raw_href = self._get_download_href(context.href, repos, 503 None, None) 504 entry = (reponame, repoinfo, repos, youngest, None, 505 raw_href) 503 506 else: 504 entry = (reponame, repoinfo, None, None, "XXX" )507 entry = (reponame, repoinfo, None, None, "XXX", None) 505 508 except TracError, err: 506 509 entry = (reponame, repoinfo, None, None, 507 exception_to_unicode(err) )510 exception_to_unicode(err), None) 508 511 repositories.append(entry) 509 512 510 513 # Ordering of repositories 511 514 if order == 'date': 512 def repo_order((reponame, repoinfo, repos, youngest, err )):515 def repo_order((reponame, repoinfo, repos, youngest, err, href)): 513 516 return youngest and youngest.date 514 517 else: 515 def repo_order((reponame, repoinfo, repos, youngest, err )):518 def repo_order((reponame, repoinfo, repos, youngest, err, href)): 516 519 return embedded_numbers(reponame.lower()) 517 520 518 521 repositories = sorted(repositories, key=repo_order, reverse=desc) … … 522 525 523 526 def _render_dir(self, req, repos, node, rev, order, desc): 524 527 req.perm(node.resource).require('BROWSER_VIEW') 528 download_href = self._get_download_href 525 529 526 530 # Entries metadata 527 531 class entry(object): 528 __slots__ = 'name rev kind isdir path content_length'.split() 532 _copy = 'name rev kind isdir path content_length'.split() 533 __slots__ = _copy + ['raw_href'] 529 534 def __init__(self, node): 530 for f in entry._ _slots__:535 for f in entry._copy: 531 536 setattr(self, f, getattr(node, f)) 537 self.raw_href = download_href(req.href, repos, node, rev) 532 538 533 539 entries = [entry(n) for n in node.get_entries() 534 540 if n.can_view(req.perm)] … … 574 580 entries = sorted(entries, key=browse_order, reverse=desc) 575 581 576 582 # ''Zip Archive'' alternate link 577 path = node.path.strip('/') 578 if repos.reponame: 579 path = repos.reponame + '/' + path 580 if any(fnmatchcase(path, p.strip('/')) 581 for p in self.downloadable_paths): 582 zip_href = req.href.changeset(rev or repos.youngest_rev, 583 repos.reponame or None, node.path, 584 old=rev, 585 old_path=repos.reponame or '/', 586 format='zip') 583 zip_href = self._get_download_href(req.href, repos, node, rev) 584 if zip_href: 587 585 add_link(req, 'alternate', zip_href, _('Zip Archive'), 588 586 'application/zip', 'zip') 589 587 … … 675 673 'annotate': force_source, 676 674 } 677 675 676 def _get_download_href(self, href, repos, node, rev): 677 """Return the URL for downloading a file, or a directory as a ZIP.""" 678 if node is not None and node.isfile: 679 return href.export(rev or 'HEAD', repos.reponame or None, 680 node.path) 681 path = npath = node is not None and node.path.strip('/') or '' 682 if repos.reponame: 683 path = (repos.reponame + '/' + npath).rstrip('/') 684 if any(fnmatchcase(path, p.strip('/')) 685 for p in self.downloadable_paths): 686 return href.changeset(rev or repos.youngest_rev, 687 repos.reponame or None, npath, 688 old=rev, old_path=repos.reponame or '/', 689 format='zip') 690 678 691 # public methods 679 692 680 693 def render_properties(self, mode, context, props): … … 739 752 elif '@' in export: 740 753 path, rev = export.split('@', 1) 741 754 else: 742 rev, path = 'HEAD', export 743 return tag.a(label, class_='export', 744 href=formatter.href.export(rev, path) + fragment) 755 rev, path = None, export 756 node, raw_href, title = self._get_link_info(path, rev, formatter.href, 757 formatter.perm) 758 if raw_href: 759 return tag.a(label, class_='export', href=raw_href + fragment, 760 title=title) 761 return tag.a(label, class_='missing export') 745 762 746 763 def _format_browser_link(self, formatter, ns, path, label): 747 764 path, query, fragment = formatter.split_link(path) … … 749 766 match = self.PATH_LINK_RE.match(path) 750 767 if match: 751 768 path, rev, marks = match.groups() 752 return tag.a(label, class_='source', 753 href=(formatter.href.browser(path, rev=rev, marks=marks) + 754 query + fragment)) 769 href = formatter.href 770 src_href = href.browser(path, rev=rev, marks=marks) + query + fragment 771 node, raw_href, title = self._get_link_info(path, rev, formatter.href, 772 formatter.perm) 773 if not node: 774 return tag.a(label, class_='missing source') 775 link = tag.a(label, class_='source', href=src_href) 776 if raw_href: 777 link = tag(link, tag.a(u'\u200b', href=raw_href + fragment, 778 title=title, class_='trac-rawlink')) 779 return link 755 780 756 781 PATH_LINK_RE = re.compile(r"([^@#:]*)" # path 757 782 r"[@:]([^#:]+)?" # rev 758 783 r"(?::(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*))?" # marks 759 784 ) 760 785 786 def _get_link_info(self, path, rev, href, perm): 787 rm = RepositoryManager(self.env) 788 reponame, repos, npath = rm.get_repository_by_path(path) 789 node = get_allowed_node(repos, npath, rev, perm) 790 if node is not None: 791 raw_href = self._get_download_href(href, repos, node, rev) 792 title = node.isfile and _("Download") \ 793 or _("Download as Zip archive") 794 return (node, raw_href, title) 795 return (None, None, None) 796 761 797 # IHTMLPreviewAnnotator methods 762 798 763 799 def get_annotation_type(self): -
trac/versioncontrol/web_ui/tests/wikisyntax.py
diff --git a/trac/versioncontrol/web_ui/tests/wikisyntax.py b/trac/versioncontrol/web_ui/tests/wikisyntax.py
a b 3 3 import unittest 4 4 5 5 from trac.test import Mock 6 from trac.versioncontrol import NoSuchChangeset 6 from trac.versioncontrol import NoSuchChangeset, NoSuchNode 7 7 from trac.versioncontrol.api import * 8 8 from trac.versioncontrol.web_ui import * 9 9 from trac.wiki.tests import formatter … … 23 23 return '200' 24 24 else: 25 25 raise NoSuchChangeset(rev) 26 26 27 def _get_node(path, rev=None): 28 if path == 'foo': 29 return Mock(path=path, rev=rev, isfile=False, 30 can_view=lambda resource: True) 31 elif path == 'missing/file': 32 raise NoSuchNode(path, rev) 33 else: 34 return Mock(path=path, rev=rev, isfile=True, 35 can_view=lambda resource: True) 36 27 37 def _get_repository(reponame): 28 38 return Mock(reponame=reponame, youngest_rev='200', 29 39 get_changeset=_get_changeset, 30 normalize_rev=_normalize_rev) 40 normalize_rev=_normalize_rev, 41 get_node=_get_node) 31 42 32 43 def repository_setup(tc): 33 44 setattr(tc.env, 'get_repository', _get_repository) … … 252 263 """ 253 264 254 265 255 SOURCE_TEST_CASES = """266 SOURCE_TEST_CASES = u""" 256 267 ============================== source: link resolver 257 268 source:/foo/bar 258 269 source:/foo/bar#42 # no long works as rev spec … … 264 275 source:/foo/bar@42#L20 265 276 source:/foo/bar@head#L20 266 277 source:/foo/bar@#L20 278 source:/missing/file 267 279 ------------------------------ 268 280 <p> 269 <a class="source" href="/browser/foo/bar">source:/foo/bar</a> 270 <a class="source" href="/browser/foo/bar#42">source:/foo/bar#42</a> # no long works as rev spec 271 <a class="source" href="/browser/foo/bar#head">source:/foo/bar#head</a> # 272 <a class="source" href="/browser/foo/bar?rev=42">source:/foo/bar@42</a> 273 <a class="source" href="/browser/foo/bar?rev=head">source:/foo/bar@head</a> 274 <a class="source" href="/browser/foo%2520bar/baz%252Bquux">source:/foo%20bar/baz%2Bquux</a> 275 <a class="source" href="/browser/?rev=42">source:@42</a> 276 <a class="source" href="/browser/foo/bar?rev=42#L20">source:/foo/bar@42#L20</a> 277 <a class="source" href="/browser/foo/bar?rev=head#L20">source:/foo/bar@head#L20</a> 278 <a class="source" href="/browser/foo/bar#L20">source:/foo/bar@#L20</a> 281 <a class="source" href="/browser/foo/bar">source:/foo/bar</a><a class="trac-rawlink" href="/export/HEAD/foo/bar" title="Download">\u200b</a> 282 <a class="source" href="/browser/foo/bar#42">source:/foo/bar#42</a><a class="trac-rawlink" href="/export/HEAD/foo/bar#42" title="Download">\u200b</a> # no long works as rev spec 283 <a class="source" href="/browser/foo/bar#head">source:/foo/bar#head</a><a class="trac-rawlink" href="/export/HEAD/foo/bar#head" title="Download">\u200b</a> # 284 <a class="source" href="/browser/foo/bar?rev=42">source:/foo/bar@42</a><a class="trac-rawlink" href="/export/42/foo/bar" title="Download">\u200b</a> 285 <a class="source" href="/browser/foo/bar?rev=head">source:/foo/bar@head</a><a class="trac-rawlink" href="/export/head/foo/bar" title="Download">\u200b</a> 286 <a class="source" href="/browser/foo%2520bar/baz%252Bquux">source:/foo%20bar/baz%2Bquux</a><a class="trac-rawlink" href="/export/HEAD/foo%2520bar/baz%252Bquux" title="Download">\u200b</a> 287 <a class="source" href="/browser/?rev=42">source:@42</a><a class="trac-rawlink" href="/export/42/" title="Download">\u200b</a> 288 <a class="source" href="/browser/foo/bar?rev=42#L20">source:/foo/bar@42#L20</a><a class="trac-rawlink" href="/export/42/foo/bar#L20" title="Download">\u200b</a> 289 <a class="source" href="/browser/foo/bar?rev=head#L20">source:/foo/bar@head#L20</a><a class="trac-rawlink" href="/export/head/foo/bar#L20" title="Download">\u200b</a> 290 <a class="source" href="/browser/foo/bar#L20">source:/foo/bar@#L20</a><a class="trac-rawlink" href="/export/HEAD/foo/bar#L20" title="Download">\u200b</a> 291 <a class="missing source">source:/missing/file</a> 279 292 </p> 280 293 ------------------------------ 281 294 ============================== source: link resolver + query … … 284 297 ------------------------------ 285 298 <p> 286 299 <a class="source" href="/browser/foo?order=size&desc=1">source:/foo?order=size&desc=1</a> 287 <a class="source" href="/browser/foo/bar?format=raw">source:/foo/bar?format=raw</a> 300 <a class="source" href="/browser/foo/bar?format=raw">source:/foo/bar?format=raw</a><a class="trac-rawlink" href="/export/HEAD/foo/bar" title="Download">\u200b</a> 288 301 </p> 289 302 ------------------------------ 290 303 ============================== source: provider, with quoting … … 294 307 [source:"even with whitespaces" Path with spaces] 295 308 ------------------------------ 296 309 <p> 297 <a class="source" href="/browser/even%20with%20whitespaces">source:'even with whitespaces'</a> 298 <a class="source" href="/browser/even%20with%20whitespaces">source:"even with whitespaces"</a> 299 <a class="source" href="/browser/even%20with%20whitespaces">Path with spaces</a> 300 <a class="source" href="/browser/even%20with%20whitespaces">Path with spaces</a> 310 <a class="source" href="/browser/even%20with%20whitespaces">source:'even with whitespaces'</a><a class="trac-rawlink" href="/export/HEAD/even%20with%20whitespaces" title="Download">\u200b</a> 311 <a class="source" href="/browser/even%20with%20whitespaces">source:"even with whitespaces"</a><a class="trac-rawlink" href="/export/HEAD/even%20with%20whitespaces" title="Download">\u200b</a> 312 <a class="source" href="/browser/even%20with%20whitespaces">Path with spaces</a><a class="trac-rawlink" href="/export/HEAD/even%20with%20whitespaces" title="Download">\u200b</a> 313 <a class="source" href="/browser/even%20with%20whitespaces">Path with spaces</a><a class="trac-rawlink" href="/export/HEAD/even%20with%20whitespaces" title="Download">\u200b</a> 301 314 </p> 302 315 ------------------------------ 303 316 ============================== export: link resolver … … 306 319 export:/foo/pict.gif@123 307 320 ------------------------------ 308 321 <p> 309 <a class="export" href="/export/HEAD/foo/bar.html" >export:/foo/bar.html</a>310 <a class="export" href="/export/123/foo/pict.gif" >export:123:/foo/pict.gif</a>311 <a class="export" href="/export/123/foo/pict.gif" >export:/foo/pict.gif@123</a>322 <a class="export" href="/export/HEAD/foo/bar.html" title="Download">export:/foo/bar.html</a> 323 <a class="export" href="/export/123/foo/pict.gif" title="Download">export:123:/foo/pict.gif</a> 324 <a class="export" href="/export/123/foo/pict.gif" title="Download">export:/foo/pict.gif@123</a> 312 325 </p> 313 326 ------------------------------ 314 327 ============================== export: link resolver + fragment 315 328 export:/foo/bar.html#header 316 329 ------------------------------ 317 330 <p> 318 <a class="export" href="/export/HEAD/foo/bar.html#header" >export:/foo/bar.html#header</a>331 <a class="export" href="/export/HEAD/foo/bar.html#header" title="Download">export:/foo/bar.html#header</a> 319 332 </p> 320 333 ------------------------------ 321 334 """ # " (be Emacs friendly...) -
trac/versioncontrol/web_ui/util.py
diff --git a/trac/versioncontrol/web_ui/util.py b/trac/versioncontrol/web_ui/util.py
a b 19 19 from genshi.builder import tag 20 20 21 21 from trac.resource import ResourceNotFound 22 from trac.util.datefmt import pretty_timedelta23 from trac.util.text import shorten_line24 22 from trac.util.translation import tag_, _ 25 23 from trac.versioncontrol.api import NoSuchNode, NoSuchChangeset 26 24 27 __all__ = ['get_changes', 'get_path_links', 'get_existing_node'] 25 __all__ = ['get_changes', 'get_path_links', 'get_existing_node', 26 'get_allowed_node'] 28 27 29 28 def get_changes(repos, revs): 30 29 changes = {} … … 69 68 tag.p(tag_("You can %(search)s in the repository history to see " 70 69 "if that path existed but was later removed", 71 70 search=search_a)))) 71 72 def get_allowed_node(repos, path, rev, perm): 73 if repos is not None: 74 try: 75 node = repos.get_node(path, rev) 76 except NoSuchNode: 77 return None 78 if node.can_view(perm): 79 return node
