Edgewall Software

Ticket #948: attachment_del_replace.1077.diff

File attachment_del_replace.1077.diff, 10.3 kB (added by cboos@…, 4 years ago)

Suggested implementation for #948 (diff to [1077])

  • templates/attachment.cs

    === templates/attachment.cs
    ==================================================================
     
    1414   <label for="file">File:</label> 
    1515   <input type="file" id="file" name="attachment" /> 
    1616  </div> 
     17  <div class="field"> 
     18   <input type="checkbox" id="replace" name="replace" checked="1" /> 
     19   <label for="replace">Replace Existing File</label> 
     20  </div> 
    1721  <fieldset> 
    1822   <legend>Attachment Info</legend> 
    1923   <div class="field"> 
  • templates/file.cs

    === templates/file.cs
    ==================================================================
     
    1313 <?cs if file.attachment_parent ?> 
    1414  <h1><a href="<?cs var:file.attachment_parent_href ?>"><?cs 
    1515    var:file.attachment_parent ?></a>: <?cs var:file.filename ?></h1> 
     16  <table id="info" summary="Attachment info"> 
     17   <tr> 
     18    <th scope="row"> 
     19     File <a href="<?cs var:attachment.href ?>"><?cs var:attachment.name ?></a>, <?cs var:attachment.size ?> 
     20     (attached by <?cs var:attachment.author ?>, <?cs var:attachment.time ?>) 
     21    </th> 
     22    <td class="message"><?cs var:attachment.descr ?></td> 
     23   </tr> 
     24  </table> 
    1625 
    1726 <?cs else ?> 
    1827  <?cs call:browser_path_links(file.path, file) ?> 
  • trac/Environment.py

    === trac/Environment.py
    ==================================================================
     
    214214    def get_attachments_dir(self): 
    215215        return os.path.join(self.path, 'attachments') 
    216216 
     217    def get_attachment(self, cnx, type, id, filename): 
     218        cursor = cnx.cursor() 
     219        cursor.execute('SELECT filename,description,type,size,time,author,ipnr ' 
     220                       'FROM attachment ' 
     221                       'WHERE type=%s AND id=%s AND filename=%s ORDER BY time', type, id, filename) 
     222        return cursor.fetchone() 
     223     
    217224    def get_attachments(self, cnx, type, id): 
    218225        cursor = cnx.cursor() 
    219226        cursor.execute('SELECT filename,description,type,size,time,author,ipnr ' 
     
    221228                       'WHERE type=%s AND id=%s ORDER BY time', type, id) 
    222229        return cursor.fetchall() 
    223230     
     231    def get_attachment_hdf(self, cnx, type, id, hdf, prefix, file, idx=None): 
     232        from Wiki import wiki_to_oneliner 
     233        if idx: 
     234            p = '%s.%d' % (prefix, idx) 
     235        else: 
     236            p = prefix 
     237        hdf.setValue(p + '.name', file['filename']) 
     238        hdf.setValue(p + '.descr', 
     239                     wiki_to_oneliner(file['description'], hdf, self, cnx)) 
     240        hdf.setValue(p + '.author', util.escape(file['author'])) 
     241        hdf.setValue(p + '.ipnr', file['ipnr']) 
     242        hdf.setValue(p + '.size', util.pretty_size(file['size'])) 
     243        hdf.setValue(p + '.time', 
     244                     time.strftime('%c', time.localtime(file['time']))) 
     245        hdf.setValue(p + '.href', 
     246                     self.href.attachment(type, id, file['filename'])) 
     247 
     248 
    224249    def get_attachments_hdf(self, cnx, type, id, hdf, prefix): 
    225250        from Wiki import wiki_to_oneliner 
    226251        files = self.get_attachments(cnx, type, id) 
    227252        idx = 0 
    228253        for file in files: 
    229             p = '%s.%d' % (prefix, idx) 
    230             hdf.setValue(p + '.name', file['filename']) 
    231             hdf.setValue(p + '.descr', 
    232                          wiki_to_oneliner(file['description'], hdf, self, cnx)) 
    233             hdf.setValue(p + '.author', util.escape(file['author'])) 
    234             hdf.setValue(p + '.ipnr', file['ipnr']) 
    235             hdf.setValue(p + '.size', util.pretty_size(file['size'])) 
    236             hdf.setValue(p + '.time', 
    237                          time.strftime('%c', time.localtime(file['time']))) 
    238             hdf.setValue(p + '.href', 
    239                          self.href.attachment(type, id, file['filename'])) 
     254            self.get_attachment_hdf(cnx, type, id, hdf, prefix, file, idx) 
    240255            idx += 1 
    241256 
    242257    def create_attachment(self, cnx, type, id, attachment, 
    243                           description, author, ipnr): 
     258                          description, author, ipnr, replace=0): 
    244259        # Maximum attachment size (in bytes) 
    245260        max_size = int(self.get_config('attachment', 'max_size', '262144')) 
    246261        if hasattr(attachment.file, 'fileno'): 
     
    258273        filename = attachment.filename.replace('\\', '/').replace(':', '/') 
    259274        filename = os.path.basename(filename) 
    260275 
     276        cursor = cnx.cursor() 
     277        cursor.execute('SELECT author FROM attachment WHERE ' 
     278                       'type=%s AND id=%s AND filename=%s', 
     279                       type, id, filename) 
     280        row = cursor.fetchone() 
     281        if row and author != row['author']: 
     282           replace = 0 
     283 
    261284        # We try to normalize the filename to utf-8 NFC if we can. 
    262285        # Files uploaded from OS X might be in NFD. 
    263286        if sys.version_info[0] > 2 or \ 
     
    265288            filename = unicodedata.normalize('NFC', unicode(filename, 'utf-8')).encode('utf-8') 
    266289             
    267290        filename = urllib.quote(filename) 
    268         path, fd = util.create_unique_file(os.path.join(dir, filename)) 
     291        path, fd = util.create_unique_file(os.path.join(dir, filename), replace) 
    269292        filename = os.path.basename(path) 
    270293        filename = urllib.unquote(filename) 
    271         cursor = cnx.cursor() 
    272         cursor.execute('INSERT INTO attachment VALUES(%s,%s,%s,%s,%s,%s,%s,%s)', 
     294 
     295        if replace: 
     296            action = "REPLACE" 
     297        else: 
     298            action = "INSERT" 
     299        cursor.execute(action + ' INTO attachment VALUES(%s,%s,%s,%s,%s,%s,%s,%s)', 
    273300                       type, id, filename, length, int(time.time()), 
    274301                       description, author, ipnr) 
    275302        shutil.copyfileobj(attachment.file, fd) 
  • trac/File.py

    === trac/File.py
    ==================================================================
     
    104104                    self.env.href.wiki(self.attachment_id)) 
    105105        assert 0 
    106106 
     107    def can_delete(self, do_assert=None): 
     108        print 'author', self.file['author'], 'authname', self.req.authname 
     109        if self.file and self.file['author'] and self.req.authname: 
     110            if self.file['author'] == self.req.authname: 
     111                return 1 
     112        perm_map = {'ticket': perm.TICKET_ADMIN, 'wiki': perm.WIKI_DELETE} 
     113        if do_assert: 
     114            self.perm.assert_permission(perm_map[self.attachment_type]) 
     115        else: 
     116            return self.perm.has_permission(perm_map[self.attachment_type]) 
     117                 
    107118    def render(self): 
    108119        FileCommon.render(self) 
    109120        self.view_form = 0 
    110121        self.attachment_type = self.args.get('type', None) 
    111122        self.attachment_id = self.args.get('id', None) 
    112123        self.filename = self.args.get('filename', None) 
    113         if self.filename: 
    114             self.filename = os.path.basename(self.filename) 
    115124 
    116125        if not self.attachment_type or not self.attachment_id: 
    117126            raise util.TracError('Unknown request') 
    118127 
     128        if self.filename: 
     129            self.filename = os.path.basename(self.filename) 
     130            self.file = self.env.get_attachment(self.db, self.attachment_type, 
     131                                                self.attachment_id, self.filename) 
     132            if self.file: 
     133                self.env.get_attachment_hdf(self.db, self.attachment_type, 
     134                                            self.attachment_id, self.req.hdf, 
     135                                            'attachment', self.file) 
     136 
    119137        if self.filename and len(self.filename) > 0 and \ 
    120138               self.args.has_key('delete'): 
    121             perm_map = {'ticket': perm.TICKET_ADMIN, 'wiki': perm.WIKI_DELETE} 
    122             self.perm.assert_permission(perm_map[self.attachment_type]) 
     139            self.can_delete(do_assert=1) 
    123140            self.env.delete_attachment(self.db, 
    124141                                       self.attachment_type, 
    125142                                       self.attachment_id, 
     
    160177                                                   self.filename, 'raw'), 
    161178                'Original Format', self.mime_type) 
    162179 
    163             perm_map = {'ticket': perm.TICKET_ADMIN, 'wiki': perm.WIKI_DELETE} 
    164             if self.perm.has_permission(perm_map[self.attachment_type]): 
     180            if self.can_delete(): 
    165181                self.req.hdf.setValue('attachment.delete_href', '?delete=yes') 
    166182 
    167183            return 
     
    187203                                                  self.args['attachment'], 
    188204                                                  self.args.get('description'), 
    189205                                                  self.args.get('author'), 
    190                                                   self.req.remote_addr) 
     206                                                  self.req.remote_addr, 
     207                                                  self.args.get('replace')) 
     208 
    191209            # Redirect the user to the newly created attachment 
    192210            self.req.redirect(self.env.href.attachment(self.attachment_type, 
    193211                                                       self.attachment_id, 
     
    210228            self.req.hdf.setValue('attachment.author', util.get_reporter_id(self.req)) 
    211229            self.req.display('attachment.cs') 
    212230            return 
     231 
    213232        self.req.hdf.setValue('file.filename', urllib.unquote(self.filename)) 
    214233        self.req.hdf.setValue('trac.active_module', self.attachment_type) # Kludge 
    215234        FileCommon.display(self) 
  • trac/util.py

    === trac/util.py
    ==================================================================
     
    235235            return '%i %s' % (r, r == 1 and unit or unit_plural) 
    236236    return '' 
    237237 
    238 def create_unique_file(path): 
     238def create_unique_file(path,replace=0): 
    239239    """Create a new file. An index is added if the path exists""" 
    240240    parts = os.path.splitext(path) 
    241241    idx = 1 
    242242    while 1: 
    243243        try: 
    244             flags = os.O_CREAT + os.O_WRONLY + os.O_EXCL 
     244            flags = os.O_CREAT + os.O_WRONLY 
     245            if not replace: 
     246                flags += os.O_EXCL 
    245247            if hasattr(os, 'O_BINARY'): 
    246248                flags += os.O_BINARY 
    247249            return path, os.fdopen(os.open(path, flags), 'w') 
    248         except OSError: 
     250        except OSError, e: 
     251            if replace: 
     252                raise e # propagate failure 
    249253            idx += 1 
    250254            # A sanity check 
    251255            if idx > 100: