import logging from Products.CMFCore.ActionInformation import Action from Products.CMFCore.utils import getToolByName from Products.TinyMCE.interfaces.utility import ITinyMCE try: from plone.outputfilters.setuphandlers import install_mimetype_and_transforms except ImportError: from Products.TinyMCE.setuphandlers import install_mimetype_and_transforms try: from Products.ResourceRegistries.interfaces.settings import IResourceRegistriesSettings HAS_BUNDLES = True except ImportError: HAS_BUNDLES = False from plone.portlets.utils import unregisterPortletType from plone.app.controlpanel.filter import IFilterSchema from plone.registry.interfaces import IRegistry from zope.component import getUtility logger = logging.getLogger('Sitesetup tools:various') def registerDisplayViews(portal, views): """ Register additional display views for content types based on "views" dictionary containing list of additional view ids for each content type. @example views dictionary: DISPLAY_VIEWS = { 'Folder': [ 'short-listing', 'extended-listing' ], 'Document': [ 'article-view', 'fancy-document-view' ] } @call from setup handler: from tools.sitesetup import registerDisplayViews registerDisplayViews(portal, DISPLAY_VIEWS) """ portal_types = getToolByName(portal, 'portal_types') for ptype in views.keys(): for view_method in views[ptype]: type_info = portal_types.getTypeInfo(ptype) if view_method not in type_info.view_methods: type_info.view_methods = type_info.view_methods + (view_method,) logger.info('"%s" display view installed for %s.' % (view_method, ptype)) def unregisterDisplayViews(portal, views): """ Unregister additional display views for content types based on "views" dictionary containing list of additional view ids for each content type (the same as for registerDisplayViews method). """ portal_types = getToolByName(portal, 'portal_types') for ptype in views.keys(): updated_views = () type_info = portal_types.getTypeInfo(ptype) for view_method in type_info.view_methods: if view_method not in views[ptype]: updated_views += (view_method,) else: logger.info('Removing "%s" display view for %s.' % (view_method, ptype)) type_info.view_methods = updated_views def setupCatalog(portal, indexes={}, metadata=[]): """ Register portal catalog indexes and metadata columns. """ catalog = getToolByName(portal, 'portal_catalog') idxs = catalog.indexes() mtds = catalog.schema() for index in indexes.keys(): if index not in idxs: catalog.addIndex(index, indexes[index]) logger.info('Catalog index "%s" installed.' % index) for mt in metadata: if mt not in mtds: catalog.addColumn(mt) logger.info('Catalog metadata "%s" installed.' % mt) def hideActions(portal, actions): """ Hide actions given dict of action categories with values as list of action ids example actions: HIDE_ACTIONS = {'user':['dashboard'], 'object':['contentrules', 'local_roles']} """ atool = getToolByName(portal, 'portal_actions') logger.info('Configuring portal actions..'); for category in actions.keys(): if category in atool.objectIds(): for action in actions[category]: if action in atool[category].objectIds(): atool[category][action].visible = False logger.info('Action "%s" in category "%s" made hidden.' % (action, category)) else: logger.warning('Action "%s" in category "%s" not found!' % (action, category)) else: logger.warning('Category "%s" not found!' % category) def registerActions(portal, actions={}): """ Register new portal actions using dict of action attributes like in the following example: CUSTOM_ACTIONS = { '1': { # order in which will be action registered 'id': 'my-action', 'category': 'site_actions', 'title': 'My action', 'i18n_domain': 'myi18n.domain', 'url_expr': string:${globals_view/navigationRootUrl}/my-action-view', 'available_expr': 'python:member is not None' 'permissions': ('View',), 'visible': True } } """ atool = getToolByName(portal, 'portal_actions') action_keys = sorted(actions.keys()) for key in action_keys: info = actions[key] uid = info.get('id') category = atool.get(info.get('category', None)) if category is not None: category._setObject(uid, Action( uid, title = info.get('title'), i18n_domain = info.get('i18n_domain'), url_expr = info.get('url_expr'), available_expr = info.get('available_expr'), permissions = info.get('permissions'), visible = info.get('visible', False) ) ) logger.warning("Action \"%s\" successfully registered" % uid) else: logger.warning("Can't register action \"%s\" for nonexisting category \"%s\"" % (uid, info.get('category'))) def setupTinyMCE(portal, settings): """ Configures tinymce wysiwyg editor. Here is an example settings object: EDITOR_SETTINGS = { 'attributes': { 'contextmenu': False, 'link_using_uids': True, 'allow_captioned_images': True, '...': True }, 'commands': { 'install_transforms': True }, 'toolbar': { 'advhr':False, 'anchor':False, 'attribs':False, 'backcolor':False, 'bold':True, 'bullist':True, 'charmap':False, 'cleanup':False, 'code':True, 'copy':False, 'cut':False, 'definitionlist':False, 'emotions':False, 'external':False, 'forecolor':False, 'fullscreen':False, 'hr':False, 'iespell':False, 'image':True, 'indent':False, 'insertdate':False, 'inserttime':False, 'italic':True, 'justifycenter':False, 'justifyfull':False, 'justifyleft':False, 'justifyright':False, 'link':True, 'media':False, 'nonbreaking':False, 'numlist':True, 'outdent':False, 'pagebreak':False, 'paste':False, 'pastetext':False, 'pasteword':False, 'preview':False, 'print':False, 'redo':False, 'removeformat':False, 'replace':False, 'save':False, 'search':False, 'strikethrough':False, 'style':True, 'sub':False, 'sup':False, 'tablecontrols':True, 'underline':False, 'undo':False, 'unlink':True, 'visualaid':False, 'visualchars':False, 'width':u'440' }, 'styles': [ 'Subheading|h3', '...|..' ], 'tablestyles': [ 'Subdued grid|plain', '...|...' ], 'linkable': [ 'News Item', '...' ], 'containsanchors': [ 'Document', '...' ], 'containsobjects': [ 'Folder', '...' ], 'imageobjects': [ 'Image', '...' ], } """ tiny = getUtility(ITinyMCE) logger.info('Configuring TinyMCE..') for k,v in settings.get('attributes', {}).items(): setattr(tiny, k, v) logger.info('"%s" set to: %s' % (k, v)) commands = settings.get('commands', {}) if commands.get('install_transforms', False): logger.info('Running command: install_mimetype_and_transforms..') install_mimetype_and_transforms(portal) for k,v in settings.get('toolbar', {}).items(): setattr(tiny, 'toolbar_' + k, v) logger.info('Toolbar action "toolbar_%s" set to: %s' % (k, v)) for group in ['styles', 'tablestyles', 'linkable', 'containsanchors', 'containsobjects', 'imageobjects']: if settings.has_key(group): val = u'\n'.join(settings.get(group, [])) setattr(tiny, group, val) logger.info('"%s" set to: %s' % (group, val)) logger.info('TinyMCE configuration completed.') def setupCTAvailability(portal, settings): """ Use this method to allow/disable content types to be globally or locally addable. All non listed content types will be automatically disabled. Here is example settings object (NOTE: "DISABLE" key is used to disable content types adding globally): CONTENT_TYPES_AVAILABILITY = { 'DISABLE': [ 'Event', 'Link' ], 'Plone Site': [ 'Folder', 'Document' ] } """ portal_types = getToolByName(portal, 'portal_types') for portal_type,info in settings.items(): if portal_type == 'DISABLE': for v in info: type_info = portal_types.getTypeInfo(v) if type_info: type_info.global_allow = False logger.info('"%s" disabled globally.' % v) else: logger.info("\"%s\" content type isn't installed so can't disable it's availability." % v) else: type_info = portal_types.getTypeInfo(portal_type) setattr(type_info, 'filter_content_types', True) setattr(type_info, 'allowed_content_types', tuple(info)) logger.info('Allowed types for "%s" set to: %s' % (portal_type, tuple(info))) def setupHTMLFiltering(portal, settings): """ Update html filtering configlet settings, by passing dict of settings as in the following example for enabling embed html content in the richtext: HTML_FILTER = { 'remove': { 'nasty': ['embed', 'object'], 'stripped': ['object', 'param'], 'custom': [], 'style_whitelist': [], 'class_blacklist': [] }, 'add': { 'nasty': [], 'stripped': [], 'custom': ['embed'], 'style_whitelist': [], 'class_blacklist': [] } } NOTE: you can ommit empty lists """ adapter = IFilterSchema(portal) to_remove = settings.get('remove', {}) to_add = settings.get('add', {}) if to_remove: nasty = to_remove.get('nasty', []) stripped = to_remove.get('stripped', []) custom = to_remove.get('custom', []) style_whitelist = to_remove.get('style_whitelist', []) class_blacklist = to_remove.get('class_blacklist', []) current_nasty = adapter.nasty_tags for item in nasty: if item in current_nasty: current_nasty.remove(item) adapter.nasty_tags = current_nasty current_stripped = adapter.stripped_tags for item in stripped: if item in current_stripped: current_stripped.remove(item) adapter.stripped_tags = current_stripped current_custom = adapter.custom_tags for item in custom: if item in current_custom: current_custom.remove(item) adapter.custom_tags = current_custom current_style_whitelist = adapter.style_whitelist for item in style_whitelist: if item in current_style_whitelist: current_style_whitelist.remove(item) adapter.style_whitelist = current_style_whitelist current_class_blacklist = adapter.class_blacklist for item in class_blacklist: if item in current_class_blacklist: current_class_blacklist.remove(item) adapter.class_blacklist = current_class_blacklist if to_add: nasty = to_add.get('nasty', []) stripped = to_add.get('stripped', []) custom = to_add.get('custom', []) style_whitelist = to_add.get('style_whitelist', []) class_blacklist = to_add.get('class_blacklist', []) current_nasty = adapter.nasty_tags for item in nasty: if item not in current_nasty: current_nasty.append(item) adapter.nasty_tags = current_nasty current_stripped = adapter.stripped_tags for item in stripped: if item not in current_stripped: current_stripped.append(item) adapter.stripped_tags = current_stripped current_custom = adapter.custom_tags for item in custom: if item not in current_custom: current_custom.append(item) adapter.custom_tags = current_custom current_style_whitelist = adapter.style_whitelist for item in style_whitelist: if item not in current_style_whitelist: current_style_whitelist.append(item) adapter.style_whitelist = current_style_whitelist current_class_blacklist = adapter.class_blacklist for item in class_blacklist: if item not in current_class_blacklist: current_class_blacklist.append(item) adapter.class_blacklist = current_class_blacklist def registerTransform(portal, name, module): """ Usage: registerTransform(portal, 'web_intelligent_plain_text_to_html', 'Products.intelligenttext.transforms.web_intelligent_plain_text_to_html') """ transforms = getToolByName(portal, 'portal_transforms') if name not in transforms.objectIds(): transforms.manage_addTransform(name, module) logger.info("Registered transform: %s" % name) else: logger.info("Transform with name: %s already exists." % name) def unregisterTransform(portal, name): """ Usage: unregisterTransform(portal, 'web_intelligent_plain_text_to_html') """ transforms = getToolByName(portal, 'portal_transforms') try: if name in transforms.objectIds(): transforms.unregisterTransform(name) logger.info("Removed transform: %s" % name) else: logger.info("Transform with name: %s doesn't exist" % name) except AttributeError: logger.info("Could not remove transform: %s" % name) def setHomePage(portal, view_name): """ Set default view for the site root. """ portal.setLayout(view_name) logger.info('Homepage set to "%s"' % view_name) def setupNavigation(portal, settings): """ Use this method to exclude/include content types from navigation globally. Here is example settings object: NAVIGATION = { 'include': [ 'Folder', 'CustomFolder' ], 'exclude': [ 'Link', 'Document', 'Event' ] } """ portal_properties = getToolByName(portal, 'portal_properties') current = list(portal_properties.navtree_properties.metaTypesNotToList) for ct in settings.get('exclude', ()): if ct not in current: current.append(ct) logger.info("Content type: %s excluded from navigation globally" % ct) for ct in settings.get('include', ()): if ct in current: current.remove(ct) logger.info("Content type: %s included into navigation globally" % ct) portal_properties.navtree_properties.manage_changeProperties(metaTypesNotToList=tuple(current)) def hidePortletTypes(portal, portlets=[]): """ Hides portlets from the portlet management pages. @param: portlets - list of portlet idenifiers used in portlets.xml to register them example: portlets=['portlets.Calendar', 'portlets.Classic'] """ for portlet in portlets: unregisterPortletType(portal, portlet) logger.info("Unregistered portlet: %s" % portlet) def registerResourceBundle(portal, bundles={}): """ Register resource registry bundles for themes (skin layers). @param: bundles - dict of skin layers with list of bundles example: RESOURCE_BUNDLES = { 'MySkin': ['default', 'jquery', 'jquerytools'], 'OtherSkin': ['default'] } """ if HAS_BUNDLES: registry = getUtility(IRegistry) settings = registry.forInterface(IResourceRegistriesSettings) if settings: current = settings.resourceBundlesForThemes for skin in bundles.keys(): current[skin] = bundles[skin] logger.info('Resource bundles for "%s" skin successfully registered.' % skin) settings.resourceBundlesForThemes = current else: logger.info('Resource bundles for themes not found - nothing updated!') else: logger.info('Bundles not supported in your environment!') def unregisterResourceBundle(portal, layers=[]): """ Unregister custom resource registry bundles for themes (skin layers). @param: layers - list of layers for which will be custom bundles unregistered so the skin layer will use only 'default' bundle. example: BUNDLES_TO_REMOVE = ['MySkin', 'OtherSkin'] """ if HAS_BUNDLES: registry = getUtility(IRegistry) settings = registry.forInterface(IResourceRegistriesSettings) if settings: current = settings.resourceBundlesForThemes for skin in layers: if skin in current: del current[skin] logger.info('Resource bundles for "%s" skin successfully unregistered.' % skin) settings.resourceBundlesForThemes = current else: logger.info('Resource bundles for themes not found - nothing updated!') else: logger.info('Bundles not supported in your environment!')