Index: trac/attachment.py
===================================================================
--- trac/attachment.py	(revision 7440)
+++ trac/attachment.py	(working copy)
@@ -22,6 +22,8 @@
 import shutil
 import time
 import unicodedata
+import zipfile
+import tempfile
 
 from genshi.builder import tag
 
@@ -375,6 +377,10 @@
 
         parent = parent_realm(id=parent_id)
         
+        if action == 'getzip':
+            self._do_getzip(req, parent)
+            return 'attachment.html', data, None
+
         # Link the attachment page to parent resource
         parent_name = get_resource_name(self.env, parent)
         parent_url = get_resource_url(self.env, parent, req.href)
@@ -517,6 +523,33 @@
 
     # Internal methods
 
+    def _do_getzip(self, req, parent):
+        db = self.env.get_db_cnx()
+
+        p_id = parent.id
+        p_type = parent.realm
+
+        zfname = 'Ticket%s.zip' % p_id
+
+        zfpath = os.path.join(tempfile.gettempdir(), zfname)
+
+        zf = zipfile.ZipFile(zfpath, 'w')
+        for attachment in Attachment.select(self.env, p_type, p_id, db):
+#            name = attachment.filename.encode('utf8')
+            name = attachment.filename.encode('cp437')
+            zf.write(attachment.path, name)
+            info = zf.getinfo(name)
+            # Set bit 11 indicate that filename and comment are in UTF-8, see http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+#            info.flag_bits |= 1 << 11
+        zf.close()
+
+        req.send_header('Content-Disposition', 'attachment; filename=%s' % zfname)
+        mime_type = 'application/octet-stream'
+        
+        req.send_file(zfpath, mime_type)
+        
+        os.remove(zfpath)
+
     def _do_save(self, req, attachment):
         req.perm(attachment.resource).require('ATTACHMENT_CREATE')
 
Index: trac/templates/macros.html
===================================================================
--- trac/templates/macros.html	(revision 7440)
+++ trac/templates/macros.html	(working copy)
@@ -208,12 +208,16 @@
   -     'alist' is an AttachmentList object (see attachment.py)
   -->
   <py:def function="attach_file_form(alist)">
-    <form py:if="alist.can_create" method="get" action="${alist.attach_href}" id="attachfile">
-      <div>
+    <div>
+    <form py:if="alist.can_create" method="get" action="${alist.attach_href}" id="attachfile" style="display:inline;">
         <input type="hidden" name="action" value="new" />
         <input type="submit" name="attachfilebutton" value="${_('Attach file')}" />
-      </div>
     </form>
+    <form py:if="alist.attachments" method="get" action="${alist.attach_href}" id="getattachmentsaszip" style="display:inline;">
+        <input type="hidden" name="action" value="getzip" />
+        <input type="submit" name="getattachmentsaszipbutton" value="${_('Save all as ZIP-file')}" />
+    </form>
+    </div>
   </py:def>
 
   <!--!  Display a generic "progress bar", for use in roadmap and milestone.

