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 82569 2007-12-30 12:21:47Z jens $
00016 """
00017 
00018 from csv import reader
00019 from csv import writer
00020 from ConfigParser import ConfigParser
00021 from StringIO import StringIO
00022 
00023 from zope.interface import implements
00024 
00025 from Products.GenericSetup.interfaces import IFilesystemExporter
00026 from Products.GenericSetup.interfaces import IFilesystemImporter
00027 from Products.GenericSetup.content import _globtest
00028 from Products.CMFCore.utils import getToolByName
00029 
00030 #
00031 #   setup_tool handlers
00032 #
00033 def exportSiteStructure(context):
00034     IFilesystemExporter(context.getSite()).export(context, 'structure', True)
00035 
00036 def importSiteStructure(context):
00037     IFilesystemImporter(context.getSite()).import_(context, 'structure', True)
00038 
00039 
00040 #
00041 #   Filesystem export/import adapters
00042 #
00043 class StructureFolderWalkingAdapter(object):
00044     """ Tree-walking exporter for "folderish" types.
00045 
00046     Folderish instances are mapped to directories within the 'structure'
00047     portion of the profile, where the folder's relative path within the site
00048     corresponds to the path of its directory under 'structure'.
00049 
00050     The subobjects of a folderish instance are enumerated in the '.objects'
00051     file in the corresponding directory.  This file is a CSV file, with one
00052     row per subobject, with the following wtructure::
00053 
00054      "<subobject id>","<subobject portal_type>"
00055 
00056     Subobjects themselves are represented as individual files or
00057     subdirectories within the parent's directory.
00058     If the import step finds that any objects specified to be created by the
00059     'structure' directory setup already exist, these objects will be deleted
00060     and then recreated by the profile.  The existence of a '.preserve' file
00061     within the 'structure' hierarchy allows specification of objects that
00062     should not be deleted.  '.preserve' files should contain one preserve
00063     rule per line, with shell-style globbing supported (i.e. 'b*' will match
00064     all objects w/ id starting w/ 'b'.
00065 
00066     Similarly, a '.delete' file can be used to specify the deletion of any
00067     objects that exist in the site but are NOT in the 'structure' hierarchy,
00068     and thus will not be recreated during the import process.
00069     """
00070 
00071     implements(IFilesystemExporter, IFilesystemImporter)
00072 
00073     def __init__(self, context):
00074         self.context = context
00075 
00076     def export(self, export_context, subdir, root=False):
00077         """ See IFilesystemExporter.
00078         """
00079         # Enumerate exportable children
00080         exportable = self.context.contentItems()
00081         exportable = [x + (IFilesystemExporter(x, None),) for x in exportable]
00082         exportable = [x for x in exportable if x[1] is not None]
00083 
00084         stream = StringIO()
00085         csv_writer = writer(stream)
00086 
00087         for object_id, object, ignored in exportable:
00088             csv_writer.writerow((object_id, object.getPortalTypeName()))
00089 
00090         if not root:
00091             subdir = '%s/%s' % (subdir, self.context.getId())
00092 
00093         export_context.writeDataFile('.objects',
00094                                      text=stream.getvalue(),
00095                                      content_type='text/comma-separated-values',
00096                                      subdir=subdir,
00097                                     )
00098 
00099         parser = ConfigParser()
00100 
00101         parser.set('DEFAULT', 'Title', self.context.Title())
00102         parser.set('DEFAULT', 'Description', self.context.Description())
00103         stream = StringIO()
00104         parser.write(stream)
00105 
00106         export_context.writeDataFile('.properties',
00107                                     text=stream.getvalue(),
00108                                     content_type='text/plain',
00109                                     subdir=subdir,
00110                                     )
00111 
00112         for id, object in self.context.objectItems():
00113 
00114             adapter = IFilesystemExporter(object, None)
00115 
00116             if adapter is not None:
00117                 adapter.export(export_context, subdir)
00118 
00119     def import_(self, import_context, subdir, root=False):
00120         """ See IFilesystemImporter.
00121         """
00122         context = self.context
00123         if not root:
00124             subdir = '%s/%s' % (subdir, context.getId())
00125 
00126         objects = import_context.readDataFile('.objects', subdir)
00127         if objects is None:
00128             return
00129 
00130         dialect = 'excel'
00131         stream = StringIO(objects)
00132 
00133         rowiter = reader(stream, dialect)
00134         ours = filter(None, tuple(rowiter))
00135         our_ids = set([item[0] for item in ours])
00136 
00137         prior = set(context.contentIds())
00138 
00139         preserve = import_context.readDataFile('.preserve', subdir)
00140         if not preserve:
00141             preserve = set()
00142         else:
00143             preservable = prior.intersection(our_ids)
00144             preserve = set(_globtest(preserve, preservable))
00145 
00146         delete = import_context.readDataFile('.delete', subdir)
00147         if not delete:
00148             delete= set()
00149         else:
00150             deletable = prior.difference(our_ids)
00151             delete = set(_globtest(delete, deletable))
00152 
00153         # if it's in our_ids and NOT in preserve, or if it's not in
00154         # our_ids but IS in delete, we're gonna delete it
00155         delete = our_ids.difference(preserve).union(delete)
00156 
00157         for id in prior.intersection(delete):
00158             context._delObject(id)
00159 
00160         existing = context.objectIds()
00161 
00162         for object_id, portal_type in ours:
00163 
00164             if object_id not in existing:
00165                 object = self._makeInstance(object_id, portal_type,
00166                                             subdir, import_context)
00167                 if object is None:
00168                     logger = import_context.getLogger('SFWA')
00169                     logger.warning("Couldn't make instance: %s/%s" %
00170                                    (subdir, object_id))
00171                     continue
00172 
00173             wrapped = context._getOb(object_id)
00174 
00175             IFilesystemImporter(wrapped).import_(import_context, subdir)
00176 
00177     def _makeInstance(self, id, portal_type, subdir, import_context):
00178 
00179         context = self.context
00180         properties = import_context.readDataFile('.properties',
00181                                                  '%s/%s' % (subdir, id))
00182         tool = getToolByName(context, 'portal_types')
00183 
00184         try:
00185             tool.constructContent(portal_type, context, id)
00186         except ValueError: # invalid type
00187             return None
00188 
00189         content = context._getOb(id)
00190 
00191         if properties is not None:
00192             lines = properties.splitlines()
00193 
00194             stream = StringIO('\n'.join(lines))
00195             parser = ConfigParser(defaults={'title': '', 'description': 'NONE'})
00196             parser.readfp(stream)
00197 
00198             title = parser.get('DEFAULT', 'title')
00199             description = parser.get('DEFAULT', 'description')
00200 
00201             content.setTitle(title)
00202             content.setDescription(description)
00203 
00204         return content