Index: trac/attachment.py
===================================================================
--- trac/attachment.py	(revision 6080)
+++ trac/attachment.py	(working copy)
@@ -177,6 +177,10 @@
         self.author = row[4]
         self.ipnr = row[5]
 
+    def _get_dirPath(self):
+	return os.path.normpath(os.path.join(self.env.path, 'attachments', self.parent_realm,
+                            unicode_quote(self.parent_id)))
+
     def _get_path(self):
         path = os.path.join(self.env.path, 'attachments', self.parent_realm,
                             unicode_quote(self.parent_id))
@@ -485,16 +489,8 @@
     def get_context_classes(self):
         yield AttachmentContext
 
-    # Internal methods
-
-    def _do_save(self, context):
+    def _do_save_one(self, context, upload, description):
         req, attachment = context.req, context.resource
-        req.perm.require('ATTACHMENT_CREATE', context)
-
-        if 'cancel' in req.args:
-            req.redirect(context.parent.resource_href())
-
-        upload = req.args['attachment']
         if not hasattr(upload, 'filename') or not upload.filename:
             raise TracError(_('No file uploaded'))
         if hasattr(upload.file, 'fileno'):
@@ -523,7 +519,7 @@
         # Now the filename is known, update the attachment context
         context.id = filename
 
-        attachment.description = req.args.get('description', '')
+        attachment.description = description
         attachment.author = get_reporter_id(req, 'author')
         attachment.ipnr = req.remote_addr
 
@@ -538,7 +534,6 @@
                 else:
                     raise InvalidAttachment(_('Invalid attachment: %(message)s',
                                               message=message))
