Back to index

plone3  3.1.7
content.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE.
00011 #
00012 ##############################################################################
00013 """Filesystem exporter / importer adapters.
00014 
00015 $Id: content.py 82572 2007-12-30 12:38:03Z jens $
00016 """
00017 
00018 from csv import reader
00019 from csv import register_dialect
00020 from csv import writer
00021 from ConfigParser import ConfigParser
00022 import re
00023 from StringIO import StringIO
00024 
00025 from zope.component import queryAdapter
00026 from zope.interface import implements
00027 from zope.interface import directlyProvides
00028 
00029 from interfaces import IContentFactory
00030 from interfaces import IContentFactoryName
00031 from interfaces import IFilesystemExporter
00032 from interfaces import IFilesystemImporter
00033 from interfaces import IINIAware
00034 from interfaces import ISetupTool
00035 from utils import _getDottedName
00036 from utils import _resolveDottedName
00037 
00038 #
00039 #   setup_tool handlers
00040 #
00041 def exportSiteStructure(context):
00042     IFilesystemExporter(context.getSite()).export(context, 'structure', True)
00043 
00044 def importSiteStructure(context):
00045     IFilesystemImporter(context.getSite()).import_(context, 'structure', True)
00046 
00047 
00048 #
00049 #   Filesystem export/import adapters
00050 #
00051 class FolderishExporterImporter(object):
00052     """ Tree-walking exporter / importer for "folderish" types.
00053 
00054     Folderish instances are mapped to directories within the 'structure'
00055     portion of the profile, where the folder's relative path within the site
00056     corresponds to the path of its directory under 'structure'.
00057 
00058     The subobjects of a folderish instance are enumerated in the '.objects'
00059     file in the corresponding directory.  This file is a CSV file, with one
00060     row per subobject, with the following wtructure::
00061 
00062      "<subobject id>","<subobject portal_type>"
00063 
00064     Subobjects themselves are represented as individual files or
00065     subdirectories within the parent's directory.
00066     """
00067 
00068     implements(IFilesystemExporter, IFilesystemImporter)
00069 
00070     def __init__(self, context):
00071         self.context = context
00072 
00073     def listExportableItems(self):
00074         """ See IFilesystemExporter.
00075         """
00076         exportable = self.context.objectItems()
00077         exportable = [x for x in exportable
00078                         if not ISetupTool.providedBy(x[1])]
00079         exportable = [x + (IFilesystemExporter(x[1], None),)
00080                         for x in exportable]
00081         return exportable
00082 
00083     def export(self, export_context, subdir, root=False):
00084         """ See IFilesystemExporter.
00085         """
00086         context = self.context
00087 
00088         if not root:
00089             subdir = '%s/%s' % (subdir, context.getId())
00090 
00091         exportable = self.listExportableItems()
00092 
00093         stream = StringIO()
00094         csv_writer = writer(stream)
00095 
00096         for object_id, object, adapter in exportable:
00097 
00098             factory_namer = IContentFactoryName(object, None)
00099             if factory_namer is None:
00100                 factory_name = _getDottedName(object.__class__)
00101             else:
00102                 factory_name = factory_namer()
00103 
00104             csv_writer.writerow((object_id, factory_name))
00105 
00106         export_context.writeDataFile('.objects',
00107                                     text=stream.getvalue(),
00108                                     content_type='text/comma-separated-values',
00109                                     subdir=subdir,
00110                                    )
00111 
00112         prop_adapter = IINIAware(context, None)
00113 
00114         if prop_adapter is not None:
00115             export_context.writeDataFile('.properties',
00116                                          text=prop_adapter.as_ini(),
00117                                          content_type='text/plain',
00118                                          subdir=subdir,
00119                                         )
00120 
00121         for object_id, object, adapter in exportable:
00122             if adapter is not None:
00123                 adapter.export(export_context, subdir)
00124 
00125     def import_(self, import_context, subdir, root=False):
00126         """ See IFilesystemImporter.
00127         """
00128         context = self.context
00129         if not root:
00130             subdir = '%s/%s' % (subdir, context.getId())
00131 
00132         prop_adapter = IINIAware(context, None)
00133 
00134         if prop_adapter is not None:
00135             prop_text = import_context.readDataFile('.properties',
00136                                                     subdir=subdir,
00137                                                    )
00138             if prop_text is not None:
00139                 prop_adapter.put_ini(prop_text)
00140 
00141         preserve = import_context.readDataFile('.preserve', subdir)
00142         must_preserve = self._mustPreserve()
00143 
00144         prior = context.objectIds()
00145 
00146         if not preserve:
00147             preserve = []
00148         else:
00149             preserve = _globtest(preserve, prior)
00150 
00151         preserve.extend([x[0] for x in must_preserve])
00152 
00153         for id in prior:
00154             if id not in preserve:
00155                 context._delObject(id)
00156 
00157         objects = import_context.readDataFile('.objects', subdir)
00158         if objects is None:
00159             return
00160 
00161         dialect = 'excel'
00162         stream = StringIO(objects)
00163 
00164         rowiter = reader(stream, dialect)
00165         rows = filter(None, tuple(rowiter))
00166 
00167         existing = context.objectIds()
00168 
00169         for object_id, type_name in rows:
00170 
00171             if object_id not in existing:
00172                 object = self._makeInstance(object_id, type_name,
00173                                             subdir, import_context)
00174                 if object is None:
00175                     logger = import_context.getLogger('SFWA')
00176                     logger.warning("Couldn't make instance: %s/%s" %
00177                                    (subdir, object_id))
00178                     continue
00179 
00180             wrapped = context._getOb(object_id)
00181 
00182             IFilesystemImporter(wrapped).import_(import_context, subdir)
00183 
00184     def _makeInstance(self, instance_id, type_name, subdir, import_context):
00185 
00186         context = self.context
00187         class _OldStyleClass:
00188             pass
00189 
00190         if '.' in type_name:
00191 
00192             factory = _resolveDottedName(type_name)
00193 
00194             if getattr(factory, '__bases__', None) is not None:
00195 
00196                 def _factory(instance_id,
00197                              container=self.context,
00198                              klass=factory):
00199                     try:
00200                         instance = klass(instance_id)
00201                     except (TypeError, ValueError):
00202                         instance = klass()
00203                     instance._setId(instance_id)
00204                     container._setObject(instance_id, instance)
00205 
00206                     return instance
00207 
00208                 factory = _factory
00209 
00210         else:
00211             factory = queryAdapter(self.context,
00212                                    IContentFactory,
00213                                    name=type_name,
00214                                    )
00215         if factory is None:
00216             return None
00217 
00218         try:
00219             instance = factory(instance_id)
00220         except ValueError: # invalid type
00221             return None
00222 
00223         if context._getOb(instance_id, None) is None:
00224             context._setObject(instance_id, instance) 
00225 
00226         return context._getOb(instance_id)
00227 
00228     def _mustPreserve(self):
00229         return [x for x in self.context.objectItems()
00230                         if ISetupTool.providedBy(x[1])]
00231  
00232 
00233 def _globtest(globpattern, namelist):
00234     """ Filter names in 'namelist', returning those which match 'globpattern'.
00235     """
00236     import re
00237     pattern = globpattern.replace(".", r"\.")       # mask dots
00238     pattern = pattern.replace("*", r".*")           # change glob sequence
00239     pattern = pattern.replace("?", r".")            # change glob char
00240     pattern = '|'.join(pattern.split())             # 'or' each line
00241 
00242     compiled = re.compile(pattern)
00243 
00244     return filter(compiled.match, namelist)
00245 
00246 
00247 class CSVAwareFileAdapter(object):
00248     """ Adapter for content whose "natural" representation is CSV.
00249     """
00250     implements(IFilesystemExporter, IFilesystemImporter)
00251 
00252     def __init__(self, context):
00253         self.context = context
00254 
00255     def export(self, export_context, subdir, root=False):
00256         """ See IFilesystemExporter.
00257         """
00258         export_context.writeDataFile('%s.csv' % self.context.getId(),
00259                                      self.context.as_csv(),
00260                                      'text/comma-separated-values',
00261                                      subdir,
00262                                     )
00263 
00264     def listExportableItems(self):
00265         """ See IFilesystemExporter.
00266         """
00267         return ()
00268 
00269     def import_(self, import_context, subdir, root=False):
00270         """ See IFilesystemImporter.
00271         """
00272         cid = self.context.getId()
00273         data = import_context.readDataFile('%s.csv' % cid, subdir)
00274         if data is None:
00275             logger = import_context.getLogger('CSAFA')
00276             logger.info('no .csv file for %s/%s' % (subdir, cid))
00277         else:
00278             stream = StringIO(data)
00279             self.context.put_csv(stream)
00280 
00281 class INIAwareFileAdapter(object):
00282     """ Exporter/importer for content whose "natural" representation is an
00283         '.ini' file.
00284     """
00285     implements(IFilesystemExporter, IFilesystemImporter)
00286 
00287     def __init__(self, context):
00288         self.context = context
00289 
00290     def export(self, export_context, subdir, root=False):
00291         """ See IFilesystemExporter.
00292         """
00293         export_context.writeDataFile('%s.ini' % self.context.getId(),
00294                                      self.context.as_ini(),
00295                                      'text/plain',
00296                                      subdir,
00297                                     )
00298 
00299     def listExportableItems(self):
00300         """ See IFilesystemExporter.
00301         """
00302         return ()
00303 
00304     def import_(self, import_context, subdir, root=False):
00305         """ See IFilesystemImporter.
00306         """
00307         cid = self.context.getId()
00308         data = import_context.readDataFile('%s.ini' % cid, subdir)
00309         if data is None:
00310             logger = import_context.getLogger('SGAIFA')
00311             logger.info('no .ini file for %s/%s' % (subdir, cid))
00312         else:
00313             self.context.put_ini(data)
00314 
00315 class SimpleINIAware(object):
00316     """ Exporter/importer for content which doesn't know from INI.
00317     """
00318     implements(IINIAware,)
00319 
00320     def __init__(self, context):
00321         self.context = context
00322 
00323     def getId(self):
00324         return self.context.getId()
00325 
00326     def as_ini(self):
00327         """
00328         """
00329         context = self.context
00330         parser = ConfigParser()
00331         stream = StringIO()
00332         for k, v in context.propertyItems():
00333             parser.set('DEFAULT', k, str(v))
00334         parser.write(stream)
00335         return stream.getvalue()
00336 
00337     def put_ini(self, text):
00338         """
00339         """
00340         context = self.context
00341         parser = ConfigParser()
00342         parser.readfp(StringIO(text))
00343         for option, value in parser.defaults().items():
00344             prop_type = context.getPropertyType(option)
00345             if prop_type is None:
00346                 context._setProperty(option, value, 'string')
00347             else:
00348                 context._updateProperty(option, value)
00349 
00350 class FauxDAVRequest:
00351 
00352     def __init__(self, **kw):
00353         self._data = {}
00354         self._headers = {}
00355         self._data.update(kw)
00356 
00357     def __getitem__(self, key):
00358         return self._data[key]
00359 
00360     def get(self, key, default=None):
00361         return self._data.get(key, default)
00362 
00363     def get_header(self, key, default=None):
00364         return self._headers.get(key, default)
00365 
00366 class FauxDAVResponse:
00367     def setHeader(self, key, value, lock=False):
00368         pass  # stub this out to mollify webdav.Resource
00369     def setStatus(self, value, reason=None):
00370         pass  # stub this out to mollify webdav.Resource
00371 
00372 class DAVAwareFileAdapter(object):
00373     """ Exporter/importer for content who handle their own FTP / DAV PUTs.
00374     """
00375     implements(IFilesystemExporter, IFilesystemImporter)
00376 
00377     def __init__(self, context):
00378         self.context = context
00379 
00380     def _getFileName(self):
00381         """ Return the name under which our file data is stored.
00382         """
00383         return '%s' % self.context.getId()
00384 
00385     def export(self, export_context, subdir, root=False):
00386         """ See IFilesystemExporter.
00387         """
00388         export_context.writeDataFile(self._getFileName(),
00389                                      self.context.manage_FTPget(),
00390                                      'text/plain',
00391                                      subdir,
00392                                     )
00393 
00394     def listExportableItems(self):
00395         """ See IFilesystemExporter.
00396         """
00397         return ()
00398 
00399     def import_(self, import_context, subdir, root=False):
00400         """ See IFilesystemImporter.
00401         """
00402         cid = self.context.getId()
00403         data = import_context.readDataFile(self._getFileName(), subdir)
00404         if data is None:
00405             logger = import_context.getLogger('SGAIFA')
00406             logger.info('no .ini file for %s/%s' % (subdir, cid))
00407         else:
00408             request = FauxDAVRequest(BODY=data, BODYFILE=StringIO(data))
00409             response = FauxDAVResponse()
00410             self.context.PUT(request, response)