File:  [Repository] / ExtFile / ExtImage.py
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jan 24 16:53:50 2007 UTC (17 years, 5 months ago) by dwinter
Branches: first, MAIN
CVS tags: release, HEAD
Auf der Basis http://www.zope.org/Members/shh/ExtFile Version 1.5.4

mit zlog ersetzt durch logging


    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>