Ticket #2827: thumbailPreviewOfAttachImages.r8466-fixed.2.patch
| File thumbailPreviewOfAttachImages.r8466-fixed.2.patch, 16.6 KB (added by anonymous, 2 years ago) |
|---|
-
trac/attachment.py
20 20 import os 21 21 import re 22 22 import shutil 23 import Image 23 24 import time 24 25 import unicodedata 25 26 … … 116 117 self.env = env 117 118 self.parent_realm = self.resource.parent.realm 118 119 self.parent_id = unicode(self.resource.parent.id) 120 self.first1000bytes = None 119 121 if self.resource.id: 120 122 self._fetch(self.resource.id, db) 121 123 else: … … 125 127 self.date = None 126 128 self.author = None 127 129 self.ipnr = None 130 self.mimetype = None 131 self.charset = None 128 132 129 133 def _set_filename(self, val): 130 134 self.resource.id = val … … 152 156 self.date = datetime.fromtimestamp(time, utc) 153 157 self.author = row[4] 154 158 self.ipnr = row[5] 159 self._get_mimetype() 160 self._get_charset() 155 161 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))) 156 201 def _get_path(self): 157 202 path = os.path.join(self.env.path, 'attachments', self.parent_realm, 158 203 unicode_quote(self.parent_id)) … … 266 311 attachment.date = datetime.fromtimestamp(time, utc) 267 312 attachment.author = author 268 313 attachment.ipnr = ipnr 314 attachment._get_mimetype() 315 attachment._get_charset() 269 316 yield attachment 270 317 271 318 def delete_all(cls, env, parent_realm, parent_id, db): … … 349 396 # IRequestHandler methods 350 397 351 398 def match_request(self, req): 352 match = re.match(r'/(raw- )?attachment/([^/]+)(?:/(.*))?$',399 match = re.match(r'/(raw-|thumb-)?attachment/([^/]+)(?:/(.*))?$', 353 400 req.path_info) 354 401 if match: 355 402 raw, realm, path = match.groups() 356 if raw :403 if raw == 'raw-': 357 404 req.args['format'] = 'raw' 405 elif raw == 'thumb-': 406 req.args['format'] = 'thumb' 358 407 req.args['realm'] = realm 359 408 if path: 360 409 req.args['path'] = path … … 415 464 416 465 def get_link_resolvers(self): 417 466 yield ('raw-attachment', self._format_link) 467 yield ('thumb-attachment', self._format_link) 418 468 yield ('attachment', self._format_link) 419 469 420 470 # Public methods … … 520 570 if format == 'raw': 521 571 kwargs.pop('format') 522 572 prefix = 'raw-attachment' 573 elif format == 'thumb': 574 kwargs.pop('format') 575 prefix = 'thumb-attachment' 523 576 parent_href = unicode_unquote(get_resource_url(self.env, 524 577 resource.parent(version=None), Href(''))) 525 578 if not resource.id: … … 544 597 545 598 # Internal methods 546 599 547 def _do_save (self, req, attachment):600 def _do_save_one(self, req, attachment, upload, description): 548 601 req.perm(attachment.resource).require('ATTACHMENT_CREATE') 549 602 550 if 'cancel' in req.args:551 req.redirect(get_resource_url(self.env, attachment.resource.parent,552 req.href))553 603 554 upload = req.args['attachment']555 604 if not hasattr(upload, 'filename') or not upload.filename: 556 605 raise TracError(_('No file uploaded')) 557 606 if hasattr(upload.file, 'fileno'): … … 579 628 raise TracError(_('No file uploaded')) 580 629 # Now the filename is known, update the attachment resource 581 630 # attachment.filename = filename 582 attachment.description = req.args.get('description', '')631 attachment.description = description 583 632 attachment.author = get_reporter_id(req, 'author') 584 633 attachment.ipnr = req.remote_addr 585 634 … … 608 657 attachment.filename = None 609 658 attachment.insert(filename, upload.file, size) 610 659 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), 612 679 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)) 613 685 614 686 def _do_delete(self, req, attachment): 615 687 req.perm(attachment.resource).require('ATTACHMENT_DELETE') … … 655 727 'title': get_resource_name(self.env, attachment.resource), 656 728 'attachment': attachment} 657 729 730 mime_type = attachment.mimetype 658 731 fd = attachment.open() 659 732 try: 660 733 mimeview = Mimeview(self.env) 661 734 662 # MIME type detection663 str_data = fd.read(1000)664 fd.seek(0)665 666 mime_type = mimeview.get_mimetype(attachment.filename, str_data)667 668 735 # Eventually send the file directly 669 736 format = req.args.get('format') 670 if format in ('raw', 'txt'):737 if format == 'thumb': 671 738 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: 672 744 # Force browser to download files instead of rendering 673 745 # them, since they might contain malicious code enabling 674 746 # XSS attacks … … 678 750 elif not mime_type: 679 751 mime_type = 'application/octet-stream' 680 752 if 'charset=' not in mime_type: 753 str_data = fd.read(1000) 754 fd.seek(0) 755 681 756 charset = mimeview.get_charset(str_data, mime_type) 682 757 mime_type = mime_type + '; charset=' + charset 683 758 req.send_file(attachment.path, mime_type) … … 730 805 format = None 731 806 if ns.startswith('raw'): 732 807 format = 'raw' 808 elif ns.startswith('thumb'): 809 format = 'thumb' 733 810 href = get_resource_url(self.env, attachment, formatter.href, 734 811 format=format) 735 812 return tag.a(label, class_='attachment', href=href + params, -
trac/htdocs/js/trac.js
71 71 window.getAncestorByTagName = function(elem, tagName) { 72 72 return $(elem).parents(tagName).get(0); 73 73 } 74 })(jQuery); 74 75 75 })(jQuery); 76 No newline at end of file 76 var ATTACHFILE_COUNTER=0; 77 function 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 109 function 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 135 function 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
189 189 </ul> 190 190 </py:when> 191 191 <py:when test="not compact"> 192 <h2>Attachments</h2>193 192 <div py:if="alist.attachments or alist.can_create" id="attachments"> 193 <h2>Attachments (Files)</h2> 194 194 <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') ]"> 196 196 <dt>${show_one_attachment(attachment)}</dt> 197 197 <dd py:if="attachment.description"> 198 198 ${wiki_to_oneliner(context, attachment.description)} 199 199 </dd> 200 200 </py:for> 201 201 </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 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> 202 231 ${attach_file_form(alist, add_button_title)} 203 232 </div> 204 233 </py:when> -
trac/templates/attachment.html
12 12 13 13 <body py:with="parent = attachments and attachments.parent or 14 14 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()"> 19 16 <div class="field"> 20 17 <label>File<py:if test="max_size >= 0"> (size limit 21 18 ${pretty_size(max_size, format='%d')})</py:if>:<br /> … … 41 38 </div> 42 39 <br /> 43 40 </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()} 44 79 <div class="buttons"> 45 80 <input type="hidden" name="action" value="new" /> 46 81 <input type="hidden" name="realm" value="$parent.realm" />
