diff --git a/trac/attachment.py b/trac/attachment.py
|
a
|
b
|
class AttachmentModule(Component): |
| 363 | 363 | """Maximum allowed file size (in bytes) for ticket and wiki |
| 364 | 364 | attachments.""") |
| 365 | 365 | |
| | 366 | max_zip_size = IntOption('attachment', 'max_zip_size', 2097152, |
| | 367 | """Maximum allowed total size (in bytes) for an attachment list to be |
| | 368 | downloadable as a `.zip`. Set this to -1 to disable download as `.zip`. |
| | 369 | (''since 0.13'')""") |
| | 370 | |
| 366 | 371 | render_unsafe_content = BoolOption('attachment', 'render_unsafe_content', |
| 367 | 372 | 'false', |
| 368 | 373 | """Whether attachments should be rendered in the browser, or |
| … |
… |
class AttachmentModule(Component): |
| 482 | 487 | for attachment in Attachment.select(self.env, parent.realm, parent.id): |
| 483 | 488 | if 'ATTACHMENT_VIEW' in context.perm(attachment.resource): |
| 484 | 489 | attachments.append(attachment) |
| | 490 | total_size = sum(attachment.size for attachment in attachments) |
| 485 | 491 | new_att = parent.child('attachment') |
| 486 | 492 | return {'attach_href': get_resource_url(self.env, new_att, |
| 487 | 493 | context.href), |
| 488 | 494 | 'download_href': get_resource_url(self.env, new_att, |
| 489 | | context.href, format='zip'), |
| | 495 | context.href, format='zip') |
| | 496 | if total_size <= self.max_zip_size else None, |
| 490 | 497 | 'can_create': 'ATTACHMENT_CREATE' in context.perm(new_att), |
| 491 | 498 | 'attachments': attachments, |
| 492 | 499 | 'parent': context.resource} |
| … |
… |
class AttachmentModule(Component): |
| 711 | 718 | 'attachment': attachment, 'max_size': self.max_size} |
| 712 | 719 | |
| 713 | 720 | def _download_as_zip(self, req, parent, attachments=None): |
| | 721 | if attachments is None: |
| | 722 | attachments = Attachment.select(self.env, parent.realm, parent.id) |
| | 723 | total_size = sum(attachment.size for attachment in attachments) |
| | 724 | if total_size > self.max_zip_size: |
| | 725 | raise TracError(_('Maximum total attachment size: %(num)s bytes', |
| | 726 | num=self.max_zip_size), _('Download failed')) |
| | 727 | |
| 714 | 728 | req.send_response(200) |
| 715 | 729 | req.send_header('Content-Type', 'application/zip') |
| 716 | 730 | filename = 'attachments-%s-%s.zip' % \ |
| 717 | 731 | (parent.realm, re.sub(r'[/\\:]', '-', unicode(parent.id))) |
| 718 | 732 | req.send_header('Content-Disposition', |
| 719 | 733 | content_disposition('inline', filename)) |
| 720 | | if attachments is None: |
| 721 | | attachments = Attachment.select(self.env, parent.realm, parent.id) |
| 722 | 734 | |
| 723 | 735 | from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED |
| 724 | 736 | |
diff --git a/trac/templates/list_of_attachments.html b/trac/templates/list_of_attachments.html
|
a
|
b
|
Arguments: |
| 35 | 35 | </li> |
| 36 | 36 | </py:for> |
| 37 | 37 | </ul> |
| 38 | | <p py:if="alist.attachments"> |
| | 38 | <p py:if="alist.download_href"> |
| 39 | 39 | Download all attachments as: <a rel="nofollow" href="$alist.download_href">.zip</a> |
| 40 | 40 | </p> |
| 41 | 41 | </div> |
| … |
… |
Arguments: |
| 51 | 51 | </dd> |
| 52 | 52 | </py:for> |
| 53 | 53 | </dl> |
| 54 | | <p py:if="alist.attachments"> |
| | 54 | <p py:if="alist.attachments and alist.download_href"> |
| 55 | 55 | Download all attachments as: <a rel="nofollow" href="$alist.download_href">.zip</a> |
| 56 | 56 | </p> |
| 57 | 57 | <xi:include href="attach_file_form.html"/> |