Edgewall Software

Ticket #2827: thumbailPreviewOfAttachImages.r8466-fixed.2.patch

File thumbailPreviewOfAttachImages.r8466-fixed.2.patch, 16.6 KB (added by anonymous, 2 years ago)

FIX bug: renew patch for r8466 (/tags/trac-0.11.4) (attached wrong file above)

  • trac/attachment.py

     
    2020import os 
    2121import re 
    2222import shutil 
     23import Image 
    2324import time 
    2425import unicodedata 
    2526 
     
    116117        self.env = env 
    117118        self.parent_realm = self.resource.parent.realm 
    118119        self.parent_id = unicode(self.resource.parent.id) 
     120        self.first1000bytes = None 
    119121        if self.resource.id: 
    120122            self._fetch(self.resource.id, db) 
    121123        else: 
     
    125127            self.date = None 
    126128            self.author = None 
    127129            self.ipnr = None 
     130            self.mimetype = None 
     131            self.charset = None 
    128132 
    129133    def _set_filename(self, val): 
    130134        self.resource.id = val 
     
    152156        self.date = datetime.fromtimestamp(time, utc) 
    153157        self.author = row[4] 
    154158        self.ipnr = row[5] 
     159        self._get_mimetype() 
     160        self._get_charset() 
    155161 
     162    def _get_first_1000_bytes(self): 
     163        if not self.first1000bytes: 
     164            fd = self.open() 
     165            try: 
     166                str_data = fd.read(1000) 
     167                fd.seek(0) 
     168            finally: 
     169                fd.close() 
     170 
     171    def _get_charset(self): 
     172        self._get_first_1000_bytes() 
     173        mimeview = Mimeview(self.env) 
     174        self.charset = mimeview.get_charset(self.first1000bytes, self.mimetype) 
     175 
     176    def _get_mimetype(self): 
     177        self._get_first_1000_bytes() 
     178        mimeview = Mimeview(self.env) 
     179        self.mimetype = mimeview.get_mimetype(self.filename, self.first1000bytes) 
     180        if not self.mimetype: 
     181            self.mimetype = 'application/octet-stream' 
     182 
     183    def getThumbail(self, size = (160,160)): 
     184        image = Image.open(self.path) 
     185        thumbDir=os.path.join(self._get_dirPath(),'.thumb') 
     186        thumbFile=os.path.join(thumbDir,unicode_quote(self.filename)) 
     187        if not os.path.isdir(thumbDir): 
     188            os.mkdir(thumbDir) 
     189        # 200 x 150 
     190        if os.path.exists(thumbFile): 
     191            return thumbFile 
     192        if image.size[0] < size[0] and image.size[0] < size[0]: 
     193            shutil.copyfile(self.path,thumbFile) 
     194            return thumbFile 
     195        image.thumbnail(size) 
     196        image.save(thumbFile) 
     197        image.close() 
     198    def _get_dirPath(self): 
     199        return os.path.normpath(os.path.join(self.env.path, 'attachments', self.parent_realm, 
     200                            unicode_quote(self.parent_id))) 
    156201    def _get_path(self): 
    157202        path = os.path.join(self.env.path, 'attachments', self.parent_realm, 
    158203                            unicode_quote(self.parent_id)) 
     
    266311            attachment.date = datetime.fromtimestamp(time, utc) 
    267312            attachment.author = author 
    268313            attachment.ipnr = ipnr 
     314            attachment._get_mimetype() 
     315            attachment._get_charset() 
    269316            yield attachment 
    270317 
    271318    def delete_all(cls, env, parent_realm, parent_id, db): 
     
    349396    # IRequestHandler methods 
    350397 
    351398    def match_request(self, req): 
    352         match = re.match(r'/(raw-)?attachment/([^/]+)(?:/(.*))?$', 
     399        match = re.match(r'/(raw-|thumb-)?attachment/([^/]+)(?:/(.*))?$', 
    353400                         req.path_info) 
    354401        if match: 
    355402            raw, realm, path = match.groups() 
    356             if raw: 
     403            if raw == 'raw-': 
    357404                req.args['format'] = 'raw' 
     405            elif raw == 'thumb-': 
     406                req.args['format'] = 'thumb' 
    358407            req.args['realm'] = realm 
    359408            if path: 
    360409                req.args['path'] = path 
     
    415464 
    416465    def get_link_resolvers(self): 
    417466        yield ('raw-attachment', self._format_link) 
     467        yield ('thumb-attachment', self._format_link) 
    418468        yield ('attachment', self._format_link) 
    419469 
    420470    # Public methods 
     
    520570        if format == 'raw': 
    521571            kwargs.pop('format') 
    522572            prefix = 'raw-attachment' 
     573        elif format == 'thumb': 
     574            kwargs.pop('format') 
     575            prefix = 'thumb-attachment' 
    523576        parent_href = unicode_unquote(get_resource_url(self.env, 
    524577                            resource.parent(version=None), Href(''))) 
    525578        if not resource.id:  
     
    544597 
    545598    # Internal methods 
    546599 
    547     def _do_save(self, req, attachment): 
     600    def _do_save_one(self, req, attachment, upload, description): 
    548601        req.perm(attachment.resource).require('ATTACHMENT_CREATE') 
    549602 
    550         if 'cancel' in req.args: 
    551             req.redirect(get_resource_url(self.env, attachment.resource.parent, 
    552                                           req.href)) 
    553603 
    554         upload = req.args['attachment'] 
    555604        if not hasattr(upload, 'filename') or not upload.filename: 
    556605            raise TracError(_('No file uploaded')) 
    557606        if hasattr(upload.file, 'fileno'): 
     
    579628            raise TracError(_('No file uploaded')) 
    580629        # Now the filename is known, update the attachment resource 
    581630        # attachment.filename = filename 
    582         attachment.description = req.args.get('description', '') 
     631        attachment.description = description 
    583632        attachment.author = get_reporter_id(req, 'author') 
    584633        attachment.ipnr = req.remote_addr 
    585634 
     
    608657            attachment.filename = None 
    609658        attachment.insert(filename, upload.file, size) 
    610659 
    611         req.redirect(get_resource_url(self.env, attachment.resource(id=None), 
     660    def _do_save_many(self,req, attachment): 
     661        curr_attach = 0 
     662        while curr_attach < int(req.args['multiple_attachments']): 
     663            upload=req.args['attachment-%d' % curr_attach] 
     664            if hasattr(upload, 'filename') and upload.filename: 
     665                attachment.filename = None 
     666                self._do_save_one(req, attachment, upload, req.args.get('description-%d' % curr_attach, '')) 
     667            curr_attach=curr_attach+1 
     668 
     669    def _do_save(self, req, attachment): 
     670        req.perm(attachment.resource).require('ATTACHMENT_CREATE') 
     671 
     672        if 'cancel' in req.args: 
     673            req.redirect(get_resource_url(self.env, attachment.resource.parent, 
     674                                          req.href)) 
     675 
     676        if 'multiple_attachments' in req.args: 
     677            self._do_save_many(req, attachment) 
     678            req.redirect(get_resource_url(self.env, attachment.resource(id=None), 
    612679                                      req.href)) 
     680        else: 
     681            self._do_save_one(req, attachment, req.args['attachment'], req.args.get('description', '')) 
     682            # Redirect the user to list of attachments (must add a trailing '/') 
     683            req.redirect(get_resource_url(self.env, attachment.resource(id=None), 
     684                                      req.href)) 
    613685 
    614686    def _do_delete(self, req, attachment): 
    615687        req.perm(attachment.resource).require('ATTACHMENT_DELETE') 
     
    655727                'title': get_resource_name(self.env, attachment.resource), 
    656728                'attachment': attachment} 
    657729 
     730        mime_type = attachment.mimetype 
    658731        fd = attachment.open() 
    659732        try: 
    660733            mimeview = Mimeview(self.env) 
    661734 
    662             # MIME type detection 
    663             str_data = fd.read(1000) 
    664             fd.seek(0) 
    665              
    666             mime_type = mimeview.get_mimetype(attachment.filename, str_data) 
    667  
    668735            # Eventually send the file directly 
    669736            format = req.args.get('format') 
    670             if format in ('raw', 'txt'): 
     737            if format == 'thumb': 
    671738                if not self.render_unsafe_content: 
     739                    req.send_header('Content-Disposition', 'attachment') 
     740                if attachment.mimetype[0:5] in ('image'): 
     741                    req.send_file(attachment.getThumbail(), mime_type) 
     742            elif format in ('raw', 'txt'): 
     743                if not self.render_unsafe_content: 
    672744                    # Force browser to download files instead of rendering 
    673745                    # them, since they might contain malicious code enabling  
    674746                    # XSS attacks 
     
    678750                elif not mime_type: 
    679751                    mime_type = 'application/octet-stream' 
    680752                if 'charset=' not in mime_type: 
     753                    str_data = fd.read(1000) 
     754                    fd.seek(0) 
     755 
    681756                    charset = mimeview.get_charset(str_data, mime_type) 
    682757                    mime_type = mime_type + '; charset=' + charset 
    683758                req.send_file(attachment.path, mime_type) 
     
    730805                format = None 
    731806                if ns.startswith('raw'): 
    732807                    format = 'raw' 
     808                elif ns.startswith('thumb'): 
     809                    format = 'thumb' 
    733810                href = get_resource_url(self.env, attachment, formatter.href, 
    734811                                        format=format) 
    735812                return tag.a(label, class_='attachment', href=href + params, 
  • trac/htdocs/js/trac.js

     
    7171  window.getAncestorByTagName = function(elem, tagName) { 
    7272    return $(elem).parents(tagName).get(0); 
    7373  } 
     74})(jQuery); 
    7475 
    75 })(jQuery); 
    76  No newline at end of file 
     76var ATTACHFILE_COUNTER=0; 
     77function manageMultipleAttachFields(_obj){ 
     78    if (_obj.value == '') { 
     79        if($("#multiAttach_tbody").get(0).rows.length == 1) return; 
     80        $("#multiAttach_tbody").get(0).deleteRow($(_obj).attr("attachnum")*1); 
     81        ATTACHFILE_COUNTER=0; 
     82        $("#multiAttach_tbody tr").each(function(index, element){ 
     83            $("input", $("td", element).get(0)) 
     84                .attr("attachnum",ATTACHFILE_COUNTER) 
     85                .get(0).name = "attachment-"+ATTACHFILE_COUNTER; 
     86            $("input", $("td", element).get(1)) 
     87                .get(0).name = "description-"+ATTACHFILE_COUNTER; 
     88            ATTACHFILE_COUNTER++; 
     89        }); 
     90        $("#multiAttach_count").get(0).value = $("#multiAttach_count").val()*1-1; 
     91    } else { 
     92        if ($(_obj).attr("attachnum") != $("#multiAttach_count").val()-1) return; 
     93        var tr = $("#multiAttach_tbody").get(0).insertRow(-1); 
     94        var td = tr.insertCell(-1); 
     95        $('<input type="file" onchange="manageMultipleAttachFields(this)">') 
     96           .attr("attachnum",$("#multiAttach_count").val()) 
     97           .appendTo(td) 
     98           .get(0).name = "attachment-"+$("#multiAttach_count").val(); 
     99 
     100        td = tr.insertCell(-1); 
     101        $('<input type="text">') 
     102            .attr("size",60) 
     103            .appendTo(td) 
     104            .get(0).name = "description-"+$("#multiAttach_count").val(); 
     105        $("#multiAttach_count").get(0).value = $("#multiAttach_count").val()*1+1; 
     106    } 
     107} 
     108 
     109function clearAuthenticationCache(page) { 
     110  // Default to a non-existing page (give error 500). 
     111  // An empty page is better, here. 
     112  if (!page) page = '.force_logout'; 
     113  try{ 
     114    var agt=navigator.userAgent.toLowerCase(); 
     115    if (agt.indexOf("msie") != -1) { 
     116      // IE clear HTTP Authentication 
     117      document.execCommand("ClearAuthenticationCache"); 
     118    } 
     119    else { 
     120      // Let's create an xmlhttp object 
     121      var xmlhttp = createXMLObject(); 
     122      // Let's prepare invalid credentials 
     123      xmlhttp.open("GET", page, true, "logout", "logout"); 
     124      // Let's send the request to the server 
     125      xmlhttp.send(""); 
     126      // Let's abort the request 
     127      xmlhttp.abort(); 
     128    } 
     129  } catch(e) { 
     130    // There was an error 
     131    return; 
     132  } 
     133} 
     134     
     135function createXMLObject() { 
     136  try { 
     137    if (window.XMLHttpRequest) { 
     138      xmlhttp = new XMLHttpRequest(); 
     139    } 
     140    // code for IE 
     141    else if (window.ActiveXObject) { 
     142      xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); 
     143    } 
     144  } catch (e) { 
     145    xmlhttp=false 
     146  } 
     147  return xmlhttp; 
     148} 
     149 
  • trac/templates/macros.html

     
    189189          </ul> 
    190190        </py:when> 
    191191        <py:when test="not compact"> 
    192           <h2>Attachments</h2> 
    193192          <div py:if="alist.attachments or alist.can_create" id="attachments"> 
     193          <h2>Attachments (Files)</h2> 
    194194            <dl py:if="alist.attachments" class="attachments"> 
    195               <py:for each="attachment in alist.attachments"> 
     195              <py:for each="attachment in [f for f in alist.attachments if f.mimetype[0:5] not in ('image') ]"> 
    196196                <dt>${show_one_attachment(attachment)}</dt> 
    197197                <dd py:if="attachment.description"> 
    198198                  ${wiki_to_oneliner(context, attachment.description)} 
    199199                </dd> 
    200200              </py:for> 
    201201            </dl> 
     202          <h2>Attachments  (Images)</h2> 
     203             <py:with vars="attachs = [f for f in alist.attachments if f.mimetype[0:5] in ('image') ]; fullrow = len(attachs)"> 
     204                <table align="center"> 
     205                <tr py:for="row in group(attachs, 3)"> 
     206                <py:for each="oneattach in row"> 
     207                <td py:if="not oneattach" width="210"> 
     208                &nbsp; 
     209                </td> 
     210                <td py:if="oneattach" align="center" style="border: 1px solid #f0f0f0; text-align: center;"> 
     211                <div style="width: 200px; height: 200px;"> 
     212                    <a href="${url_of(oneattach.resource)}"> 
     213                        <img src="${url_of(oneattach.resource,format='thumb')}" 
     214                        style="cursor: pointer;" 
     215                        border="0" 
     216                        id="img_${oneattach.filename}" 
     217                        /> 
     218                    </a> 
     219                </div> 
     220                <div style="background-color: #f0f0f0;"> 
     221                    <py:with vars="attach_size = round(float(oneattach.size)/float(1024),3)"> 
     222                    <small>${oneattach.filename} (${attach_size} kb)<br /> 
     223                    <i>${oneattach.description}</i> 
     224                    </small> 
     225                    </py:with> 
     226                </div> 
     227                </td> 
     228                </py:for> 
     229                </tr></table> 
     230             </py:with> 
    202231            ${attach_file_form(alist, add_button_title)} 
    203232          </div> 
    204233        </py:when> 
  • trac/templates/attachment.html

     
    1212 
    1313  <body py:with="parent = attachments and attachments.parent or 
    1414                          attachment.resource.parent"> 
    15     <div py:choose="mode" id="content" class="attachment"> 
    16       <py:when test="'new'"> 
    17         <h1>Add Attachment to <a href="${url_of(parent)}">${name_of(parent)}</a></h1> 
    18         <form id="attachment" method="post" enctype="multipart/form-data" action=""> 
     15    <py:def function="attach_oneFile()"> 
    1916          <div class="field"> 
    2017            <label>File<py:if test="max_size >= 0"> (size limit 
    2118                  ${pretty_size(max_size, format='%d')})</py:if>:<br /> 
     
    4138            </div> 
    4239            <br /> 
    4340          </fieldset> 
     41     
     42    </py:def> 
     43    <py:def function="attach_multiFile()"> 
     44         <table><thead> 
     45          <tr><th align="left">File</th><th align="left">Description</th></tr> 
     46         </thead> 
     47        <tbody id="multiAttach_tbody"> 
     48        <tr> 
     49            <td><input type="file" attachnum="0" name="attachment-0" onchange="manageMultipleAttachFields(this);"/></td> 
     50            <td><input type="text" name="description-0" size="60" /></td> 
     51        </tr> 
     52        </tbody></table> 
     53        <input type="hidden" id="multiAttach_count" name="multiple_attachments" value="1" /> 
     54 
     55          <fieldset> 
     56            <legend>Attachment Info</legend> 
     57            <py:if test="authname == 'anonymous'"> 
     58              <div class="field"> 
     59                <label>Your email or username:<br /> 
     60                  <input type="text" name="author" size="30" value="$author" /> 
     61                </label> 
     62              </div> 
     63              </py:if> 
     64            <div class="options"> 
     65              <label><input type="checkbox" name="replace" /> 
     66                Replace existing attachments of the same name</label> 
     67            </div> 
     68            <br /> 
     69          </fieldset> 
     70     
     71    </py:def> 
     72 
     73    <div py:choose="mode" id="content" class="attachment"> 
     74      <py:when test="'new'"> 
     75        <h1>Add Attachment to <a href="${url_of(parent)}">${name_of(parent)}</a></h1> 
     76        <form id="attachment" method="post" enctype="multipart/form-data" action=""> 
     77         
     78        ${attach_multiFile()} 
    4479          <div class="buttons"> 
    4580            <input type="hidden" name="action" value="new" /> 
    4681            <input type="hidden" name="realm" value="$parent.realm" />