from Acquisition import aq_base from AccessControl.Permissions import copy_or_move from zExceptions import BadRequest from Products.CMFCore.permissions import DeleteObjects, ModifyPortalContent, \ ManagePortal, View from Products.CMFCore.utils import getToolByName from Products.CMFDynamicViewFTI.migrate import migrateFTI from Products.CMFDynamicViewFTI.fti import DynamicViewTypeInformation from Products.CMFPlone.utils import _createObjectByType, base_hasattr from Products.CMFPlone.migrations.migration_util import installOrReinstallProduct, \ safeGetMemberDataTool from Products.CMFCore.Expression import Expression import transaction fti_meta_type = DynamicViewTypeInformation.meta_type def two05_alpha1(portal): """2.0.5 -> 2.1-alpha1 """ out = [] # Convert Plone Site FTI to CMFDynamicViewFTI this must be done before # any reindexing, we'll also do it later so that we don't force people to # migrate from alpha convertPloneFTIToCMFDynamicViewFTI(portal, out) # We do this earlier to avoid reindexing twice migrated = migrateCatalogIndexes(portal, out) reindex = tweakIndexes(portal, out) # Tweak Properties And CSS. This needs to happen earlier so that # ATCT migration doesn't fail tweakPropertiesAndCSS(portal, out) if not migrated and reindex: refreshSkinData(portal, out) # XXX We shouldn't do a full recatalog here, but only reindex those # indexes which we actually need for the ATCT migration. We do a full # recatalog at the end of the migrations anyways... reindexCatalog(portal, out) # Install SecureMailHost replaceMailHost(portal, out) # Remove legacy tools deleteTool(portal, out, 'portal_form') deleteTool(portal, out, 'portal_navigation') # Remove old properties deletePropertySheet(portal, out, 'form_properties') deletePropertySheet(portal, out, 'navigation_properties') # Install Archetypes installArchetypes(portal, out) # Install ATContentTypes installATContentTypes(portal, out) # Switch over to ATCT migrateToATCT10(portal, out) transaction.savepoint(optimistic=True) return out def tweakPropertiesAndCSS(portal, out): # Migrate ResourceRegistries if it's already installed migrateResourceRegistries(portal, out) # Install CSS and Javascript registries if they aren't installed already # also install default CSS and JS in the registry tools installCSSandJSRegistries(portal, out) # Add translation service tool to portal root addTranslationServiceTool(portal, out) # Add new memberdata properties addMemberdataHome_Page(portal, out) addMemberdataLocation(portal, out) addMemberdataDescription(portal, out) addMemberdataLanguage(portal, out) addMemberdataExtEditor(portal,out) # Update navtree_properties updateNavTreeProperties(portal, out) def tweakIndexes(portal, out): reindex = 0 # Switch path index to ExtendedPathIndex reindex += switchPathIndex(portal, out) # Add getObjPositionInParent index reindex += addGetObjPositionInParentIndex(portal, out) # Add getObjSize support to catalog reindex += addGetObjSizeMetadata(portal, out) # Add exclude_from_nav metadata reindex += addExclude_from_navMetadata(portal, out) # Install DateIndexes and DateRangeIndexes reindex += migrateDateIndexes(portal, out) reindex += migrateDateRangeIndexes(portal, out) # Add sortable_title index reindex += addSortable_TitleIndex(portal, out) # add is_folderish metadata reindex += addIs_FolderishMetadata(portal, out) # Get rid of CMF typo 'ExpiresDate' metadata in favor of proper DC # 'ExpirationDate' metadata reindex += switchToExpirationDateMetadata(portal, out) return reindex def alpha1_alpha2(portal): """2.1-alpha1 -> 2.1-alpha2 """ out = [] reindex = 0 # Add full_screen action addFullScreenAction(portal, out) addFullScreenActionIcon(portal, out) # Make visible_ids a site-wide property addVisibleIdsSiteProperty(portal, out) # No longer delete this, as we don't want to loose custom settings. # deleteVisibleIdsMemberProperty(portal, out) # Remove obsolete formtooltips property deleteFormToolTipsMemberProperty(portal, out) # Make a new property exposeDCMetaTags addExposeDCMetaTagsProperty(portal, out) # Add sitemap action addSitemapAction(portal, out) # Add types_not_searched site property addUnfriendlyTypesSiteProperty(portal, out) # Add non_default_page_types site property addNonDefaultPageTypesSiteProperty(portal, out) # Remove old portal_tabs actions removePortalTabsActions(portal, out) # Add news folder addNewsFolder(portal, out) # Add news topic addNewsTopic(portal, out) # Add events folder addEventsFolder(portal, out) # Add events topic addEventsTopic(portal, out) # Add objec cut/copy/paste/delete + batch buttons addEditContentActions(portal, out) # Add groups 'administrators' and 'reviewers' addDefaultGroups(portal, out) # Put default types in portal_factory addDefaultTypesToPortalFactory(portal, out) # Add non_default_page_types site property addDisableFolderSectionsSiteProperty(portal, out) # Add selectable_views to portal root addSiteRootViewTemplates(portal, out) # Fix the conditions and permissions on the folder_buttons actions fixFolderButtonsActions(portal, out) # Change the condition for the change_state action alterChangeStateActionCondition(portal, out) # Change condition for the external editor action so it checks for the # user preferences. alterExtEditorActionCondition(portal, out) # Add typesUseViewActionInListings site_property for types like Image and File, # which shouldn't use immediate_view from folder_contents and listings addTypesUseViewActionInListingsProperty(portal, out) # Change name of plone_setup action changePloneSetupActionToSiteSetup(portal,out) # Change plone site FTI icon changePloneSiteIcon(portal, out) # ADD NEW STUFF BEFORE THIS LINE AND LEAVE THE TRAILER ALONE! # Rebuild catalog if reindex: refreshSkinData(portal, out) migtool = getToolByName(portal, 'portal_migration') migtool._needRecatalog = True return out def addTranslationServiceTool(portal, out): """Adds the TranslationServiceTool to portal.""" addCMFPloneTool = portal.manage_addProduct['CMFPlone'].manage_addTool translationServiceTool = getToolByName(portal, 'translation_service', None) if translationServiceTool is None: addCMFPloneTool('Portal Translation Service Tool', None) out.append('Added TranslationService Tool') def replaceMailHost(portal, out): """Replaces the mailhost with a secure mail host.""" id = 'MailHost' smtp_port = 25 smtp_host = '' title = '' oldmh = getattr(aq_base(portal), id, None) if oldmh is not None: if oldmh.meta_type == 'Secure Mail Host': out.append('Secure Mail Host already installed') return title = oldmh.title smtp_host = oldmh.smtp_host smtp_port = oldmh.smtp_port portal.manage_delObjects([id]) out.append('Removed old MailHost') addMailhost = portal.manage_addProduct['SecureMailHost'].manage_addMailHost addMailhost(id, title=title, smtp_host=smtp_host, smtp_port=smtp_port) out.append('Added new MailHost (SecureMailHost): %s:%s' % (smtp_host, smtp_port)) def deleteTool(portal, out, tool_name): """Deletes a tool.""" if hasattr(aq_base(portal), tool_name): portal._delObject(tool_name) out.append('Deleted %s tool.' % tool_name) def deletePropertySheet(portal, out, sheet_name): """Deletes a property sheet from portal_properties.""" proptool = portal.portal_properties if hasattr(aq_base(proptool), sheet_name): proptool._delObject(sheet_name) out.append('Deleted %s property sheet.' % sheet_name) def installArchetypes(portal, out): """Quickinstalls Archetypes if not installed yet.""" for product_name in ('MimetypesRegistry', 'PortalTransforms', 'Archetypes'): installOrReinstallProduct(portal, product_name, out) def installATContentTypes(portal, out): """Quickinstalls ATContentTypes if not installed yet.""" for product_name in ('ATContentTypes',): installOrReinstallProduct(portal, product_name, out) def migrateToATCT(portal, out): """Switches portal to ATContentTypes. """ transaction.savepoint(optimistic=True) migrateFromCMFtoATCT = portal.migrateFromCMFtoATCT switchCMF2ATCT = portal.switchCMF2ATCT #out.append('Migrating and switching to ATContentTypes ...') result = migrateFromCMFtoATCT() out.append(result) try: switchCMF2ATCT(skip_rename=False) except IndexError: switchCMF2ATCT(skip_rename=True) transaction.savepoint(optimistic=True) #out.append('Switched portal to ATContentTypes.') def migrateToATCT10(portal, out): """Switches portal to ATCT 1.0 """ transaction.savepoint(optimistic=True) tool = portal.portal_atct tool.migrateToATCT() transaction.savepoint(optimistic=True) def addFullScreenAction(portal, out): """Adds the full screen mode action.""" actionsTool = getToolByName(portal, 'portal_actions', None) if actionsTool is not None: for action in actionsTool.listActions(): if action.getId() == 'full_screen': break # We already have the action else: actionsTool.addAction('full_screen', name='Toggle full screen mode', action='string:javascript:toggleFullScreenMode();', condition='member', permission=View, category='document_actions', visible=1, ) out.append("Added 'full_screen' action to actions tool.") def addFullScreenActionIcon(portal, out): """Adds an icon for the full screen mode action. """ iconsTool = getToolByName(portal, 'portal_actionicons', None) if iconsTool is not None: for icon in iconsTool.listActionIcons(): if icon.getActionId() == 'full_screen': break # We already have the icon else: iconsTool.addActionIcon( category='plone', action_id='full_screen', icon_expr='fullscreenexpand_icon.gif', title='Toggle full screen mode', ) out.append("Added 'full_screen' icon to actionicons tool.") def addVisibleIdsSiteProperty(portal, out): """Adds sitewide config for editable short names.""" propTool = getToolByName(portal, 'portal_properties', None) if propTool is not None: propSheet = getattr(aq_base(propTool), 'site_properties', None) if propSheet is not None: if not propSheet.hasProperty('visible_ids'): propSheet.manage_addProperty('visible_ids', 0, 'boolean') out.append("Added 'visible_ids' property to site_properties.") def deleteVisibleIdsMemberProperty(portal, out): """Deletes visible_ids memberdata property.""" memberdata = safeGetMemberDataTool(portal) if memberdata is not None: if memberdata.hasProperty('visible_ids'): memberdata.manage_delProperties(['visible_ids']) out.append("Deleted 'visible_ids' property from portal_memberdata.") def deleteFormToolTipsMemberProperty(portal, out): """Deletes formtooltips memberdata property.""" memberdata = safeGetMemberDataTool(portal) if memberdata is not None: if memberdata.hasProperty('formtooltips'): memberdata.manage_delProperties(['formtooltips']) out.append("Deleted 'formtooltips' property from portal_memberdata.") def addExposeDCMetaTagsProperty(portal, out): """Adds sitewide config for whether DC metatags are shown.""" propTool = getToolByName(portal, 'portal_properties', None) if propTool is not None: propSheet = getattr(aq_base(propTool), 'site_properties', None) if propSheet is not None: if not propSheet.hasProperty('exposeDCMetaTags'): propSheet.manage_addProperty('exposeDCMetaTags', 0, 'boolean') out.append("Added 'exposeDCMetaTags' property to site_properties.") def switchPathIndex(portal, out): """Changes the 'path' index to ExtendedPathIndex.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: try: index = catalog._catalog.getIndex('path') except KeyError: pass else: indextype = index.__class__.__name__ if indextype == 'ExtendedPathIndex': return 0 catalog.delIndex('path') out.append("Deleted %s 'path' from portal_catalog." % indextype) catalog.addIndex('path', 'ExtendedPathIndex') out.append("Added ExtendedPathIndex 'path' to portal_catalog.") return 1 # Ask for reindexing return 0 def addGetObjPositionInParentIndex(portal, out): """Adds the getObjPositionInParent FieldIndex.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: try: index = catalog._catalog.getIndex('getObjPositionInParent') except KeyError: pass else: indextype = index.__class__.__name__ if indextype == 'FieldIndex': return 0 catalog.delIndex('getObjPositionInParent') out.append("Deleted %s 'getObjPositionInParent' from portal_catalog." % indextype) catalog.addIndex('getObjPositionInParent', 'FieldIndex') out.append("Added FieldIndex 'getObjPositionInParent' to portal_catalog.") return 1 # Ask for reindexing return 0 def addGetObjSizeMetadata(portal, out): """Adds getObjSize column to the catalog.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: if 'getObjSize' in catalog.schema(): return 0 catalog.addColumn('getObjSize') out.append("Added 'getObjSize' metadata to portal_catalog.") return 1 # Ask for reindexing return 0 def updateNavTreeProperties(portal, out): """Updates navtree_properties for new NavTree.""" # Plone setup has changed because of the new NavTree implementation. # StatelessTreeNav had a createNavTreePropertySheet method which is now # in Portal.py (including the new properties). # If typesTolist is not there we're dealing with a real migration: propTool = getToolByName(portal, 'portal_properties', None) if propTool is not None: propSheet = getattr(aq_base(propTool), 'navtree_properties', None) if propSheet is not None: if not propSheet.hasProperty('typesToList'): propSheet._setProperty('typesToList', ['Folder', 'Large Plone Folder', 'Topic'], 'lines') if not propSheet.hasProperty('sortAttribute'): propSheet._setProperty('sortAttribute', 'getObjPositionInParent', 'string') if not propSheet.hasProperty('sortOrder'): propSheet._setProperty('sortOrder', 'asc', 'string') if not propSheet.hasProperty('sitemapDepth'): propSheet._setProperty('sitemapDepth', 3, 'int') if not propSheet.hasProperty('showAllParents'): propSheet._setProperty('showAllParents', 1, 'boolean') out.append('Updated navtree_properties.') def addSitemapAction(portal, out): """Adds the sitemap action.""" actionsTool = getToolByName(portal, 'portal_actions', None) if actionsTool is not None: for action in actionsTool.listActions(): if action.getId() == 'sitemap': break # We already have the action else: actionsTool.addAction('sitemap', name='Site Map', action='string:$portal_url/sitemap', condition='', permission=View, category='site_actions', visible=1, ) out.append("Added 'sitemap' action to actions tool.") def addDefaultGroups(portal, out): "Adds default groups Administrators and Reviewers.""" # See http://plone.org/collector/3522 groups = ( {'id': 'Administrators', 'title': 'Administrators', 'roles': ('Manager',), }, {'id': 'Reviewers', 'title': 'Reviewers', 'roles': ('Reviewer',), }, ) groupsTool = getToolByName(portal, 'portal_groups', None) acl_users = getToolByName(portal, 'acl_users', None) has_gruf_userfolder = hasattr(acl_users, 'getGroupByName') if groupsTool is not None and has_gruf_userfolder: # Don't create workspaces flag = groupsTool.groupWorkspacesCreationFlag groupsTool.groupWorkspacesCreationFlag = False for group in groups: # Group already exists: if groupsTool.getGroupById(group['id']): continue groupsTool.addGroup(group['id'], group['roles'], title = group['title']) out.append("Added default group '%s'." % group['id']) groupsTool.groupWorkspacesCreationFlag = flag def refreshSkinData(portal, out=None): """Refreshes skins to make new scripts available in the current transaction. """ if hasattr(aq_base(portal), 'clearCurrentSkin'): portal.clearCurrentSkin() else: # CMF 1.4 portal._v_skindata = None portal.setupCurrentSkin() def reindexCatalog(portal, out): """Rebuilds the portal_catalog.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: # Reduce threshold for the reindex run old_threshold = catalog.threshold pg_threshold = getattr(catalog, 'pgthreshold', 0) catalog.pgthreshold = 300 catalog.threshold = 2000 catalog.refreshCatalog(clear=1) catalog.threshold = old_threshold catalog.pgthreshold = pg_threshold out.append("Reindexed portal_catalog.") def installCSSandJSRegistries(portal, out): """Installs the CSS and JS registries.""" qi = getToolByName(portal, 'portal_quickinstaller', None) if qi is not None: if qi.isProductInstalled('CSSRegistry'): qi.uninstallProduct('CSSRegistry') if not qi.isProductInstalled('ResourceRegistries'): qi.installProduct('ResourceRegistries', locked=0) cssreg = getToolByName(portal, 'portal_css', None) if cssreg is not None: stylesheet_ids = cssreg.getResourceIds() new_styles = [ ('ploneRTL.css', {'expression': "python:object.isRightToLeft(domain='plone')"}), ('plonePresentation.css', {'media': 'presentation'}), ('plonePrint.css', {'media': 'print'}), ('ploneMobile.css', {'media': 'handheld'}), ('ploneGenerated.css', {'media': 'screen'}), ('ploneMember.css', {'expression': "not: portal/portal_membership/isAnonymousUser"}), ('ploneColumns.css', {'media': 'screen'}), ('ploneAuthoring.css', {'media': 'screen'}), ('plonePublic.css', {'media': 'screen'}), ('ploneBase.css', {'media': 'screen'}), ('ploneCustom.css', {}), ] removable_ids = [ 'plone.css', 'ploneTextSmall.css', 'ploneTextLarge.css', ] + [x[0] for x in new_styles] # also remove new styles for sid in removable_ids: if sid in stylesheet_ids: cssreg.unregisterResource(sid) # reverse the list, so the order is correct when we add a style and # move it to the top new_styles.reverse() for style_id, style_kwargs in new_styles: cssreg.registerStylesheet(style_id, **style_kwargs) cssreg.moveResourceToTop(style_id) # ploneCustom.css needs to be the last one cssreg.moveResourceToBottom('ploneCustom.css') jsreg = getToolByName(portal, 'portal_javascripts', None) if jsreg is not None: script_ids = jsreg.getResourceIds() new_scripts = [ ('register_function.js', {}), ('plone_javascript_variables.js', {}), ('nodeutilities.js', {}), ('cookie_functions.js', {}), ('livesearch.js', {}), ('fullscreenmode.js', {}), ('select_all.js', {}), ('plone_menu.js', {}), ('mark_special_links.js', {}), ('collapsiblesections.js', {}), ('highlightsearchterms.js', {}), ('first_input_focus.js', {}), ('folder_contents_filter.js', {}), ('folder_contents_hideAddItems.js', {}), ('styleswitcher.js', {}), ('table_sorter.js', {}), ('calendar_formfield.js', {}), ('calendarpopup.js', {}), ('ie5fixes.js', {}), ('formUnload.js', {}), ('sarissa.js', {}), ('plone_minwidth.js', {'enabled': False}), ('correctPREformatting.js', {'enabled': False}), ('vcXMLRPC.js', {'enabled': False}), ] removable_ids = [ 'plone_javascripts.js', ] + [x[0] for x in new_scripts] # also remove new scripts for sid in removable_ids: if sid in script_ids: jsreg.unregisterResource(sid) # reverse the list, so the order is correct when we add a script and # move it to the top new_scripts.reverse() for script_id, script_kwargs in new_scripts: jsreg.registerScript(script_id, **script_kwargs) jsreg.moveResourceToTop(script_id) out.append('Installed CSSRegistry and JSRegistry.') def addUnfriendlyTypesSiteProperty(portal, out): """Adds types_not_searched site property.""" # Types which will be installed as "unfriendly" and thus hidden for search # purposes BASE_TYPES_NOT_SEARCHED = ['ATBooleanCriterion', 'ATDateCriteria', 'ATDateRangeCriterion', 'ATListCriterion', 'ATPortalTypeCriterion', 'ATReferenceCriterion', 'ATSelectionCriterion', 'ATSimpleIntCriterion', 'ATSimpleStringCriterion', 'ATSortCriterion', 'Discussion Item', 'Plone Site', 'TempFolder'] propTool = getToolByName(portal, 'portal_properties', None) if propTool is not None: propSheet = getattr(propTool, 'site_properties', None) if propSheet is not None: if not propSheet.hasProperty('types_not_searched'): propSheet.manage_addProperty('types_not_searched', BASE_TYPES_NOT_SEARCHED, 'lines') out.append("Added 'types_not_searched' property to site_properties.") def addNonDefaultPageTypesSiteProperty(portal, out): """Adds non_default_page_types site property.""" # Types which are not selectable as a default_page BASE_NON_DEFAULT_PAGE_TYPES = ['Folder', 'Large Plone Folder', 'Image', 'File'] propTool = getToolByName(portal, 'portal_properties', None) if propTool is not None: propSheet = getattr(propTool, 'site_properties', None) if propSheet is not None: if not propSheet.hasProperty('non_default_page_types'): propSheet.manage_addProperty('non_default_page_types', BASE_NON_DEFAULT_PAGE_TYPES, 'lines') out.append("Added 'non_default_page_types' property to site_properties.") def removePortalTabsActions(portal, out): """Remove portal_tabs actions""" actionsTool = getToolByName(portal, 'portal_actions', None) if actionsTool is not None: new_actions = actionsTool._cloneActions() for action in new_actions: if action.getId() in ['Members','news']: action.visible = 0 actionsTool._actions = new_actions out.append("Disabled 'news' and 'Members' portal tabs actions.") def addNewsFolder(portal, out): """Add news folder to portal root""" if 'news' not in portal.objectIds(): _createObjectByType('Large Plone Folder', portal, id='news', title='News', description='Site News') out.append("Added news folder.") news = getattr(aq_base(portal), 'news') # Enable ConstrainTypes and set to News addable_types = ['News Item'] if getattr(news.aq_base, 'setConstrainTypesMode', None) is not None: news.setConstrainTypesMode(1) news.setImmediatelyAddableTypes(addable_types) news.setLocallyAllowedTypes(addable_types) out.append("Set constrain types for news folder.") # Add news_listing.pt as default page # property manager hasProperty can give odd results ask forgiveness instead try: news.manage_addProperty('default_page', ['news_topic','news_listing','index_html'], 'lines') except BadRequest: pass out.append("Added default view for news folder.") def addEventsFolder(portal, out): """Add events folder to portal root""" if 'events' not in portal.objectIds(): _createObjectByType('Large Plone Folder', portal, id='events', title='Events', description='Site Events') out.append("Added events folder.") events = getattr(aq_base(portal), 'events') # Enable ConstrainTypes and set to Event addable_types = ['Event'] if getattr(events.aq_base, 'setConstrainTypesMode', None) is not None: events.setConstrainTypesMode(1) events.setImmediatelyAddableTypes(addable_types) events.setLocallyAllowedTypes(addable_types) out.append("Set constrain types for events folder.") # Add events_listing.pt as default page # property manager hasProperty can give odd results ask forgiveness instead try: events.manage_addProperty('default_page', ['events_topic','events_listing','index_html'], 'lines') except BadRequest: pass out.append("Added default view for events folder.") def addExclude_from_navMetadata(portal, out): """Adds the exclude_from_nav metadata.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: if 'exclude_from_nav' in catalog.schema(): return 0 catalog.addColumn('exclude_from_nav') out.append("Added 'exclude_from_nav' metadata to portal_catalog.") return 1 # Ask for reindexing return 0 def addEditContentActions(portal, out): """Add edit actions in content menu and move contents action to batch menu. """ CATEGORY = 'object_buttons' ACTIONS = ( {'id' : 'delete', 'name' : 'Delete', 'action' : 'string:${object_url}/object_delete', 'condition' : 'python:portal.portal_membership.checkPermission("Delete objects", object.aq_inner.aq_parent) and object is not portal.portal_url.getPortalObject()', 'permission': DeleteObjects, }, {'id' : 'paste', 'name' : 'Paste', 'action' : 'string:${object_url}/object_paste', 'condition' : 'folder/cb_dataValid', 'permission': View, }, {'id' : 'copy', 'name' : 'Copy', 'action' : 'string:${object_url}/object_copy', 'condition' : 'python:object is not portal.portal_url.getPortalObject()', 'permission': copy_or_move, }, {'id' : 'cut', 'name' : 'Cut', 'action' : 'string:${object_url}/object_cut', 'condition' : 'python:portal.portal_membership.checkPermission("Delete objects", object.aq_inner.aq_parent) and object is not portal.portal_url.getPortalObject()', 'permission': copy_or_move, }, {'id' : 'batch', 'name' : 'Contents', 'action' : "python:((object.isDefaultPageInFolder() and object.getParentNode().absolute_url()) or folder_url)+'/folder_contents'", 'condition' : 'python:folder.displayContentsTab()', 'permission': View, 'category' : 'batch', }, ) actionsTool = getToolByName(portal, 'portal_actions', None) if actionsTool is not None: # first we don't need old 'Contents' action anymore new_actions = actionsTool._cloneActions() for action in new_actions: if action.getId() == 'folderContents': action.visible = False actionsTool._actions = new_actions # then we add new actions for newaction in ACTIONS: category = newaction.get('category', CATEGORY) try: actionsTool.unrestrictedTraverse('/'.join([category, newaction['id']])) except (KeyError, AttributeError): # This is a new action actionsTool.addAction(newaction['id'], name=newaction['name'], action=newaction['action'], condition=newaction['condition'], permission=newaction['permission'], category=newaction.get('category', CATEGORY), visible=1) out.append("Added '%s' contentmenu action to actions tool." % newaction['name']) def indexMembersFolder(portal, out): """Makes sure the Members folder is cataloged.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: membershipTool = getToolByName(portal, 'portal_membership', None) if membershipTool is not None: members = membershipTool.getMembersFolder() if members is not None: members.indexObject() out.append('Recataloged Members folder.') def indexNewsFolder(portal, out): """Makes sure the News folder is cataloged.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: if hasattr(aq_base(portal), 'news'): portal.news.indexObject() out.append('Recataloged news folder.') def indexEventsFolder(portal, out): """Makes sure the Events folder is cataloged.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: if hasattr(aq_base(portal), 'events'): portal.events.indexObject() out.append('Recataloged events folder.') class Record: def __init__(self, **kw): self.__dict__.update(kw) def migrateDateIndexes(portal, out): """Converts FieldIndexes to DateIndexes.""" DATEINDEXES = ( 'created', 'modified', 'Date', 'start', 'end', 'effective', 'expires', ) changed = 0 catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: for indexname in DATEINDEXES: try: index = catalog._catalog.getIndex(indexname) except KeyError: pass else: indextype = index.__class__.__name__ if indextype == 'DateIndex': continue catalog.delIndex(indexname) out.append("Deleted %s '%s'." % (indextype, indexname)) catalog.addIndex(indexname, 'DateIndex') out.append("Added DateIndex '%s'." % indexname) changed = 1 # Ask for reindexing return changed def migrateDateRangeIndexes(portal, out): """Creates new DateRangeIndexes.""" DATERANGEINDEXES = ( ('effectiveRange', 'effective', 'expires'), #('eventRange', 'start', 'end'), ) changed = 0 catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: for indexname, since, until in DATERANGEINDEXES: try: index = catalog._catalog.getIndex(indexname) except KeyError: pass else: indextype = index.__class__.__name__ if indextype == 'DateRangeIndex': continue catalog.delIndex(indexname) out.append("Deleted %s '%s'." % (indextype, indexname)) extra = Record(since_field=since, until_field=until) catalog.addIndex(indexname, 'DateRangeIndex', extra) out.append("Added DateRangeIndex '%s' (%s, %s)." % (indexname, since, until)) changed = 1 # Ask for reindexing return changed def addSortable_TitleIndex(portal, out): """Adds the sortable_title FieldIndex.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: try: index = catalog._catalog.getIndex('sortable_title') except KeyError: pass else: indextype = index.__class__.__name__ if indextype == 'FieldIndex': return 0 catalog.delIndex('sortable_title') out.append("Deleted %s 'sortable_title' from portal_catalog." % indextype) catalog.addIndex('sortable_title', 'FieldIndex') out.append("Added FieldIndex 'sortable_title' to portal_catalog.") return 1 # Ask for reindexing return 0 def addIs_FolderishMetadata(portal, out): """Adds the is_folderish metadata.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: if 'is_folderish' in catalog.schema(): return 0 catalog.addColumn('is_folderish') out.append("Added 'is_folderish' metadata to portal_catalog.") return 1 # Ask for reindexing return 0 def addDefaultTypesToPortalFactory(portal, out): """Put the default content types in portal_factory""" factory = getToolByName(portal, 'portal_factory', None) if factory is not None: types = factory.getFactoryTypes().keys() for metaType in ('Document', 'Event', 'File', 'Folder', 'Image', 'Large Plone Folder', 'Link', 'News Item', 'Topic'): if metaType not in types: types.append(metaType) factory.manage_setPortalFactoryTypes(listOfTypeIds = types) out.append('Added default content types to portal_factory.') def addNewsTopic(portal, out): news = portal.news if 'news_topic' not in news.objectIds() and getattr(portal,'portal_atct', None) is not None: _createObjectByType('Topic', news, id='news_topic', title='News', description='Site News') topic = news.news_topic type_crit = topic.addCriterion('Type','ATPortalTypeCriterion') type_crit.setValue('News Item') sort_crit = topic.addCriterion('created','ATSortCriterion') out.append('Added Topic for default news folder view.') else: out.append('Topic default news folder view already in place or ATCT is not installed.') def addEventsTopic(portal, out): events = portal.events if 'events_topic' not in events.objectIds() and getattr(portal,'portal_atct', None) is not None: _createObjectByType('Topic', events, id='events_topic', title='Events', description='Site Events') topic = events.events_topic type_crit = topic.addCriterion('Type','ATPortalTypeCriterion') type_crit.setValue('Event') sort_crit = topic.addCriterion('start','ATSortCriterion') out.append('Added Topic for default events folder view.') else: out.append('Topic default events folder view already in place or ATCT is not installed.') def addDisableFolderSectionsSiteProperty(portal, out): """Adds disable_folder_sections site property.""" # Boolean to disable using toplevel folders as tabs # users who turn this on may want to readd the Members and # News actions. propTool = getToolByName(portal, 'portal_properties', None) if propTool is not None: propSheet = getattr(propTool, 'site_properties', None) if propSheet is not None: if not propSheet.hasProperty('disable_folder_sections'): propSheet.manage_addProperty('disable_folder_sections', False, 'boolean') out.append("Added 'disable_folder_sections' property to site_properties.") def addSiteRootViewTemplates(portal, out): """Add default view templates to site root""" if not portal.hasProperty('selectable_views'): portal.manage_addProperty('selectable_views', ['folder_listing', 'news_listing'], 'lines') out.append("Added 'selectable_views' property to portal root") def addMemberdataHome_Page(portal, out): memberdata_tool = safeGetMemberDataTool(portal) if memberdata_tool is not None: if not memberdata_tool.hasProperty('home_page'): memberdata_tool.manage_addProperty('home_page', '', 'string') out.append("Added 'home_page' property to portal_memberdata.") def addMemberdataLocation(portal, out): memberdata_tool = safeGetMemberDataTool(portal) if memberdata_tool is not None: if not memberdata_tool.hasProperty('location'): memberdata_tool.manage_addProperty('location', '', 'string') out.append("Added 'location' property to portal_memberdata.") def addMemberdataDescription(portal, out): memberdata_tool = safeGetMemberDataTool(portal) if memberdata_tool is not None: if not memberdata_tool.hasProperty('description'): memberdata_tool.manage_addProperty('description', '', 'text') out.append("Added 'description' property to portal_memberdata.") def addMemberdataLanguage(portal, out): memberdata_tool = safeGetMemberDataTool(portal) if memberdata_tool is not None: if not memberdata_tool.hasProperty('language'): memberdata_tool.manage_addProperty('language', '', 'string') out.append("Added 'language' property to portal_memberdata.") def addMemberdataExtEditor(portal, out): memberdata_tool = safeGetMemberDataTool(portal) if memberdata_tool is not None: if not memberdata_tool.hasProperty('ext_editor'): memberdata_tool.manage_addProperty('ext_editor', '1', 'boolean') out.append("Added 'ext_editor' property to portal_memberdata.") def alterChangeStateActionCondition(portal, out): """ Change the change_state action so that it checks either Modify portal content or Review portal content. """ newaction = {'id' : 'change_state', 'name' : 'Change State', 'action' : 'string:content_status_history:method', 'condition' : 'python:portal.portal_membership.checkPermission("Modify portal content", object) or portal.portal_membership.checkPermission("Review portal content", object)', 'permission': View, 'category': 'folder_buttons', } exists = False actionsTool = getToolByName(portal, 'portal_actions', None) if actionsTool is not None: new_actions = actionsTool._cloneActions() for action in new_actions: if action.getId() == 'change_state' and action.category == newaction['category']: exists = True if not action.condition: action.permissions = (newaction['permission'],) action.condition = Expression(text=newaction['condition']) or '' out.append('Modified existing change_state action') if exists: actionsTool._actions = new_actions else: actionsTool.addAction(newaction['id'], name=newaction['name'], action=newaction['action'], condition=newaction['condition'], permission=newaction['permission'], category=newaction['category'], visible=1) out.append("Added missing change_state action") def alterExtEditorActionCondition(portal, out): """ Change the extedit action so that it checks the user preferences for external editing. """ newaction = {'id' : 'extedit', 'name' : 'Change State', 'action' : 'string:$object_url/external_edit', 'condition' : 'python: member and hasattr(member, "ext_editor") and member.ext_editor and object.absolute_url() != portal_url', 'permission': ModifyPortalContent, 'category': 'document_actions', } exists = False actionsTool = getToolByName(portal, 'portal_actions', None) if actionsTool is not None: new_actions = actionsTool._cloneActions() for action in new_actions: if action.getId() == 'extedit' and action.category == newaction['category']: exists = True action.condition = Expression(text=newaction['condition']) or '' out.append('Modified existing extedit action') if exists: actionsTool._actions = new_actions else: actionsTool.addAction(newaction['id'], name=newaction['name'], action=newaction['action'], condition=newaction['condition'], permission=newaction['permission'], category=newaction['category'], visible=1) out.append("Added missing extedit action") def fixFolderButtonsActions(portal, out): """ Change the copy and cut actions so that they check either more appropriate permissions. """ CATEGORY = 'folder_buttons' ACTIONS = ( {'id' : 'copy', 'name' : 'Copy', 'action' : 'string:folder_copy:method', 'condition' : '', 'permission': copy_or_move, 'category' : 'folder_buttons', }, {'id' : 'cut', 'name' : 'Cut', 'action' : 'string:folder_cut:method', 'condition': 'python:portal.portal_membership.checkPermission("Delete objects", object)', 'permission': copy_or_move, 'category' : 'folder_buttons', }, ) actionsTool = getToolByName(portal, 'portal_actions', None) if actionsTool is not None: for newaction in ACTIONS: category = newaction.get('category', CATEGORY) action = actionsTool.getActionObject('/'.join([category, newaction['id']])) if action is None: # Action doesn't exist: add it actionsTool.addAction(newaction['id'], name=newaction['name'], action=newaction['action'], condition=newaction['condition'], permission=newaction['permission'], category=newaction['category'], visible=1) out.append("Added missing %s action"%newaction['id']) else: if action.permissions != (copy_or_move,): action.permissions = (newaction['permission'],) action.condition = Expression(text=newaction['condition']) or '' out.append('Modified existing %s action'%newaction['id']) def addTypesUseViewActionInListingsProperty(portal, out): # Adds a typesUseViewActionInListings list property to site_properties # which is used to determine which types should use not immediate_view # in folder listings (and batch mode). This is important for types like # Image and File. propTool = getToolByName(portal, 'portal_properties', None) if propTool is not None: propSheet = getattr(propTool, 'site_properties', None) if propSheet is not None: if not propSheet.hasProperty('typesUseViewActionInListings'): propSheet.manage_addProperty('typesUseViewActionInListings', ['Image','File'], 'lines') out.append("Added 'typesUseViewActionInListings' property to site_properties.") def switchToExpirationDateMetadata(portal, out): """Remove ExpiresDate and add ExpirationDate columns the catalog.""" catalog = getToolByName(portal, 'portal_catalog', None) if catalog is not None: schema = catalog.schema() if 'ExpiresDate' in schema: catalog.delColumn('ExpiresDate') out.append("Removed 'ExpiresDate' metadata from portal_catalog.") if 'ExpirationDate' in schema: return 0 catalog.addColumn('ExpirationDate') out.append("Added 'ExpirationDate' metadata to portal_catalog.") return 1 # Ask for reindexing return 0 def changePloneSetupActionToSiteSetup(portal, out): """ Change the plone_setup action so that its title is Site Setup. """ newaction = {'id': 'plone_setup', 'name': 'Site Setup', 'action': 'string:${portal_url}/plone_control_panel', 'condition': '', # condition 'permission': ManagePortal, 'category': 'user', 'visible': 1} exists = False actionsTool = getToolByName(portal, 'portal_actions', None) if actionsTool is not None: new_actions = actionsTool._cloneActions() for action in new_actions: if action.getId() == newaction['id'] and action.category == newaction['category']: exists = True action.title = newaction['name'] out.append('Modified existing plone_setup action') if exists: actionsTool._actions = new_actions else: actionsTool.addAction(newaction['id'], name=newaction['name'], action=newaction['action'], condition=newaction['condition'], permission=newaction['permission'], category=newaction['category'], visible=1) out.append("Added missing plone_setup action") def changePloneSiteIcon(portal, out): """Change the icon for plone site from folder_icon to site_icon""" typesTool = getToolByName(portal, 'portal_types', None) if typesTool is not None: plone_FTI = getattr(typesTool, 'Plone Site', None) if plone_FTI is not None and plone_FTI.content_icon == 'folder_icon.gif': plone_FTI.content_icon = 'site_icon.gif' out.append("Changed Plone Site icon") def migrateCatalogIndexes(portal, out): """Migrate catalog indexes if running under Zope 2.8""" from Products.ZCatalog.ZCatalog import ZCatalog migrated = False if not hasattr(ZCatalog, 'manage_convertIndexes'): return migrated FLAG = '_migrated_280' for obj in portal.objectValues(): if not isinstance(obj, ZCatalog): continue if getattr(aq_base(obj), FLAG, False): continue out.append("Running manage_convertIndexes on " "ZCatalog instance '%s'" % obj.getId()) p_threshold = getattr(obj, 'pgthreshold', 0) obj.pgthreshold = 300 obj.manage_convertIndexes() obj.pgthreshold = p_threshold # migrate the catalog length by calling len(catalog) len(obj) out.append("Finished migrating catalog indexes " "for ZCatalog instance '%s'" % obj.getId()) migrated = True return migrated def convertPloneFTIToCMFDynamicViewFTI(portal, out): FTI='Plone Site' views = ('folder_listing','news_listing') ttool = getToolByName(portal, 'portal_types', None) if ttool is not None: fti = getattr(ttool, 'Plone Site', None) if fti is not None: if fti.meta_type != fti_meta_type: # Get full type name name = [t[0] for t in ttool.listDefaultTypeInformation() if t[1].get('id','')=='Plone Root'] if name: name = name[0] else: name = None migrateFTI(portal, 'Plone Site', name, fti_meta_type) out.append("Converted Plone Site to CMFDynamicViewFTI") # limi says don't bother transferring old views # Transfer old default page old_default = getattr(portal, '_selected_default_page', ()) if old_default is not (): del portal._selected_default_page portal.setDefaultPage(old_default) out.append("Transferred portal default page") # add sensible default methods new_fti = portal.getTypeInfo() if 'folder_listing' not in portal.getAvailableLayouts(): new_fti.manage_changeProperties(view_methods=views, default_view=views and views[0]) out.append("Updated portal selectable views") def migrateResourceRegistries(portal, out): """Migrate ResourceRegistries ResourceRegistries got refactored to use one base class, that needs a migration. """ out.append("Migrating CSSRegistry.") cssreg = getToolByName(portal, 'portal_css', None) if cssreg is not None: if base_hasattr(cssreg, 'stylesheets'): stylesheets = list(cssreg.stylesheets) stylesheets.reverse() # the order was reversed cssreg.resources = tuple(stylesheets) del cssreg.stylesheets if base_hasattr(cssreg, 'cookedstylesheets'): cssreg.cookedresources = cssreg.cookedstylesheets del cssreg.cookedstylesheets if base_hasattr(cssreg, 'concatenatedstylesheets'): cssreg.concatenatedresources = cssreg.concatenatedstylesheets del cssreg.concatenatedstylesheets cssreg.cookResources() out.append("Done migrating CSSRegistry.") else: out.append("No CSSRegistry found.") out.append("Migrating JSRegistry.") jsreg = getToolByName(portal, 'portal_javascripts', None) if jsreg is not None: if base_hasattr(jsreg, 'scripts'): jsreg.resources = jsreg.scripts del jsreg.scripts if base_hasattr(jsreg, 'cookedscripts'): jsreg.cookedresources = jsreg.cookedscripts del jsreg.cookedscripts if base_hasattr(jsreg, 'concatenatedscripts'): jsreg.concatenatedresources = jsreg.concatenatedscripts del jsreg.concatenatedscripts jsreg.cookResources() out.append("Done migrating JSRegistry.") else: out.append("No JSRegistry found.")