Annotation of ExtFile/ExtImage.py, revision 1.1.1.1
1.1 dwinter 1: """ExtImage product module."""
2: ###############################################################################
3: #
4: # Copyright (c) 2001 Gregor Heine <mac.gregor@gmx.de>. All rights reserved.
5: # ExtFile Home: http://www.zope.org/Members/MacGregor/ExtFile/index_html
6: #
7: # Redistribution and use in source and binary forms, with or without
8: # modification, are permitted provided that the following conditions
9: # are met:
10: #
11: # 1. Redistributions of source code must retain the above copyright
12: # notice, this list of conditions and the following disclaimer.
13: # 2. Redistributions in binary form must reproduce the above copyright
14: # notice, this list of conditions and the following disclaimer in the
15: # documentation and/or other materials provided with the distribution.
16: # 3. The name of the author may not be used to endorse or promote products
17: # derived from this software without specific prior written permission
18: #
19: # Disclaimer
20: #
21: # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22: # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23: # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24: # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25: # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26: # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27: # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28: # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29: # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30: # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31: #
32: # In accordance with the license provided for by the software upon
33: # which some of the source code has been derived or used, the following
34: # acknowledgement is hereby provided :
35: #
36: # "This product includes software developed by Digital Creations
37: # for use in the Z Object Publishing Environment
38: # (http://www.zope.org/)."
39: #
40: ###############################################################################
41:
42: __doc__ = """ExtImage product module.
43: The ExtImage-Product works like the Zope Image-product, but stores the
44: uploaded image externally in a repository-direcory. It creates a preview
45: of the image (requires PIL)."""
46:
47: __version__='1.5.4'
48:
49: import Globals
50: from Products.ExtFile.ExtFile import *
51: from Globals import HTMLFile, MessageDialog, InitializeClass
52: from AccessControl import ClassSecurityInfo
53: from webdav.Lockable import ResourceLockedError
54: import urllib, os, string, types
55: from os.path import join, isfile
56: from tempfile import TemporaryFile
57:
58: from webdav.WriteLockInterface import WriteLockInterface
59: from IExtFile import IExtImage
60:
61: from zLOG import *
62: _SUBSYS = 'ExtImage'
63: _debug = 0
64:
65: try: from zExceptions import Redirect
66: except ImportError: Redirect = 'Redirect'
67:
68: from Config import REPOSITORY_UMASK
69:
70: NO_PREVIEW = 0
71: GENERATE = 1
72: UPLOAD_NORESIZE = 2
73: UPLOAD_RESIZE = 3
74:
75: manage_addExtImageForm = HTMLFile('dtml/extImageAdd', globals())
76:
77:
78: def manage_addExtImage(self, id='', title='', descr='', file='', preview='',
79: content_type='', create_prev=0, maxx='', maxy='',
80: ratio=0, permission_check=0, redirect_default_view=0, REQUEST=None):
81: """ Add an ExtImage to a folder. """
82: if not id and getattr(file, 'filename', None) is not None:
83: # generate id from filename and make sure, it has no 'bad' chars
84: id = file.filename
85: id = id[max(string.rfind(id,'/'),
86: string.rfind(id,'\\'),
87: string.rfind(id,':'))+1:]
88: title = title or id
89: id = normalize_id(id)
90: tempExtImage = ExtImage(id, title, descr, permission_check, redirect_default_view)
91: self._setObject(id, tempExtImage)
92: if file != '':
93: self._getOb(id).manage_file_upload(file, content_type, 0, create_prev, maxx, maxy, ratio)
94: if create_prev==UPLOAD_NORESIZE or create_prev==UPLOAD_RESIZE:
95: self._getOb(id).manage_file_upload(preview, content_type, 1, create_prev, maxx, maxy, ratio)
96: if REQUEST is not None:
97: return self.manage_main(self, REQUEST, update_menu=0)
98: return id
99:
100:
101:
102: class ExtImage(ExtFile):
103: """ The ExtImage-Product works like the Zope Image-product, but stores the
104: uploaded image externally in a repository-direcory. It can create a
105: preview of the image (requires PIL)."""
106:
107: __implements__ = (IExtImage, WriteLockInterface)
108:
109: security = ClassSecurityInfo()
110:
111: # what do people think they're adding?
112: meta_type = 'ExtImage'
113:
114: # default,min,max-sizes for the preview image
115: _image_size={'default':256,'min':1,'max':999}
116:
117: # store maxx and maxy
118: prev_maxx = _image_size['default']
119: prev_maxy = _image_size['default']
120:
121: ################################
122: # Init method #
123: ################################
124:
125: def __init__(self, id, title='', descr='', permission_check=0, redirect_default_view=0):
126: """ Initialize a new instance of ExtImage """
127: ExtImage.inheritedAttribute("__init__")(self, id, title, descr, permission_check, redirect_default_view)
128: self.prev_filename = []
129: self.prev_content_type = ''
130: self.prev_ratio = 1
131: self.has_preview = 0
132:
133: ################################
134: # Public methods #
135: ################################
136:
137: def __str__(self):
138: return self.tag()
139:
140: security.declareProtected(ViewPermission, 'tag')
141: def tag(self, preview=0, icon=0, height=None, width=None, alt=None,
142: scale=0, xscale=0, yscale=0, border='0', REQUEST=None, **args):
143: """ Generate an HTML IMG tag for this image, with customization.
144: Arguments to self.tag() can be any valid attributes of an IMG tag.
145: 'src' will always be an absolute pathname, to prevent redundant
146: downloading of images. Defaults are applied intelligently for
147: 'height', 'width', and 'alt'. If specified, the 'scale', 'xscale',
148: and 'yscale' keyword arguments will be used to automatically adjust
149: the output height and width values of the image tag.
150: Adopted and adapted from OFS/Image.py
151: """
152: if not self.is_webviewable():
153: preview = 1
154: if not self._access_permitted():
155: preview = 1
156: if preview and not self.has_preview:
157: icon = 1
158: if icon:
159: url = self._static_url(icon=1)
160: img_width, img_height = (32, 32)
161: elif preview:
162: url = self._static_url(preview=1)
163: img_width, img_height = self._getImageSize(self.prev_filename)
164: else:
165: url = self._static_url()
166: img_width, img_height = self._getImageSize(self.filename)
167: height = height or img_height
168: width = width or img_width
169:
170: # Auto-scaling support
171: xdelta = xscale or scale
172: ydelta = yscale or scale
173: if xdelta and width != None:
174: width = str(int(width) * xdelta)
175: if ydelta and height != None:
176: height = str(int(height) * ydelta)
177:
178: if alt is None: alt = self.title or ''
179: strg = '<img src="%s" border="%s" alt="%s"' % \
180: (url, border, alt)
181: if height: strg = '%s height="%s"' % (strg, height)
182: if width: strg = '%s width="%s"' % (strg, width)
183: for key in args.keys():
184: value = args.get(key)
185: strg = '%s %s="%s"' % (strg, key, value)
186: strg="%s />" % (strg)
187: return strg
188:
189: security.declareProtected(ViewPermission, 'preview')
190: def preview(self):
191: """ Return a preview of the image """
192: raise Redirect, self._static_url(preview=1)
193:
194: security.declareProtected(ViewPermission, 'preview_tag')
195: def preview_tag(self):
196: """ Generates the HTML IMG tag for the preview image """
197: return self.tag(preview=1)
198:
199: security.declareProtected(ViewPermission, 'preview_html')
200: def preview_html(self):
201: """ Same as preview_tag """
202: return self.preview_tag()
203:
204: security.declareProtected(ViewPermission, 'is_broken')
205: def is_broken(self):
206: """ Check if external file exists and return true (1) or false (0) """
207: if self.has_preview and self.filename != self.prev_filename:
208: if not self._get_fsname(self.prev_filename):
209: return 1
210: return ExtImage.inheritedAttribute("is_broken")(self)
211:
212: security.declareProtected(ViewPermission, 'is_webviewable')
213: def is_webviewable(self):
214: """ Return 1 for GIF, JPEG, and PNG images, otherwise return 0 """
215: format = self.format()
216: if format=='JPEG' or format=='GIF' or format=='PNG':
217: return 1
218: else:
219: return 0
220:
221: security.declareProtected(ViewPermission, 'get_prev_size')
222: def get_prev_size(self):
223: """ Returns the size of the preview file """
224: fn = self._get_fsname(self.prev_filename)
225: if fn:
226: return os.stat(fn)[6]
227: return 0
228:
229: security.declareProtected(ViewPermission, 'prev_rawsize')
230: def prev_rawsize(self):
231: """ Same as get_prev_size """
232: return self.get_prev_size()
233:
234: security.declareProtected(ViewPermission, 'prev_size')
235: def prev_size(self):
236: """ Returns a formatted stringified version of the preview size """
237: return self._bytetostring(self.get_prev_size())
238:
239: security.declareProtected(ViewPermission, 'width')
240: def width(self):
241: """ Pixel width of the image """
242: return self._getImageSize(self.filename)[0]
243:
244: security.declareProtected(ViewPermission, 'height')
245: def height(self):
246: """ Pixel height of the image """
247: return self._getImageSize(self.filename)[1]
248:
249: security.declareProtected(ViewPermission, 'prev_width')
250: def prev_width(self):
251: """ Pixel width of the preview """
252: return self._getImageSize(self.prev_filename)[0]
253:
254: security.declareProtected(ViewPermission, 'prev_height')
255: def prev_height(self):
256: """ Pixel height of the preview """
257: return self._getImageSize(self.prev_filename)[1]
258:
259: security.declareProtected(ViewPermission, 'format')
260: def format(self):
261: """ Get the PIL file format of the image """
262: filename = self._get_fsname(self.filename)
263: try:
264: from PIL import Image
265: im = Image.open(filename)
266: return im.format
267: except:
268: return 'unknown'
269:
270: security.declareProtected(AccessPermission, 'get_prev_filename')
271: def get_prev_filename(self):
272: """ Returns the preview file name for display """
273: return self._fsname(self.prev_filename)
274:
275: ################################
276: # Protected management methods #
277: ################################
278:
279: # Management Interface
280: security.declareProtected(AccessPermission, 'manage_main')
281: manage_main = HTMLFile('dtml/extImageEdit', globals())
282:
283: security.declareProtected(ChangePermission, 'manage_del_prev')
284: def manage_del_prev(self, REQUEST=None):
285: """ Delete the Preview Image """
286: if self.has_preview and self.filename != self.prev_filename:
287: tmp_fn = self._temp_fsname(self.prev_filename)
288: fn = self._fsname(self.prev_filename)
289: if isfile(tmp_fn):
290: try: os.rename(tmp_fn, fn+'.undo')
291: except OSError: pass
292: else:
293: try: os.remove(fn)
294: except OSError: pass
295: elif isfile(fn):
296: try: os.rename(fn, fn+'.undo')
297: except OSError: pass
298: self.prev_content_type = ''
299: self.has_preview = 0
300:
301: self.ZCacheable_invalidate()
302:
303: if REQUEST is not None:
304: return self.manage_main(self, REQUEST, manage_tabs_message='Preview deleted.')
305:
306: security.declareProtected(ChangePermission, 'manage_create_prev')
307: def manage_create_prev(self, maxx=0, maxy=0, ratio=0, REQUEST=None):
308: """ Create a preview Image """
309: maxx, maxy = self._formatDimensions(maxx, maxy)
310: if maxx!=0 and maxy!=0:
311: self._register() # Register with TM
312: try:
313: new_fn = self._get_ufn(self.prev_filename, content_type='image/jpeg')
314: self._createPreview(self.filename, new_fn, maxx, maxy, ratio)
315: finally:
316: self._dir__unlock()
317: if REQUEST is None:
318: return self.has_preview
319: else:
320: if self.has_preview:
321: return self.manage_main(self, REQUEST, manage_tabs_message='Preview created.')
322: elif maxx=='0' and maxy=='0':
323: return MessageDialog(
324: title = 'Attention',
325: message = "You must enter a value > 0",
326: action = './manage_main',)
327: else:
328: return MessageDialog(
329: title = 'Warning',
330: message = "An error occurred while generating the preview.",
331: action = './manage_main',)
332:
333: # File upload Interface
334: security.declareProtected(AccessPermission, 'manage_uploadForm')
335: manage_uploadForm = HTMLFile('dtml/extImageUpload', globals())
336:
337: security.declareProtected(ChangePermission, 'manage_upload')
338: def manage_upload(self, file='', content_type='', is_preview=0,
339: create_prev=NO_PREVIEW, maxx='', maxy='', ratio=0,
340: REQUEST=None):
341: """ Upload image from file handle or string buffer """
342: if self.wl_isLocked():
343: raise ResourceLockedError, "File is locked via WebDAV"
344:
345: if type(file) == types.StringType:
346: temp_file = TemporaryFile()
347: temp_file.write(file)
348: temp_file.seek(0)
349: else:
350: temp_file = file
351: return self.manage_file_upload(temp_file, content_type, is_preview,
352: create_prev, maxx, maxy, ratio, REQUEST)
353:
354: security.declareProtected(ChangePermission, 'manage_file_upload')
355: def manage_file_upload(self, file='', content_type='', is_preview=0,
356: create_prev=NO_PREVIEW, maxx='', maxy='', ratio=0,
357: REQUEST=None):
358: """ Upload image from file handle or local directory """
359: if self.wl_isLocked():
360: raise ResourceLockedError, "File is locked via WebDAV"
361:
362: if is_preview:
363: if type(file) == types.StringType:
364: cant_read_exc = "Can't open: "
365: try: file = open(file, 'rb')
366: except: raise cant_read_exc, file
367: maxx, maxy = self._formatDimensions(maxx, maxy)
368: if create_prev==UPLOAD_RESIZE and maxx!=0 and maxy!=0:
369: self._register() # Register with TM
370: try:
371: new_fn = self._get_ufn(self.prev_filename, content_type='image/jpeg')
372: self._update_data(file, self._temp_fsname(new_fn))
373: finally:
374: self._dir__unlock()
375: self._createPreview(new_fn, new_fn, maxx, maxy, ratio)
376: else:
377: if content_type:
378: file = HTTPUpload(file, content_type)
379: self.prev_content_type = self._get_content_type(file, file.read(100),
380: self.id, self.prev_content_type)
381: file.seek(0)
382: self._register() # Register with TM
383: try:
384: new_fn = self._get_ufn(self.prev_filename, content_type=self.prev_content_type)
385: self._update_data(file, self._temp_fsname(new_fn))
386: finally:
387: self._dir__unlock()
388: self.prev_filename = new_fn
389: self._initPreview()
390: else:
391: ExtImage.inheritedAttribute("manage_file_upload")(self, file, content_type)
392: if create_prev==GENERATE:
393: maxx, maxy = self._formatDimensions(maxx, maxy)
394: if maxx!=0 and maxy!=0:
395: self._register() # Register with TM
396: try:
397: new_fn = self._get_ufn(self.prev_filename, content_type='image/jpeg')
398: self._createPreview(self.filename, new_fn, maxx, maxy, ratio)
399: finally:
400: self._dir__unlock()
401: if REQUEST is not None:
402: return self.manage_main(self, REQUEST, manage_tabs_message='Upload complete.')
403:
404: security.declareProtected(ChangePermission, 'manage_http_upload')
405: def manage_http_upload(self, url, is_preview=0, REQUEST=None):
406: """ Upload image from http-server """
407: if self.wl_isLocked():
408: raise ResourceLockedError, "File is locked via WebDAV"
409:
410: if is_preview:
411: url = urllib.quote(url,'/:')
412: cant_read_exc = "Can't open: "
413: try: file = urllib.urlopen(url)
414: except: raise cant_read_exc, url
415: file = HTTPUpload(file)
416: self.prev_content_type = self._get_content_type(file, file.read(100),
417: self.id, self.prev_content_type)
418: file.seek(0)
419: self._register() # Register with TM
420: try:
421: new_fn = self._get_ufn(self.prev_filename, content_type=self.prev_content_type)
422: self._update_data(file, self._temp_fsname(new_fn))
423: finally:
424: self._dir__unlock()
425: self.prev_filename = new_fn
426: self._initPreview()
427: else:
428: ExtImage.inheritedAttribute("manage_http_upload")(self, url)
429: #if self.has_preview:
430: # maxx, maxy = self._formatDimensions(self.prev_maxx, self.prev_maxy)
431: # self._register() # Register with TM
432: # try:
433: # new_fn = self._get_ufn(self.prev_filename, content_type='image/jpeg')
434: # self._createPreview(self.filename, new_fn, maxx, maxy, self.prev_ratio)
435: # finally:
436: # self._dir__unlock()
437: if REQUEST is not None:
438: return self.manage_main(self, REQUEST, manage_tabs_message='Upload complete.')
439:
440: security.declareProtected(ChangePermission, 'PUT')
441: def PUT(self, REQUEST, RESPONSE):
442: """ Handle HTTP PUT requests """
443: RESPONSE = ExtImage.inheritedAttribute("PUT")(self, REQUEST, RESPONSE)
444: if self.has_preview:
445: maxx, maxy = self._formatDimensions(self.prev_maxx, self.prev_maxy)
446: self._register() # Register with TM
447: try:
448: # Need to pass in the path as webdav.NullResource calls PUT
449: # on an unwrapped object.
450: try:
451: self.aq_parent # This raises AttributeError if no context
452: except AttributError:
453: path = self._get_zodb_path(REQUEST.PARENTS[0])
454: else:
455: path = None
456: new_fn = self._get_ufn(self.prev_filename, content_type='image/jpeg', path=path)
457: self._createPreview(self.filename, new_fn, maxx, maxy, self.prev_ratio)
458: finally:
459: self._dir__unlock()
460: return RESPONSE
461:
462: ################################
463: # Private methods #
464: ################################
465:
466: def _getImageSize(self, filename):
467: """ Return width, height tuple using PIL """
468: filename = self._get_fsname(filename)
469: try:
470: from PIL import Image
471: im = Image.open(filename)
472: return im.size[0], im.size[1]
473: except:
474: return 0, 0
475:
476: def _createPreview(self, from_filename, to_filename, maxx, maxy, ratio):
477: """ Generate a preview using PIL """
478: try:
479: from PIL import Image
480: except ImportError:
481: pass
482: else:
483: imfile = self._get_fsname(from_filename)
484: if imfile:
485: im = Image.open(imfile)
486: if im.mode!='RGB':
487: im = im.convert('RGB')
488: filter = Image.BICUBIC
489: if hasattr(Image, 'ANTIALIAS'): # PIL 1.1.3
490: filter = Image.ANTIALIAS
491: if ratio: # keep aspect-ratio
492: im.thumbnail((maxx,maxy), filter)
493: else: # distort to fixed size
494: im = im.resize((maxx,maxy), filter)
495: umask = os.umask(REPOSITORY_UMASK)
496: outfile = self._temp_fsname(to_filename)
497: try:
498: im.save(outfile, 'JPEG', quality=85)
499: except:
500: os.umask(umask)
501: if isfile(outfile):
502: try: os.remove(outfile)
503: except OSError: pass
504: raise
505: else:
506: os.umask(umask)
507: self.prev_content_type = 'image/jpeg'
508: self.prev_filename = to_filename
509: self.prev_maxx = maxx
510: self.prev_maxy = maxy
511: self.prev_ratio = ratio
512: self._initPreview()
513:
514: def _initPreview(self):
515: """ Verify the preview """
516: self.ZCacheable_invalidate()
517:
518: prev_width, prev_height = self._getImageSize(self.prev_filename)
519: if prev_width<=0 or prev_height<=0:
520: self.has_preview = 0
521: else:
522: self.has_preview = 1
523:
524: def _formatDimensions(self, maxx, maxy):
525: """ Make sure, the dimensions are valid int's """
526: if type(maxx) is types.StringType:
527: try: maxx = int(maxx)
528: except ValueError: maxx = self._image_size['default']
529: if type(maxy) is types.StringType:
530: try: maxy = int(maxy)
531: except ValueError: maxy = self._image_size['default']
532: if maxx!=0 and maxy!=0:
533: if maxx<self._image_size['min']: maxx = self._image_size['min']
534: elif maxx>self._image_size['max']: maxx = self._image_size['max']
535: if maxy<self._image_size['min']: maxy = self._image_size['min']
536: elif maxy>self._image_size['max']: maxy = self._image_size['max']
537: return maxx, maxy
538:
539: def _undo(self):
540: """ Restore filename after delete or copy-paste """
541: if self.has_preview and self.filename != self.prev_filename:
542: fn = self._fsname(self.prev_filename)
543: if not isfile(fn) and isfile(fn+'.undo'):
544: self._register() # Register with TM
545: os.rename(fn+'.undo', self._temp_fsname(self.prev_filename))
546: return ExtImage.inheritedAttribute("_undo")(self)
547:
548: def _get_content_type(self, file, body, id, content_type=None):
549: """ Determine the mime-type """
550: from OFS.Image import getImageInfo
551: ct, w, h = getImageInfo(body)
552: if ct:
553: content_type = ct
554: else:
555: content_type = ExtImage.inheritedAttribute('_get_content_type')(self,
556: file, body, id, content_type)
557: return content_type
558:
559: ################################
560: # Special management methods #
561: ################################
562:
563: security.declarePrivate('manage_afterClone')
564: def manage_afterClone(self, item):
565: """ When a copy of the object is created (zope copy-paste-operation),
566: this function is called by CopySupport.py. A copy of the external
567: file is created and self.filename is changed.
568: """
569: try:
570: self.aq_parent # This raises AttributeError if no context
571: except AttributeError:
572: pass
573: else:
574: result = ExtImage.inheritedAttribute("manage_afterClone")(self, item)
575: self._register() # Register with TM
576: try:
577: new_prev_fn = self._get_new_ufn(content_type=self.prev_content_type)
578: if self.has_preview and self.filename != self.prev_filename:
579: old_prev_fn = self._get_fsname(self.prev_filename)
580: if old_prev_fn:
581: self._update_data(old_prev_fn, self._temp_fsname(new_prev_fn))
582: self.prev_filename = new_prev_fn
583: else:
584: self.prev_filename = []
585: self.has_preview = 0
586: elif self.has_preview:
587: # XXX: This seems to be an impossible state?
588: old_fn = self._get_fsname(self.filename)
589: if not old_fn:
590: self.prev_filename = []
591: self.has_preview = 0
592: else:
593: self.prev_filename = []
594: finally:
595: self._dir__unlock()
596: return result
597: return ExtImage.inheritedAttribute("manage_afterClone")(self, item)
598:
599: security.declarePrivate('manage_afterAdd')
600: def manage_afterAdd(self, item, container):
601: """ When a copy of the object is created (zope copy-paste-operation),
602: this function is called by CopySupport.py. A copy of the external
603: file is created and self.filename is changed.
604: """
605: return ExtImage.inheritedAttribute("manage_afterAdd")(self, item, container)
606:
607: security.declarePrivate('manage_beforeDelete')
608: def manage_beforeDelete(self, item, container):
609: """ This method is called, when the object is deleted. To support
610: undo-functionality and because this happens too, when the object
611: is moved (cut-paste) or renamed, the external file is not deleted.
612: It is just renamed to filename.undo and remains in the
613: repository, until it is deleted manually.
614: """
615: if self.has_preview and self.filename != self.prev_filename:
616: tmp_fn = self._temp_fsname(self.prev_filename)
617: fn = self._fsname(self.prev_filename)
618: if isfile(tmp_fn):
619: try: os.rename(tmp_fn, fn+'.undo')
620: except OSError: pass
621: else:
622: try: os.remove(fn)
623: except OSError: pass
624: elif isfile(fn):
625: try: os.rename(fn, fn+'.undo')
626: except OSError: pass
627: return ExtImage.inheritedAttribute("manage_beforeDelete")(self, item, container)
628:
629: ################################
630: # Transaction manager methods #
631: ################################
632:
633: def _finish(self):
634: """ Commits the temporary file """
635: if self.prev_filename and self.filename != self.prev_filename:
636: tmp_fn = self._temp_fsname(self.prev_filename)
637: if _debug: LOG(_SUBSYS, INFO, 'finishing %s' % tmp_fn)
638: if isfile(tmp_fn):
639: if _debug: LOG(_SUBSYS, INFO, 'isfile %s' % tmp_fn)
640: fn = self._fsname(self.prev_filename)
641: try: os.remove(fn)
642: except OSError: pass
643: os.rename(tmp_fn, fn)
644: ExtImage.inheritedAttribute('_finish')(self)
645:
646: def _abort(self):
647: """ Deletes the temporary file """
648: if self.prev_filename and self.filename != self.prev_filename:
649: tmp_fn = self._temp_fsname(self.prev_filename)
650: if _debug: LOG(_SUBSYS, INFO, 'aborting %s' % tmp_fn)
651: if isfile(tmp_fn):
652: if _debug: LOG(_SUBSYS, INFO, 'isfile %s' % tmp_fn)
653: try: os.remove(tmp_fn)
654: except OSError: pass
655: ExtImage.inheritedAttribute('_abort')(self)
656:
657:
658: InitializeClass(ExtImage)
659:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>