from __future__ import nested_scopes
from ISilvaObject import ISilvaObject
from IContainer import IContainer
from IVersionedContent import IVersionedContent, ICatalogedVersionedContent
from IVersion import IVersion, ICatalogedVersion
from Membership import NoneMember, noneMember
from helpers import check_valid_id
def check_reserved_ids(obj):
"""Walk through the entire tree to find objects of which the id is not
allowed, and return a list of the urls of those objects
"""
illegal_urls = []
for o in obj.objectValues():
if IContainer.isImplementedBy(o):
illegal_urls += check_reserved_ids(o)
if (ISilvaObject.isImplementedBy(o) and
check_valid_id(obj, str(o.id), 1)):
print 'Illegal id found:', o.absolute_url()
id = o.id
while (check_valid_id(obj, id, 1) or
id in obj.objectIds()):
id = 'renamed_%s' % id
obj.manage_renameObject(o.id, id)
illegal_urls.append(o.absolute_url())
return illegal_urls
def from091to092(self, root):
"""Upgrade Silva content from 0.9.1 to 0.9.2
"""
# the manage_beforeDelete of the original Document doesn't work, since the
# code will look for a 'content' attribute, which doesn't yet exist on
# them, therefore we're going to have to do some trickery here...
# This is a nasty piece of monkeypatching, mainly because other functionality
# might require the original use of manage_beforeDelete somewhere, and
# it isn't possible to monkeypatch the object since no reference to the object
# can be used in monkeypatched object methods (self is not passed as a variable to
# those methods)
from Document import Document
old_manage_beforeDelete = Document.manage_beforeDelete
def manage_beforeDelete_old_style_docs(self, item, container):
Document.inheritedAttribute('manage_beforeDelete')(self, item, container)
Document.manage_beforeDelete = manage_beforeDelete_old_style_docs
print 'Going to check ids'
# first check the object tree for illegal ids, since they will break the upgrade
illegal_urls = check_reserved_ids(root)
print 'Going to upgrade'
# set the '_allow_authentication_requests' attribute on servce_members if it isn't there yet
sm = getattr(root, 'service_members', None)
if sm is not None:
if hasattr(sm, '_allow_subscription'):
sm._allow_authentication_requests = sm._allow_subscription
del sm._allow_subscription
elif not hasattr(sm, '_allow_authentication_requests'):
sm._allow_authentication_requests = 0
try:
upgrade_using_registry(root, '0.9.2')
finally:
Document.manage_beforeDelete = old_manage_beforeDelete
if illegal_urls:
return ('Upgrade succeeded, but the following objects have been renamed to '
'be able to continue:
') + '
\n'.join(illegal_urls)
else:
return ('Upgrade succeeded')
def from09to091(self, root):
"""Upgrade Silva from 0.9 to 0.9.1
"""
# upgrade member objects in the site if they're still using the old system
upgrade_memberobjects(root)
# upgrade xml in the site
upgrade_using_registry(root, '0.9.1')
def from086to09(self, root):
"""Upgrade Silva from 0.8.6(.1) to 0.9.
"""
id = root.id
# Put a copy of the current Silva Root in a backup folder.
backup_id = id + '_086'
self.manage_addFolder(backup_id)
cb = self.manage_copyObjects([id])
backup_folder = getattr(self, backup_id)
backup_folder.manage_pasteObjects(cb_copy_data=cb)
# Delete and re-install the DirectoryViews
from install import add_fss_directory_view
root.service_views.manage_delObjects(['Silva'])
add_fss_directory_view(root.service_views, 'Silva', __file__, 'views')
root.manage_delObjects([
'globals', 'service_utils', 'service_widgets'])
add_fss_directory_view(root, 'globals', __file__, 'globals')
add_fss_directory_view(root, 'service_utils', __file__, 'service_utils')
add_fss_directory_view(root, 'service_widgets', __file__, 'widgets')
def from085to086(self, root):
"""Upgrade Silva from 0.8.5 to 0.8.6 as a simple matter of programming.
"""
# rename silva root so we can drop in fresh new one
id = root.id
backup_id = id + '_085'
self.manage_renameObject(id, backup_id)
orig_root = getattr(self, backup_id)
# create new silva root
self.manage_addProduct['Silva'].manage_addRoot(id, orig_root.title)
dest_root = getattr(self, id)
# wipe out layout stuff from root as we're going to copy it over
delete_ids = [obj.getId() for obj in dest_root.objectValues() if
obj.meta_type in ['DTML Method', 'Script (Python)', 'Page Template']]
dest_root.manage_delObjects(delete_ids)
# now copy over silva contents from old root; everything should be a
# SilvaObject
copy_ids = [obj.getId() for obj in orig_root.objectValues() if
ISilvaObject.isImplementedBy(obj)]
cb = orig_root.manage_copyObjects(copy_ids)
dest_root.manage_pasteObjects(cb_copy_data=cb)
# also copy over layout stuff and various services
layout_ids = [obj.getId() for obj in orig_root.objectValues() if
obj.meta_type in ['DTML Method', 'Script (Python)', 'Page Template'] or \
obj.getId() in ('service_groups', 'service_files', 'service_mailhost', 'service_catalog') ]
other_ids = [obj.getId() for obj in orig_root.objectValues() if
obj.meta_type not in ['DTML Method', 'Script (Python)', 'Page Template', \
'Silva View Registry', 'XMLWidgets Editor Service', 'XMLWidgets Registry'] \
and obj.getId() not in ['globals', 'service_utils', 'service_setup', 'service_widgets', 'service_groups', 'service_files'] \
and not ISilvaObject.isImplementedBy(obj) ]
cb = orig_root.manage_copyObjects(layout_ids)
dest_root.manage_pasteObjects(cb_copy_data=cb)
# now to copy over properties
# figure out what changed
dest_properties = dest_root.propertyIds()
new_properties = []
changed_properties = []
for id in orig_root.propertyIds():
if id in dest_properties:
changed_properties.append(id)
else:
new_properties.append(id)
# alter properties that need to be altered
for id in changed_properties:
dest_root.manage_changeProperties({id: orig_root.getProperty(id)})
# add properties that need to be added
for id in new_properties:
dest_root.manage_addProperty(id, orig_root.getProperty(id),
orig_root.getPropertyType(id))
# now copy over the roles information
if hasattr(orig_root, '__ac_local_roles__'):
dest_root.__ac_local_roles__ = orig_root.__ac_local_roles__
if hasattr(orig_root, '__ac_local_groups__'):
dest_root.__ac_local_groups__ = orig_root.__ac_local_groups__
# if there's an 'acl_users', 'images', or 'locals' copy that over as well.
to_copy_ids = []
for id in ['acl_users', 'images', 'locals']:
if hasattr(orig_root.aq_base, id):
to_copy_ids.append(id)
if id in other_ids:
other_ids.remove(id)
cb = orig_root.manage_copyObjects(to_copy_ids)
dest_root.manage_pasteObjects(cb_copy_data=cb)
# copy over order information
dest_root._ordered_ids = orig_root._ordered_ids
# we still may not have everything, but a good part..
# should advise the upgrader to copy over the rest by hand
return other_ids
def upgrade_memberobjects(obj):
service_members = obj.service_members
for o in obj.aq_explicit.objectValues():
info = getattr(o, '_last_author_info', None)
if info is not None and type(info) == type({}):
if info.has_key('uid'):
o._last_author_info = service_members.get_cached_member(
info['uid'])
else:
o._last_author_info = noneMember
if IContainer.isImplementedBy(o):
upgrade_memberobjects(o)
def upgrade_list_titles_in_parsed_xml(top):
for child in top.childNodes:
if child.nodeName in ('list', 'nlist', 'dlist'):
for list_child in child.childNodes:
if list_child.nodeType == list_child.TEXT_NODE:
continue
if list_child.nodeName == 'title':
data = ''
for data_child in list_child.childNodes:
data += data_child.data
list_child.parentNode.removeChild(list_child)
if data.strip() == '':
break
heading = list_child.ownerDocument.createElement(
u'heading')
heading_text = list_child.ownerDocument.createTextNode(
data)
heading.appendChild(heading_text)
heading.setAttribute(u'type', u'subsub')
child.parentNode.insertBefore(heading, child)
break
elif list_child.nodeName != 'title':
break
if child.nodeName == 'nlist':
for list_child in child.childNodes:
if list_child.nodeType == list_child.TEXT_NODE:
continue
upgrade_list_titles_in_parsed_xml(list_child)
if child.nodeName == 'image':
if child.hasAttribute('image_path'):
path = child.getAttribute('image_path')
newpath = path
try:
image = top.restrictedTraverse(path.split('/'))
newpath = '/'.join(image.getPhysicalPath())
except:
if path[0] == '/':
try:
image = top.restrictedTraverse(path[1:].split('/'))
newpath = '/'.join(image.getPhysicalPath())
except:
newpath = path
child.removeAttribute('image_path')
if child.hasAttribute('image_id'):
child.removeAttribute('image_id')
child.setAttribute('path', newpath)
elif child.hasAttribute('image_id'):
id = child.getAttribute('image_id')
# XXX somehow, this way the image is not found...
image = getattr(top.get_container(), id, None)
newpath = id
if image:
newpath = unicode('/'.join(image.getPhysicalPath()))
child.removeAttribute('image_id')
child.setAttribute('path', newpath)
if child.nodeName == 'table':
for table_child in child.childNodes:
if table_child.nodeType == table_child.TEXT_NODE:
continue
if table_child.nodeName != 'row':
continue
for field in table_child.childNodes:
if field.nodeType == field.TEXT_NODE:
continue
upgrade_list_titles_in_parsed_xml(field)
#-----------------------------------------------------------------------------
# Upgrade registry, this will be used to upgrade versions >= 0.9.1
#-----------------------------------------------------------------------------
def upgrade_using_registry(obj, version):
"""Upgrades obj recursively for a specific Silva version
"""
for o in obj.objectValues():
mt = o.meta_type
if upgrade_registry.is_registered(mt, version):
for upgrade in upgrade_registry.get_meta_type(mt, version):
print 'Going to run %s on %s' % (upgrade, o.absolute_url())
res = upgrade(o)
# sometimes upgrade methods will replace objects, if so the
# new object should be returned so that can be used for the rest
# of the upgrade chain instead of the old (probably deleted) one
if res:
o = res
if hasattr(o, 'objectValues'):
#print 'Going to descend into', o.id
upgrade_using_registry(o, version)
class UpgradeRegistry:
"""Here people can register upgrade methods for their objects
"""
def __init__(self):
self.__registry = {}
def register(self, meta_type, upgrade_handler, version):
"""Register a meta_type for upgrade.
The upgrade handler is called with the object as its only argument
when the upgrade script encounters an object of the specified
meta_type.
"""
if not self.__registry.has_key(meta_type):
self.__registry[meta_type] = {}
if not self.__registry[meta_type].has_key(version):
self.__registry[meta_type][version] = []
self.__registry[meta_type][version].append(upgrade_handler)
def get_meta_type(self, meta_type, version):
"""Return the registered upgrade_handlers of meta_type
"""
#print 'Going to return upgrades for %s: %s' % (meta_type, version)
#print 'Upgrades:', self.__registry[meta_type][version]
return self.__registry[meta_type][version]
def is_registered(self, meta_type, version):
"""Returns whether the meta_type is registered"""
return self.__registry.has_key(meta_type) and self.__registry[meta_type].has_key(version)
upgrade_registry = UpgradeRegistry()
#-----------------------------------------------------------------------------
# Upgrade functions using the upgrade registry
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# 0.9 to 0.9.1
#-----------------------------------------------------------------------------
# Some upgrade stuff
def upgrade_document_091(obj):
for o in obj.objectValues():
if o.meta_type == 'Silva Document Version':
upgrade_list_titles_in_parsed_xml(o.content.documentElement)
def upgrade_demoobject_091(obj):
for o in obj.objectValues():
if o.meta_type == 'Silva DemoObject Version':
upgrade_list_titles_in_parsed_xml(o.content.documentElement)
upgrade_registry.register('Silva Document', upgrade_document_091, '0.9.1')
upgrade_registry.register('Silva DemoObject', upgrade_demoobject_091, '0.9.1')
#-----------------------------------------------------------------------------
# 0.9.1 to 0.9.2
#-----------------------------------------------------------------------------
# This is a complicated set of upgrades!! The order of the upgrades is important
# (they are called in the order in which they are registered), because most
# methods expect objects to be a certain type and the type can change somewhere
# in the chain of methods (old-style ParsedXML versions are converted to Version
# objects, which means that the way to perform certain actions on them changes)
from StringIO import StringIO
# small helper function to get the xml from an old-style version
def get_version_xml(obj, version):
v = getattr(obj, version, None)
if v is None:
raise Exception, 'No version %s!' % version
s = StringIO()
v.documentElement.writeStream(s)
return s.getvalue().encode('UTF8')
from Document import DocumentVersion
def convert_document_092(obj):
#print 'Converting document', obj.id
from random import randrange
from string import lowercase
from DateTime import DateTime
for version in ['unapproved', 'approved', 'public', 'last_closed']:
v = getattr(obj, 'get_%s_version' % version)()
if v is not None and not hasattr(getattr(obj, v).aq_base, 'documentElement'):
# bypass this document, as upgrade seems to be done already
return
def upgrade_doc_version(doc, version):
xml = get_version_xml(doc, version)
newver = DocumentVersion(version, doc._title)
newver.content.manage_edit(xml)
setattr(doc, version, newver)
doc._update_publication_status()
for version in ['unapproved', 'approved', 'public']:
v = getattr(obj, 'get_%s_version' % version)()
if v is not None:
upgrade_doc_version(obj, v)
if obj._previous_versions is not None:
# the last element of previous versions is last_closed,
# so that's done already
# Make sure the versions all exist, users sometimes tend
# to delete versions manually from the ZMI, since there's no
# way to do that from the SMI, which corrupts the previous versions
# list
new_previous_versions = []
for versionid, pdt, edt in obj._previous_versions:
if not hasattr(obj, versionid):
continue
upgrade_doc_version(obj, versionid)
new_previous_versions.append((versionid, pdt, edt))
obj._previous_versions = new_previous_versions
upgrade_registry.register('Silva Document', convert_document_092, '0.9.2')
# converting the data of the object to unicode will mostly (if not only)
# consist of converting metadata fields, so it is probably nice to do
# both in one go
# mapping from metadata set name to a mapping of old metadata field names to
# new (the second mapping can not be the other way round since there can be several
# old names for one new metadata field)
# the old names can refer to a Zope Property, an attribute or a method on the object
# note that this mapping is used to find out the fields that should be
# converted, so all to-be-converted fields should be mentioned
set_el_name_mapping = {
'silva-content': {},
'silva-extra': {
'document_comment': 'comment',
'container_comment': 'comment',
'subject': 'subject',
'contact_email': 'contactemail',
'contact_name': 'contactname',
}
}
# some helper function
def get_versions_or_self(obj):
#print 'Getting versions of', obj.id
ret = []
if (IVersionedContent.isImplementedBy(obj) or
ICatalogedVersionedContent.isImplementedBy(obj)):
versions = []
for version in ['_unapproved_version', '_approved_version',
'_public_version']:
v = getattr(obj, version)
if v[0] is not None:
versions.append(v)
#print version, ':', v
if obj._previous_versions is not None:
versions += obj._previous_versions
#print 'Previous versions:', obj._previous_versions
for version in versions:
#print 'Going to get version', version
#print getattr(obj, version[0])
ret.append(getattr(obj, version[0]))
else:
ret = [obj]
return ret
def getPropertyOrAttrValue(object, attr):
value = None
if object.hasProperty(attr):
value = object.getProperty(attr)
object.manage_delProperties(ids=[attr])
elif hasattr(object.aq_inner, attr):
value = getattr(object.aq_inner, attr)
# if it is not a string, skip it (e.g. when a folder we're
# upgrading has content with an id which is also a name in
# the old style metadata.
if type(value) != type(u'') or type(value) != type(''):
return None
#if callable(value):
# value = value()
delattr(object.aq_inner, attr)
return value
def unicode_and_metadata_092(obj):
# get the metadata sets for the object
ms = obj.service_metadata
values = {}
for set_name, el_name_mapping in set_el_name_mapping.items():
for old_name, new_name in el_name_mapping.items():
#print 'Testing for', old_name, 'on object', obj.absolute_url()
old_value = getPropertyOrAttrValue(obj, old_name)
if old_value is None:
#print 'Not found'
continue
#print 'Old value:', old_value
# if somehow the string is already unicode (who knows, right :)
# skip conversion
new_value = old_value
if type(old_value) != type(u''):
# we are assuming all data is cp1252 here, since that's what we've
# been using all along, but maybe some funky custom setups may want
# to interfere here...
# XXX do we really want to continue on errors here instead of an exception?
new_value = unicode(old_value, 'cp1252', 'replace')
values[new_name] = new_value
# now set it
# if an object is a VersionedContent, walk through all versions, else just
# set it to the current version
objects = get_versions_or_self(obj)
for object in objects:
binding = ms.getMetadata(object)
if binding is None:
#print 'Binding object:', object.meta_type
continue
if set_name in binding.getSetNames():
binding._setData(values, set_id=set_name, reindex=0)
#print 'Values', str(values).encode('ascii', 'replace'), 'set on', obj.absolute_url()
upgrade_registry.register('Silva Root', unicode_and_metadata_092, '0.9.2')
upgrade_registry.register('Silva Folder', unicode_and_metadata_092, '0.9.2')
upgrade_registry.register('Silva Publication', unicode_and_metadata_092, '0.9.2')
upgrade_registry.register('Silva Document', unicode_and_metadata_092, '0.9.2')
upgrade_registry.register('Silva DemoObject', unicode_and_metadata_092, '0.9.2')
# FIXME: upgrade metadata for Ghosts and Assets? The title?
#upgrade_registry.register('Silva Ghost', unicode_and_metadata_092, '0.9.2')
upgrade_registry.register('Silva Image', unicode_and_metadata_092, '0.9.2')
upgrade_registry.register('Silva File', unicode_and_metadata_092, '0.9.2')
upgrade_registry.register('Silva SQL Data Source', unicode_and_metadata_092, '0.9.2')
def set_cache_data_092(obj):
""" add the new cache data variable """
# XXX does not check if content is already upgraded
obj.cleanPublicRenderingCache()
upgrade_registry.register('Silva Document', set_cache_data_092, '0.9.2')
upgrade_registry.register('Silva Ghost', set_cache_data_092, '0.9.2')
upgrade_registry.register('Silva DemoObject', set_cache_data_092, '0.9.2')
def add_default_doc_092(obj):
if not hasattr(obj, '_title'):
# is new style folder, no title anymore
return
if obj.get_default() is None:
obj.manage_addProduct['Silva'].manage_addDocument('index', obj._title)
upgrade_registry.register('Silva Folder', add_default_doc_092, '0.9.2')
upgrade_registry.register('Silva Publication', add_default_doc_092, '0.9.2')
upgrade_registry.register('Silva Root', add_default_doc_092, '0.9.2')
def replace_container_title_092(obj):
"""Move the title to the metadata
Is a bit of a hairball situation, since the
title should be set on all the versions
of the default document, if one of those is
available. If not, the title should just be
removed
"""
#print 'Replace container'
if not '_title' in obj.aq_inner.__dict__.keys():
return
title = obj._title
del obj._title
if type(title) != type(u''):
title = unicode(title, 'cp1252', 'replace')
short_title = getPropertyOrAttrValue(obj, 'short_title')
default = obj.get_default()
if default is not None:
#print 'Setting title', title.encode('ascii', 'replace'), 'on default of', obj.id
# because this code is called *before* the folder is traversed,
# the versionedcontent objects are still old style here, so we don't
# have to go through all kinds of trouble to get all the versions
# for those kind of objects
default._title = title
else:
obj.manage_addProduct['Silva'].manage_addDocument('index', title)
default = obj.index
if short_title is not None:
# set it to the default for now, the replace_object_title method
# will pick it up then
default.short_title = short_title
def replace_object_title_092(obj):
"""Move the title to the metadata
"""
# get the metadata sets for the object
ms = obj.service_metadata
#print 'Replace object title for', obj.id
if not '_title' in obj.aq_base.__dict__.keys():
#print 'No title available on', obj.absolute_url()
return
#print obj.absolute_url()
title = obj.aq_inner._title
short_title = getPropertyOrAttrValue(obj, 'short_title')
if short_title is not None and type(short_title) != type(u''):
short_title = unicode(short_title, 'cp1252', 'replace')
#print 'Title:', title.encode('ascii', 'replace')
#print 'Plain title attribute:', obj.aq_inner.title
#print dir(obj.aq_inner)
#print obj.aq_base.__dict__
del obj.aq_inner._title
if type(title) != type(u''):
title = unicode(title, 'cp1252', 'replace')
objects = get_versions_or_self(obj)
for object in objects:
binding = ms.getMetadata(object)
if object.get_title():
continue
#print 'Going to replace title of version', object.getPhysicalPath()
#print 'New title:', title.encode('ascii', 'replace')
object.set_title(title)
#print type(title)
#print type(object.title)
if short_title is not None:
set_name = 'silva-content'
values = {'shorttitle': short_title}
if binding is None:
#print 'Binding object:', object.meta_type
continue
if set_name in binding.getSetNames():
binding._setData(values, set_id=set_name, reindex=0)
upgrade_registry.register('Silva Root', replace_container_title_092, '0.9.2')
upgrade_registry.register('Silva Publication', replace_container_title_092, '0.9.2')
upgrade_registry.register('Silva Folder', replace_container_title_092, '0.9.2')
upgrade_registry.register('Silva Document', replace_object_title_092, '0.9.2')
upgrade_registry.register('Silva DemoObject', replace_object_title_092, '0.9.2')
upgrade_registry.register('Silva File', replace_object_title_092, '0.9.2')
upgrade_registry.register('Silva Image', replace_object_title_092, '0.9.2')
upgrade_registry.register('Silva SQL Data Source', replace_object_title_092, '0.9.2')
upgrade_registry.register('Silva Indexer', replace_object_title_092, '0.9.2')
def catalog_092(obj):
obj.index_object()
def catalog_version_092(obj):
"""Do initial catalogin of objects"""
if not ICatalogedVersion.isImplementedBy(obj):
return
if obj.version_status() in ['last_closed', 'closed']:
return
obj.index_object()
upgrade_registry.register('Silva Root',
catalog_092, '0.9.2')
upgrade_registry.register('Silva Folder',
catalog_092, '0.9.2')
upgrade_registry.register('Silva Publication',
catalog_092, '0.9.2')
upgrade_registry.register('Silva Document Version',
catalog_version_092, '0.9.2')
upgrade_registry.register('Silva DemoObject Version',
catalog_version_092, '0.9.2')
upgrade_registry.register('Silva Ghost Version',
catalog_version_092, '0.9.2')
# helper methods for no-bullet list conversion
def get_text_from_node(node):
nodelist = []
for child in node.childNodes:
if child.nodeType == 3 or child.nodeType == 6:
nodelist.append(child)
return nodelist
def replace_list(node):
for child in node.childNodes:
if child.nodeName == u'list' and child.getAttribute('type') == u'none':
#print 'No bulleted lists'
p = child.createElement('p')
for sc in child.childNodes:
if sc.nodeName == 'li':
textnodes = get_text_from_node(sc)
for textnode in textnodes:
p.appendChild(textnode)
breaknode = child.createElement('br')
p.appendChild(breaknode)
node.replaceChild(p, child)
elif child.hasChildNodes():
replace_list(child)
def convert_no_bullet_lists_092(obj):
topnode = obj.content.documentElement
replace_list(topnode)
upgrade_registry.register('Silva Document Version', convert_no_bullet_lists_092, '0.9.2')
upgrade_registry.register('Silva DemoObject Version', convert_no_bullet_lists_092, '0.9.2')
def update_indexers_092(obj):
obj.update_index()
upgrade_registry.register('Silva Indexer', update_indexers_092, '0.9.2')