Back to index

plone3  3.1.7
migrate_ptk.py
Go to the documentation of this file.
00001 from Acquisition import aq_base, aq_inner, aq_parent
00002 from Globals import PersistentMapping
00003 import sys
00004 
00005 
00006 #
00007 # Routines generally useful for migration purposes.
00008 #
00009 
00010 
00011 class Converter:
00012     def allowDescendChildren(self): raise 'Not implemented'
00013     def convert(self, ob): raise 'Not implemented'
00014     def showDuplicationError(self): raise 'Not implemented'
00015 
00016 
00017 class Migrator:
00018 
00019     def __init__(self, conversions, skip):
00020         self.conversions = conversions
00021         self.skip = skip
00022         self.visited_folders = []
00023         self.warnings = []
00024         self.copied = []
00025         self.skipped = []
00026 
00027     def migrateObjectManager(self, src_folder, dst_folder, place=()):
00028         self.visited_folders.append( '/'.join(place) )
00029         for id, s_ob in src_folder.objectItems():
00030             d_ob = getattr(dst_folder, id, None)
00031             to_store = self.migrateObject(id, s_ob, d_ob, dst_folder,
00032                                           place + (id,))
00033             if to_store is not None:
00034                 owner = getattr(to_store, '_owner', None)
00035                 if hasattr(dst_folder, '_setObject'):
00036                     dst_folder._setObject(id, to_store)
00037                 else:
00038                     setattr(dst_folder, id, to_store)
00039                 if owner is not None:
00040                     # Retain ownership.
00041                     to_store._owner = owner
00042 
00043     def migrateDiscussionContainer(self, src_folder, dst_folder, place=()):
00044         self.visited_folders.append( '/'.join(place) )
00045         dst_container = getattr(dst_folder, '_container', None)
00046         if dst_container is None:
00047             dst_container = dst_folder._container = PersistentMapping()
00048         for id, s_ob in src_folder._container.items():
00049             d_ob = dst_container.get(id)
00050             to_store = self.migrateObject(id, s_ob, d_ob, dst_folder,
00051                                           place + (id,))
00052             if to_store is not None:
00053                 dst_container[id] = aq_base(to_store)
00054 
00055     def migratePossibleContainer(self, s_ob, d_ob, place):
00056         base_ob = aq_base(s_ob)
00057         if hasattr(base_ob, 'objectItems'):
00058             self.migrateObjectManager(s_ob, d_ob, place)
00059         elif hasattr(base_ob, '_container'):
00060             self.migrateDiscussionContainer(s_ob, d_ob, place)
00061 
00062     def migrateObject(self, id, s_ob, d_ob, dst_folder, place):
00063         # Doesn't store changes, only returns the
00064         # object to store.
00065         conversions = self.conversions
00066         klass = s_ob.__class__
00067         descend_ok = 1
00068         base_ob = aq_base(s_ob)
00069         to_store = None
00070         pathname = '/'.join(place)
00071         if self.skip.has_key(id):
00072             # Don't migrate objects by this name, but we can still
00073             # migrate subobjects.
00074             descend_ok = self.skip[id]
00075             if descend_ok and d_ob is None:
00076                 descend_ok = 0
00077             self.skipped.append(pathname +
00078                                (descend_ok and ' (descended)' or ' '))
00079         elif d_ob is not None:
00080             # The dest already has something with this ID.
00081             descend_ok = 1
00082             show_message = 1
00083             converter = conversions.get(klass, None)
00084             if converter is not None:
00085                 descend_ok = converter.allowDescendChildren()
00086                 show_message = converter.showDuplicationError()
00087             if show_message:
00088                 self.warnings.append('Already existed: %s' % pathname)
00089         elif conversions.has_key(klass):
00090             # Invoke the appropriate converter.
00091             converter = conversions[klass]
00092             to_store = converter.convert(s_ob)
00093             self.copied.append(pathname)
00094         elif hasattr(base_ob, '_getCopy'):
00095             # Make a direct copy.
00096             to_store = s_ob._getCopy(dst_folder)
00097             self.warnings.append('Copied %s directly.' % pathname)
00098             descend_ok = 0
00099         else:
00100             # No way to copy.
00101             descend_ok = 0
00102             self.warnings.append('Could not copy %s' % pathname)
00103         if descend_ok:
00104             if to_store is not None:
00105                 d_ob = to_store
00106             if d_ob is not None:
00107                 try: d_ob._p_jar = dst_folder._p_jar
00108                 except: pass
00109                 self.migratePossibleContainer(s_ob, d_ob, place)
00110         return to_store
00111 
00112 
00113 class SimpleClassConverter (Converter):
00114     def __init__(self, to_class, descend, show_dup=1):
00115         self._klass = to_class
00116         self._descend = descend
00117         self._show_dup = show_dup
00118 
00119     def allowDescendChildren(self):
00120         return self._descend
00121 
00122     def showDuplicationError(self):
00123         return self._show_dup
00124 
00125     def convert(self, ob):
00126         # Creates a copy of ob without its children.
00127         ob = aq_base(ob)
00128         k = self._klass
00129         if hasattr(k, '__basicnew__'):
00130             newob = k.__basicnew__()
00131         else:
00132             newob = new.instance(k, {})
00133         id = ob.id
00134         if callable(id):
00135             id = id()
00136         try: newob._setId(id)
00137         except AttributeError: newob.id = id
00138         newob.__dict__.update(ob.__dict__)
00139         if hasattr(newob, '_objects'):
00140             # Clear the children.
00141             for info in newob._objects:
00142                 del newob.__dict__[info['id']]
00143             newob._objects = ()
00144         if hasattr(newob, '_container'):
00145             # Clear the children.
00146             newob._container = PersistentMapping()
00147         return newob
00148 
00149 TupleType = type(())
00150 
00151 def setupDirectConversion(old_prod, new_prod, modname, classname,
00152                           conversions, descend=1, show_dup=1):
00153     try:
00154         old_module = sys.modules['Products.' + old_prod + '.' + modname]
00155         new_module = sys.modules['Products.' + new_prod + '.' + modname]
00156         old_class = getattr(old_module, classname)
00157         new_class = getattr(new_module, classname)
00158         conversions[old_class] = SimpleClassConverter(new_class, descend,
00159                                                       show_dup)
00160     except:
00161         print 'Failed to set up conversion', old_prod, new_prod, modname, classname
00162         import traceback
00163         traceback.print_exc()
00164 
00165 
00166 def setupDirectConversions(old_prod, new_prod, modnames, conversions):
00167     for info in modnames:
00168         if type(info) is TupleType:
00169             modname, classname = info
00170         else:
00171             modname = classname = info
00172         setupDirectConversion(old_prod, new_prod, modname, classname,
00173                               conversions)
00174 
00175 
00176 def _cleanupOwnership(ob, res, cleanup_children):
00177     '''
00178     If the user name of the owner of the referenced object
00179     is not found in its current user database but is found
00180     in the local user database, this function changes the
00181     ownership of the object to the local database.
00182     '''
00183     try: changed = ob._p_changed
00184     except: changed = 0
00185 
00186     owner = getattr(ob, '_owner', None)
00187     if owner:
00188         udb, uid = owner
00189         #res.append('Owner of %s is %s!%s' % (
00190         #    '/'.join( ob.getPhysicalPath() ), '/'.join(udb), uid,))
00191         root = ob.getPhysicalRoot()
00192         try:
00193             db = root.unrestrictedTraverse(udb, None)
00194             user = db.getUserById(uid)
00195             if hasattr(ob, 'aq_inContextOf'):
00196                 ucontext = aq_parent(aq_inner(db))
00197                 if not ob.aq_inContextOf(ucontext):
00198                     # Not in the right context.
00199                     user = None
00200         except:
00201             user = None
00202         if user is None:
00203             # Try to change to a local database.
00204             p = ob
00205             old_udb = udb
00206             udb = None
00207             while p is not None:
00208                 if hasattr(p, 'acl_users'):
00209                     acl_users = p.acl_users
00210                     try:
00211                         user = acl_users.getUserById(uid)
00212                     except:
00213                         user = None
00214                     if user is not None:
00215                         # Found the right database.
00216                         udb = acl_users.getPhysicalPath()[1:]
00217                         break
00218                 p = aq_parent(aq_inner(p))
00219             if udb is not None:
00220                 ob._owner = udb, uid
00221                 res.append('Changed ownership of %s from %s!%s to %s!%s' %
00222                            ('/'.join( ob.getPhysicalPath() ),
00223                             '/'.join(old_udb), uid,
00224                             '/'.join(udb), uid,))
00225             else:
00226                 res.append('Could not fix the ownership of %s, '
00227                            'which is set to %s!%s' %
00228                            ('/'.join( ob.getPhysicalPath() ),
00229                             '/'.join(old_udb), uid,))
00230 
00231     if cleanup_children:
00232         if hasattr(ob, 'objectValues'):
00233             for subob in ob.objectValues():
00234                 _cleanupOwnership(subob, res, 1)
00235 
00236     # Deactivate object if possible.
00237     if changed is None: ob._p_deactivate()
00238 
00239     return res
00240 
00241 def _copyUsers(src_folder, dst_folder):
00242     source = src_folder.acl_users
00243     target = dst_folder.acl_users
00244     for user in source.getUsers():
00245         target._addUser(name=user.name, password=user.__, confirm=user.__,
00246                         roles=user.roles, domains=user.domains, REQUEST=None)
00247 
00248 #
00249 # PTK to CMF Migration script.
00250 #
00251 
00252 
00253 def migrate(self, src_path='', dest_path='', copy_users=0, ownership_only=0):
00254     if not src_path or not dest_path:
00255         return '''
00256         <html><body><form action="%s" method="POST">
00257         <h2>Migrate PTK content to CMF site</h2>
00258         <p>Path (not including server URL) to PTK instance (source):
00259         <input type="text" name="src_path"></p>
00260         <p>Path (not including server URL) to CMF site (destination):
00261         <input type="text" name="dest_path"></p>
00262         <p>Copy users:
00263         <input type="checkbox" name="copy_users" value="1"></p>
00264         <input type="submit" name="submit" value="Migrate">
00265         <input type="submit" name="ownership_only"
00266         value="Just clean up ownership">
00267         </form></body></html>
00268         ''' % self.REQUEST['URL']
00269     root = self.getPhysicalRoot()
00270     dst_folder = root.restrictedTraverse(dest_path)
00271     if not ownership_only:
00272         src_folder = root.restrictedTraverse(src_path)
00273         if copy_users:
00274             _copyUsers(src_folder, dst_folder)
00275         m = Migrator(ptk2cmf_conversions, ptk2cmf_skip)
00276         m.migrateObjectManager(src_folder, dst_folder)
00277     ownership_res = []
00278     _cleanupOwnership(dst_folder, ownership_res, 1)
00279     return '''
00280         <html><body><p>Finished migration.</p>
00281         <p>Warnings (if any):<ul><li>%s</li></ul></p>
00282         <p>Visited folders:<ul><li>%s</li></ul></p>
00283         <p>Skipped:<ul><li>%s</li></ul></p>
00284         <p>Converted content:</p><pre>%s</pre>
00285         <p>Fixed up ownership:</p><pre>%s</pre>
00286         </body></html>
00287         ''' % ('</li>\n<li>'.join(m.warnings),
00288                '</li>\n<li>'.join(m.visited_folders),
00289                '</li>\n<li>'.join(m.skipped),
00290                '\n'.join(m.copied),
00291                '\n'.join(ownership_res),
00292                )
00293 
00294 migrate_ptk = migrate
00295 
00296 #
00297 # PTK to CMF Conversion definitions.
00298 #
00299 
00300 
00301 ptk2cmf_conversions = {}
00302 
00303 ptk2cmf_skip = {
00304     'portal_actions':0,
00305     'portal_catalog':0,
00306     'portal_discussion':0,
00307     'portal_memberdata':0,
00308     'portal_membership':0,
00309     'portal_properties':0,
00310     'portal_registration':0,
00311     'portal_skins':1,
00312     'portal_types':0,
00313     'portal_undo':0,
00314     'portal_url':0,
00315     'portal_workflow':0,
00316     'MailHost':0,
00317     'cookie_authentication':0,
00318     }
00319 
00320 demo_conversions = (
00321     'Document',
00322     'NewsItem',
00323     'Image',
00324     'File',
00325     'Link',
00326     'Favorite',
00327     'DiscussionItem',
00328     ('DiscussionItem', 'DiscussionItemContainer'),
00329     )
00330 
00331 
00332 BEFORE_CONTENT_MOVE = 0
00333 
00334 if BEFORE_CONTENT_MOVE:
00335     content_product = 'PTKBase'
00336 else:
00337     content_product = 'PTKDemo'
00338 
00339 
00340 setupDirectConversions(content_product, 'CMFDefault', demo_conversions,
00341                        ptk2cmf_conversions)
00342 
00343 setupDirectConversion('PTKBase', 'CMFCore', 'DirectoryView', 'DirectoryView',
00344                        ptk2cmf_conversions, 0, 0)
00345 
00346 setupDirectConversion('PTKBase', 'CMFCore', 'PortalFolder', 'PortalFolder',
00347                        ptk2cmf_conversions, 1)
00348 
00349 from OFS.Folder import Folder
00350 from Products.CMFCore.PortalFolder import PortalFolder
00351 ptk2cmf_conversions[Folder] = SimpleClassConverter(PortalFolder, 1)