-
         if req.args.get('replace'):
             try:
                 old_attachment = Attachment(self.env, attachment.parent_realm,
@@ -552,9 +547,33 @@
             attachment.filename = None
         attachment.insert(filename, upload.file, size)
 
-        # Redirect the user to list of attachments (must add a trailing '/')
-        req.redirect(context.resource_href('..') + '/')
 
+    def _do_save_many(self, context):
+	req = context.req
+	curr_attach = 0
+
+	while curr_attach < int(req.args['multiple_attachments']):
+	    upload=req.args['attachment-%d' % curr_attach]
+	    if hasattr(upload, 'filename') and upload.filename:
+		self._do_save_one(context, upload, req.args.get('description-%d' % curr_attach, ''))
+	    curr_attach=curr_attach+1
+    # Internal methods
+
+    def _do_save(self, context):
+        req, attachment = context.req, context.resource
+        req.perm.require('ATTACHMENT_CREATE', context)
+
+        if 'cancel' in req.args:
+            req.redirect(context.parent.resource_href())
+
+	if 'multiple_attachments' in req.args:
+	    self._do_save_many(context)
+	    req.redirect(context.parent.resource_href())
+	else:
+	    self._do_save_one(context, req.args['attachment'], req.args.get('description', ''))
+	    # Redirect the user to list of attachments (must add a trailing '/')
+            req.redirect(context.resource_href('..') + '/')
+
     def _do_delete(self, context):
         req, attachment = context.req, context.resource
         req.perm.require('ATTACHMENT_DELETE', context)
Index: trac/templates/attachment.html
===================================================================
--- trac/templates/attachment.html	(revision 6080)
+++ trac/templates/attachment.html	(working copy)
@@ -13,19 +13,8 @@
   <body py:with="parent = attachments and attachments.parent or context.parent;
                  parent_name = parent.name();
                  parent_href = parent.get_href(href)">
-
-    <div id="ctxtnav" class="nav">
-      <h2>Attachment Navigation</h2>
-      <ul>
-        <li class="first">
-          <a href="${chrome.links.up[0].href}">${_("Back to %(parent)s", parent=parent_name)}</a>
-        </li>
-      </ul>
-    </div>
-
-    <div py:choose="mode" id="content" class="attachment">
-      <py:when test="'new'">
-        <h1>Add Attachment to <a href="$parent_href">$parent_name</a></h1>
+    <py:def function="attach_file_origForm()">
+	<!--Old form placed here-->	
         <form id="attachment" method="post" enctype="multipart/form-data" action="">
           <div class="field">
             <label>File:<br /><input type="file" name="attachment" /></label>
@@ -58,6 +47,92 @@
             <input type="submit" name="cancel" value="Cancel" />
           </div>
         </form>
+    </py:def>
+    <py:def function="attach_multiFile_form()">
+<script type="text/javascript">
+function multipleAttach_manageFields(_obj){
+    var count = document.getElementById("multiAttach_count");
+    var tbody = document.getElementById("multiAttach_tbody");
+    if(tbody.rows.length == 1) return;
+    if (_obj.value == '') {
+	tbody.deleteRow(_obj.getAttribute("attachNum")*1+1);
+	var loop;
+	for (loop=0;loop != tbody.rows.length; loop ++){
+	    tbody.rows[loop].setAttribute("id","multiAttach_tr-"+loop);
+	}
+	var loop1=0, inp;
+	for (loop=0; loop != count.value*1; loop ++){
+	    inp = document.getElementById("multiAttach_attachment-"+loop);
+	    if (inp == null) continue;
+	    inp.setAttribute("attachnum",loop1);
+	    inp.name="attachment-"+loop1;
+	    inp.setAttribute("name","attachment-"+loop1);
+	    inp.setAttribute("id","multiAttach_attachment"+loop1);
+	    inp = document.getElementById("multiAttach_description-"+loop);
+	    inp.name="description-"+loop1;
+	    inp.setAttribute("name","description-"+loop1);
+	    inp.setAttribute("id","multiAttach_description"+loop1);
+	    loop1++;
+	}
+	count.value = count.value*1-1;
+    } else {
+	if (_obj.getAttribute("attachnum") != count.value-1) return;
+	var tr = tbody.insertRow(-1);
+	tr.setAttribute("id","multiAttach_tr-"+count.value);
+	var td = tr.insertCell(-1);
+	td.innerHTML='<input type="file" id="multiAttach_attachment-'+count.value+'" attachnum="'+count.value+'" name="attachment-'+count.value+'" onchange="multipleAttach_manageFields(this);"/>';
+	var td = tr.insertCell(-1);
+	td.innerHTML='<input type="text" id="multiAttach_description-'+count.value+'" name="description-'+count.value+'" size="60" />';
+	count.value = count.value*1+1;
+    }
+}
+</script>
+     <form id="attachment" method="post" enctype="multipart/form-data" action="${ attachments and  attachments.attach_href() or None}">
+    	<table><thead>
+        <tr py:if="authname == 'anonymous'"><td colspan="2">
+            <label>Your email or username:<br />
+              <input type="text" name="author" size="30" value="$author" />
+            </label>
+        </td></tr>
+        <tr><td colspan="2">
+    	    <label>
+    		<input type="checkbox" name="replace" />
+                Replace existing attachments of the same name
+            </label>
+	</td></tr>
+	</thead>
+	<tbody id="multiAttach_tbody">
+    	<tr><th align="left">File</th><th align="left">Description</th></tr>
+    	<tr id="multiAttach_tr-0">
+    	    <td><input type="file" id="multiAttach_attachment-0" attachnum="0" name="attachment-0" onchange="multipleAttach_manageFields(this);"/></td>
+    	    <td><input type="text" id="multiAttach_description-0" name="description-0" size="60" /></td>
+    	</tr>
+    	</tbody>
+    	</table>
+          <div class="buttons">
+            <input type="hidden" name="action" value="new" />
+            <input type="hidden" id="multiAttach_count" name="multiple_attachments" value="1" />
+            <input type="hidden" name="realm" value="$context.parent.realm" />
+            <input type="hidden" name="id" value="$context.parent.id" />
+            <input type="submit" value="Attach files" />
+            <input type="button" name="cancel" value="Cancel" onclick="showAttachmentPanel();"/>
+          </div>
+        </form>
+    </py:def>
+    <div id="ctxtnav" class="nav">
+      <h2>Attachment Navigation</h2>
+      <ul>
+        <li class="first">
+          <a href="${chrome.links.up[0].href}">${_("Back to %(parent)s", parent=parent_name)}</a>
+        </li>
+      </ul>
+    </div>
+
+    <div py:choose="mode" id="content" class="attachment">
+      <py:when test="'new'">
+        <h1>Add Attachment to <a href="$parent_href">$parent_name</a></h1>
+        <!-- ${attach_file_origForm()} -->
+        ${attach_multiFile_form()}
       </py:when>
 
       <py:when test="'delete'">

