Logo Search packages:      
Sourcecode: zope-attachmentfield version File versions  Download package

FlexStorage.py

## -*- coding: utf-8 -*-
## Copyright (C) 2006 Ingeniweb

## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program; see the file COPYING. If not, write to the
## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import StringIO

from AccessControl import ClassSecurityInfo

from Products.CMFCore.utils import getToolByName
from Products.CMFCore import CMFCorePermissions

from Products.Archetypes.BaseUnit import BaseUnit
from Products.Archetypes.Storage import StorageLayer
from Products.Archetypes.interfaces.storage import IStorage
from Products.Archetypes.interfaces.layer import ILayer


00031 class FlexStorage(StorageLayer):
    """
        FlexStorage is a storage proxy. It can switch between different storages for
        content on demand. In example, it can switch data from ZODB (Attribute Storage)
        to file system (FSS).
    """

    __implements__ = StorageLayer.__implements__

    ##### WARNING #####
    # All methods or attribute MUST be different from anything that can be found
    # in a storage, or it must call this method on the storage

    ## This list contains all the storage that can be used
    ## First data is the import statement. If import fails, the next storage is tried
    ## second is the storage name, and the storage class name that will be used
    ## third is a flog indicating if this is the default choice
00048     supportedFlexStorageMethods = [
        ("from Products.Archetypes.Storage import AttributeStorage", "AttributeStorage"),
        ("from Products.FileSystemStorage.FileSystemStorage import FileSystemStorage", "FileSystemStorage"),
    ]

    security = ClassSecurityInfo()

    def __init__(self):
        self.flexStorages = {}
        for storageMethodImport, storageName in self.supportedFlexStorageMethods:
            try:
                exec storageMethodImport
                self.flexStorages[storageName] = eval("%s()" % storageName)
            except ImportError, e:
                print e
                ### XXX todo log a warning
                pass

    security.declarePublic('getAvailableFlexStorages')
00067     def getAvailableFlexStorages(self):
        """
            Return the list of storage that are working (ie classes are found)
        """
        return self.flexStorages.keys()

    security.declarePublic('getName')
    def getName(self):
        return self.__class__.__name__

    def __repr__(self):
        return "<Storage %s>" % (self.getName())

    def __cmp__(self, other):
        return cmp(self.getName(), other.getName())


#    def __getattr__(self, name):
#        """
#            Return an attribute of the underlying storage system.
#
#            This method is only called if no attribute is found in this class.
#            It's a fallback method.
#        """
#        ## we have to check if the name is prefixed
#        print "requesting %s" % name
#        return getattr(self.getFlexStorageBackend(), name)


    security.declarePrivate('initializeInstance')
    def initializeInstance(self, instance, item=None, container=None):
        storage = self.getFlexStorageBackend(instance)
        if ILayer.isImplementedBy(storage):
            return storage.initializeInstance(instance, item, container)

    security.declarePrivate('cleanupInstance')
    def cleanupInstance(self, instance, item=None, container=None):
        storage = self.getFlexStorageBackend(instance)
        if ILayer.isImplementedBy(storage):
            return storage.cleanupInstance(instance, item, container)

    security.declarePrivate('initializeField')
    def initializeField(self, instance, field):
        storage = self.getFlexStorageBackend(instance)
        if ILayer.isImplementedBy(storage):
            return storage.initializeField(instance, field)

    security.declarePrivate('cleanupField')
    def cleanupField(self, instance, field):
        storage = self.getFlexStorageBackend(instance)
        if ILayer.isImplementedBy(storage):
            return storage.cleanupField(instance, field)

    def get(self, name, instance, **kwargs):
        storage = self.getFlexStorageBackend(instance)
        return storage.get(name, instance, **kwargs)

    def set(self, name, instance, value, **kwargs):
        return self.getFlexStorageBackend(instance).set(name, instance, value, **kwargs)

    def unset(self, name, instance, **kwargs):
        return self.getFlexStorageBackend(instance).unset(name, instance, **kwargs)

    security.declarePublic('getFlexStorageBackend')
00131     def getFlexStorageBackend(self, instance):
        """
            Return the name of the currently used storage backend
        """
        aTool = getToolByName(instance, 'portal_attachment')
        currentFlexStorageBackend = aTool.currentFlexStorageBackend
        if currentFlexStorageBackend in self.flexStorages.keys():
            return self.flexStorages[currentFlexStorageBackend]
        else:
            raise RuntimeError("%s requested, but this storage has not been initialized." % currentFlexStorageBackend)

    security.declareProtected(CMFCorePermissions.ManagePortal, "changeFlexStorageBackend")
00143     def changeFlexStorageBackend(self, instance, newStorageName, typesWithAF):
        """
            Change the backend used to store data. Migrate the already existing
            fields.
        """
        aTool = getToolByName(instance, 'portal_attachment')
        if aTool.currentFlexStorageBackend == newStorageName:
            return
        oldStorage = self.getFlexStorageBackend(instance)
        aTool.currentFlexStorageBackend = newStorageName
        newStorage = self.getFlexStorageBackend(instance)

        cat = getToolByName(instance, 'portal_catalog')
        brains = cat({'portal_type': typesWithAF.keys()})
        out = StringIO.StringIO()
        for b in brains:
            o = b.getObject()
            fields = typesWithAF[o.portal_type]
            self.migrateContent(oldStorage, newStorage, o, fields, out)
            print >> out, ""

        return out.getvalue()

    security.declarePrivate('migrateContent')
00167     def migrateContent(self, oldStorage, newStorage, content, fieldNames, out):
        """
            Change the storage backend of one content.
            oldStorage is the old storage where we get the data (and remove it)
            newStorage is the storage where we put this data
            content is the content
        """
        print >> out, '/'.join(content.getPhysicalPath()), ":",

        for name in fieldNames:
            print >> out, "'%s'" % name,
            f = content.getField(name)
            # error if field has already a content
            #if f.get_size(content) != 0:
            #    raise RuntimeError("already set")

            # get content from old storage and delete old storage
            try:
                value = oldStorage.get(name, content)
            except AttributeError:
                print >> out, "no old value",
                continue

            # Unwrap value
            data = BaseUnit(
                name,
                str(value),
                instance=content,
                filename=getattr(value, "filename", "unknowFilename"),
                mimetype=value.getContentType(),
            )

            ### newStorage.initializeField(content, f) #FIXME: really needed?
            f.set(content, data)
            oldStorage.unset(name, content)
            # unset empty files, this avoid empty files on disk
            if f.get_size(content) == 0:
                print >> out, "no data, so unset",
                f.set(content, "DELETE_FILE")

            print >> out, ".",


Generated by  Doxygen 1.6.0   Back to index