Edgewall Software

Ticket #2141: trac.multifile.patch

File trac.multifile.patch, 8.2 KB (added by slavazanko@…, 13 months ago)

Add multifile upload feature

  • trac/attachment.py

     
    177177        self.author = row[4] 
    178178        self.ipnr = row[5] 
    179179 
     180    def _get_dirPath(self): 
     181        return os.path.normpath(os.path.join(self.env.path, 'attachments', self.parent_realm, 
     182                            unicode_quote(self.parent_id))) 
     183 
    180184    def _get_path(self): 
    181185        path = os.path.join(self.env.path, 'attachments', self.parent_realm, 
    182186                            unicode_quote(self.parent_id)) 
     
    485489    def get_context_classes(self): 
    486490        yield AttachmentContext 
    487491 
    488     # Internal methods 
    489  
    490     def _do_save(self, context): 
     492    def _do_save_one(self, context, upload, description): 
    491493        req, attachment = context.req, context.resource 
    492         req.perm.require('ATTACHMENT_CREATE', context) 
    493  
    494         if 'cancel' in req.args: 
    495             req.redirect(context.parent.resource_href()) 
    496  
    497         upload = req.args['attachment'] 
    498494        if not hasattr(upload, 'filename') or not upload.filename: 
    499495            raise TracError(_('No file uploaded')) 
    500496        if hasattr(upload.file, 'fileno'): 
     
    523519        # Now the filename is known, update the attachment context 
    524520        context.id = filename 
    525521 
    526         attachment.description = req.args.get('description', '') 
     522        attachment.description = description 
    527523        attachment.author = get_reporter_id(req, 'author') 
    528524        attachment.ipnr = req.remote_addr 
    529525 
     
    538534                else: 
    539535                    raise InvalidAttachment(_('Invalid attachment: %(message)s', 
    540536                                              message=message)) 
    541  
    542537        if req.args.get('replace'): 
    543538            try: 
    544539                old_attachment = Attachment(self.env, attachment.parent_realm, 
     
    552547            attachment.filename = None 
    553548        attachment.insert(filename, upload.file, size) 
    554549 
    555         # Redirect the user to list of attachments (must add a trailing '/') 
    556         req.redirect(context.resource_href('..') + '/') 
    557550 
     551    def _do_save_many(self, context): 
     552        req = context.req 
     553        curr_attach = 0 
     554 
     555        while curr_attach < int(req.args['multiple_attachments']): 
     556            upload=req.args['attachment-%d' % curr_attach] 
     557            if hasattr(upload, 'filename') and upload.filename: 
     558                self._do_save_one(context, upload, req.args.get('description-%d' % curr_attach, '')) 
     559            curr_attach=curr_attach+1 
     560    # Internal methods 
     561 
     562    def _do_save(self, context): 
     563        req, attachment = context.req, context.resource 
     564        req.perm.require('ATTACHMENT_CREATE', context) 
     565 
     566        if 'cancel' in req.args: 
     567            req.redirect(context.parent.resource_href()) 
     568 
     569        if 'multiple_attachments' in req.args: 
     570            self._do_save_many(context) 
     571            req.redirect(context.parent.resource_href()) 
     572        else: 
     573            self._do_save_one(context, req.args['attachment'], req.args.get('description', '')) 
     574            # Redirect the user to list of attachments (must add a trailing '/') 
     575            req.redirect(context.resource_href('..') + '/') 
     576 
    558577    def _do_delete(self, context): 
    559578        req, attachment = context.req, context.resource 
    560579        req.perm.require('ATTACHMENT_DELETE', context) 
  • trac/templates/attachment.html

     
    1313  <body py:with="parent = attachments and attachments.parent or context.parent; 
    1414                 parent_name = parent.name(); 
    1515                 parent_href = parent.get_href(href)"> 
    16  
    17     <div id="ctxtnav" class="nav"> 
    18       <h2>Attachment Navigation</h2> 
    19       <ul> 
    20         <li class="first"> 
    21           <a href="${chrome.links.up[0].href}">${_("Back to %(parent)s", parent=parent_name)}</a> 
    22         </li> 
    23       </ul> 
    24     </div> 
    25  
    26     <div py:choose="mode" id="content" class="attachment"> 
    27       <py:when test="'new'"> 
    28         <h1>Add Attachment to <a href="$parent_href">$parent_name</a></h1> 
     16    <py:def function="attach_file_origForm()"> 
     17        <!--Old form placed here-->      
    2918        <form id="attachment" method="post" enctype="multipart/form-data" action=""> 
    3019          <div class="field"> 
    3120            <label>File:<br /><input type="file" name="attachment" /></label> 
     
    5847            <input type="submit" name="cancel" value="Cancel" /> 
    5948          </div> 
    6049        </form> 
     50    </py:def> 
     51    <py:def function="attach_multiFile_form()"> 
     52<script type="text/javascript"> 
     53function multipleAttach_manageFields(_obj){ 
     54    var count = document.getElementById("multiAttach_count"); 
     55    var tbody = document.getElementById("multiAttach_tbody"); 
     56    if(tbody.rows.length == 1) return; 
     57    if (_obj.value == '') { 
     58        tbody.deleteRow(_obj.getAttribute("attachNum")*1+1); 
     59        var loop; 
     60        for (loop=0;loop != tbody.rows.length; loop ++){ 
     61            tbody.rows[loop].setAttribute("id","multiAttach_tr-"+loop); 
     62        } 
     63        var loop1=0, inp; 
     64        for (loop=0; loop != count.value*1; loop ++){ 
     65            inp = document.getElementById("multiAttach_attachment-"+loop); 
     66            if (inp == null) continue; 
     67            inp.setAttribute("attachnum",loop1); 
     68            inp.name="attachment-"+loop1; 
     69            inp.setAttribute("name","attachment-"+loop1); 
     70            inp.setAttribute("id","multiAttach_attachment"+loop1); 
     71            inp = document.getElementById("multiAttach_description-"+loop); 
     72            inp.name="description-"+loop1; 
     73            inp.setAttribute("name","description-"+loop1); 
     74            inp.setAttribute("id","multiAttach_description"+loop1); 
     75            loop1++; 
     76        } 
     77        count.value = count.value*1-1; 
     78    } else { 
     79        if (_obj.getAttribute("attachnum") != count.value-1) return; 
     80        var tr = tbody.insertRow(-1); 
     81        tr.setAttribute("id","multiAttach_tr-"+count.value); 
     82        var td = tr.insertCell(-1); 
     83        td.innerHTML='<input type="file" id="multiAttach_attachment-'+count.value+'" attachnum="'+count.value+'" name="attachment-'+count.value+'" onchange="multipleAttach_manageFields(this);"/>'; 
     84        var td = tr.insertCell(-1); 
     85        td.innerHTML='<input type="text" id="multiAttach_description-'+count.value+'" name="description-'+count.value+'" size="60" />'; 
     86        count.value = count.value*1+1; 
     87    } 
     88} 
     89</script> 
     90     <form id="attachment" method="post" enctype="multipart/form-data" action="${ attachments and  attachments.attach_href() or None}"> 
     91        <table><thead> 
     92        <tr py:if="authname == 'anonymous'"><td colspan="2"> 
     93            <label>Your email or username:<br /> 
     94              <input type="text" name="author" size="30" value="$author" /> 
     95            </label> 
     96        </td></tr> 
     97        <tr><td colspan="2"> 
     98            <label> 
     99                <input type="checkbox" name="replace" /> 
     100                Replace existing attachments of the same name 
     101            </label> 
     102        </td></tr> 
     103        </thead> 
     104        <tbody id="multiAttach_tbody"> 
     105        <tr><th align="left">File</th><th align="left">Description</th></tr> 
     106        <tr id="multiAttach_tr-0"> 
     107            <td><input type="file" id="multiAttach_attachment-0" attachnum="0" name="attachment-0" onchange="multipleAttach_manageFields(this);"/></td> 
     108            <td><input type="text" id="multiAttach_description-0" name="description-0" size="60" /></td> 
     109        </tr> 
     110        </tbody> 
     111        </table> 
     112          <div class="buttons"> 
     113            <input type="hidden" name="action" value="new" /> 
     114            <input type="hidden" id="multiAttach_count" name="multiple_attachments" value="1" /> 
     115            <input type="hidden" name="realm" value="$context.parent.realm" /> 
     116            <input type="hidden" name="id" value="$context.parent.id" /> 
     117            <input type="submit" value="Attach files" /> 
     118            <input type="button" name="cancel" value="Cancel" onclick="showAttachmentPanel();"/> 
     119          </div> 
     120        </form> 
     121    </py:def> 
     122    <div id="ctxtnav" class="nav"> 
     123      <h2>Attachment Navigation</h2> 
     124      <ul> 
     125        <li class="first"> 
     126          <a href="${chrome.links.up[0].href}">${_("Back to %(parent)s", parent=parent_name)}</a> 
     127        </li> 
     128      </ul> 
     129    </div> 
     130 
     131    <div py:choose="mode" id="content" class="attachment"> 
     132      <py:when test="'new'"> 
     133        <h1>Add Attachment to <a href="$parent_href">$parent_name</a></h1> 
     134        <!-- ${attach_file_origForm()} --> 
     135        ${attach_multiFile_form()} 
    61136      </py:when> 
    62137 
    63138      <py:when test="'delete'">